Source code

Revision control

Other Tools

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::{ImageDescriptor, ImageDescriptorFlags, DirtyRect};
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::gpu_cache::GpuCache;
13
use crate::internal_types::FastHashMap;
14
use crate::prim_store::image::ImageCacheKey;
15
use crate::prim_store::gradient::GradientCacheKey;
16
use crate::prim_store::line_dec::LineDecorationCacheKey;
17
use crate::resource_cache::CacheItem;
18
use std::{mem, usize, f32, i32};
19
use crate::texture_cache::{TextureCache, TextureCacheHandle, Eviction};
20
use crate::render_target::RenderTargetKind;
21
use crate::render_task::{RenderTask, RenderTaskLocation};
22
use crate::render_task_graph::{RenderTaskGraph, RenderTaskId};
23
24
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
25
#[cfg_attr(feature = "capture", derive(Serialize))]
26
#[cfg_attr(feature = "replay", derive(Deserialize))]
27
pub enum RenderTaskCacheKeyKind {
28
BoxShadow(BoxShadowCacheKey),
29
Image(ImageCacheKey),
30
BorderSegment(BorderSegmentCacheKey),
31
LineDecoration(LineDecorationCacheKey),
32
Gradient(GradientCacheKey),
33
}
34
35
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
36
#[cfg_attr(feature = "capture", derive(Serialize))]
37
#[cfg_attr(feature = "replay", derive(Deserialize))]
38
pub struct RenderTaskCacheKey {
39
pub size: DeviceIntSize,
40
pub kind: RenderTaskCacheKeyKind,
41
}
42
43
#[derive(Debug)]
44
#[cfg_attr(feature = "capture", derive(Serialize))]
45
#[cfg_attr(feature = "replay", derive(Deserialize))]
46
pub struct RenderTaskCacheEntry {
47
user_data: Option<[f32; 3]>,
48
is_opaque: bool,
49
pub handle: TextureCacheHandle,
50
}
51
52
#[derive(Debug, MallocSizeOf)]
53
#[cfg_attr(feature = "capture", derive(Serialize))]
54
pub enum RenderTaskCacheMarker {}
55
56
// A cache of render tasks that are stored in the texture
57
// cache for usage across frames.
58
#[derive(Debug)]
59
#[cfg_attr(feature = "capture", derive(Serialize))]
60
#[cfg_attr(feature = "replay", derive(Deserialize))]
61
pub struct RenderTaskCache {
62
map: FastHashMap<RenderTaskCacheKey, FreeListHandle<RenderTaskCacheMarker>>,
63
cache_entries: FreeList<RenderTaskCacheEntry, RenderTaskCacheMarker>,
64
}
65
66
pub type RenderTaskCacheEntryHandle = WeakFreeListHandle<RenderTaskCacheMarker>;
67
68
impl RenderTaskCache {
69
pub fn new() -> Self {
70
RenderTaskCache {
71
map: FastHashMap::default(),
72
cache_entries: FreeList::new(),
73
}
74
}
75
76
pub fn clear(&mut self) {
77
self.map.clear();
78
self.cache_entries.clear();
79
}
80
81
pub fn begin_frame(
82
&mut self,
83
texture_cache: &mut TextureCache,
84
) {
85
// Drop any items from the cache that have been
86
// evicted from the texture cache.
87
//
88
// This isn't actually necessary for the texture
89
// cache to be able to evict old render tasks.
90
// It will evict render tasks as required, since
91
// the access time in the texture cache entry will
92
// be stale if this task hasn't been requested
93
// for a while.
94
//
95
// Nonetheless, we should remove stale entries
96
// from here so that this hash map doesn't
97
// grow indefinitely!
98
let cache_entries = &mut self.cache_entries;
99
100
self.map.retain(|_, handle| {
101
let retain = texture_cache.is_allocated(
102
&cache_entries.get(handle).handle,
103
);
104
if !retain {
105
let handle = mem::replace(handle, FreeListHandle::invalid());
106
cache_entries.free(handle);
107
}
108
retain
109
});
110
}
111
112
fn alloc_render_task(
113
render_task: &mut RenderTask,
114
entry: &mut RenderTaskCacheEntry,
115
gpu_cache: &mut GpuCache,
116
texture_cache: &mut TextureCache,
117
) {
118
// Find out what size to alloc in the texture cache.
119
let size = match render_task.location {
120
RenderTaskLocation::Fixed(..) |
121
RenderTaskLocation::PictureCache { .. } |
122
RenderTaskLocation::TextureCache { .. } => {
123
panic!("BUG: dynamic task was expected");
124
}
125
RenderTaskLocation::Dynamic(_, size) => size,
126
};
127
128
// Select the right texture page to allocate from.
129
let image_format = match render_task.target_kind() {
130
RenderTargetKind::Color => texture_cache.shared_color_expected_format(),
131
RenderTargetKind::Alpha => texture_cache.shared_alpha_expected_format(),
132
};
133
134
let flags = if entry.is_opaque {
135
ImageDescriptorFlags::IS_OPAQUE
136
} else {
137
ImageDescriptorFlags::empty()
138
};
139
140
let descriptor = ImageDescriptor::new(
141
size.width,
142
size.height,
143
image_format,
144
flags,
145
);
146
147
// Allocate space in the texture cache, but don't supply
148
// and CPU-side data to be uploaded.
149
//
150
// Note that we currently use Eager eviction for cached render
151
// tasks, which means that any cached item not used in the last
152
// frame is discarded. There's room to be a lot smarter here,
153
// especially by considering the relative costs of re-rendering
154
// each type of item (box shadow blurs are an order of magnitude
155
// more expensive than borders, for example). Telemetry could
156
// inform our decisions here as well.
157
texture_cache.update(
158
&mut entry.handle,
159
descriptor,
160
TextureFilter::Linear,
161
None,
162
entry.user_data.unwrap_or([0.0; 3]),
163
DirtyRect::All,
164
gpu_cache,
165
None,
166
render_task.uv_rect_kind(),
167
Eviction::Eager,
168
);
169
170
// Get the allocation details in the texture cache, and store
171
// this in the render task. The renderer will draw this
172
// task into the appropriate layer and rect of the texture
173
// cache on this frame.
174
let (texture_id, texture_layer, uv_rect, _, _) =
175
texture_cache.get_cache_location(&entry.handle);
176
177
render_task.location = RenderTaskLocation::TextureCache {
178
texture: texture_id,
179
layer: texture_layer,
180
rect: uv_rect.to_i32(),
181
};
182
}
183
184
pub fn request_render_task<F>(
185
&mut self,
186
key: RenderTaskCacheKey,
187
texture_cache: &mut TextureCache,
188
gpu_cache: &mut GpuCache,
189
render_tasks: &mut RenderTaskGraph,
190
user_data: Option<[f32; 3]>,
191
is_opaque: bool,
192
f: F,
193
) -> Result<RenderTaskCacheEntryHandle, ()>
194
where
195
F: FnOnce(&mut RenderTaskGraph) -> Result<RenderTaskId, ()>,
196
{
197
// Get the texture cache handle for this cache key,
198
// or create one.
199
let cache_entries = &mut self.cache_entries;
200
let entry_handle = self.map.entry(key).or_insert_with(|| {
201
let entry = RenderTaskCacheEntry {
202
handle: TextureCacheHandle::invalid(),
203
user_data,
204
is_opaque,
205
};
206
cache_entries.insert(entry)
207
});
208
let cache_entry = cache_entries.get_mut(entry_handle);
209
210
// Check if this texture cache handle is valid.
211
if texture_cache.request(&cache_entry.handle, gpu_cache) {
212
// Invoke user closure to get render task chain
213
// to draw this into the texture cache.
214
let render_task_id = f(render_tasks)?;
215
render_tasks.cacheable_render_tasks.push(render_task_id);
216
217
cache_entry.user_data = user_data;
218
cache_entry.is_opaque = is_opaque;
219
220
RenderTaskCache::alloc_render_task(
221
&mut render_tasks[render_task_id],
222
cache_entry,
223
gpu_cache,
224
texture_cache,
225
);
226
}
227
228
Ok(entry_handle.weak())
229
}
230
231
pub fn get_cache_entry(
232
&self,
233
handle: &RenderTaskCacheEntryHandle,
234
) -> &RenderTaskCacheEntry {
235
self.cache_entries
236
.get_opt(handle)
237
.expect("bug: invalid render task cache handle")
238
}
239
240
#[allow(dead_code)]
241
pub fn get_cache_item_for_render_task(&self,
242
texture_cache: &TextureCache,
243
key: &RenderTaskCacheKey)
244
-> CacheItem {
245
// Get the texture cache handle for this cache key.
246
let handle = self.map.get(key).unwrap();
247
let cache_entry = self.cache_entries.get(handle);
248
texture_cache.get(&cache_entry.handle)
249
}
250
251
#[allow(dead_code)]
252
pub fn get_allocated_size_for_render_task(&self,
253
texture_cache: &TextureCache,
254
key: &RenderTaskCacheKey)
255
-> Option<usize> {
256
let handle = self.map.get(key).unwrap();
257
let cache_entry = self.cache_entries.get(handle);
258
texture_cache.get_allocated_size(&cache_entry.handle)
259
}
260
}
261
262
// TODO(gw): Rounding the content rect here to device pixels is not
263
// technically correct. Ideally we should ceil() here, and ensure that
264
// the extra part pixel in the case of fractional sizes is correctly
265
// handled. For now, just use rounding which passes the existing
266
// Gecko tests.
267
// Note: zero-square tasks are prohibited in WR task graph, so
268
// we ensure each dimension to be at least the length of 1 after rounding.
269
pub fn to_cache_size(size: DeviceSize) -> DeviceIntSize {
270
DeviceIntSize::new(
271
1.max(size.width.round() as i32),
272
1.max(size.height.round() as i32),
273
)
274
}