@@ -8,29 +8,29 @@ use crate::renderer::{RenderDevice, RenderQueue};
8
8
9
9
use super :: Buffer ;
10
10
11
- pub struct StorageVec < T : AsStd430 + Default > {
11
+ /// A helper for a storage buffer binding with a variable-sized array
12
+ /// Use a struct with length: u32, data: array<T> in the shader.
13
+ pub struct StorageVec < T : AsStd430 > {
12
14
values : Vec < T > ,
13
15
scratch : Vec < u8 > ,
14
16
storage_buffer : Option < Buffer > ,
15
17
capacity : usize ,
16
18
item_size : usize ,
17
19
}
18
20
19
- impl < T : AsStd430 + Default > Default for StorageVec < T > {
21
+ impl < T : AsStd430 > Default for StorageVec < T > {
20
22
fn default ( ) -> Self {
21
23
Self {
22
24
values : Vec :: new ( ) ,
23
25
scratch : Vec :: new ( ) ,
24
26
storage_buffer : None ,
25
27
capacity : 0 ,
26
- // FIXME: Is this correct or even necessary? Maybe only need T::std430_size_static()?
27
- item_size : ( T :: std430_size_static ( ) + <T as AsStd430 >:: Output :: ALIGNMENT - 1 )
28
- & !( <T as AsStd430 >:: Output :: ALIGNMENT - 1 ) ,
28
+ item_size : T :: std430_size_static ( ) ,
29
29
}
30
30
}
31
31
}
32
32
33
- impl < T : AsStd430 + Default > StorageVec < T > {
33
+ impl < T : AsStd430 > StorageVec < T > {
34
34
#[ inline]
35
35
pub fn storage_buffer ( & self ) -> Option < & Buffer > {
36
36
self . storage_buffer . as_ref ( )
@@ -41,7 +41,7 @@ impl<T: AsStd430 + Default> StorageVec<T> {
41
41
Some ( BindingResource :: Buffer ( BufferBinding {
42
42
buffer : self . storage_buffer ( ) ?,
43
43
offset : 0 ,
44
- size : Some ( NonZeroU64 :: new ( ( self . item_size * self . values . len ( ) ) as u64 ) . unwrap ( ) ) ,
44
+ size : Some ( NonZeroU64 :: new ( ( self . size ( ) ) as u64 ) . unwrap ( ) ) ,
45
45
} ) )
46
46
}
47
47
@@ -71,9 +71,9 @@ impl<T: AsStd430 + Default> StorageVec<T> {
71
71
}
72
72
73
73
pub fn reserve ( & mut self , capacity : usize , device : & RenderDevice ) -> bool {
74
- if capacity > self . capacity {
74
+ if self . storage_buffer . is_none ( ) || capacity > self . capacity {
75
75
self . capacity = capacity;
76
- let size = self . item_size * capacity ;
76
+ let size = self . size ( ) ;
77
77
self . scratch . resize ( size, 0 ) ;
78
78
self . storage_buffer = Some ( device. create_buffer ( & BufferDescriptor {
79
79
label : None ,
@@ -87,16 +87,29 @@ impl<T: AsStd430 + Default> StorageVec<T> {
87
87
}
88
88
}
89
89
90
+ pub fn size ( & self ) -> usize {
91
+ // 4 bytes for a u32 array length plus the necessary padding according to T's alignment
92
+ ( ( 4 + <T as AsStd430 >:: Output :: ALIGNMENT - 1 ) & !( <T as AsStd430 >:: Output :: ALIGNMENT - 1 ) )
93
+ // Variable size arrays must have at least 1 element
94
+ + self . item_size * self . capacity . max ( 1 )
95
+ }
96
+
90
97
pub fn write_buffer ( & mut self , device : & RenderDevice , queue : & RenderQueue ) {
91
- if self . values . is_empty ( ) {
92
- self . values . push ( T :: default ( ) ) ;
93
- }
94
98
self . reserve ( self . values . len ( ) , device) ;
95
99
if let Some ( storage_buffer) = & self . storage_buffer {
96
- let range = 0 ..self . item_size * self . values . len ( ) ;
100
+ let range = 0 ..self . size ( ) ;
97
101
let mut writer = std430:: Writer :: new ( & mut self . scratch [ range. clone ( ) ] ) ;
102
+ // NOTE: Can only represent up to u32::MAX values
103
+ debug_assert ! ( self . values. len( ) <= u32 :: MAX as usize ) ;
98
104
// NOTE: Failing to write should be non-fatal. It would likely cause visual
99
105
// artifacts but a warning message should suffice.
106
+ // First write the length of the array
107
+ writer
108
+ . write ( & ( self . values . len ( ) as u32 ) )
109
+ . map_err ( |e| warn ! ( "{:?}" , e) )
110
+ . ok ( ) ;
111
+ // Then write the array. Note that padding bytes may be added between the array length
112
+ // and the array in order to align the array to the alignment requirements of T
100
113
writer
101
114
. write ( self . values . as_slice ( ) )
102
115
. map_err ( |e| warn ! ( "{:?}" , e) )
0 commit comments