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 }