sdl3/gpu/
device.rs

1use crate::{
2    get_error,
3    gpu::{
4        BufferBuilder, ColorTargetInfo, CommandBuffer, CopyPass, DepthStencilTargetInfo,
5        GraphicsPipelineBuilder, PresentMode, RenderPass, Sampler, SamplerCreateInfo,
6        ShaderBuilder, ShaderFormat, SwapchainComposition, Texture, TextureCreateInfo,
7        TextureFormat, TransferBufferBuilder,
8    },
9    sys,
10    video::Window,
11    Error,
12};
13use std::sync::{Arc, Weak};
14use sys::gpu::{
15    SDL_BeginGPUComputePass, SDL_BeginGPUCopyPass, SDL_BeginGPURenderPass, SDL_CreateGPUDevice,
16    SDL_CreateGPUSampler, SDL_CreateGPUTexture, SDL_DestroyGPUDevice, SDL_GPUColorTargetInfo,
17    SDL_GPUDepthStencilTargetInfo, SDL_GPUDevice, SDL_GPUViewport,
18    SDL_GetGPUSwapchainTextureFormat, SDL_SetGPUViewport,
19};
20
21use super::{
22    pass::Fence,
23    pipeline::{StorageBufferReadWriteBinding, StorageTextureReadWriteBinding},
24    ComputePass, ComputePipelineBuilder,
25};
26
27pub struct Viewport {
28    inner: SDL_GPUViewport,
29}
30impl Viewport {
31    #[doc(alias = "SDL_GPUViewport")]
32    pub fn new(x: f32, y: f32, w: f32, h: f32, min_depth: f32, max_depth: f32) -> Self {
33        Self {
34            inner: SDL_GPUViewport {
35                x,
36                y,
37                w,
38                h,
39                min_depth,
40                max_depth,
41            },
42        }
43    }
44
45    #[inline]
46    pub fn raw(&self) -> *const SDL_GPUViewport {
47        &self.inner
48    }
49}
50
51/// Manages the raw `SDL_GPUDevice` pointer and releases it on drop
52pub(super) struct DeviceContainer(*mut SDL_GPUDevice);
53impl DeviceContainer {
54    pub(super) fn raw(&self) -> *mut SDL_GPUDevice {
55        self.0
56    }
57}
58impl Drop for DeviceContainer {
59    #[doc(alias = "SDL_DestroyGPUDevice")]
60    fn drop(&mut self) {
61        unsafe { SDL_DestroyGPUDevice(self.0) }
62    }
63}
64
65pub(super) type WeakDevice = Weak<DeviceContainer>;
66
67#[derive(Clone)]
68pub struct Device {
69    inner: Arc<DeviceContainer>,
70    // Keep a `VideoSubsystem` in each `Device`,
71    // to properly drop data in the right order
72    subsystem: Option<crate::VideoSubsystem>,
73}
74impl Device {
75    #[inline]
76    pub fn raw(&self) -> *mut SDL_GPUDevice {
77        self.inner.0
78    }
79
80    pub(super) fn weak(&self) -> WeakDevice {
81        Arc::downgrade(&self.inner)
82    }
83
84    #[doc(alias = "SDL_CreateGPUDevice")]
85    pub fn new(flags: ShaderFormat, debug_mode: bool) -> Result<Self, Error> {
86        let raw_device = unsafe { SDL_CreateGPUDevice(flags.0, debug_mode, std::ptr::null()) };
87        if raw_device.is_null() {
88            Err(get_error())
89        } else {
90            Ok(Self {
91                inner: Arc::new(DeviceContainer(raw_device)),
92                subsystem: None,
93            })
94        }
95    }
96
97    #[doc(alias = "SDL_ClaimWindowForGPUDevice")]
98    pub fn with_window(mut self, window: &crate::video::Window) -> Result<Self, Error> {
99        self.subsystem = Some(window.subsystem().clone());
100
101        let p = unsafe { sys::gpu::SDL_ClaimWindowForGPUDevice(self.inner.0, window.raw()) };
102        if p {
103            Ok(self)
104        } else {
105            Err(get_error())
106        }
107    }
108
109    #[doc(alias = "SDL_AcquireGPUCommandBuffer")]
110    pub fn acquire_command_buffer(&self) -> Result<CommandBuffer, Error> {
111        let raw_buffer = unsafe { sys::gpu::SDL_AcquireGPUCommandBuffer(self.inner.0) };
112        if raw_buffer.is_null() {
113            Err(get_error())
114        } else {
115            Ok(CommandBuffer::new(raw_buffer))
116        }
117    }
118
119    pub fn create_shader(&self) -> ShaderBuilder<'_> {
120        ShaderBuilder::new(self)
121    }
122
123    #[doc(alias = "SDL_CreateGPUBuffer")]
124    pub fn create_buffer(&self) -> BufferBuilder<'_> {
125        BufferBuilder::new(self)
126    }
127
128    #[doc(alias = "SDL_CreateGPUTransferBuffer")]
129    pub fn create_transfer_buffer(&self) -> TransferBufferBuilder<'_> {
130        TransferBufferBuilder::new(self)
131    }
132
133    #[doc(alias = "SDL_CreateGPUSampler")]
134    pub fn create_sampler(&self, create_info: SamplerCreateInfo) -> Result<Sampler, Error> {
135        let raw_sampler = unsafe { SDL_CreateGPUSampler(self.raw(), &create_info.inner) };
136        if raw_sampler.is_null() {
137            Err(get_error())
138        } else {
139            Ok(Sampler::new(self, raw_sampler))
140        }
141    }
142
143    #[doc(alias = "SDL_CreateGPUTexture")]
144    pub fn create_texture(
145        &self,
146        create_info: TextureCreateInfo,
147    ) -> Result<Texture<'static>, Error> {
148        let raw_texture = unsafe { SDL_CreateGPUTexture(self.raw(), &create_info.inner) };
149        if raw_texture.is_null() {
150            Err(get_error())
151        } else {
152            Ok(Texture::new(
153                self,
154                raw_texture,
155                create_info.inner.width,
156                create_info.inner.height,
157            ))
158        }
159    }
160
161    #[doc(alias = "SDL_SetGPUViewport")]
162    pub fn set_viewport(&self, render_pass: &RenderPass, viewport: Viewport) {
163        unsafe { SDL_SetGPUViewport(render_pass.inner, viewport.raw()) }
164    }
165
166    pub fn get_swapchain_texture_format(&self, w: &crate::video::Window) -> TextureFormat {
167        unsafe { std::mem::transmute(SDL_GetGPUSwapchainTextureFormat(self.inner.0, w.raw()).0) }
168    }
169
170    // You cannot begin another render pass, or begin a compute pass or copy pass until you have ended the render pass.
171    #[doc(alias = "SDL_BeginGPURenderPass")]
172    pub fn begin_render_pass(
173        &self,
174        command_buffer: &CommandBuffer,
175        color_info: &[ColorTargetInfo],
176        depth_stencil_target: Option<&DepthStencilTargetInfo>,
177    ) -> Result<RenderPass, Error> {
178        let p = unsafe {
179            SDL_BeginGPURenderPass(
180                command_buffer.inner,
181                color_info.as_ptr() as *const SDL_GPUColorTargetInfo, //heavy promise
182                color_info.len() as u32,
183                if let Some(p) = depth_stencil_target {
184                    p as *const _ as *const SDL_GPUDepthStencilTargetInfo //heavy promise
185                } else {
186                    std::ptr::null()
187                },
188            )
189        };
190        if !p.is_null() {
191            Ok(RenderPass { inner: p })
192        } else {
193            Err(get_error())
194        }
195    }
196
197    #[doc(alias = "SDL_EndGPURenderPass")]
198    pub fn end_render_pass(&self, pass: RenderPass) {
199        unsafe {
200            sys::gpu::SDL_EndGPURenderPass(pass.inner);
201        }
202    }
203
204    #[doc(alias = "SDL_BeginGPUCopyPass")]
205    pub fn begin_copy_pass(&self, command_buffer: &CommandBuffer) -> Result<CopyPass, Error> {
206        let p = unsafe { SDL_BeginGPUCopyPass(command_buffer.inner) };
207        if !p.is_null() {
208            Ok(CopyPass { inner: p })
209        } else {
210            Err(get_error())
211        }
212    }
213    #[doc(alias = "SDL_EndGPUCopyPass")]
214    pub fn end_copy_pass(&self, pass: CopyPass) {
215        unsafe {
216            sys::gpu::SDL_EndGPUCopyPass(pass.inner);
217        }
218    }
219
220    #[doc(alias = "SDL_BeginGPUComputePass")]
221    pub fn begin_compute_pass(
222        &self,
223        command_buffer: &CommandBuffer,
224        storage_texture_bindings: &[StorageTextureReadWriteBinding],
225        storage_buffer_bindings: &[StorageBufferReadWriteBinding],
226    ) -> Result<ComputePass, Error> {
227        let p = unsafe {
228            SDL_BeginGPUComputePass(
229                command_buffer.inner,
230                storage_texture_bindings.as_ptr().cast(),
231                storage_texture_bindings.len() as u32,
232                storage_buffer_bindings.as_ptr().cast(),
233                storage_buffer_bindings.len() as u32,
234            )
235        };
236        if !p.is_null() {
237            Ok(ComputePass { inner: p })
238        } else {
239            Err(get_error())
240        }
241    }
242    #[doc(alias = "SDL_EndGPUComputePass")]
243    pub fn end_compute_pass(&self, pass: ComputePass) {
244        unsafe {
245            sys::gpu::SDL_EndGPUComputePass(pass.inner);
246        }
247    }
248
249    pub fn create_graphics_pipeline<'a>(&'a self) -> GraphicsPipelineBuilder<'a> {
250        GraphicsPipelineBuilder::new(self)
251    }
252
253    pub fn create_compute_pipeline<'a>(&'a self) -> ComputePipelineBuilder<'a> {
254        ComputePipelineBuilder::new(self)
255    }
256
257    #[doc(alias = "SDL_WaitForGPUFences")]
258    pub fn wait_fences(&self, wait_all: bool, fences: &[Fence]) -> Result<(), Error> {
259        let fences: Vec<_> = fences.iter().map(|x| x.raw()).collect();
260        unsafe {
261            if !sys::gpu::SDL_WaitForGPUFences(
262                self.raw(),
263                wait_all,
264                fences.as_ptr(),
265                fences.len() as u32,
266            ) {
267                Err(get_error())
268            } else {
269                Ok(())
270            }
271        }
272    }
273
274    #[doc(alias = "SDL_GetGPUShaderFormats")]
275    pub fn get_shader_formats(&self) -> ShaderFormat {
276        unsafe { std::mem::transmute(sys::gpu::SDL_GetGPUShaderFormats(self.raw())) }
277    }
278
279    #[doc(alias = "SDL_SetGPUSwapchainParameters")]
280    pub fn set_swapchain_parameters(
281        &self,
282        window: &Window,
283        present_mode: PresentMode,
284        swapchain_composition: SwapchainComposition,
285    ) -> Result<(), Error> {
286        let raw_device_ptr = self.raw();
287        let raw_window_ptr = window.raw();
288
289        let c_present_mode = sys::gpu::SDL_GPUPresentMode(present_mode as i32);
290        let c_swapchain_composition =
291            sys::gpu::SDL_GPUSwapchainComposition(swapchain_composition as i32);
292
293        let success = unsafe {
294            sys::gpu::SDL_SetGPUSwapchainParameters(
295                raw_device_ptr,
296                raw_window_ptr,
297                c_swapchain_composition,
298                c_present_mode,
299            )
300        };
301
302        if success {
303            Ok(())
304        } else {
305            Err(get_error())
306        }
307    }
308
309    // NOTE: for Xbox builds, the target is a UWP, e.g.: x86_64-uwp-windows-msvc
310    #[cfg(target_vendor = "uwp")]
311    #[doc(alias = "SDL_GDKSuspendGPU")]
312    pub fn gdk_suspend(&self) {
313        unsafe {
314            sys::gpu::SDL_GDKSuspendGPU(self.inner);
315        }
316    }
317
318    #[cfg(target_vendor = "uwp")]
319    #[doc(alias = "SDL_GDKResumeGPU")]
320    pub fn gdk_resume(&self) {
321        unsafe {
322            sys::gpu::SDL_GDKResumeGPU(self.inner);
323        }
324    }
325}