Skip to content

Commit c64d90a

Browse files
authored
Improve GPU memory management and error handling (Rust-SDL2#87)
This change wraps the inner points for the GPU Shader, Texture, GraphicsPipeline, Buffer, and TransferBuffer resources in Arcs so that manual memory management isn't necessary. These resources require the device to be released, so they also track a weak pointer back to the device, and their release is conditional on the device still existing. The device itself now has its raw pointer wrapped in an Arc. Clone implementations are added to all of these to make working with them in a larger project easier. The release functions were all removed, and the resources can simply be more idiomatically dropped now. Additionally, the `wait_and_acquire_swapchain_texture` and `acquire_swapchain_texture` functions were moved from the Device into the CommandBuffer. This made more sense organizationally since neither functions use the device at all, and their first argument is the command buffer pointer. Finally, missing error handling was added for many unsafe SDL function calls.
1 parent 5d838fb commit c64d90a

File tree

4 files changed

+354
-212
lines changed

4 files changed

+354
-212
lines changed

examples/gpu-clear.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
1717
// by default, and we specify that our shaders will be SPIR-V ones (even through we
1818
// aren't using any shaders)
1919
// We'll also turn on debug mode to true, so we get debug stuff
20-
let gpu = sdl3::gpu::Device::new(sdl3::gpu::ShaderFormat::SpirV, true).with_window(&window)?;
20+
let gpu = sdl3::gpu::Device::new(sdl3::gpu::ShaderFormat::SpirV, true)?.with_window(&window)?;
2121

2222
let mut event_pump = sdl_context.event_pump()?;
2323
println!(
@@ -40,12 +40,11 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
4040
//
4141
// This is because a swapchain needs to be "allocated", and it can quickly run out
4242
// if we don't properly time the rendering process.
43-
let mut command_buffer = gpu.acquire_command_buffer();
44-
if let Ok(swapchain) = gpu.wait_and_acquire_swapchain_texture(&window, &mut command_buffer)
45-
{
43+
let mut command_buffer = gpu.acquire_command_buffer()?;
44+
if let Ok(swapchain) = command_buffer.wait_and_acquire_swapchain_texture(&window) {
4645
let color_targets = [
4746
sdl3::gpu::ColorTargetInfo::default()
48-
.with_texture(swapchain) // Use swapchain texture
47+
.with_texture(&swapchain) // Use swapchain texture
4948
.with_load_op(sdl3::gpu::LoadOp::Clear) // Clear when load
5049
.with_store_op(sdl3::gpu::StoreOp::Store) // Store back
5150
.with_clear_color(sdl3::pixels::Color::RGB(5, 3, 255)), //blue with small RG bias

examples/gpu-cube.rs

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use sdl3::{
1111
},
1212
keyboard::Keycode,
1313
pixels::Color,
14+
Error,
1415
};
1516

1617
extern crate sdl3;
@@ -87,10 +88,10 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
8788
.build()
8889
.map_err(|e| e.to_string())?;
8990

90-
let gpu = sdl3::gpu::Device::new(
91+
let gpu = Device::new(
9192
ShaderFormat::SpirV | ShaderFormat::Dxil | ShaderFormat::Dxbc | ShaderFormat::MetalLib,
9293
true,
93-
)
94+
)?
9495
.with_window(&window)?;
9596

9697
// Our shaders, require to be precompiled by a SPIR-V compiler beforehand
@@ -127,7 +128,7 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
127128
VertexInputState::new()
128129
.with_vertex_buffer_descriptions(&[VertexBufferDescription::new()
129130
.with_slot(0)
130-
.with_pitch((size_of::<f32>() * 3) as u32) // 3 floats per vertex
131+
.with_pitch(size_of::<VertexPosition>() as u32)
131132
.with_input_rate(VertexInputRate::Vertex)
132133
.with_instance_step_rate(0)])
133134
.with_vertex_attributes(&[VertexAttribute::new()
@@ -160,8 +161,8 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
160161
.build()?;
161162

162163
// The pipeline now holds copies of our shaders, so we can release them
163-
vert_shader.release(&gpu);
164-
frag_shader.release(&gpu);
164+
drop(vert_shader);
165+
drop(frag_shader);
165166

166167
// Next, we create a transfer buffer that is large enough to hold either
167168
// our vertices or indices since we will be transferring both with it.
@@ -171,10 +172,10 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
171172
.create_transfer_buffer()
172173
.with_size(vertices_len_bytes.max(indices_len_bytes) as u32)
173174
.with_usage(TransferBufferUsage::Upload)
174-
.build();
175+
.build()?;
175176

176177
// We need to start a copy pass in order to transfer data to the GPU
177-
let copy_commands = gpu.acquire_command_buffer();
178+
let copy_commands = gpu.acquire_command_buffer()?;
178179
let copy_pass = gpu.begin_copy_pass(&copy_commands)?;
179180

180181
// Create GPU buffers to hold our vertices and indices and transfer data to them
@@ -184,17 +185,17 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
184185
&copy_pass,
185186
BufferUsageFlags::Vertex,
186187
&CUBE_VERTICES,
187-
);
188+
)?;
188189
let index_buffer = create_buffer_with_data(
189190
&gpu,
190191
&transfer_buffer,
191192
&copy_pass,
192193
BufferUsageFlags::Index,
193194
&CUBE_INDICES,
194-
);
195+
)?;
195196

196197
// We're done with the transfer buffer now, so release it.
197-
transfer_buffer.release(&gpu);
198+
drop(transfer_buffer);
198199

199200
// Now complete and submit the copy pass commands to actually do the transfer work
200201
gpu.end_copy_pass(copy_pass);
@@ -211,7 +212,7 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
211212
.with_sample_count(SampleCount::NoMultiSampling)
212213
.with_format(TextureFormat::D16Unorm)
213214
.with_usage(TextureUsage::Sampler | TextureUsage::DepthStencilTarget),
214-
);
215+
)?;
215216

216217
let mut rotation = 45.0f32;
217218
let mut event_pump = sdl_context.event_pump()?;
@@ -232,12 +233,11 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
232233
//
233234
// This is because a swapchain needs to be "allocated", and it can quickly run out
234235
// if we don't properly time the rendering process.
235-
let mut command_buffer = gpu.acquire_command_buffer();
236-
if let Ok(swapchain) = gpu.wait_and_acquire_swapchain_texture(&window, &mut command_buffer)
237-
{
236+
let mut command_buffer = gpu.acquire_command_buffer()?;
237+
if let Ok(swapchain) = command_buffer.wait_and_acquire_swapchain_texture(&window) {
238238
// Again, like in gpu-clear.rs, we'd want to define basic operations for our cube
239239
let color_targets = [ColorTargetInfo::default()
240-
.with_texture(swapchain)
240+
.with_texture(&swapchain)
241241
.with_load_op(LoadOp::Clear)
242242
.with_store_op(StoreOp::Store)
243243
.with_clear_color(Color::RGB(128, 128, 128))];
@@ -296,7 +296,7 @@ fn create_buffer_with_data<T: Copy>(
296296
copy_pass: &CopyPass,
297297
usage: BufferUsageFlags,
298298
data: &[T],
299-
) -> Buffer {
299+
) -> Result<Buffer, Error> {
300300
// Figure out the length of the data in bytes
301301
let len_bytes = data.len() * std::mem::size_of::<T>();
302302

@@ -305,7 +305,7 @@ fn create_buffer_with_data<T: Copy>(
305305
.create_buffer()
306306
.with_size(len_bytes as u32)
307307
.with_usage(usage)
308-
.build();
308+
.build()?;
309309

310310
// Map the transfer buffer's memory into a place we can copy into, and copy the data
311311
//
@@ -335,5 +335,5 @@ fn create_buffer_with_data<T: Copy>(
335335
true,
336336
);
337337

338-
buffer
338+
Ok(buffer)
339339
}

examples/gpu-triangle.rs

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
extern crate sdl3;
22

33
use sdl3::keyboard::Keycode;
4-
use sdl3::pixels::Color;
54
use sdl3::{event::Event, gpu::GraphicsPipelineTargetInfo};
6-
use sdl3_sys::render;
7-
use std::time::Duration;
85

96
pub fn main() -> Result<(), Box<dyn std::error::Error>> {
107
let sdl_context = sdl3::init()?;
@@ -26,7 +23,7 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
2623
| sdl3::gpu::ShaderFormat::Dxbc
2724
| sdl3::gpu::ShaderFormat::MetalLib,
2825
true,
29-
)
26+
)?
3027
.with_window(&window)?;
3128

3229
let fs_source = include_bytes!("shaders/triangle.frag.spv");
@@ -73,8 +70,9 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
7370
)
7471
.build()?;
7572

76-
vs_shader.release(&gpu);
77-
fs_shader.release(&gpu);
73+
// The pipeline now holds copies of our shaders, so we can release them
74+
drop(vs_shader);
75+
drop(fs_shader);
7876

7977
let mut event_pump = sdl_context.event_pump()?;
8078
println!(
@@ -97,13 +95,12 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
9795
//
9896
// This is because a swapchain needs to be "allocated", and it can quickly run out
9997
// if we don't properly time the rendering process.
100-
let mut command_buffer = gpu.acquire_command_buffer();
101-
if let Ok(swapchain) = gpu.wait_and_acquire_swapchain_texture(&window, &mut command_buffer)
102-
{
98+
let mut command_buffer = gpu.acquire_command_buffer()?;
99+
if let Ok(swapchain) = command_buffer.wait_and_acquire_swapchain_texture(&window) {
103100
// Again, like in gpu-clear.rs, we'd want to define basic operations for our triangle
104101
let color_targets = [
105102
sdl3::gpu::ColorTargetInfo::default()
106-
.with_texture(swapchain)
103+
.with_texture(&swapchain)
107104
.with_load_op(sdl3::gpu::LoadOp::Clear)
108105
.with_store_op(sdl3::gpu::StoreOp::Store)
109106
.with_clear_color(sdl3::pixels::Color::RGB(5, 3, 255)), //blue with small RG bias
@@ -120,5 +117,6 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
120117
command_buffer.cancel();
121118
}
122119
}
120+
123121
Ok(())
124122
}

0 commit comments

Comments
 (0)