tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit d385716b1faebe6b5a3187a5eec2a4b0e263f014
parent bd246bc20b5d2cb76e75464d310247acc9d0abc1
Author: Nicolas Silva <nical@fastmail.com>
Date:   Tue,  9 Dec 2025 08:19:04 +0000

Bug 1996818 - Avoid re-generating uv rect addresses for images multiple times. r=gw

This is important because with external images we are sometimes producing normalized UVs, and replacing them with the non-normalized UVs in the common path then leads to incorrect behavior.

Differential Revision: https://phabricator.services.mozilla.com/D275049

Diffstat:
Mgfx/wr/webrender/src/render_task.rs | 37++-----------------------------------
Mgfx/wr/webrender/src/render_task_graph.rs | 36+++++++++++++++++++++++++-----------
Mgfx/wr/webrender/src/texture_cache.rs | 37+++++++++++++++++++++++++++----------
3 files changed, 54 insertions(+), 56 deletions(-)

diff --git a/gfx/wr/webrender/src/render_task.rs b/gfx/wr/webrender/src/render_task.rs @@ -15,7 +15,7 @@ use crate::profiler::{add_text_marker}; use crate::spatial_tree::SpatialNodeIndex; use crate::filterdata::SFilterData; use crate::frame_builder::FrameBuilderConfig; -use crate::gpu_types::{BorderInstance, ImageSource, UvRectKind, TransformPaletteId, BlurEdgeMode}; +use crate::gpu_types::{BorderInstance, UvRectKind, TransformPaletteId, BlurEdgeMode}; use crate::internal_types::{CacheTextureId, FastHashMap, FilterGraphNode, FilterGraphOp, FilterGraphPictureReference, SVGFE_CONVOLVE_VALUES_LIMIT, TextureSource, Swizzle}; use crate::picture::{ResolvedSurfaceTexture, MAX_SURFACE_SIZE}; use crate::prim_store::ClipData; @@ -1040,7 +1040,7 @@ pub struct RenderTask { /// manages the handle. pub uv_rect_handle: GpuBufferAddress, pub cache_handle: Option<RenderTaskCacheEntryHandle>, - uv_rect_kind: UvRectKind, + pub uv_rect_kind: UvRectKind, } impl RenderTask { @@ -2755,39 +2755,6 @@ impl RenderTask { self.kind.target_kind() } - pub fn write_gpu_blocks( - &mut self, - target_rect: DeviceIntRect, - gpu_buffer: &mut GpuBufferBuilder, - ) { - profile_scope!("write_gpu_blocks"); - - self.kind.write_gpu_blocks(gpu_buffer); - - // Render tasks are re-created each frame so we can safely check that we aren't - // producing their uv address multiple times. This is important because some - // code paths may produce a different uv rect than here, for example normalized - // uv rects coming from external images. In these cases we rely on the uv address - // being et earlier in the frame. - // Cases where we expect to already have a uv rect handle include: - // - cached render tasks, - // - Image requests (in some cases but not all). - if self.uv_rect_handle.is_valid() { - return; - } - - let p0 = target_rect.min.to_f32(); - let p1 = target_rect.max.to_f32(); - let image_source = ImageSource { - p0, - p1, - user_data: [0.0; 4], - uv_rect_kind: self.uv_rect_kind, - }; - - self.uv_rect_handle = image_source.write_gpu_blocks(&mut gpu_buffer.f32).address_unchecked(); - } - /// Called by the render task cache. /// /// Tells the render task that it is cached (which means its gpu cache diff --git a/gfx/wr/webrender/src/render_task_graph.rs b/gfx/wr/webrender/src/render_task_graph.rs @@ -9,6 +9,7 @@ use api::units::*; use api::ImageFormat; +use crate::gpu_types::ImageSource; use crate::internal_types::{TextureSource, CacheTextureId, FastHashMap, FastHashSet, FrameId}; use crate::internal_types::size_of_frame_vec; use crate::render_task::{StaticRenderTaskSurface, RenderTaskLocation, RenderTask}; @@ -619,9 +620,9 @@ impl RenderTaskGraphBuilder { // considered to be immutable for the rest of the frame building process. for task in &mut graph.tasks { - // First check whether the render task texture and uv rects are managed - // externally. This is the case for image tasks and cached tasks. In both - // cases it results in a finding the information in the texture cache. + // Check whether the render task texture and uv rects are managed externally. + // This is the case for image tasks and cached tasks. In both cases it + // results in a finding the information in the texture cache. let cache_item = if let Some(ref cache_handle) = task.cache_handle { Some(resolve_cached_render_task( cache_handle, @@ -640,12 +641,13 @@ impl RenderTaskGraphBuilder { None }; - if let Some(cache_item) = cache_item { + if let Some(cache_item) = &cache_item { + task.uv_rect_handle = gpu_buffers.f32.resolve_handle(cache_item.uv_rect_handle); + // Update the render task even if the item is invalid. // We'll handle it later and it's easier to not have to // deal with unexpected location variants like // RenderTaskLocation::CacheRequest when we do. - task.uv_rect_handle = gpu_buffers.f32.resolve_handle(cache_item.uv_rect_handle); if let RenderTaskLocation::CacheRequest { .. } = &task.location { let source = cache_item.texture_id; task.location = RenderTaskLocation::Static { @@ -655,14 +657,25 @@ impl RenderTaskGraphBuilder { } } - // Give the render task an opportunity to add any - // information to the GPU cache, if appropriate. + // This has to be done after we do the task location fixup above. let target_rect = task.get_target_rect(); - task.write_gpu_blocks( - target_rect, - gpu_buffers, - ); + // If the uv rect is not managed externally, generate it now. + if cache_item.is_none() { + let image_source = ImageSource { + p0: target_rect.min.to_f32(), + p1: target_rect.max.to_f32(), + user_data: [0.0; 4], + uv_rect_kind: task.uv_rect_kind, + }; + + let uv_rect_handle = image_source.write_gpu_blocks(&mut gpu_buffers.f32); + task.uv_rect_handle = gpu_buffers.f32.resolve_handle(uv_rect_handle); + } + + // Give the render task an opportunity to add any + // information to the GPU cache, if appropriate. + task.kind.write_gpu_blocks(gpu_buffers); graph.task_data.push( task.kind.write_task_data(target_rect) @@ -740,6 +753,7 @@ impl RenderTaskGraph { } let uv_address = task.get_texture_address(); + assert!(uv_address.is_valid()); Some((uv_address, texture_source)) } diff --git a/gfx/wr/webrender/src/texture_cache.rs b/gfx/wr/webrender/src/texture_cache.rs @@ -98,11 +98,14 @@ pub struct CacheEntry { /// Arbitrary user data associated with this item. pub user_data: [f32; 4], /// The last frame this item was requested for rendering. - // TODO(gw): This stamp is only used for picture cache tiles, and some checks - // in the glyph cache eviction code. We could probably remove it - // entirely in future (or move to PictureCacheEntry). pub last_access: FrameStamp, /// Address of the resource rect in the GPU cache. + /// + /// The handle is stored in the cache entry to avoid duplicates when an + /// item is used multiple times per frame, but greate care must be taken + /// to not reuse a handle that was created in a previous frame. + /// TODO: For now the validity of the handle can be checked by comparing + /// last_access with the current FrameStamp, but this is error prone. pub uv_rect_handle: GpuBufferHandle, /// Image format of the data that the entry expects. pub input_format: ImageFormat, @@ -581,7 +584,7 @@ pub struct TextureCache { pub pending_updates: TextureUpdateList, /// The current `FrameStamp`. Used for cache eviction policies. - now: FrameStamp, + pub now: FrameStamp, /// Cache of texture cache handles with automatic lifetime management, evicted /// in a least-recently-used order. @@ -846,10 +849,12 @@ impl TextureCache { }, }; entry.map_or(true, |entry| { - // If an image is requested that is already in the cache, - // refresh the GPU buffer data associated with this item. - entry.last_access = now; - entry.write_gpu_blocks(gpu_buffer); + if entry.last_access != now { + // If an image is requested that is already in the cache, + // refresh the GPU buffer data associated with this item. + entry.last_access = now; + entry.write_gpu_blocks(gpu_buffer); + } false }) } @@ -941,6 +946,7 @@ impl TextureCache { dirty_rect = DirtyRect::All; } + let now = self.now; let entry = self.get_entry_opt_mut(handle) .expect("BUG: There must be an entry at this handle now"); @@ -948,8 +954,13 @@ impl TextureCache { entry.eviction_notice = eviction_notice.cloned(); entry.uv_rect_kind = uv_rect_kind; - // Upload the resource rect and texture array layer. - entry.write_gpu_blocks(gpu_buffer); + // If we just allocated the entry, its framestamp is up to date but it does + // not uset have up-to-date gpu blocks. + if entry.last_access != now || realloc { + entry.last_access = now; + // Upload the resource rect and texture array layer. + entry.write_gpu_blocks(gpu_buffer); + } // Create an update command, which the render thread processes // to upload the new image data into the correct location @@ -1025,6 +1036,12 @@ impl TextureCache { ) -> Option<(CacheTextureId, DeviceIntRect, Swizzle, GpuBufferHandle, [f32; 4])> { let entry = self.get_entry_opt(handle)?; let origin = entry.details.describe(); + if entry.last_access != self.now { + // On rare occasions we may have an image request that does not materialize + // into up to date data in the cache. For example if we failed to produce a + // stacking context snapshot. + return None; + } Some(( entry.texture_id, DeviceIntRect::from_origin_and_size(origin, entry.size),