1use api::{DirtyRect, ImageDescriptor, ImageDescriptorFlags, SnapshotImageKey};
7use api::units::*;
8use crate::border::BorderSegmentCacheKey;
9use crate::box_shadow::BoxShadowCacheKey;
10use crate::device::TextureFilter;
11use crate::freelist::{FreeList, FreeListHandle, WeakFreeListHandle};
12use crate::gpu_cache::GpuCache;
13use crate::internal_types::FastHashMap;
14use crate::prim_store::image::ImageCacheKey;
15use crate::prim_store::gradient::{
16 FastLinearGradientCacheKey, LinearGradientCacheKey, RadialGradientCacheKey,
17 ConicGradientCacheKey,
18};
19use crate::prim_store::line_dec::LineDecorationCacheKey;
20use crate::resource_cache::CacheItem;
21use std::{mem, usize, f32, i32};
22use crate::surface::SurfaceBuilder;
23use crate::texture_cache::{TextureCache, TextureCacheHandle, Eviction, TargetShader};
24use crate::renderer::GpuBufferBuilderF;
25use crate::render_target::RenderTargetKind;
26use crate::render_task::{RenderTask, StaticRenderTaskSurface, RenderTaskLocation, RenderTaskKind, CachedTask};
27use crate::render_task_graph::{RenderTaskGraphBuilder, RenderTaskId};
28use euclid::Scale;
29
30const MAX_CACHE_TASK_SIZE: f32 = 4096.0;
31
32pub enum RenderTaskParent {
37 Surface,
39 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))]
46pub 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}
57
58#[derive(Clone, Debug, Hash, PartialEq, Eq)]
59#[cfg_attr(feature = "capture", derive(Serialize))]
60#[cfg_attr(feature = "replay", derive(Deserialize))]
61pub struct RenderTaskCacheKey {
62 pub size: DeviceIntSize,
63 pub kind: RenderTaskCacheKeyKind,
64}
65
66#[derive(Debug)]
67#[cfg_attr(feature = "capture", derive(Serialize))]
68#[cfg_attr(feature = "replay", derive(Deserialize))]
69pub struct RenderTaskCacheEntry {
70 user_data: Option<[f32; 4]>,
71 target_kind: RenderTargetKind,
72 is_opaque: bool,
73 frame_id: u64,
74 pub handle: TextureCacheHandle,
75 pub render_task_id: Option<RenderTaskId>,
80}
81
82#[derive(Debug, MallocSizeOf)]
83#[cfg_attr(feature = "capture", derive(Serialize))]
84pub enum RenderTaskCacheMarker {}
85
86#[derive(Debug)]
89#[cfg_attr(feature = "capture", derive(Serialize))]
90#[cfg_attr(feature = "replay", derive(Deserialize))]
91pub struct RenderTaskCache {
92 map: FastHashMap<RenderTaskCacheKey, FreeListHandle<RenderTaskCacheMarker>>,
93 cache_entries: FreeList<RenderTaskCacheEntry, RenderTaskCacheMarker>,
94 frame_id: u64,
95}
96
97pub type RenderTaskCacheEntryHandle = WeakFreeListHandle<RenderTaskCacheMarker>;
98
99impl RenderTaskCache {
100 pub fn new() -> Self {
101 RenderTaskCache {
102 map: FastHashMap::default(),
103 cache_entries: FreeList::new(),
104 frame_id: 0,
105 }
106 }
107
108 pub fn clear(&mut self) {
109 self.map.clear();
110 self.cache_entries.clear();
111 }
112
113 pub fn begin_frame(
114 &mut self,
115 texture_cache: &mut TextureCache,
116 ) {
117 self.frame_id += 1;
118 profile_scope!("begin_frame");
119 let cache_entries = &mut self.cache_entries;
133 let frame_id = self.frame_id;
134
135 self.map.retain(|_, handle| {
136 let mut retain = texture_cache.is_allocated(
137 &cache_entries.get(handle).handle,
138 );
139 if retain {
140 let entry = cache_entries.get_mut(&handle);
141 if frame_id > entry.frame_id + 10 {
142 texture_cache.evict_handle(&entry.handle);
143 retain = false;
144 }
145 }
146
147 if !retain {
148 let handle = mem::replace(handle, FreeListHandle::invalid());
149 cache_entries.free(handle);
150 }
151
152 retain
153 });
154
155 for (_, handle) in &self.map {
159 let entry = self.cache_entries.get_mut(handle);
160 entry.render_task_id = None;
161 }
162 }
163
164 fn alloc_render_task(
165 size: DeviceIntSize,
166 render_task: &mut RenderTask,
167 entry: &mut RenderTaskCacheEntry,
168 gpu_cache: &mut GpuCache,
169 texture_cache: &mut TextureCache,
170 ) {
171 let target_kind = render_task.target_kind();
173
174 let image_format = match target_kind {
176 RenderTargetKind::Color => texture_cache.shared_color_expected_format(),
177 RenderTargetKind::Alpha => texture_cache.shared_alpha_expected_format(),
178 };
179
180 let flags = if entry.is_opaque {
181 ImageDescriptorFlags::IS_OPAQUE
182 } else {
183 ImageDescriptorFlags::empty()
184 };
185
186 let descriptor = ImageDescriptor::new(
187 size.width,
188 size.height,
189 image_format,
190 flags,
191 );
192
193 texture_cache.update(
196 &mut entry.handle,
197 descriptor,
198 TextureFilter::Linear,
199 None,
200 entry.user_data.unwrap_or([0.0; 4]),
201 DirtyRect::All,
202 gpu_cache,
203 None,
204 render_task.uv_rect_kind(),
205 Eviction::Auto,
206 TargetShader::Default,
207 );
208
209 let (texture_id, uv_rect, _, _, _) =
213 texture_cache.get_cache_location(&entry.handle);
214
215 let surface = StaticRenderTaskSurface::TextureCache {
216 texture: texture_id,
217 target_kind,
218 };
219
220 render_task.location = RenderTaskLocation::Static {
221 surface,
222 rect: uv_rect.to_i32(),
223 };
224 }
225
226 pub fn request_render_task(
227 &mut self,
228 key: Option<RenderTaskCacheKey>,
229 texture_cache: &mut TextureCache,
230 is_opaque: bool,
231 parent: RenderTaskParent,
232 gpu_cache: &mut GpuCache,
233 gpu_buffer_builder: &mut GpuBufferBuilderF,
234 rg_builder: &mut RenderTaskGraphBuilder,
235 surface_builder: &mut SurfaceBuilder,
236 f: &mut dyn FnMut(&mut RenderTaskGraphBuilder, &mut GpuBufferBuilderF, &mut GpuCache) -> RenderTaskId,
237 ) -> RenderTaskId {
238 let (task_id, rendered_this_frame) = match key {
242 None => (f(rg_builder, gpu_buffer_builder, gpu_cache), true),
243 Some(key) => self.request_render_task_impl(
244 key,
245 is_opaque,
246 texture_cache,
247 gpu_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 surface_builder.add_child_render_task(
261 task_id,
262 rg_builder,
263 );
264 }
265 RenderTaskParent::RenderTask(parent_render_task_id) => {
266 rg_builder.add_dependency(
269 parent_render_task_id,
270 task_id,
271 );
272 }
273 }
274 }
275
276 task_id
277 }
278
279 fn request_render_task_impl(
282 &mut self,
283 key: RenderTaskCacheKey,
284 is_opaque: bool,
285 texture_cache: &mut TextureCache,
286 gpu_cache: &mut GpuCache,
287 gpu_buffer_builder: &mut GpuBufferBuilderF,
288 rg_builder: &mut RenderTaskGraphBuilder,
289 f: &mut dyn FnMut(&mut RenderTaskGraphBuilder, &mut GpuBufferBuilderF, &mut GpuCache) -> RenderTaskId,
290 ) -> (RenderTaskId, bool) {
291 let frame_id = self.frame_id;
292 let size = key.size;
293 let cache_entries = &mut self.cache_entries;
296 let entry_handle = self.map.entry(key).or_insert_with(|| {
297 let entry = RenderTaskCacheEntry {
298 handle: TextureCacheHandle::invalid(),
299 user_data: None,
300 target_kind: RenderTargetKind::Color, is_opaque,
302 frame_id,
303 render_task_id: None,
304 };
305 cache_entries.insert(entry)
306 });
307 let cache_entry = cache_entries.get_mut(entry_handle);
308 cache_entry.frame_id = self.frame_id;
309
310 if texture_cache.request(&cache_entry.handle, gpu_cache) {
312 let render_task_id = f(rg_builder, gpu_buffer_builder, gpu_cache);
315
316 cache_entry.user_data = None;
317 cache_entry.is_opaque = is_opaque;
318 cache_entry.render_task_id = Some(render_task_id);
319
320 let render_task = rg_builder.get_task_mut(render_task_id);
321 let task_size = render_task.location.size();
322
323 render_task.mark_cached(entry_handle.weak());
324 cache_entry.target_kind = render_task.kind.target_kind();
325
326 RenderTaskCache::alloc_render_task(
327 task_size,
328 render_task,
329 cache_entry,
330 gpu_cache,
331 texture_cache,
332 );
333 }
334
335 if let Some(render_task_id) = cache_entry.render_task_id {
336 return (render_task_id, true);
337 }
338
339 let target_kind = cache_entry.target_kind;
340 let mut task = RenderTask::new(
341 RenderTaskLocation::CacheRequest { size, },
342 RenderTaskKind::Cached(CachedTask {
343 target_kind,
344 }),
345 );
346 task.mark_cached(entry_handle.weak());
347 let render_task_id = rg_builder.add().init(task);
348
349 (render_task_id, false)
350 }
351
352 pub fn get_cache_entry(
353 &self,
354 handle: &RenderTaskCacheEntryHandle,
355 ) -> &RenderTaskCacheEntry {
356 self.cache_entries
357 .get_opt(handle)
358 .expect("bug: invalid render task cache handle")
359 }
360
361 #[allow(dead_code)]
362 pub fn get_cache_item_for_render_task(&self,
363 texture_cache: &TextureCache,
364 key: &RenderTaskCacheKey)
365 -> CacheItem {
366 let handle = self.map.get(key).unwrap();
368 let cache_entry = self.cache_entries.get(handle);
369 texture_cache.get(&cache_entry.handle)
370 }
371
372 #[allow(dead_code)]
373 pub fn get_allocated_size_for_render_task(&self,
374 texture_cache: &TextureCache,
375 key: &RenderTaskCacheKey)
376 -> Option<usize> {
377 let handle = self.map.get(key).unwrap();
378 let cache_entry = self.cache_entries.get(handle);
379 texture_cache.get_allocated_size(&cache_entry.handle)
380 }
381}
382
383pub fn to_cache_size(size: LayoutSize, device_pixel_scale: &mut Scale<f32, LayoutPixel, DevicePixel>) -> DeviceIntSize {
391 let mut device_size = (size * *device_pixel_scale).round();
392
393 if device_size.width > MAX_CACHE_TASK_SIZE || device_size.height > MAX_CACHE_TASK_SIZE {
394 let scale = MAX_CACHE_TASK_SIZE / f32::max(device_size.width, device_size.height);
395 *device_pixel_scale = *device_pixel_scale * Scale::new(scale);
396 device_size = (size * *device_pixel_scale).round();
397 }
398
399 DeviceIntSize::new(
400 1.max(device_size.width as i32),
401 1.max(device_size.height as i32),
402 )
403}