1use std::mem;
6use smallvec::SmallVec;
7use api::{ImageFormat, ImageBufferKind, DebugFlags};
8use api::units::*;
9use crate::device::TextureFilter;
10use crate::internal_types::{
11 CacheTextureId, TextureUpdateList, Swizzle, TextureCacheAllocInfo, TextureCacheCategory,
12 TextureSource, FrameStamp, FrameId,
13};
14use crate::profiler::{self, TransactionProfile};
15use crate::gpu_types::{ImageSource, UvRectKind};
16use crate::gpu_cache::{GpuCache, GpuCacheHandle};
17use crate::freelist::{FreeList, FreeListHandle, WeakFreeListHandle};
18
19
20#[derive(Debug, PartialEq)]
21#[cfg_attr(feature = "capture", derive(Serialize))]
22#[cfg_attr(feature = "replay", derive(Deserialize))]
23pub enum PictureCacheEntryMarker {}
24
25malloc_size_of::malloc_size_of_is_0!(PictureCacheEntryMarker);
26
27pub type PictureCacheTextureHandle = WeakFreeListHandle<PictureCacheEntryMarker>;
28
29use std::cmp;
30
31#[derive(Debug)]
35#[cfg_attr(feature = "capture", derive(Serialize))]
36#[cfg_attr(feature = "replay", derive(Deserialize))]
37pub struct PictureCacheEntry {
38 pub size: DeviceIntSize,
40 pub last_access: FrameStamp,
45 pub uv_rect_handle: GpuCacheHandle,
47 pub texture_id: CacheTextureId,
49}
50
51impl PictureCacheEntry {
52 fn update_gpu_cache(&mut self, gpu_cache: &mut GpuCache) {
53 if let Some(mut request) = gpu_cache.request(&mut self.uv_rect_handle) {
54 let origin = DeviceIntPoint::zero();
55 let image_source = ImageSource {
56 p0: origin.to_f32(),
57 p1: (origin + self.size).to_f32(),
58 uv_rect_kind: UvRectKind::Rect,
59 user_data: [0.0; 4],
60 };
61 image_source.write_gpu_blocks(&mut request);
62 }
63 }
64}
65
66#[cfg_attr(feature = "capture", derive(Serialize))]
68#[cfg_attr(feature = "replay", derive(Deserialize))]
69struct PictureTexture {
70 texture_id: CacheTextureId,
71 size: DeviceIntSize,
72 is_allocated: bool,
73 last_frame_used: FrameId,
74}
75
76#[cfg_attr(feature = "capture", derive(Serialize))]
78#[cfg_attr(feature = "replay", derive(Deserialize))]
79pub struct PictureTextures {
80 textures: Vec<PictureTexture>,
82 default_tile_size: DeviceIntSize,
84 allocated_texture_count: usize,
86 filter: TextureFilter,
88
89 debug_flags: DebugFlags,
90
91 cache_entries: FreeList<PictureCacheEntry, PictureCacheEntryMarker>,
93 cache_handles: Vec<FreeListHandle<PictureCacheEntryMarker>>,
95
96 now: FrameStamp,
97}
98
99impl PictureTextures {
100 pub fn new(
101 default_tile_size: DeviceIntSize,
102 filter: TextureFilter,
103 ) -> Self {
104 PictureTextures {
105 textures: Vec::new(),
106 default_tile_size,
107 allocated_texture_count: 0,
108 filter,
109 debug_flags: DebugFlags::empty(),
110 cache_entries: FreeList::new(),
111 cache_handles: Vec::new(),
112 now: FrameStamp::INVALID,
113 }
114 }
115
116 pub fn begin_frame(&mut self, stamp: FrameStamp, pending_updates: &mut TextureUpdateList) {
117 self.now = stamp;
118
119 self.expire_old_tiles(pending_updates);
123 }
124
125 pub fn default_tile_size(&self) -> DeviceIntSize {
126 self.default_tile_size
127 }
128
129 pub fn update(
130 &mut self,
131 tile_size: DeviceIntSize,
132 handle: &mut Option<PictureCacheTextureHandle>,
133 gpu_cache: &mut GpuCache,
134 next_texture_id: &mut CacheTextureId,
135 pending_updates: &mut TextureUpdateList,
136 ) {
137 debug_assert!(self.now.is_valid());
138 debug_assert!(tile_size.width > 0 && tile_size.height > 0);
139
140 let need_alloc = match handle {
141 None => true,
142 Some(handle) => {
143 !self.entry_exists(&handle)
145 },
146 };
147
148 if need_alloc {
149 let new_handle = self.get_or_allocate_tile(
150 tile_size,
151 next_texture_id,
152 pending_updates,
153 );
154
155 *handle = Some(new_handle);
156 }
157
158 if let Some(handle) = handle {
159 self.cache_entries
161 .get_opt_mut(handle)
162 .expect("BUG: handle must be valid now")
163 .update_gpu_cache(gpu_cache);
164 } else {
165 panic!("The handle should be valid picture cache handle now")
166 }
167 }
168
169 pub fn get_or_allocate_tile(
170 &mut self,
171 tile_size: DeviceIntSize,
172 next_texture_id: &mut CacheTextureId,
173 pending_updates: &mut TextureUpdateList,
174 ) -> PictureCacheTextureHandle {
175 let mut texture_id = None;
176 self.allocated_texture_count += 1;
177
178 for texture in &mut self.textures {
179 if texture.size == tile_size && !texture.is_allocated {
180 texture.is_allocated = true;
183 texture.last_frame_used = FrameId::INVALID;
184 texture_id = Some(texture.texture_id);
185 break;
186 }
187 }
188
189 let texture_id = texture_id.unwrap_or_else(|| {
192 let texture_id = *next_texture_id;
193 next_texture_id.0 += 1;
194
195 let info = TextureCacheAllocInfo {
197 target: ImageBufferKind::Texture2D,
198 width: tile_size.width,
199 height: tile_size.height,
200 format: ImageFormat::RGBA8,
201 filter: self.filter,
202 is_shared_cache: false,
203 has_depth: true,
204 category: TextureCacheCategory::PictureTile,
205 };
206
207 pending_updates.push_alloc(texture_id, info);
208
209 self.textures.push(PictureTexture {
210 texture_id,
211 is_allocated: true,
212 size: tile_size,
213 last_frame_used: FrameId::INVALID,
214 });
215
216 texture_id
217 });
218
219 let cache_entry = PictureCacheEntry {
220 size: tile_size,
221 last_access: self.now,
222 uv_rect_handle: GpuCacheHandle::new(),
223 texture_id,
224 };
225
226 let strong_handle = self.cache_entries.insert(cache_entry);
228 let new_handle = strong_handle.weak();
229
230 self.cache_handles.push(strong_handle);
231
232 new_handle
233 }
234
235 pub fn free_tile(
236 &mut self,
237 id: CacheTextureId,
238 current_frame_id: FrameId,
239 pending_updates: &mut TextureUpdateList,
240 ) {
241 self.allocated_texture_count -= 1;
242
243 let texture = self.textures
244 .iter_mut()
245 .find(|t| t.texture_id == id)
246 .expect("bug: invalid texture id");
247
248 assert!(texture.is_allocated);
249 texture.is_allocated = false;
250
251 assert_eq!(texture.last_frame_used, FrameId::INVALID);
252 texture.last_frame_used = current_frame_id;
253
254 if self.debug_flags.contains(
255 DebugFlags::TEXTURE_CACHE_DBG |
256 DebugFlags::TEXTURE_CACHE_DBG_CLEAR_EVICTED)
257 {
258 pending_updates.push_debug_clear(
259 id,
260 DeviceIntPoint::zero(),
261 texture.size.width,
262 texture.size.height,
263 );
264 }
265 }
266
267 pub fn request(&mut self, handle: &PictureCacheTextureHandle, gpu_cache: &mut GpuCache) -> bool {
268 let entry = self.cache_entries.get_opt_mut(handle);
269 let now = self.now;
270 entry.map_or(true, |entry| {
271 entry.last_access = now;
274 entry.update_gpu_cache(gpu_cache);
275 false
276 })
277 }
278
279 pub fn get_texture_source(&self, handle: &PictureCacheTextureHandle) -> TextureSource {
280 let entry = self.cache_entries.get_opt(handle)
281 .expect("BUG: was dropped from cache or not updated!");
282
283 debug_assert_eq!(entry.last_access, self.now);
284
285 TextureSource::TextureCache(entry.texture_id, Swizzle::default())
286 }
287
288 pub fn expire_old_tiles(&mut self, pending_updates: &mut TextureUpdateList) {
292 for i in (0 .. self.cache_handles.len()).rev() {
293 let evict = {
294 let entry = self.cache_entries.get(
295 &self.cache_handles[i]
296 );
297
298 entry.last_access.frame_id() < self.now.frame_id() - 1
303 };
304
305 if evict {
306 let handle = self.cache_handles.swap_remove(i);
307 let entry = self.cache_entries.free(handle);
308 self.free_tile(entry.texture_id, self.now.frame_id(), pending_updates);
309 }
310 }
311 }
312
313 pub fn clear(&mut self, pending_updates: &mut TextureUpdateList) {
314 for handle in mem::take(&mut self.cache_handles) {
315 let entry = self.cache_entries.free(handle);
316 self.free_tile(entry.texture_id, self.now.frame_id(), pending_updates);
317 }
318
319 for texture in self.textures.drain(..) {
320 pending_updates.push_free(texture.texture_id);
321 }
322 }
323
324 pub fn update_profile(&self, profile: &mut TransactionProfile) {
325 profile.set(profiler::PICTURE_TILES, self.textures.len());
326 }
327
328 pub fn gc(
330 &mut self,
331 pending_updates: &mut TextureUpdateList,
332 ) {
333 let free_texture_count = self.textures.len() - self.allocated_texture_count;
337 let allowed_retained_count = (self.allocated_texture_count as f32 * 0.25).ceil() as usize;
338 let do_gc = free_texture_count > allowed_retained_count;
339
340 if do_gc {
341 self.textures.sort_unstable_by_key(|t| cmp::Reverse(t.last_frame_used));
343
344 let mut allocated_targets = SmallVec::<[PictureTexture; 32]>::new();
346 let mut retained_targets = SmallVec::<[PictureTexture; 32]>::new();
347
348 for target in self.textures.drain(..) {
349 if target.is_allocated {
350 allocated_targets.push(target);
352 } else if retained_targets.len() < allowed_retained_count {
353 retained_targets.push(target);
355 } else {
356 assert_ne!(target.last_frame_used, FrameId::INVALID);
358 pending_updates.push_free(target.texture_id);
359 }
360 }
361
362 self.textures.extend(retained_targets);
363 self.textures.extend(allocated_targets);
364 }
365 }
366
367 pub fn entry_exists(&self, handle: &PictureCacheTextureHandle) -> bool {
368 self.cache_entries.get_opt(handle).is_some()
369 }
370
371 pub fn set_debug_flags(&mut self, flags: DebugFlags) {
372 self.debug_flags = flags;
373 }
374
375 #[cfg(feature = "replay")]
376 pub fn filter(&self) -> TextureFilter {
377 self.filter
378 }
379}