Skip to content

Commit 6c18fc6

Browse files
committed
[add] buffer abstraction and replace direct wgpu usage in lambda-rs.
1 parent d3dc435 commit 6c18fc6

File tree

3 files changed

+185
-31
lines changed

3 files changed

+185
-31
lines changed
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
//! Buffer wrappers and builders for the platform layer.
2+
//!
3+
//! This module provides a thin wrapper over `wgpu::Buffer` plus a small
4+
//! builder that handles common initialization patterns and keeps label and
5+
//! usage metadata for debugging/inspection.
6+
7+
use crate::wgpu::{
8+
types as wgpu,
9+
types::util::DeviceExt,
10+
};
11+
12+
#[derive(Clone, Copy, Debug)]
13+
/// Platform buffer usage flags.
14+
pub struct Usage(pub(crate) wgpu::BufferUsages);
15+
16+
impl Usage {
17+
/// Vertex buffer usage.
18+
pub const VERTEX: Usage = Usage(wgpu::BufferUsages::VERTEX);
19+
/// Index buffer usage.
20+
pub const INDEX: Usage = Usage(wgpu::BufferUsages::INDEX);
21+
/// Uniform buffer usage.
22+
pub const UNIFORM: Usage = Usage(wgpu::BufferUsages::UNIFORM);
23+
/// Storage buffer usage.
24+
pub const STORAGE: Usage = Usage(wgpu::BufferUsages::STORAGE);
25+
/// Copy destination (for CPU-visible uploads).
26+
pub const COPY_DST: Usage = Usage(wgpu::BufferUsages::COPY_DST);
27+
28+
pub(crate) fn to_wgpu(self) -> wgpu::BufferUsages {
29+
return self.0;
30+
}
31+
}
32+
33+
impl std::ops::BitOr for Usage {
34+
type Output = Usage;
35+
fn bitor(self, rhs: Usage) -> Usage {
36+
return Usage(self.0 | rhs.0);
37+
}
38+
}
39+
40+
impl Default for Usage {
41+
fn default() -> Self {
42+
return Usage(wgpu::BufferUsages::VERTEX);
43+
}
44+
}
45+
46+
#[derive(Debug)]
47+
/// Wrapper around `wgpu::Buffer` with metadata.
48+
pub struct Buffer {
49+
pub(crate) raw: wgpu::Buffer,
50+
pub(crate) label: Option<String>,
51+
pub(crate) size: wgpu::BufferAddress,
52+
pub(crate) usage: wgpu::BufferUsages,
53+
}
54+
55+
impl Buffer {
56+
/// Borrow the underlying `wgpu::Buffer`.
57+
pub fn raw(&self) -> &wgpu::Buffer {
58+
return &self.raw;
59+
}
60+
61+
/// Optional debug label.
62+
pub fn label(&self) -> Option<&str> {
63+
return self.label.as_deref();
64+
}
65+
66+
/// Size in bytes at creation time.
67+
pub fn size(&self) -> wgpu::BufferAddress {
68+
return self.size;
69+
}
70+
71+
/// Usage flags used to create the buffer.
72+
pub fn usage(&self) -> wgpu::BufferUsages {
73+
return self.usage;
74+
}
75+
}
76+
77+
#[derive(Default)]
78+
/// Builder for creating a `Buffer` with optional initial contents.
79+
pub struct BufferBuilder {
80+
label: Option<String>,
81+
size: usize,
82+
usage: Usage,
83+
cpu_visible: bool,
84+
}
85+
86+
impl BufferBuilder {
87+
/// Create a new builder with zero size and VERTEX usage.
88+
pub fn new() -> Self {
89+
return Self {
90+
label: None,
91+
size: 0,
92+
usage: Usage::VERTEX,
93+
cpu_visible: false,
94+
};
95+
}
96+
97+
/// Attach a label for debugging/profiling.
98+
pub fn with_label(mut self, label: &str) -> Self {
99+
self.label = Some(label.to_string());
100+
return self;
101+
}
102+
103+
/// Set the total size in bytes. If zero, size is inferred from data length.
104+
pub fn with_size(mut self, size: usize) -> Self {
105+
self.size = size;
106+
return self;
107+
}
108+
109+
/// Set usage flags.
110+
pub fn with_usage(mut self, usage: Usage) -> Self {
111+
self.usage = usage;
112+
return self;
113+
}
114+
115+
/// Hint that buffer will be updated from CPU via queue writes.
116+
pub fn with_cpu_visible(mut self, cpu_visible: bool) -> Self {
117+
self.cpu_visible = cpu_visible;
118+
return self;
119+
}
120+
121+
/// Create a buffer initialized with `contents`.
122+
pub fn build_init(self, device: &wgpu::Device, contents: &[u8]) -> Buffer {
123+
let size = if self.size == 0 {
124+
contents.len()
125+
} else {
126+
self.size
127+
};
128+
129+
let mut usage = self.usage.to_wgpu();
130+
if self.cpu_visible {
131+
usage |= wgpu::BufferUsages::COPY_DST;
132+
}
133+
134+
let raw = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
135+
label: self.label.as_deref(),
136+
contents,
137+
usage,
138+
});
139+
140+
return Buffer {
141+
raw,
142+
label: self.label,
143+
size: size as wgpu::BufferAddress,
144+
usage,
145+
};
146+
}
147+
}
148+
149+
#[cfg(test)]
150+
mod tests {
151+
use super::*;
152+
153+
#[test]
154+
fn usage_bitor_combines_flags() {
155+
let u = Usage::VERTEX | Usage::INDEX | Usage::UNIFORM;
156+
let flags = u.to_wgpu();
157+
assert!(flags.contains(wgpu::BufferUsages::VERTEX));
158+
assert!(flags.contains(wgpu::BufferUsages::INDEX));
159+
assert!(flags.contains(wgpu::BufferUsages::UNIFORM));
160+
}
161+
}

