11use crate :: binding:: { BindingRequirements , BindingUtil } ;
22use bit_set:: BitSet ;
33use librashader_common:: map:: FastHashMap ;
4- use librashader_common:: Size ;
4+ use librashader_common:: { ImageFormat , Size } ;
55use librashader_reflect:: reflect:: semantics:: BindingMeta ;
66use std:: collections:: VecDeque ;
77use std:: ops:: { Index , IndexMut } ;
8- use num_traits:: AsPrimitive ;
8+
9+ #[ derive( Debug , Copy , Clone , PartialEq , Eq , Hash ) ]
10+ pub ( crate ) struct FramebufferKey {
11+ pub size : Size < u32 > ,
12+ pub format : ImageFormat ,
13+ pub mipmap : bool ,
14+ }
915
1016#[ derive( Debug , Copy , Clone , PartialEq , Eq , Hash ) ]
1117pub ( crate ) struct Slot ( i32 ) ;
@@ -37,18 +43,18 @@ pub struct FramebufferPool<F> {
3743}
3844
3945impl < F > FramebufferPool < F > {
40- /// Calculate liveness assignments based on pass sizes .
41- pub ( crate ) fn prepare ( & mut self , sizes : & [ Size < u32 > ] ) {
46+ /// Calculate liveness assignments based on pass allocation requirements .
47+ pub ( crate ) fn prepare ( & mut self , keys : & [ FramebufferKey ] ) {
4248 struct Event {
4349 slot : usize ,
44- size : u64 ,
50+ key : FramebufferKey ,
4551 next_slot : usize ,
4652 }
4753
48- let n = sizes . len ( ) ;
54+ let n = keys . len ( ) ;
4955
50- // `free[size ]` holds slots currently free and of that size .
51- let mut free: FastHashMap < u64 , Vec < usize > > = FastHashMap :: default ( ) ;
56+ // `free[key ]` holds slots currently free and allocation-compatible .
57+ let mut free: FastHashMap < FramebufferKey , Vec < usize > > = FastHashMap :: default ( ) ;
5258
5359 let mut freed_at = vec ! [ usize :: MAX ; n + 1 ] ;
5460 let mut events: Vec < Event > = Vec :: with_capacity ( n) ;
@@ -57,21 +63,21 @@ impl<F> FramebufferPool<F> {
5763 for pass in 0 ..n {
5864 let mut event = freed_at[ pass] ;
5965 while event != usize:: MAX {
60- let Event { slot, size , next_slot } = events[ event] ;
61- free. entry ( size ) . or_default ( ) . push ( slot) ;
66+ let Event { slot, key , next_slot } = events[ event] ;
67+ free. entry ( key ) . or_default ( ) . push ( slot) ;
6268 event = next_slot;
6369 }
6470
65- let size = sizes [ pass] . as_ ( ) ;
66- let slot = free. get_mut ( & size ) . and_then ( Vec :: pop) . unwrap_or_else ( || {
71+ let key = keys [ pass] ;
72+ let slot = free. get_mut ( & key ) . and_then ( Vec :: pop) . unwrap_or_else ( || {
6773 let slot = slot_count;
6874 slot_count += 1 ;
6975 slot
7076 } ) ;
7177
7278 self . slots [ pass] = Slot :: new ( slot) ;
7379 let free_at = self . last_use [ pass] + 1 ;
74- events. push ( Event { slot, size , next_slot : freed_at[ free_at] } ) ;
80+ events. push ( Event { slot, key , next_slot : freed_at[ free_at] } ) ;
7581 freed_at[ free_at] = events. len ( ) - 1 ;
7682 }
7783 }
@@ -254,8 +260,8 @@ fn init_feedback_framebuffers<F, I, E>(
254260
255261#[ cfg( test) ]
256262mod tests {
257- use super :: { FramebufferPool , Slot } ;
258- use librashader_common:: Size ;
263+ use super :: { FramebufferKey , FramebufferPool , Slot } ;
264+ use librashader_common:: { ImageFormat , Size } ;
259265 use std:: collections:: HashSet ;
260266
261267 fn fb ( last_use : Vec < usize > ) -> FramebufferPool < ( ) > {
@@ -267,6 +273,14 @@ mod tests {
267273 }
268274 }
269275
276+ fn key ( size : Size < u32 > ) -> FramebufferKey {
277+ FramebufferKey {
278+ size,
279+ format : ImageFormat :: R8G8B8A8Unorm ,
280+ mipmap : false ,
281+ }
282+ }
283+
270284 fn distinct ( slots : & [ Slot ] ) -> usize {
271285 slots. iter ( ) . copied ( ) . collect :: < HashSet < _ > > ( ) . len ( )
272286 }
@@ -275,42 +289,79 @@ mod tests {
275289 fn liveness_pools_chain ( ) {
276290 let size = |w, h| Size :: < u32 > :: new ( w, h) ;
277291 let last_use = vec ! [ 1 , 2 , 10 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 10 ] ;
278- let sizes = vec ! [
279- size( 1280 , 960 ) , // p0
280- size( 1280 , 960 ) , // p1
281- size( 1280 , 3360 ) , // p2 (retained, read by p3..p10)
282- size( 1280 , 1680 ) , // p3
283- size( 1280 , 1680 ) , // p4
284- size( 1280 , 1680 ) , // p5
285- size( 1280 , 1680 ) , // p6
286- size( 1280 , 1680 ) , // p7
287- size( 1280 , 1680 ) , // p8
288- size( 3840 , 1680 ) , // p9
289- size( 1280 , 720 ) , // p10 (final / viewport)
292+ let keys = vec ! [
293+ key ( size( 1280 , 960 ) ) , // p0
294+ key ( size( 1280 , 960 ) ) , // p1
295+ key ( size( 1280 , 3360 ) ) , // p2 (retained, read by p3..p10)
296+ key ( size( 1280 , 1680 ) ) , // p3
297+ key ( size( 1280 , 1680 ) ) , // p4
298+ key ( size( 1280 , 1680 ) ) , // p5
299+ key ( size( 1280 , 1680 ) ) , // p6
300+ key ( size( 1280 , 1680 ) ) , // p7
301+ key ( size( 1280 , 1680 ) ) , // p8
302+ key ( size( 3840 , 1680 ) ) , // p9
303+ key ( size( 1280 , 720 ) ) , // p10 (final / viewport)
290304 ] ;
291305
292306 let mut output = fb ( last_use) ;
293- output. prepare ( & sizes ) ;
307+ output. prepare ( & keys ) ;
294308
295309 // p3..=p8 (six same-size passes, each live only into the next) collapse onto two
296310 // ping-pong buffers.
297311 assert_eq ! ( distinct( & output. slots[ 3 ..=8 ] ) , 2 ) ;
298312
299313 // The whole chain needs 7 physical buffers instead of 11, and no buffer is ever
300- // assigned two different sizes (the invariant that keeps it deferred-safe).
314+ // assigned two different allocation keys (the invariant that keeps it deferred-safe).
301315 assert_eq ! ( distinct( & output. slots) , 7 ) ;
302- let mut seen: std:: collections:: HashMap < Slot , Size < u32 > > = Default :: default ( ) ;
316+ let mut seen: std:: collections:: HashMap < Slot , FramebufferKey > = Default :: default ( ) ;
303317 for ( pass, & slot) in output. slots . iter ( ) . enumerate ( ) {
304- assert_eq ! ( * seen. entry( slot) . or_insert( sizes [ pass] ) , sizes [ pass] ) ;
318+ assert_eq ! ( * seen. entry( slot) . or_insert( keys [ pass] ) , keys [ pass] ) ;
305319 }
306320 }
307321
308322 // A uniform-size linear chain (the common case) collapses to a 2-buffer ping-pong.
309323 #[ test]
310324 fn liveness_pools_uniform_chain ( ) {
311- let sizes = vec ! [ Size :: <u32 >:: new( 1920 , 1080 ) ; 8 ] ;
325+ let keys = vec ! [ key ( Size :: <u32 >:: new( 1920 , 1080 ) ) ; 8 ] ;
312326 let mut output = fb ( vec ! [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 7 ] ) ;
313- output. prepare ( & sizes ) ;
327+ output. prepare ( & keys ) ;
314328 assert_eq ! ( distinct( & output. slots) , 2 ) ;
315329 }
330+
331+ // Same-size passes with different formats or mipmap requirements must never share a
332+ // buffer: a pooled `scale` would recreate it mid-frame and destroy an image still
333+ // referenced by a deferred command buffer (koko-aio's bloom chain hit this).
334+ #[ test]
335+ fn liveness_respects_format_and_mipmap ( ) {
336+ let size = Size :: < u32 > :: new ( 960 , 540 ) ;
337+ let keys = vec ! [
338+ FramebufferKey {
339+ size,
340+ format: ImageFormat :: R8G8B8A8Unorm ,
341+ mipmap: false ,
342+ } ,
343+ FramebufferKey {
344+ size,
345+ format: ImageFormat :: R8G8B8A8Unorm ,
346+ mipmap: true ,
347+ } ,
348+ FramebufferKey {
349+ size,
350+ format: ImageFormat :: R16G16B16A16Sfloat ,
351+ mipmap: false ,
352+ } ,
353+ FramebufferKey {
354+ size,
355+ format: ImageFormat :: R8G8B8A8Unorm ,
356+ mipmap: false ,
357+ } ,
358+ ] ;
359+
360+ let mut output = fb ( vec ! [ 1 , 2 , 3 , 3 ] ) ;
361+ output. prepare ( & keys) ;
362+
363+ // p3 may only reuse p0's buffer (same key); p1 and p2 differ in mipmap/format.
364+ assert_eq ! ( output. slots[ 3 ] , output. slots[ 0 ] ) ;
365+ assert_eq ! ( distinct( & output. slots) , 3 ) ;
366+ }
316367}
0 commit comments