|
1 | 1 | //! GPU buffer management |
| 2 | +//! |
| 3 | +//! Uses double-buffered DP slots so the CPU can read back results from |
| 4 | +//! the previous dispatch while the GPU is already executing the next one. |
2 | 5 |
|
3 | 6 | use super::{ |
4 | 7 | GpuAffinePoint, GpuConfig, GpuContext, GpuDistinguishedPoint, GpuKangaroo, KangarooPipeline, |
5 | 8 | }; |
6 | 9 | use anyhow::Result; |
7 | 10 | use wgpu::{BindGroup, Buffer, BufferUsages}; |
8 | 11 |
|
9 | | -/// GPU buffer collection |
| 12 | +/// Number of DP buffer slots for double buffering |
| 13 | +const NUM_SLOTS: usize = 2; |
| 14 | + |
| 15 | +/// One slot of DP-related buffers (dp_buffer + dp_count + staging + bind_group) |
| 16 | +struct DpSlot { |
| 17 | + dp_buffer: Buffer, |
| 18 | + dp_count_buffer: Buffer, |
| 19 | + staging_buffer: Buffer, |
| 20 | + bind_group: BindGroup, |
| 21 | +} |
| 22 | + |
| 23 | +/// GPU buffer collection with double-buffered DP slots |
10 | 24 | pub struct GpuBuffers { |
11 | 25 | pub config_buffer: Buffer, |
12 | 26 | #[allow(dead_code)] |
13 | 27 | jump_points_buffer: Buffer, |
14 | 28 | #[allow(dead_code)] |
15 | 29 | jump_distances_buffer: Buffer, |
16 | 30 | pub kangaroos_buffer: Buffer, |
17 | | - pub dp_buffer: Buffer, |
18 | | - pub dp_count_buffer: Buffer, |
19 | | - pub staging_buffer: Buffer, |
20 | | - pub bind_group: BindGroup, |
| 31 | + slots: [DpSlot; NUM_SLOTS], |
21 | 32 | } |
22 | 33 |
|
23 | 34 | impl GpuBuffers { |
24 | | - /// Create GPU buffers |
| 35 | + /// Create GPU buffers with double-buffered DP slots |
25 | 36 | pub fn new( |
26 | 37 | ctx: &GpuContext, |
27 | 38 | pipeline: &KangarooPipeline, |
@@ -49,80 +60,106 @@ impl GpuBuffers { |
49 | 60 | jump_distances, |
50 | 61 | ); |
51 | 62 |
|
52 | | - // Kangaroos buffer |
53 | 63 | let kangaroos_buffer = ctx.create_buffer::<GpuKangaroo>( |
54 | 64 | "Kangaroos Buffer", |
55 | 65 | BufferUsages::STORAGE | BufferUsages::COPY_DST | BufferUsages::COPY_SRC, |
56 | 66 | num_kangaroos as u64, |
57 | 67 | ); |
58 | 68 |
|
59 | | - // DP buffer |
60 | | - let dp_buffer = ctx.create_buffer::<GpuDistinguishedPoint>( |
61 | | - "DP Buffer", |
62 | | - BufferUsages::STORAGE | BufferUsages::COPY_SRC, |
63 | | - max_dps as u64, |
64 | | - ); |
65 | | - |
66 | | - // DP count buffer (atomic u32) |
67 | | - let dp_count_buffer = ctx.create_buffer_init( |
68 | | - "DP Count Buffer", |
69 | | - BufferUsages::STORAGE | BufferUsages::COPY_SRC | BufferUsages::COPY_DST, |
70 | | - &[0u32], |
71 | | - ); |
72 | | - |
73 | | - // Staging buffer for readback |
74 | | - // Must be large enough to hold either kangaroos (for normalization) or DPs |
75 | 69 | let kangaroos_size = (num_kangaroos as usize) * std::mem::size_of::<GpuKangaroo>(); |
76 | 70 | let dp_size = (max_dps as usize) * std::mem::size_of::<GpuDistinguishedPoint>(); |
77 | 71 | let staging_size = std::cmp::max(kangaroos_size, dp_size) as u64 + 4; |
78 | 72 |
|
79 | | - let staging_buffer = ctx.create_buffer::<u8>( |
80 | | - "Staging Buffer", |
81 | | - BufferUsages::MAP_READ | BufferUsages::COPY_DST, |
82 | | - staging_size, |
83 | | - ); |
| 73 | + let slots = std::array::from_fn(|i| { |
| 74 | + let label_suffix = if i == 0 { "A" } else { "B" }; |
| 75 | + |
| 76 | + let dp_buffer = ctx.create_buffer::<GpuDistinguishedPoint>( |
| 77 | + &format!("DP Buffer {label_suffix}"), |
| 78 | + BufferUsages::STORAGE | BufferUsages::COPY_SRC, |
| 79 | + max_dps as u64, |
| 80 | + ); |
| 81 | + |
| 82 | + let dp_count_buffer = ctx.create_buffer_init( |
| 83 | + &format!("DP Count Buffer {label_suffix}"), |
| 84 | + BufferUsages::STORAGE | BufferUsages::COPY_SRC | BufferUsages::COPY_DST, |
| 85 | + &[0u32], |
| 86 | + ); |
84 | 87 |
|
85 | | - // Create bind group |
86 | | - let bind_group = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor { |
87 | | - label: Some("Kangaroo Bind Group"), |
88 | | - layout: &pipeline.bind_group_layout, |
89 | | - entries: &[ |
90 | | - wgpu::BindGroupEntry { |
91 | | - binding: 0, |
92 | | - resource: config_buffer.as_entire_binding(), |
93 | | - }, |
94 | | - wgpu::BindGroupEntry { |
95 | | - binding: 1, |
96 | | - resource: jump_points_buffer.as_entire_binding(), |
97 | | - }, |
98 | | - wgpu::BindGroupEntry { |
99 | | - binding: 2, |
100 | | - resource: jump_distances_buffer.as_entire_binding(), |
101 | | - }, |
102 | | - wgpu::BindGroupEntry { |
103 | | - binding: 3, |
104 | | - resource: kangaroos_buffer.as_entire_binding(), |
105 | | - }, |
106 | | - wgpu::BindGroupEntry { |
107 | | - binding: 4, |
108 | | - resource: dp_buffer.as_entire_binding(), |
109 | | - }, |
110 | | - wgpu::BindGroupEntry { |
111 | | - binding: 5, |
112 | | - resource: dp_count_buffer.as_entire_binding(), |
113 | | - }, |
114 | | - ], |
| 88 | + let staging_buffer = ctx.create_buffer::<u8>( |
| 89 | + &format!("Staging Buffer {label_suffix}"), |
| 90 | + BufferUsages::MAP_READ | BufferUsages::COPY_DST, |
| 91 | + staging_size, |
| 92 | + ); |
| 93 | + |
| 94 | + let bind_group = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor { |
| 95 | + label: Some(if i == 0 { |
| 96 | + "Kangaroo Bind Group A" |
| 97 | + } else { |
| 98 | + "Kangaroo Bind Group B" |
| 99 | + }), |
| 100 | + layout: &pipeline.bind_group_layout, |
| 101 | + entries: &[ |
| 102 | + wgpu::BindGroupEntry { |
| 103 | + binding: 0, |
| 104 | + resource: config_buffer.as_entire_binding(), |
| 105 | + }, |
| 106 | + wgpu::BindGroupEntry { |
| 107 | + binding: 1, |
| 108 | + resource: jump_points_buffer.as_entire_binding(), |
| 109 | + }, |
| 110 | + wgpu::BindGroupEntry { |
| 111 | + binding: 2, |
| 112 | + resource: jump_distances_buffer.as_entire_binding(), |
| 113 | + }, |
| 114 | + wgpu::BindGroupEntry { |
| 115 | + binding: 3, |
| 116 | + resource: kangaroos_buffer.as_entire_binding(), |
| 117 | + }, |
| 118 | + wgpu::BindGroupEntry { |
| 119 | + binding: 4, |
| 120 | + resource: dp_buffer.as_entire_binding(), |
| 121 | + }, |
| 122 | + wgpu::BindGroupEntry { |
| 123 | + binding: 5, |
| 124 | + resource: dp_count_buffer.as_entire_binding(), |
| 125 | + }, |
| 126 | + ], |
| 127 | + }); |
| 128 | + |
| 129 | + DpSlot { |
| 130 | + dp_buffer, |
| 131 | + dp_count_buffer, |
| 132 | + staging_buffer, |
| 133 | + bind_group, |
| 134 | + } |
115 | 135 | }); |
116 | 136 |
|
117 | 137 | Ok(Self { |
118 | 138 | config_buffer, |
119 | 139 | jump_points_buffer, |
120 | 140 | jump_distances_buffer, |
121 | 141 | kangaroos_buffer, |
122 | | - dp_buffer, |
123 | | - dp_count_buffer, |
124 | | - staging_buffer, |
125 | | - bind_group, |
| 142 | + slots, |
126 | 143 | }) |
127 | 144 | } |
| 145 | + |
| 146 | + /// Get the bind group for a given slot |
| 147 | + pub fn bind_group(&self, slot: usize) -> &BindGroup { |
| 148 | + &self.slots[slot].bind_group |
| 149 | + } |
| 150 | + |
| 151 | + /// Get the DP buffer for a given slot |
| 152 | + pub fn dp_buffer(&self, slot: usize) -> &Buffer { |
| 153 | + &self.slots[slot].dp_buffer |
| 154 | + } |
| 155 | + |
| 156 | + /// Get the DP count buffer for a given slot |
| 157 | + pub fn dp_count_buffer(&self, slot: usize) -> &Buffer { |
| 158 | + &self.slots[slot].dp_count_buffer |
| 159 | + } |
| 160 | + |
| 161 | + /// Get the staging buffer for a given slot |
| 162 | + pub fn staging_buffer(&self, slot: usize) -> &Buffer { |
| 163 | + &self.slots[slot].staging_buffer |
| 164 | + } |
128 | 165 | } |
0 commit comments