crates/lambda-rs-platform/src/wgpu/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use wgpu::rwh::{
1616
use crate::winit::WindowHandle;
1717

1818
pub mod bind;
19+
pub mod buffer;
1920

2021
#[derive(Debug, Clone)]
2122
/// Builder for creating a `wgpu::Instance` with consistent defaults.

crates/lambda-rs/src/render/buffer.rs

Lines changed: 23 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
33
use std::rc::Rc;
44

5-
use lambda_platform::wgpu::types::{
6-
self as wgpu,
7-
util::DeviceExt,
5+
use lambda_platform::wgpu::{
6+
buffer as platform_buffer,
7+
types as wgpu,
88
};
99

1010
use super::{
@@ -26,22 +26,20 @@ pub enum BufferType {
2626
}
2727

2828
#[derive(Clone, Copy, Debug)]
29-
/// A thin newtype for `wgpu::BufferUsages` that supports bitwise ops while
30-
/// keeping explicit construction points in the API surface.
31-
pub struct Usage(wgpu::BufferUsages);
29+
/// Buffer usage flags (engine-facing), mapped to platform usage internally.
30+
pub struct Usage(platform_buffer::Usage);
3231

3332
impl Usage {
3433
/// Mark buffer usable as a vertex buffer.
35-
pub const VERTEX: Usage = Usage(wgpu::BufferUsages::VERTEX);
34+
pub const VERTEX: Usage = Usage(platform_buffer::Usage::VERTEX);
3635
/// Mark buffer usable as an index buffer.
37-
pub const INDEX: Usage = Usage(wgpu::BufferUsages::INDEX);
36+
pub const INDEX: Usage = Usage(platform_buffer::Usage::INDEX);
3837
/// Mark buffer usable as a uniform buffer.
39-
pub const UNIFORM: Usage = Usage(wgpu::BufferUsages::UNIFORM);
38+
pub const UNIFORM: Usage = Usage(platform_buffer::Usage::UNIFORM);
4039
/// Mark buffer usable as a storage buffer.
41-
pub const STORAGE: Usage = Usage(wgpu::BufferUsages::STORAGE);
40+
pub const STORAGE: Usage = Usage(platform_buffer::Usage::STORAGE);
4241

43-
/// Extract the inner `wgpu` flags.
44-
pub fn to_wgpu(self) -> wgpu::BufferUsages {
42+
fn to_platform(self) -> platform_buffer::Usage {
4543
self.0
4644
}
4745
}
@@ -50,7 +48,7 @@ impl std::ops::BitOr for Usage {
5048
type Output = Usage;
5149

5250
fn bitor(self, rhs: Usage) -> Usage {
53-
Usage(self.0 | rhs.0)
51+
return Usage(self.0 | rhs.0);
5452
}
5553
}
5654

@@ -90,8 +88,8 @@ impl Default for Properties {
9088
/// when binding to pipeline inputs.
9189
#[derive(Debug)]
9290
pub struct Buffer {
93-
buffer: Rc<wgpu::Buffer>,
94-
stride: wgpu::BufferAddress,
91+
buffer: Rc<platform_buffer::Buffer>,
92+
stride: u64,
9593
buffer_type: BufferType,
9694
}
9795

@@ -101,14 +99,10 @@ impl Buffer {
10199
pub fn destroy(self, _render_context: &RenderContext) {}
102100

103101
pub(super) fn raw(&self) -> &wgpu::Buffer {
104-
return self.buffer.as_ref();
102+
return self.buffer.raw();
105103
}
106104

107-
pub(super) fn raw_rc(&self) -> Rc<wgpu::Buffer> {
108-
return self.buffer.clone();
109-
}
110-
111-
pub(super) fn stride(&self) -> wgpu::BufferAddress {
105+
pub(super) fn stride(&self) -> u64 {
112106
return self.stride;
113107
}
114108

@@ -244,7 +238,6 @@ impl BufferBuilder {
244238
render_context: &mut RenderContext,
245239
data: Vec<Data>,
246240
) -> Result<Buffer, &'static str> {
247-
let device = render_context.device();
248241
let element_size = std::mem::size_of::<Data>();
249242
let buffer_length = if self.buffer_length == 0 {
250243
element_size * data.len()
@@ -266,20 +259,19 @@ impl BufferBuilder {
266259
)
267260
};
268261

269-
let mut usage = self.usage.to_wgpu();
270-
if self.properties.cpu_visible() {
271-
usage |= wgpu::BufferUsages::COPY_DST;
262+
let mut builder = platform_buffer::BufferBuilder::new()
263+
.with_size(buffer_length)
264+
.with_usage(self.usage.to_platform())
265+
.with_cpu_visible(self.properties.cpu_visible());
266+
if let Some(label) = &self.label {
267+
builder = builder.with_label(label);
272268
}
273269

274-
let buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
275-
label: self.label.as_deref(),
276-
contents: bytes,
277-
usage,
278-
});
270+
let buffer = builder.build_init(render_context.device(), bytes);
279271

280272
return Ok(Buffer {
281273
buffer: Rc::new(buffer),
282-
stride: element_size as wgpu::BufferAddress,
274+
stride: element_size as u64,
283275
buffer_type: self.buffer_type,
284276
});
285277
}

0 commit comments

Comments
 (0)