1use std::mem;
6use smallvec::SmallVec;
7use api::{ImageFormat, ImageBufferKind, DebugFlags, TextureCacheCategory};
8use api::units::*;
9use crate::device::TextureFilter;
10use crate::internal_types::{
11 CacheTextureId, TextureUpdateList, Swizzle, TextureCacheAllocInfo,
12 TextureSource, FrameStamp, FrameId,
13};
14use crate::profiler::{self, TransactionProfile};
15use crate::freelist::{FreeList, FreeListHandle, WeakFreeListHandle};
16
17#[derive(Debug, PartialEq)]
18#[cfg_attr(feature = "capture", derive(Serialize))]
19#[cfg_attr(feature = "replay", derive(Deserialize))]
20pub enum PictureCacheEntryMarker {}
21
22malloc_size_of::malloc_size_of_is_0!(PictureCacheEntryMarker);
23
24pub type PictureCacheTextureHandle = WeakFreeListHandle<PictureCacheEntryMarker>;
25
26use std::cmp;
27
28#[derive(Debug)]
32#[cfg_attr(feature = "capture", derive(Serialize))]
33#[cfg_attr(feature = "replay", derive(Deserialize))]
34pub struct PictureCacheEntry {
35 pub last_access: FrameStamp,
37 pub texture_id: CacheTextureId,
39}
40
41#[cfg_attr(feature = "capture", derive(Serialize))]
43#[cfg_attr(feature = "replay", derive(Deserialize))]
44struct PictureTexture {
45 texture_id: CacheTextureId,
46 size: DeviceIntSize,
47 is_allocated: bool,
48 last_frame_used: FrameId,
49}
50
51#[cfg_attr(feature = "capture", derive(Serialize))]
53#[cfg_attr(feature = "replay", derive(Deserialize))]
54pub struct PictureTextures {
55 textures: Vec<PictureTexture>,
57 default_tile_size: DeviceIntSize,
59 allocated_texture_count: usize,
61 filter: TextureFilter,
63
64 debug_flags: DebugFlags,
65
66 cache_entries: FreeList<PictureCacheEntry, PictureCacheEntryMarker>,
68 cache_handles: Vec<FreeListHandle<PictureCacheEntryMarker>>,
70
71 now: FrameStamp,
72}
73
74impl PictureTextures {
75 pub fn new(
76 default_tile_size: DeviceIntSize,
77 filter: TextureFilter,
78 ) -> Self {
79 PictureTextures {
80 textures: Vec::new(),
81 default_tile_size,
82 allocated_texture_count: 0,
83 filter,
84 debug_flags: DebugFlags::empty(),
85 cache_entries: FreeList::new(),
86 cache_handles: Vec::new(),
87 now: FrameStamp::INVALID,
88 }
89 }
90
91 pub fn begin_frame(&mut self, stamp: FrameStamp, pending_updates: &mut TextureUpdateList) {
92 self.now = stamp;
93
94 self.expire_old_tiles(pending_updates);
98 }
99
100 pub fn default_tile_size(&self) -> DeviceIntSize {
101 self.default_tile_size
102 }
103
104 pub fn update(
105 &mut self,
106 tile_size: DeviceIntSize,
107 handle: &mut Option<PictureCacheTextureHandle>,
108 next_texture_id: &mut CacheTextureId,
109 pending_updates: &mut TextureUpdateList,
110 ) {
111 debug_assert!(self.now.is_valid());
112 debug_assert!(tile_size.width > 0 && tile_size.height > 0);
113
114 let need_alloc = match handle {
115 None => true,
116 Some(handle) => {
117 !self.entry_exists(&handle)
119 },
120 };
121
122 if need_alloc {
123 let new_handle = self.get_or_allocate_tile(
124 tile_size,
125 next_texture_id,
126 pending_updates,
127 );
128
129 *handle = Some(new_handle);
130 }
131
132 assert!(handle.is_some(), "The handle should be valid picture cache handle now");
133 }
134
135 pub fn get_or_allocate_tile(
136 &mut self,
137 tile_size: DeviceIntSize,
138 next_texture_id: &mut CacheTextureId,
139 pending_updates: &mut TextureUpdateList,
140 ) -> PictureCacheTextureHandle {
141 let mut texture_id = None;
142 self.allocated_texture_count += 1;
143
144 for texture in &mut self.textures {
145 if texture.size == tile_size && !texture.is_allocated {
146 texture.is_allocated = true;
149 texture.last_frame_used = FrameId::INVALID;
150 texture_id = Some(texture.texture_id);
151 break;
152 }
153 }
154
155 let texture_id = texture_id.unwrap_or_else(|| {
158 let texture_id = *next_texture_id;
159 next_texture_id.0 += 1;
160
161 let info = TextureCacheAllocInfo {
163 target: ImageBufferKind::Texture2D,
164 width: tile_size.width,
165 height: tile_size.height,
166 format: ImageFormat::RGBA8,
167 filter: self.filter,
168 is_shared_cache: false,
169 has_depth: true,
170 category: TextureCacheCategory::PictureTile,
171 };
172
173 pending_updates.push_alloc(texture_id, info);
174
175 self.textures.push(PictureTexture {
176 texture_id,
177 is_allocated: true,
178 size: tile_size,
179 last_frame_used: FrameId::INVALID,
180 });
181
182 texture_id
183 });
184
185 let cache_entry = PictureCacheEntry {
186 last_access: self.now,
187 texture_id,
188 };
189
190 let strong_handle = self.cache_entries.insert(cache_entry);
192 let new_handle = strong_handle.weak();
193
194 self.cache_handles.push(strong_handle);
195
196 new_handle
197 }
198
199 pub fn free_tile(
200 &mut self,
201 id: CacheTextureId,
202 current_frame_id: FrameId,
203 pending_updates: &mut TextureUpdateList,
204 ) {
205 self.allocated_texture_count -= 1;
206
207 let texture = self.textures
208 .iter_mut()
209 .find(|t| t.texture_id == id)
210 .expect("bug: invalid texture id");
211
212 assert!(texture.is_allocated);
213 texture.is_allocated = false;
214
215 assert_eq!(texture.last_frame_used, FrameId::INVALID);
216 texture.last_frame_used = current_frame_id;
217
218 if self.debug_flags.contains(
219 DebugFlags::TEXTURE_CACHE_DBG |
220 DebugFlags::TEXTURE_CACHE_DBG_CLEAR_EVICTED)
221 {
222 pending_updates.push_debug_clear(
223 id,
224 DeviceIntPoint::zero(),
225 texture.size.width,
226 texture.size.height,
227 );
228 }
229 }
230
231 pub fn request(&mut self, handle: &PictureCacheTextureHandle) -> bool {
232 let entry = self.cache_entries.get_opt_mut(handle);
233 let now = self.now;
234 entry.map_or(true, |entry| {
235 entry.last_access = now;
238 false
239 })
240 }
241
242 pub fn get_texture_source(&self, handle: &PictureCacheTextureHandle) -> TextureSource {
243 let entry = self.cache_entries.get_opt(handle)
244 .expect("BUG: was dropped from cache or not updated!");
245
246 debug_assert_eq!(entry.last_access, self.now);
247
248 TextureSource::TextureCache(entry.texture_id, Swizzle::default())
249 }
250
251 pub fn expire_old_tiles(&mut self, pending_updates: &mut TextureUpdateList) {
255 for i in (0 .. self.cache_handles.len()).rev() {
256 let evict = {
257 let entry = self.cache_entries.get(
258 &self.cache_handles[i]
259 );
260
261 entry.last_access.frame_id() < self.now.frame_id() - 1
266 };
267
268 if evict {
269 let handle = self.cache_handles.swap_remove(i);
270 let entry = self.cache_entries.free(handle);
271 self.free_tile(entry.texture_id, self.now.frame_id(), pending_updates);
272 }
273 }
274 }
275
276 pub fn clear(&mut self, pending_updates: &mut TextureUpdateList) {
277 for handle in mem::take(&mut self.cache_handles) {
278 let entry = self.cache_entries.free(handle);
279 self.free_tile(entry.texture_id, self.now.frame_id(), pending_updates);
280 }
281
282 for texture in self.textures.drain(..) {
283 pending_updates.push_free(texture.texture_id);
284 }
285 }
286
287 pub fn update_profile(&self, profile: &mut TransactionProfile) {
288 profile.set(profiler::PICTURE_TILES, self.textures.len());
289 }
290
291 pub fn gc(
293 &mut self,
294 pending_updates: &mut TextureUpdateList,
295 ) {
296 let free_texture_count = self.textures.len() - self.allocated_texture_count;
300 let allowed_retained_count = (self.allocated_texture_count as f32 * 0.25).ceil() as usize;
301 let do_gc = free_texture_count > allowed_retained_count;
302
303 if do_gc {
304 self.textures.sort_unstable_by_key(|t| cmp::Reverse(t.last_frame_used));
306
307 let mut allocated_targets = SmallVec::<[PictureTexture; 32]>::new();
309 let mut retained_targets = SmallVec::<[PictureTexture; 32]>::new();
310
311 for target in self.textures.drain(..) {
312 if target.is_allocated {
313 allocated_targets.push(target);
315 } else if retained_targets.len() < allowed_retained_count {
316 retained_targets.push(target);
318 } else {
319 assert_ne!(target.last_frame_used, FrameId::INVALID);
321 pending_updates.push_free(target.texture_id);
322 }
323 }
324
325 self.textures.extend(retained_targets);
326 self.textures.extend(allocated_targets);
327 }
328 }
329
330 pub fn entry_exists(&self, handle: &PictureCacheTextureHandle) -> bool {
331 self.cache_entries.get_opt(handle).is_some()
332 }
333
334 pub fn set_debug_flags(&mut self, flags: DebugFlags) {
335 self.debug_flags = flags;
336 }
337
338 #[cfg(feature = "replay")]
339 pub fn filter(&self) -> TextureFilter {
340 self.filter
341 }
342}