sdl3/gpu/
device.rs

1use crate::{
2    get_error,
3    gpu::{
4        BufferBuilder, ColorTargetInfo, CommandBuffer, CopyPass, DepthStencilTargetInfo,
5        GraphicsPipelineBuilder, RenderPass, Sampler, SamplerCreateInfo, ShaderBuilder,
6        ShaderFormat, Texture, TextureCreateInfo, TextureFormat, TransferBufferBuilder,
7    },
8    sys, Error,
9};
10use std::sync::{Arc, Weak};
11use sys::gpu::{
12    SDL_BeginGPUComputePass, SDL_BeginGPUCopyPass, SDL_BeginGPURenderPass, SDL_CreateGPUDevice,
13    SDL_CreateGPUSampler, SDL_CreateGPUTexture, SDL_DestroyGPUDevice, SDL_GPUColorTargetInfo,
14    SDL_GPUDepthStencilTargetInfo, SDL_GPUDevice, SDL_GPUViewport,
15    SDL_GetGPUSwapchainTextureFormat, SDL_SetGPUViewport,
16};
17
18use super::{
19    pipeline::{StorageBufferReadWriteBinding, StorageTextureReadWriteBinding},
20    ComputePass, ComputePipelineBuilder,
21};
22
23/// Manages the raw `SDL_GPUDevice` pointer and releases it on drop
24pub(super) struct DeviceContainer(*mut SDL_GPUDevice);
25impl DeviceContainer {
26    pub(super) fn raw(&self) -> *mut SDL_GPUDevice {
27        self.0
28    }
29}
30impl Drop for DeviceContainer {
31    #[doc(alias = "SDL_DestroyGPUDevice")]
32    fn drop(&mut self) {
33        unsafe { SDL_DestroyGPUDevice(self.0) }
34    }
35}
36
37pub(super) type WeakDevice = Weak<DeviceContainer>;
38
39#[derive(Clone)]
40pub struct Device {
41    inner: Arc<DeviceContainer>,
42}
43impl Device {
44    #[inline]
45    pub fn raw(&self) -> *mut SDL_GPUDevice {
46        self.inner.0
47    }
48
49    pub(super) fn weak(&self) -> WeakDevice {
50        Arc::downgrade(&self.inner)
51    }
52
53    #[doc(alias = "SDL_CreateGPUDevice")]
54    pub fn new(flags: ShaderFormat, debug_mode: bool) -> Result<Self, Error> {
55        let raw_device = unsafe { SDL_CreateGPUDevice(flags as u32, debug_mode, std::ptr::null()) };
56        if raw_device.is_null() {
57            Err(get_error())
58        } else {
59            Ok(Self {
60                inner: Arc::new(DeviceContainer(raw_device)),
61            })
62        }
63    }
64
65    #[doc(alias = "SDL_ClaimWindowForGPUDevice")]
66    pub fn with_window(self, w: &crate::video::Window) -> Result<Self, Error> {
67        let p = unsafe { sys::gpu::SDL_ClaimWindowForGPUDevice(self.inner.0, w.raw()) };
68        if p {
69            Ok(self)
70        } else {
71            Err(get_error())
72        }
73    }
74
75    #[doc(alias = "SDL_AcquireGPUCommandBuffer")]
76    pub fn acquire_command_buffer(&self) -> Result<CommandBuffer, Error> {
77        let raw_buffer = unsafe { sys::gpu::SDL_AcquireGPUCommandBuffer(self.inner.0) };
78        if raw_buffer.is_null() {
79            Err(get_error())
80        } else {
81            Ok(CommandBuffer::new(raw_buffer))
82        }
83    }
84
85    pub fn create_shader(&self) -> ShaderBuilder {
86        ShaderBuilder::new(self)
87    }
88
89    #[doc(alias = "SDL_CreateGPUBuffer")]
90    pub fn create_buffer(&self) -> BufferBuilder {
91        BufferBuilder::new(self)
92    }
93
94    #[doc(alias = "SDL_CreateGPUTransferBuffer")]
95    pub fn create_transfer_buffer(&self) -> TransferBufferBuilder {
96        TransferBufferBuilder::new(self)
97    }
98
99    #[doc(alias = "SDL_CreateGPUSampler")]
100    pub fn create_sampler(&self, create_info: SamplerCreateInfo) -> Result<Sampler, Error> {
101        let raw_sampler = unsafe { SDL_CreateGPUSampler(self.raw(), &create_info.inner) };
102        if raw_sampler.is_null() {
103            Err(get_error())
104        } else {
105            Ok(Sampler::new(self, raw_sampler))
106        }
107    }
108
109    #[doc(alias = "SDL_CreateGPUTexture")]
110    pub fn create_texture(
111        &self,
112        create_info: TextureCreateInfo,
113    ) -> Result<Texture<'static>, Error> {
114        let raw_texture = unsafe { SDL_CreateGPUTexture(self.raw(), &create_info.inner) };
115        if raw_texture.is_null() {
116            Err(get_error())
117        } else {
118            Ok(Texture::new(
119                self,
120                raw_texture,
121                create_info.inner.width,
122                create_info.inner.height,
123            ))
124        }
125    }
126
127    #[doc(alias = "SDL_SetGPUViewport")]
128    pub fn set_viewport(&self, render_pass: &RenderPass, viewport: SDL_GPUViewport) {
129        unsafe { SDL_SetGPUViewport(render_pass.inner, &viewport) }
130    }
131
132    pub fn get_swapchain_texture_format(&self, w: &crate::video::Window) -> TextureFormat {
133        unsafe { std::mem::transmute(SDL_GetGPUSwapchainTextureFormat(self.inner.0, w.raw()).0) }
134    }
135
136    // You cannot begin another render pass, or begin a compute pass or copy pass until you have ended the render pass.
137    #[doc(alias = "SDL_BeginGPURenderPass")]
138    pub fn begin_render_pass(
139        &self,
140        command_buffer: &CommandBuffer,
141        color_info: &[ColorTargetInfo],
142        depth_stencil_target: Option<&DepthStencilTargetInfo>,
143    ) -> Result<RenderPass, Error> {
144        let p = unsafe {
145            SDL_BeginGPURenderPass(
146                command_buffer.inner,
147                color_info.as_ptr() as *const SDL_GPUColorTargetInfo, //heavy promise
148                color_info.len() as u32,
149                if let Some(p) = depth_stencil_target {
150                    p as *const _ as *const SDL_GPUDepthStencilTargetInfo //heavy promise
151                } else {
152                    std::ptr::null()
153                },
154            )
155        };
156        if !p.is_null() {
157            Ok(RenderPass { inner: p })
158        } else {
159            Err(get_error())
160        }
161    }
162
163    #[doc(alias = "SDL_EndGPURenderPass")]
164    pub fn end_render_pass(&self, pass: RenderPass) {
165        unsafe {
166            sys::gpu::SDL_EndGPURenderPass(pass.inner);
167        }
168    }
169
170    #[doc(alias = "SDL_BeginGPUCopyPass")]
171    pub fn begin_copy_pass(&self, command_buffer: &CommandBuffer) -> Result<CopyPass, Error> {
172        let p = unsafe { SDL_BeginGPUCopyPass(command_buffer.inner) };
173        if !p.is_null() {
174            Ok(CopyPass { inner: p })
175        } else {
176            Err(get_error())
177        }
178    }
179    #[doc(alias = "SDL_EndGPUCopyPass")]
180    pub fn end_copy_pass(&self, pass: CopyPass) {
181        unsafe {
182            sys::gpu::SDL_EndGPUCopyPass(pass.inner);
183        }
184    }
185
186    #[doc(alias = "SDL_BeginGPUComputePass")]
187    pub fn begin_compute_pass(
188        &self,
189        command_buffer: &CommandBuffer,
190        storage_texture_bindings: &[StorageTextureReadWriteBinding],
191        storage_buffer_bindings: &[StorageBufferReadWriteBinding],
192    ) -> Result<ComputePass, Error> {
193        let p = unsafe {
194            SDL_BeginGPUComputePass(
195                command_buffer.inner,
196                storage_texture_bindings.as_ptr().cast(),
197                storage_buffer_bindings.len() as u32,
198                storage_buffer_bindings.as_ptr().cast(),
199                storage_buffer_bindings.len() as u32,
200            )
201        };
202        if !p.is_null() {
203            Ok(ComputePass { inner: p })
204        } else {
205            Err(get_error())
206        }
207    }
208    #[doc(alias = "SDL_EndGPUComputePass")]
209    pub fn end_compute_pass(&self, pass: ComputePass) {
210        unsafe {
211            sys::gpu::SDL_EndGPUComputePass(pass.inner);
212        }
213    }
214
215    pub fn create_graphics_pipeline<'a>(&'a self) -> GraphicsPipelineBuilder<'a> {
216        GraphicsPipelineBuilder::new(self)
217    }
218
219    pub fn create_compute_pipeline<'a>(&'a self) -> ComputePipelineBuilder<'a> {
220        ComputePipelineBuilder::new(self)
221    }
222
223    #[doc(alias = "SDL_GetGPUShaderFormats")]
224    pub fn get_shader_formats(&self) -> ShaderFormat {
225        unsafe { std::mem::transmute(sys::gpu::SDL_GetGPUShaderFormats(self.raw())) }
226    }
227
228    // NOTE: for Xbox builds, the target is a UWP, e.g.: x86_64-uwp-windows-msvc
229    #[cfg(target_vendor = "uwp")]
230    #[doc(alias = "SDL_GDKSuspendGPU")]
231    pub fn gdk_suspend(&self) {
232        unsafe {
233            sys::gpu::SDL_GDKSuspendGPU(self.inner);
234        }
235    }
236
237    #[cfg(target_vendor = "uwp")]
238    #[doc(alias = "SDL_GDKResumeGPU")]
239    pub fn gdk_resume(&self) {
240        unsafe {
241            sys::gpu::SDL_GDKResumeGPU(self.inner);
242        }
243    }
244}