Bug 1579235 - Part 7 - Fix UV rect calculation for external textures. r=Bert
authorGlenn Watson <gw@intuitionlibrary.com>
Sun, 01 Mar 2020 03:56:02 +0000
changeset 516286 8f267ed8f2e3d09ced8c665af0a6db3694197116
parent 516285 6814870342efc284b2edec7c9aeb45240cb0fe09
child 516287 85f45093d17263d329e170335a62240058ac31aa
push id37172
push usermalexandru@mozilla.com
push dateMon, 02 Mar 2020 09:48:18 +0000
treeherdermozilla-central@51efc4b931f7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersBert
bugs1579235
milestone75.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1579235 - Part 7 - Fix UV rect calculation for external textures. r=Bert This patch fixes an oversight in part 5 of this patch series that could result in an incorrect UV rect being used for an external texture that uses a custom UV rect. When the texture is an external texture, the UV rect is not known when the external surface descriptor is created, because external textures are not resolved until the lock() callback is invoked at the start of the frame render. To handle this, query the texture resolver for the UV rect if it's an external texture, otherwise use the default UV rect. Differential Revision: https://phabricator.services.mozilla.com/D64687
gfx/wr/webrender/res/composite.glsl
gfx/wr/webrender/src/composite.rs
gfx/wr/webrender/src/device/gl.rs
gfx/wr/webrender/src/gpu_types.rs
gfx/wr/webrender/src/renderer.rs
gfx/wr/webrender_api/src/units.rs
--- a/gfx/wr/webrender/res/composite.glsl
+++ b/gfx/wr/webrender/res/composite.glsl
@@ -51,35 +51,35 @@ void main(void) {
     float yuv_coefficient = aParams.w;
 
     vYuvColorMatrix = get_yuv_color_matrix(yuv_color_space);
     vYuvCoefficient = yuv_coefficient;
     vYuvFormat = yuv_format;
 
     write_uv_rect(
         aUvRect0.xy,
-        aUvRect0.xy + aUvRect0.zw,
+        aUvRect0.zw,
         aTextureLayers.x,
         uv,
         TEX_SIZE(sColor0),
         vUV_y,
         vUVBounds_y
     );
     write_uv_rect(
         aUvRect1.xy,
-        aUvRect1.xy + aUvRect1.zw,
+        aUvRect1.zw,
         aTextureLayers.y,
         uv,
         TEX_SIZE(sColor1),
         vUV_u,
         vUVBounds_u
     );
     write_uv_rect(
         aUvRect2.xy,
-        aUvRect2.xy + aUvRect2.zw,
+        aUvRect2.zw,
         aTextureLayers.z,
         uv,
         TEX_SIZE(sColor2),
         vUV_v,
         vUVBounds_v
     );
 #else
     vUv = uv;
--- a/gfx/wr/webrender/src/composite.rs
+++ b/gfx/wr/webrender/src/composite.rs
@@ -1,15 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use api::{ColorF, ImageKey, YuvColorSpace, YuvFormat, ImageRendering};
 use api::units::{DeviceRect, DeviceIntSize, DeviceIntRect, DeviceIntPoint, WorldRect};
-use api::units::{DevicePixelScale, DevicePoint, PictureRect};
+use api::units::{DevicePixelScale, DevicePoint, PictureRect, TexelRect};
 use crate::batch::{resolve_image, get_buffer_kind};
 use crate::gpu_cache::GpuCache;
 use crate::gpu_types::{ZBufferId, ZBufferIdGenerator};
 use crate::internal_types::TextureSource;
 use crate::picture::{ResolvedSurfaceTexture, TileCacheInstance, TileSurface};
 use crate::prim_store::DeferredResolve;
 use crate::renderer::ImageBufferKind;
 use crate::resource_cache::{ImageRequest, ResourceCache};
@@ -111,25 +111,25 @@ pub struct ExternalSurfaceDescriptor {
 }
 
 /// Information about a plane in a YUV surface.
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub struct YuvPlaneDescriptor {
     pub texture: TextureSource,
     pub texture_layer: i32,
-    pub uv_rect: DeviceRect,
+    pub uv_rect: TexelRect,
 }
 
 impl YuvPlaneDescriptor {
     fn invalid() -> Self {
         YuvPlaneDescriptor {
             texture: TextureSource::Invalid,
             texture_layer: 0,
-            uv_rect: DeviceRect::zero(),
+            uv_rect: TexelRect::invalid(),
         }
     }
 }
 
 /// An ExternalSurfaceDescriptor that has had image keys
 /// resolved to texture handles. This contains all the
 /// information that the compositor step in renderer
 /// needs to know.
@@ -486,17 +486,17 @@ impl CompositeState {
                 );
 
                 if cache_item.texture_id != TextureSource::Invalid {
                     valid_plane_count += 1;
 
                     *plane = YuvPlaneDescriptor {
                         texture: cache_item.texture_id,
                         texture_layer: cache_item.texture_layer,
-                        uv_rect: cache_item.uv_rect.to_f32(),
+                        uv_rect: cache_item.uv_rect.into(),
                     };
                 }
             }
 
             // Check if there are valid images added for each YUV plane
             if valid_plane_count < required_plane_count {
                 warn!("Warnings: skip a YUV compositor surface, found {}/{} valid images",
                     valid_plane_count,
--- a/gfx/wr/webrender/src/device/gl.rs
+++ b/gfx/wr/webrender/src/device/gl.rs
@@ -481,31 +481,42 @@ impl<T> Drop for VBO<T> {
 }
 
 #[cfg_attr(feature = "replay", derive(Clone))]
 #[derive(Debug)]
 pub struct ExternalTexture {
     id: gl::GLuint,
     target: gl::GLuint,
     swizzle: Swizzle,
+    uv_rect: TexelRect,
 }
 
 impl ExternalTexture {
-    pub fn new(id: u32, target: TextureTarget, swizzle: Swizzle) -> Self {
+    pub fn new(
+        id: u32,
+        target: TextureTarget,
+        swizzle: Swizzle,
+        uv_rect: TexelRect,
+    ) -> Self {
         ExternalTexture {
             id,
             target: get_gl_target(target),
             swizzle,
+            uv_rect,
         }
     }
 
     #[cfg(feature = "replay")]
     pub fn internal_id(&self) -> gl::GLuint {
         self.id
     }
+
+    pub fn get_uv_rect(&self) -> TexelRect {
+        self.uv_rect
+    }
 }
 
 bitflags! {
     #[derive(Default)]
     pub struct TextureFlags: u32 {
         /// This texture corresponds to one of the shared texture caches.
         const IS_SHARED_TEXTURE_CACHE = 1 << 0;
     }
@@ -613,16 +624,23 @@ impl Texture {
     }
 
     #[cfg(feature = "replay")]
     pub fn into_external(mut self) -> ExternalTexture {
         let ext = ExternalTexture {
             id: self.id,
             target: self.target,
             swizzle: Swizzle::default(),
+            // TODO(gw): Support custom UV rect for external textures during captures
+            uv_rect: TexelRect::new(
+                0.0,
+                0.0,
+                self.size.width as f32,
+                self.size.height as f32,
+            ),
         };
         self.id = 0; // don't complain, moved out
         ext
     }
 }
 
 impl Drop for Texture {
     fn drop(&mut self) {
--- a/gfx/wr/webrender/src/gpu_types.rs
+++ b/gfx/wr/webrender/src/gpu_types.rs
@@ -250,17 +250,17 @@ pub struct CompositeInstance {
 
     // Packed into a single vec4 (aParams)
     z_id: f32,
     yuv_color_space: f32,       // YuvColorSpace
     yuv_format: f32,            // YuvFormat
     yuv_rescale: f32,
 
     // UV rectangles (pixel space) for color / yuv texture planes
-    uv_rects: [DeviceRect; 3],
+    uv_rects: [TexelRect; 3],
 
     // Texture array layers for color / yuv texture planes
     texture_layers: [f32; 3],
 }
 
 impl CompositeInstance {
     pub fn new(
         rect: DeviceRect,
@@ -273,29 +273,29 @@ impl CompositeInstance {
             rect,
             clip_rect,
             color,
             z_id: z_id.0 as f32,
             yuv_color_space: 0.0,
             yuv_format: 0.0,
             yuv_rescale: 0.0,
             texture_layers: [layer, 0.0, 0.0],
-            uv_rects: [DeviceRect::zero(); 3],
+            uv_rects: [TexelRect::invalid(); 3],
         }
     }
 
     pub fn new_yuv(
         rect: DeviceRect,
         clip_rect: DeviceRect,
         z_id: ZBufferId,
         yuv_color_space: YuvColorSpace,
         yuv_format: YuvFormat,
         yuv_rescale: f32,
         texture_layers: [f32; 3],
-        uv_rects: [DeviceRect; 3],
+        uv_rects: [TexelRect; 3],
     ) -> Self {
         CompositeInstance {
             rect,
             clip_rect,
             color: PremultipliedColorF::WHITE,
             z_id: z_id.0 as f32,
             yuv_color_space: pack_as_float(yuv_color_space as u32),
             yuv_format: pack_as_float(yuv_format as u32),
--- a/gfx/wr/webrender/src/renderer.rs
+++ b/gfx/wr/webrender/src/renderer.rs
@@ -1248,16 +1248,36 @@ impl TextureResolver {
                 Some((&self.texture_cache_map[&index], swizzle))
             }
             TextureSource::RenderTaskCache(saved_index, swizzle) => {
                 Some((&self.saved_targets[saved_index.0], swizzle))
             }
         }
     }
 
+    // Retrieve the deferred / resolved UV rect if an external texture, otherwise
+    // return the default supplied UV rect.
+    fn get_uv_rect(
+        &self,
+        source: &TextureSource,
+        default_value: TexelRect,
+    ) -> TexelRect {
+        match source {
+            TextureSource::External(ref external_image) => {
+                let texture = self.external_images
+                    .get(&(external_image.id, external_image.channel_index))
+                    .expect("BUG: External image should be resolved by now");
+                texture.get_uv_rect()
+            }
+            _ => {
+                default_value
+            }
+        }
+    }
+
     fn report_memory(&self) -> MemoryReport {
         let mut report = MemoryReport::default();
 
         // We're reporting GPU memory rather than heap-allocations, so we don't
         // use size_of_op.
         for t in self.texture_cache_map.values() {
             report.texture_cache_textures += t.size_in_bytes();
         }
@@ -4368,34 +4388,41 @@ impl Renderer {
                     VertexArrayKind::Composite,
                     &current_textures,
                     stats,
                 );
                 instances.clear();
             }
             current_textures = textures;
 
+            // When the texture is an external texture, the UV rect is not known when
+            // the external surface descriptor is created, because external textures
+            // are not resolved until the lock() callback is invoked at the start of
+            // the frame render. To handle this, query the texture resolver for the
+            // UV rect if it's an external texture, otherwise use the default UV rect.
+            let uv_rects = [
+                self.texture_resolver.get_uv_rect(&textures.colors[0], surface.yuv_planes[0].uv_rect),
+                self.texture_resolver.get_uv_rect(&textures.colors[1], surface.yuv_planes[1].uv_rect),
+                self.texture_resolver.get_uv_rect(&textures.colors[2], surface.yuv_planes[2].uv_rect),
+            ];
+
             // Create the instance and add to current batch
             let instance = CompositeInstance::new_yuv(
                 surface.device_rect,
                 surface.clip_rect,
                 surface.z_id,
                 surface.yuv_color_space,
                 surface.yuv_format,
                 surface.yuv_rescale,
                 [
                     surface.yuv_planes[0].texture_layer as f32,
                     surface.yuv_planes[1].texture_layer as f32,
                     surface.yuv_planes[2].texture_layer as f32,
                 ],
-                [
-                    surface.yuv_planes[0].uv_rect,
-                    surface.yuv_planes[1].uv_rect,
-                    surface.yuv_planes[2].uv_rect,
-                ],
+                uv_rects,
             );
             instances.push(instance);
         }
 
         // Flush the last batch
         if !instances.is_empty() {
             self.draw_instanced_batch(
                 &instances,
@@ -5144,27 +5171,37 @@ impl Renderer {
             };
 
             // In order to produce the handle, the external image handler may call into
             // the GL context and change some states.
             self.device.reset_state();
 
             let texture = match image.source {
                 ExternalImageSource::NativeTexture(texture_id) => {
-                    ExternalTexture::new(texture_id, texture_target, Swizzle::default())
+                    ExternalTexture::new(
+                        texture_id,
+                        texture_target,
+                        Swizzle::default(),
+                        image.uv,
+                    )
                 }
                 ExternalImageSource::Invalid => {
                     warn!("Invalid ext-image");
                     debug!(
                         "For ext_id:{:?}, channel:{}.",
                         ext_image.id,
                         ext_image.channel_index
                     );
                     // Just use 0 as the gl handle for this failed case.
-                    ExternalTexture::new(0, texture_target, Swizzle::default())
+                    ExternalTexture::new(
+                        0,
+                        texture_target,
+                        Swizzle::default(),
+                        image.uv,
+                    )
                 }
                 ExternalImageSource::RawData(_) => {
                     panic!("Raw external data is not expected for deferred resolves!");
                 }
             };
 
             self.texture_resolver
                 .external_images
--- a/gfx/wr/webrender_api/src/units.rs
+++ b/gfx/wr/webrender_api/src/units.rs
@@ -165,16 +165,25 @@ impl TexelRect {
     pub fn invalid() -> Self {
         TexelRect {
             uv0: DevicePoint::new(-1.0, -1.0),
             uv1: DevicePoint::new(-1.0, -1.0),
         }
     }
 }
 
+impl Into<TexelRect> for DeviceIntRect {
+    fn into(self) -> TexelRect {
+        TexelRect {
+            uv0: self.min().to_f32(),
+            uv1: self.max().to_f32(),
+        }
+    }
+}
+
 const MAX_AU_FLOAT: f32 = 1.0e6;
 
 pub trait AuHelpers<T> {
     fn from_au(data: T) -> Self;
     fn to_au(&self) -> T;
 }
 
 impl AuHelpers<LayoutSizeAu> for LayoutSize {