tor-browser

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

render_task_cache.rs (14788B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 
      6 use api::{DirtyRect, ImageDescriptor, ImageDescriptorFlags, SnapshotImageKey};
      7 use api::units::*;
      8 use crate::border::BorderSegmentCacheKey;
      9 use crate::box_shadow::BoxShadowCacheKey;
     10 use crate::device::TextureFilter;
     11 use crate::freelist::{FreeList, FreeListHandle, WeakFreeListHandle};
     12 use crate::internal_types::FastHashMap;
     13 use crate::prim_store::image::ImageCacheKey;
     14 use crate::prim_store::gradient::{
     15    FastLinearGradientCacheKey, LinearGradientCacheKey, RadialGradientCacheKey,
     16    ConicGradientCacheKey,
     17 };
     18 use crate::prim_store::line_dec::LineDecorationCacheKey;
     19 use crate::quad::QuadCacheKey;
     20 use crate::resource_cache::CacheItem;
     21 use std::{mem, usize, f32, i32};
     22 use crate::surface::SurfaceBuilder;
     23 use crate::texture_cache::{TextureCache, TextureCacheHandle, Eviction, TargetShader};
     24 use crate::renderer::GpuBufferBuilderF;
     25 use crate::render_target::RenderTargetKind;
     26 use crate::render_task::{RenderTask, StaticRenderTaskSurface, RenderTaskLocation, RenderTaskKind, CachedTask};
     27 use crate::render_task_graph::{RenderTaskGraphBuilder, RenderTaskId};
     28 use euclid::Scale;
     29 
     30 const MAX_CACHE_TASK_SIZE: f32 = 4096.0;
     31 
     32 /// Describes a parent dependency for a render task. Render tasks
     33 /// may depend on a surface (e.g. when a surface uses a cached border)
     34 /// or an arbitrary render task (e.g. when a clip mask uses a blurred
     35 /// box-shadow input).
     36 pub enum RenderTaskParent {
     37    /// Parent is a surface
     38    Surface,
     39    /// Parent is a render task
     40    RenderTask(RenderTaskId),
     41 }
     42 
     43 #[derive(Clone, Debug, Hash, PartialEq, Eq)]
     44 #[cfg_attr(feature = "capture", derive(Serialize))]
     45 #[cfg_attr(feature = "replay", derive(Deserialize))]
     46 pub enum RenderTaskCacheKeyKind {
     47    BoxShadow(BoxShadowCacheKey),
     48    Image(ImageCacheKey),
     49    BorderSegment(BorderSegmentCacheKey),
     50    LineDecoration(LineDecorationCacheKey),
     51    FastLinearGradient(FastLinearGradientCacheKey),
     52    LinearGradient(LinearGradientCacheKey),
     53    RadialGradient(RadialGradientCacheKey),
     54    ConicGradient(ConicGradientCacheKey),
     55    Snapshot(SnapshotImageKey),
     56    Quad(QuadCacheKey),
     57 }
     58 
     59 #[derive(Clone, Debug, Hash, PartialEq, Eq)]
     60 #[cfg_attr(feature = "capture", derive(Serialize))]
     61 #[cfg_attr(feature = "replay", derive(Deserialize))]
     62 pub struct RenderTaskCacheKey {
     63    pub size: DeviceIntSize,
     64    pub kind: RenderTaskCacheKeyKind,
     65 }
     66 
     67 #[derive(Debug)]
     68 #[cfg_attr(feature = "capture", derive(Serialize))]
     69 #[cfg_attr(feature = "replay", derive(Deserialize))]
     70 pub struct RenderTaskCacheEntry {
     71    user_data: Option<[f32; 4]>,
     72    target_kind: RenderTargetKind,
     73    is_opaque: bool,
     74    frame_id: u64,
     75    pub handle: TextureCacheHandle,
     76    /// If a render task was generated for this cache entry on _this_ frame,
     77    /// we need to track the task id here. This allows us to hook it up as
     78    /// a dependency of any parent tasks that make a reqiest from the render
     79    /// task cache.
     80    pub render_task_id: Option<RenderTaskId>,
     81 }
     82 
     83 #[derive(Debug, MallocSizeOf)]
     84 #[cfg_attr(feature = "capture", derive(Serialize))]
     85 pub enum RenderTaskCacheMarker {}
     86 
     87 // A cache of render tasks that are stored in the texture
     88 // cache for usage across frames.
     89 #[derive(Debug)]
     90 #[cfg_attr(feature = "capture", derive(Serialize))]
     91 #[cfg_attr(feature = "replay", derive(Deserialize))]
     92 pub struct RenderTaskCache {
     93    map: FastHashMap<RenderTaskCacheKey, FreeListHandle<RenderTaskCacheMarker>>,
     94    cache_entries: FreeList<RenderTaskCacheEntry, RenderTaskCacheMarker>,
     95    frame_id: u64,
     96 }
     97 
     98 pub type RenderTaskCacheEntryHandle = WeakFreeListHandle<RenderTaskCacheMarker>;
     99 
    100 impl RenderTaskCache {
    101    pub fn new() -> Self {
    102        RenderTaskCache {
    103            map: FastHashMap::default(),
    104            cache_entries: FreeList::new(),
    105            frame_id: 0,
    106        }
    107    }
    108 
    109    pub fn clear(&mut self) {
    110        self.map.clear();
    111        self.cache_entries.clear();
    112    }
    113 
    114    pub fn begin_frame(
    115        &mut self,
    116        texture_cache: &mut TextureCache,
    117    ) {
    118        self.frame_id += 1;
    119        profile_scope!("begin_frame");
    120        // Drop any items from the cache that have been
    121        // evicted from the texture cache.
    122        //
    123        // This isn't actually necessary for the texture
    124        // cache to be able to evict old render tasks.
    125        // It will evict render tasks as required, since
    126        // the access time in the texture cache entry will
    127        // be stale if this task hasn't been requested
    128        // for a while.
    129        //
    130        // Nonetheless, we should remove stale entries
    131        // from here so that this hash map doesn't
    132        // grow indefinitely!
    133        let cache_entries = &mut self.cache_entries;
    134        let frame_id = self.frame_id;
    135 
    136        self.map.retain(|_, handle| {
    137            let mut retain = texture_cache.is_allocated(
    138                &cache_entries.get(handle).handle,
    139            );
    140            if retain {
    141                let entry = cache_entries.get_mut(&handle);
    142                if frame_id > entry.frame_id + 10 {
    143                    texture_cache.evict_handle(&entry.handle);
    144                    retain = false;
    145                }
    146            }
    147 
    148            if !retain {
    149                let handle = mem::replace(handle, FreeListHandle::invalid());
    150                cache_entries.free(handle);
    151            }
    152 
    153            retain
    154        });
    155 
    156        // Clear out the render task ID of any remaining cache entries that were drawn
    157        // on the previous frame, so we don't accidentally hook up stale dependencies
    158        // when building the frame graph.
    159        for (_, handle) in &self.map {
    160            let entry = self.cache_entries.get_mut(handle);
    161            entry.render_task_id = None;
    162        }
    163    }
    164 
    165    fn alloc_render_task(
    166        size: DeviceIntSize,
    167        render_task: &mut RenderTask,
    168        entry: &mut RenderTaskCacheEntry,
    169        gpu_buffer: &mut GpuBufferBuilderF,
    170        texture_cache: &mut TextureCache,
    171    ) {
    172        // Find out what size to alloc in the texture cache.
    173        let target_kind = render_task.target_kind();
    174 
    175        // Select the right texture page to allocate from.
    176        let image_format = match target_kind {
    177            RenderTargetKind::Color => texture_cache.shared_color_expected_format(),
    178            RenderTargetKind::Alpha => texture_cache.shared_alpha_expected_format(),
    179        };
    180 
    181        let flags = if entry.is_opaque {
    182            ImageDescriptorFlags::IS_OPAQUE
    183        } else {
    184            ImageDescriptorFlags::empty()
    185        };
    186 
    187        let descriptor = ImageDescriptor::new(
    188            size.width,
    189            size.height,
    190            image_format,
    191            flags,
    192        );
    193 
    194        // Allocate space in the texture cache, but don't supply
    195        // and CPU-side data to be uploaded.
    196        texture_cache.update(
    197            &mut entry.handle,
    198            descriptor,
    199            TextureFilter::Linear,
    200            None,
    201            entry.user_data.unwrap_or([0.0; 4]),
    202            DirtyRect::All,
    203            gpu_buffer,
    204            None,
    205            render_task.uv_rect_kind(),
    206            Eviction::Auto,
    207            TargetShader::Default,
    208            false,
    209        );
    210 
    211        // Get the allocation details in the texture cache, and store
    212        // this in the render task. The renderer will draw this task
    213        // into the appropriate rect of the texture cache on this frame.
    214        let (texture_id, uv_rect, _, _, _) =
    215            texture_cache.get_cache_location(&entry.handle);
    216 
    217        let surface = StaticRenderTaskSurface::TextureCache {
    218            texture: texture_id,
    219            target_kind,
    220        };
    221 
    222        render_task.location = RenderTaskLocation::Static {
    223            surface,
    224            rect: uv_rect.to_i32(),
    225        };
    226    }
    227 
    228    pub fn request_render_task(
    229        &mut self,
    230        key: Option<RenderTaskCacheKey>,
    231        texture_cache: &mut TextureCache,
    232        is_opaque: bool,
    233        parent: RenderTaskParent,
    234        gpu_buffer_builder: &mut GpuBufferBuilderF,
    235        rg_builder: &mut RenderTaskGraphBuilder,
    236        surface_builder: &mut SurfaceBuilder,
    237        f: &mut dyn FnMut(&mut RenderTaskGraphBuilder, &mut GpuBufferBuilderF) -> RenderTaskId,
    238    ) -> RenderTaskId {
    239        // If this render task cache is being drawn this frame, ensure we hook up the
    240        // render task for it as a dependency of any render task that uses this as
    241        // an input source.
    242        let (task_id, rendered_this_frame) = match key {
    243            None => (f(rg_builder, gpu_buffer_builder), true),
    244            Some(key) => self.request_render_task_impl(
    245                key,
    246                is_opaque,
    247                texture_cache,
    248                gpu_buffer_builder,
    249                rg_builder,
    250                f
    251            )
    252        };
    253 
    254        if rendered_this_frame {
    255            match parent {
    256                RenderTaskParent::Surface => {
    257                    // If parent is a surface, use helper fn to add this dependency,
    258                    // which correctly takes account of the render task configuration
    259                    // of the surface.
    260                    surface_builder.add_child_render_task(
    261                        task_id,
    262                        rg_builder,
    263                    );
    264                }
    265                RenderTaskParent::RenderTask(parent_render_task_id) => {
    266                    // For render tasks, just add it as a direct dependency on the
    267                    // task graph builder.
    268                    rg_builder.add_dependency(
    269                        parent_render_task_id,
    270                        task_id,
    271                    );
    272                }
    273            }
    274        }
    275 
    276        task_id
    277    }
    278 
    279    /// Returns the render task id and a boolean indicating whether the
    280    /// task was rendered this frame (was not already in cache).
    281    fn request_render_task_impl(
    282        &mut self,
    283        key: RenderTaskCacheKey,
    284        is_opaque: bool,
    285        texture_cache: &mut TextureCache,
    286        gpu_buffer_builder: &mut GpuBufferBuilderF,
    287        rg_builder: &mut RenderTaskGraphBuilder,
    288        f: &mut dyn FnMut(&mut RenderTaskGraphBuilder, &mut GpuBufferBuilderF) -> RenderTaskId,
    289    ) -> (RenderTaskId, bool) {
    290        let frame_id = self.frame_id;
    291        let size = key.size;
    292        // Get the texture cache handle for this cache key,
    293        // or create one.
    294        let cache_entries = &mut self.cache_entries;
    295        let entry_handle = self.map.entry(key).or_insert_with(|| {
    296            let entry = RenderTaskCacheEntry {
    297                handle: TextureCacheHandle::invalid(),
    298                user_data: None,
    299                target_kind: RenderTargetKind::Color, // will be set below.
    300                is_opaque,
    301                frame_id,
    302                render_task_id: None,
    303            };
    304            cache_entries.insert(entry)
    305        });
    306        let cache_entry = cache_entries.get_mut(entry_handle);
    307        cache_entry.frame_id = self.frame_id;
    308 
    309        // Check if this texture cache handle is valid.
    310        if texture_cache.request(&cache_entry.handle, gpu_buffer_builder) {
    311            // Invoke user closure to get render task chain
    312            // to draw this into the texture cache.
    313            let render_task_id = f(rg_builder, gpu_buffer_builder);
    314 
    315            cache_entry.user_data = None;
    316            cache_entry.is_opaque = is_opaque;
    317            cache_entry.render_task_id = Some(render_task_id);
    318 
    319            let render_task = rg_builder.get_task_mut(render_task_id);
    320            let task_size = render_task.location.size();
    321 
    322            render_task.mark_cached(entry_handle.weak());
    323            cache_entry.target_kind = render_task.kind.target_kind();
    324 
    325            RenderTaskCache::alloc_render_task(
    326                task_size,
    327                render_task,
    328                cache_entry,
    329                gpu_buffer_builder,
    330                texture_cache,
    331            );
    332        }
    333 
    334        if let Some(render_task_id) = cache_entry.render_task_id {
    335            return (render_task_id, true);
    336        }
    337 
    338        let target_kind = cache_entry.target_kind;
    339        let mut task = RenderTask::new(
    340            RenderTaskLocation::CacheRequest { size, },
    341            RenderTaskKind::Cached(CachedTask {
    342                target_kind,
    343            }),
    344        );
    345        task.mark_cached(entry_handle.weak());
    346        let render_task_id = rg_builder.add().init(task);
    347 
    348        (render_task_id, false)
    349    }
    350 
    351    pub fn get_cache_entry(
    352        &self,
    353        handle: &RenderTaskCacheEntryHandle,
    354    ) -> &RenderTaskCacheEntry {
    355        self.cache_entries
    356            .get_opt(handle)
    357            .expect("bug: invalid render task cache handle")
    358    }
    359 
    360    #[allow(dead_code)]
    361    pub fn get_cache_item_for_render_task(&self,
    362                                          texture_cache: &TextureCache,
    363                                          key: &RenderTaskCacheKey)
    364                                          -> CacheItem {
    365        // Get the texture cache handle for this cache key.
    366        let handle = self.map.get(key).unwrap();
    367        let cache_entry = self.cache_entries.get(handle);
    368        texture_cache.get(&cache_entry.handle)
    369    }
    370 
    371    #[allow(dead_code)]
    372    pub fn get_allocated_size_for_render_task(&self,
    373                                              texture_cache: &TextureCache,
    374                                              key: &RenderTaskCacheKey)
    375                                              -> Option<usize> {
    376        let handle = self.map.get(key).unwrap();
    377        let cache_entry = self.cache_entries.get(handle);
    378        texture_cache.get_allocated_size(&cache_entry.handle)
    379    }
    380 }
    381 
    382 // TODO(gw): Rounding the content rect here to device pixels is not
    383 // technically correct. Ideally we should ceil() here, and ensure that
    384 // the extra part pixel in the case of fractional sizes is correctly
    385 // handled. For now, just use rounding which passes the existing
    386 // Gecko tests.
    387 // Note: zero-square tasks are prohibited in WR task graph, so
    388 // we ensure each dimension to be at least the length of 1 after rounding.
    389 pub fn to_cache_size(size: LayoutSize, device_pixel_scale: &mut Scale<f32, LayoutPixel, DevicePixel>) -> DeviceIntSize {
    390    let mut device_size = (size * *device_pixel_scale).round();
    391 
    392    if device_size.width > MAX_CACHE_TASK_SIZE || device_size.height > MAX_CACHE_TASK_SIZE {
    393        let scale = MAX_CACHE_TASK_SIZE / f32::max(device_size.width, device_size.height);
    394        *device_pixel_scale = *device_pixel_scale * Scale::new(scale);
    395        device_size = (size * *device_pixel_scale).round();
    396    }
    397 
    398    DeviceIntSize::new(
    399        1.max(device_size.width as i32),
    400        1.max(device_size.height as i32),
    401    )
    402 }