rokol/glue/
sdl.rs

1/*!
2Glue code for SDL support
3*/
4
5#![allow(dead_code)]
6
7use std::fmt;
8
9pub use rokol_ffi::gfx::sg_context_desc as SgContextDesc;
10
11#[cfg(feature = "serde")]
12use serde::{Deserialize, Serialize};
13
14use crate::gfx as rg;
15
16/// Enum compatible with [`PixelFormat`] in `rokol::gfx`
17///
18/// [`PixelFormat`]: crate::gfx::PixelFormat
19#[derive(Debug, Clone, Copy)]
20#[repr(u32)]
21pub enum ColorFormat {
22    Rgba8 = rg::PixelFormat::Rgba8 as u32,
23    Bgra8 = rg::PixelFormat::Bgra8 as u32,
24}
25
26impl ColorFormat {
27    pub fn to_ffi(self) -> rokol_ffi::gfx::sg_pixel_format {
28        match self {
29            Self::Rgba8 => rokol_ffi::gfx::sg_pixel_format::SG_PIXELFORMAT_RGBA8,
30            Self::Bgra8 => rokol_ffi::gfx::sg_pixel_format::SG_PIXELFORMAT_BGRA8,
31        }
32    }
33}
34
35/// Enum compatible with [`PixelFormat`] in `rokol::gfx`
36///
37/// [`PixelFormat`]: crate::gfx::PixelFormat
38#[derive(Debug, Clone, Copy)]
39#[repr(u32)]
40pub enum DepthFormat {
41    Depth = rg::PixelFormat::Depth as u32,
42    DepthStencil = rg::PixelFormat::DepthStencil as u32,
43}
44
45impl DepthFormat {
46    pub fn to_ffi(self) -> rokol_ffi::gfx::sg_pixel_format {
47        match self {
48            Self::Depth => rokol_ffi::gfx::sg_pixel_format::SG_PIXELFORMAT_DEPTH,
49            Self::DepthStencil => rokol_ffi::gfx::sg_pixel_format::SG_PIXELFORMAT_DEPTH_STENCIL,
50        }
51    }
52}
53
54#[derive(Debug, Clone)]
55pub struct ResourceSettings {
56    pub color_format: ColorFormat,
57    pub depth_format: DepthFormat,
58    /// MSAA sample count of the default frame buffer
59    pub sample_count: u32,
60}
61
62impl Default for ResourceSettings {
63    fn default() -> Self {
64        Self {
65            color_format: ColorFormat::Rgba8,
66            depth_format: DepthFormat::Depth,
67            sample_count: 1,
68        }
69    }
70}
71
72impl ResourceSettings {
73    fn apply(&self, desc: &mut SgContextDesc) {
74        desc.color_format = self.color_format.to_ffi();
75        desc.depth_format = self.depth_format.to_ffi();
76        desc.sample_count = self.sample_count as i32;
77    }
78
79    #[cfg(rokol_gfx = "glcore33")]
80    fn create_context(&self) -> SgContextDesc {
81        let mut desc = SgContextDesc::default();
82        self.apply(&mut desc);
83        // for OpenGL backend, we don't have to set context
84
85        // TODO: support non-OpenGL backends
86        // desc.gl.force_gles2 = sapp_gles2();
87        // desc.metal.device = sapp_metal_get_device();
88        // desc.metal.renderpass_descriptor_cb = sapp_metal_get_renderpass_descriptor;
89        // desc.metal.drawable_cb = sapp_metal_get_drawable;
90        // desc.d3d11.device = sapp_d3d11_get_device();
91        // desc.d3d11.device_context = sapp_d3d11_get_device_context();
92        // desc.d3d11.render_target_view_cb = sapp_d3d11_get_render_target_view;
93        // desc.d3d11.depth_stencil_view_cb = sapp_d3d11_get_depth_stencil_view;
94        // desc.wgpu.device = sapp_wgpu_get_device();
95        // desc.wgpu.render_view_cb = sapp_wgpu_get_render_view;
96        // desc.wgpu.resolve_view_cb = sapp_wgpu_get_resolve_view;
97        // desc.wgpu.depth_stencil_view_cb = sapp_wgpu_get_depth_stencil_view;
98        desc
99    }
100
101    pub fn init_gfx(&self) {
102        let desc = rokol_ffi::gfx::sg_desc {
103            context: self.create_context(),
104            ..Default::default()
105        };
106
107        unsafe {
108            rokol_ffi::gfx::sg_setup(&desc as *const _);
109        }
110    }
111}
112
113/// Set of SDL objects
114///
115/// Call `sg_sthudown` on end of your application.
116pub struct WindowHandle {
117    /// SDL lifetime (calls `SDL_QUIT` on drop)
118    pub sdl: sdl2::Sdl,
119    /// Lifetime of graphics (?)
120    pub vid: sdl2::VideoSubsystem,
121    /// SDL window lifetime (calls `SDL_DestroyWindow` on drop)
122    pub win: sdl2::video::Window,
123    /// SDL graphics lifetime (calls `SDL_GL_DeleteContext` on drop)
124    #[cfg(rokol_gfx = "glcore33")]
125    pub gcx: sdl2::video::GLContext,
126}
127
128impl WindowHandle {
129    /// Call at the end of a frame to swap frame buffers
130    #[cfg(rokol_gfx = "glcore33")]
131    pub fn swap_window(&self) {
132        self.win.gl_swap_window();
133    }
134}
135
136impl fmt::Debug for WindowHandle {
137    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
138        f.debug_struct("WindowHandle")
139            .field("sdl", &"<sdl2::Sdl>")
140            .field("vid", &self.vid)
141            .field("win", &"<sdl2::video::Window>")
142            .field("gcx", &"<sdl2::video::GLContext>")
143            .finish()
144    }
145}
146
147#[derive(Debug)]
148#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
149pub struct Init {
150    pub title: String,
151    pub w: u32,
152    pub h: u32,
153    pub use_high_dpi: bool,
154    #[cfg_attr(feature = "serde", serde(skip))]
155    pub settings: ResourceSettings,
156}
157
158impl Default for Init {
159    fn default() -> Self {
160        Self {
161            title: "unnamed".to_string(),
162            w: 1280,
163            h: 720,
164            use_high_dpi: false,
165            settings: Default::default(),
166        }
167    }
168}
169
170impl Init {
171    /// Initializes Rust-SDL2 and `rokol::gfx`
172    ///
173    /// I learned from this gist for using OpenGL with Sokol:
174    /// <https://gist.github.com/sherjilozair/c0fa81250c1b8f5e4234b1588e755bca>
175    #[cfg(rokol_gfx = "glcore33")]
176    pub fn init(
177        &self,
178        mut f: impl FnMut(&mut sdl2::video::WindowBuilder),
179    ) -> Result<WindowHandle, String> {
180        // initialize SDL2 with selected graphics backend
181        let sdl = sdl2::init()?;
182        let vid = sdl.video()?;
183
184        {
185            // GlCore33
186            let attr = vid.gl_attr();
187            attr.set_context_profile(sdl2::video::GLProfile::Core);
188            attr.set_context_version(3, 3);
189        }
190
191        let win = {
192            let mut b = vid.window(&self.title, self.w, self.h);
193            b.opengl();
194            if self.use_high_dpi {
195                b.allow_highdpi();
196            }
197            f(&mut b);
198            b.build().map_err(|e| e.to_string())?
199        };
200
201        let gcx = win.gl_create_context()?;
202
203        // initialize rokol with selected graphics backend
204        self.settings.init_gfx();
205
206        Ok(WindowHandle { sdl, vid, win, gcx })
207    }
208}