Bug 1560853 - Fix picture cache tiles with fractional origins. r=kvark
authorGlenn Watson <github@intuitionlibrary.com>
Tue, 25 Jun 2019 20:28:16 +0000
changeset 542916 c396b50af035b2205a3bc32190e105c97705c497
parent 542915 021c05e47e6f4932a63fc1eeabc3c965fae6e882
child 542917 e927a7bfb85e6ef07fc84c34b5e967fa1924e426
push id2131
push userffxbld-merge
push dateMon, 26 Aug 2019 18:30:20 +0000
treeherdermozilla-release@b19ffb3ca153 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskvark
bugs1560853
milestone69.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 1560853 - Fix picture cache tiles with fractional origins. r=kvark Gecko layouts typically produce a picture cache where the origin of the picture rect is an integer. However, it can occasionally be a fractional origin. In these cases, we need to ensure the content origin is floored, to maintain consistent snapping. When this case occurs, the UV rect for the tile also needs adjusting, to ensure the exact 1:1 texel:pixel mapping when drawing the tile. Differential Revision: https://phabricator.services.mozilla.com/D35761
gfx/wr/webrender/res/brush_image.glsl
gfx/wr/webrender/src/batch.rs
gfx/wr/webrender/src/picture.rs
gfx/wr/webrender/src/texture_cache.rs
--- a/gfx/wr/webrender/res/brush_image.glsl
+++ b/gfx/wr/webrender/res/brush_image.glsl
@@ -117,39 +117,39 @@ void brush_vs(
 
     vUvSampleBounds = vec4(
         min_uv + vec2(0.5),
         max_uv - vec2(0.5)
     ) / texture_size.xyxy;
 
     vec2 f = (vi.local_pos - local_rect.p0) / local_rect.size;
 
-#ifdef WR_FEATURE_ALPHA_PASS
-    int color_mode = prim_user_data.x & 0xffff;
-    int blend_mode = prim_user_data.x >> 16;
     int raster_space = prim_user_data.y;
-
-    if (color_mode == COLOR_MODE_FROM_PASS) {
-        color_mode = uMode;
-    }
-
     // Derive the texture coordinates for this image, based on
     // whether the source image is a local-space or screen-space
     // image.
     switch (raster_space) {
         case RASTER_SCREEN: {
             // Since the screen space UVs specify an arbitrary quad, do
             // a bilinear interpolation to get the correct UV for this
             // local position.
             f = get_image_quad_uv(segment_user_data, f);
             break;
         }
         default:
             break;
     }
+
+#ifdef WR_FEATURE_ALPHA_PASS
+    int color_mode = prim_user_data.x & 0xffff;
+    int blend_mode = prim_user_data.x >> 16;
+
+    if (color_mode == COLOR_MODE_FROM_PASS) {
+        color_mode = uMode;
+    }
 #endif
 
     // Offset and scale vUv here to avoid doing it in the fragment shader.
     vec2 repeat = local_rect.size / stretch_size;
     vUv = mix(uv0, uv1, f) - min_uv;
     vUv /= texture_size;
     vUv *= repeat.xy;
     if (perspective_interpolate == 0.0) {
--- a/gfx/wr/webrender/src/batch.rs
+++ b/gfx/wr/webrender/src/batch.rs
@@ -1199,17 +1199,17 @@ impl BatchBuilder {
                                         .get_address(&cache_item.uv_rect_handle)
                                         .as_int();
                                     let textures = BatchTextures::color(cache_item.texture_id);
                                     let batch_params = BrushBatchParameters::shared(
                                         BrushBatchKind::Image(ImageBufferKind::Texture2DArray),
                                         textures,
                                         [
                                             ShaderColorMode::Image as i32 | ((AlphaType::PremultipliedAlpha as i32) << 16),
-                                            RasterizationSpace::Local as i32,
+                                            RasterizationSpace::Screen as i32,
                                             get_shader_opacity(1.0),
                                             0,
                                         ],
                                         uv_rect_address,
                                     );
 
                                     let prim_header_index = prim_headers.push(
                                         &prim_header,
--- a/gfx/wr/webrender/src/picture.rs
+++ b/gfx/wr/webrender/src/picture.rs
@@ -1229,19 +1229,39 @@ impl TileCacheInstance {
                 }
 
                 // Ensure that this texture is allocated.
                 if !resource_cache.texture_cache.is_allocated(&tile.handle) {
                     let tile_size = DeviceIntSize::new(
                         TILE_SIZE_WIDTH,
                         TILE_SIZE_HEIGHT,
                     );
+
+                    let content_origin_f = tile.world_rect.origin * frame_context.global_device_pixel_scale;
+                    let content_origin_i = content_origin_f.floor();
+
+                    // Calculate the UV coords for this tile. These are generally 0-1, but if the
+                    // local rect of the cache has fractional coordinates, then the content origin
+                    // of the tile is floor'ed, and so we need to adjust the UV rect in order to
+                    // ensure a correct 1:1 texel:pixel mapping and correct snapping.
+                    let s0 = (content_origin_f.x - content_origin_i.x) / tile.world_rect.size.width;
+                    let t0 = (content_origin_f.y - content_origin_i.y) / tile.world_rect.size.height;
+                    let s1 = 1.0;
+                    let t1 = 1.0;
+
+                    let uv_rect_kind = UvRectKind::Quad {
+                        top_left: DeviceHomogeneousVector::new(s0, t0, 0.0, 1.0),
+                        top_right: DeviceHomogeneousVector::new(s1, t0, 0.0, 1.0),
+                        bottom_left: DeviceHomogeneousVector::new(s0, t1, 0.0, 1.0),
+                        bottom_right: DeviceHomogeneousVector::new(s1, t1, 0.0, 1.0),
+                    };
                     resource_cache.texture_cache.update_picture_cache(
                         tile_size,
                         &mut tile.handle,
+                        uv_rect_kind,
                         gpu_cache,
                     );
                 }
 
                 tile.visibility_mask = PrimitiveVisibilityMask::empty();
 
                 // If we run out of dirty regions, then force the last dirty region to
                 // be a union of any remaining regions. This is an inefficiency, in that
@@ -2335,19 +2355,20 @@ impl PicturePrimitive {
 
                         for key in &tile_cache.tiles_to_draw {
                             let tile = tile_cache.tiles.get_mut(key).expect("bug: no tile found!");
 
                             if tile.is_valid {
                                 continue;
                             }
 
-                            // TODO(gw): Is this ever fractional? Can that cause seams?
-                            let content_origin = (tile.world_rect.origin * device_pixel_scale)
-                                .round().to_i32();
+                            // The content origin for surfaces is always an integer value (this preserves
+                            // the same snapping on a surface as would occur if drawn directly to parent).
+                            let content_origin_f = tile.world_rect.origin * device_pixel_scale;
+                            let content_origin = content_origin_f.floor().to_i32();
 
                             let cache_item = frame_state.resource_cache.texture_cache.get(&tile.handle);
 
                             let task = RenderTask::new_picture(
                                 RenderTaskLocation::PictureCache {
                                     texture: cache_item.texture_id,
                                     layer: cache_item.texture_layer,
                                     size: tile_size.to_i32(),
--- a/gfx/wr/webrender/src/texture_cache.rs
+++ b/gfx/wr/webrender/src/texture_cache.rs
@@ -1303,16 +1303,17 @@ impl TextureCache {
         self.upsert_entry(new_cache_entry, handle)
     }
 
     // Update the data stored by a given texture cache handle for picture caching specifically.
     pub fn update_picture_cache(
         &mut self,
         tile_size: DeviceIntSize,
         handle: &mut TextureCacheHandle,
+        uv_rect_kind: UvRectKind,
         gpu_cache: &mut GpuCache,
     ) {
         debug_assert!(self.now.is_valid());
         debug_assert!(tile_size.width > 0 && tile_size.height > 0);
 
         if self.entries.get_opt(handle).is_none() {
             let cache_entry = {
                 let texture_index = self.picture_textures
@@ -1334,20 +1335,21 @@ impl TextureCache {
                     layer_index,
                     self.now,
                 )
             };
             self.upsert_entry(cache_entry, handle)
         }
 
         // Upload the resource rect and texture array layer.
-        self.entries
+        let entry = self.entries
             .get_opt_mut(handle)
-            .expect("BUG: handle must be valid now")
-            .update_gpu_cache(gpu_cache);
+            .expect("BUG: handle must be valid now");
+        entry.uv_rect_kind = uv_rect_kind;
+        entry.update_gpu_cache(gpu_cache);
     }
 }
 
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 #[derive(Copy, Clone, PartialEq)]
 struct SlabSize {
     width: i32,