Skip to content

Commit a89f9a3

Browse files
aeitwoenoritwoen
andauthored
refactor(gpu): double-buffer DP slots and merge copy encoder (#33)
Eliminate the conditional second GPU round-trip by copying both dp_count and dp_buffer to staging in a single command encoder. Add two alternating DP slots (buffer + count + staging + bind_group) so each dispatch writes to the opposite slot, preparing for future CPU/GPU overlap. - GpuBuffers now holds DpSlot[2] with per-slot accessors - step() rotates current_slot 0↔1 after each dispatch - Calibration resets both slots after warmup Co-authored-by: oritwoen <18102267+oritwoen@users.noreply.github.com>
1 parent b36a7e3 commit a89f9a3

2 files changed

Lines changed: 142 additions & 124 deletions

File tree

src/gpu/buffers.rs

Lines changed: 99 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,38 @@
11
//! 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.
25
36
use super::{
47
GpuAffinePoint, GpuConfig, GpuContext, GpuDistinguishedPoint, GpuKangaroo, KangarooPipeline,
58
};
69
use anyhow::Result;
710
use wgpu::{BindGroup, Buffer, BufferUsages};
811

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
1024
pub struct GpuBuffers {
1125
pub config_buffer: Buffer,
1226
#[allow(dead_code)]
1327
jump_points_buffer: Buffer,
1428
#[allow(dead_code)]
1529
jump_distances_buffer: Buffer,
1630
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],
2132
}
2233

2334
impl GpuBuffers {
24-
/// Create GPU buffers
35+
/// Create GPU buffers with double-buffered DP slots
2536
pub fn new(
2637
ctx: &GpuContext,
2738
pipeline: &KangarooPipeline,
@@ -49,80 +60,106 @@ impl GpuBuffers {
4960
jump_distances,
5061
);
5162

52-
// Kangaroos buffer
5363
let kangaroos_buffer = ctx.create_buffer::<GpuKangaroo>(
5464
"Kangaroos Buffer",
5565
BufferUsages::STORAGE | BufferUsages::COPY_DST | BufferUsages::COPY_SRC,
5666
num_kangaroos as u64,
5767
);
5868

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
7569
let kangaroos_size = (num_kangaroos as usize) * std::mem::size_of::<GpuKangaroo>();
7670
let dp_size = (max_dps as usize) * std::mem::size_of::<GpuDistinguishedPoint>();
7771
let staging_size = std::cmp::max(kangaroos_size, dp_size) as u64 + 4;
7872

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+
);
8487

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+
}
115135
});
116136

117137
Ok(Self {
118138
config_buffer,
119139
jump_points_buffer,
120140
jump_distances_buffer,
121141
kangaroos_buffer,
122-
dp_buffer,
123-
dp_count_buffer,
124-
staging_buffer,
125-
bind_group,
142+
slots,
126143
})
127144
}
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+
}
128165
}

0 commit comments

Comments
 (0)