ul_next/
gpu_driver.rs

1//! Ultralight custom gpu driver.
2//!
3//! Ultralight allows to create a custom GPU driver to receive low level GPU commands
4//! and render them on custom Textures, which can be used to integrate to your
5//! game/application seamlessly.
6//!
7//! There is an example `C++` implementation for `OpenGL`, `DirectX11`, `DirectX12`
8//! and `Metal` in the [`AppCore`](https://github.com/ultralight-ux/AppCore) repository.
9//!
10//! This library also have a custom GPU driver for [`glium`].
11
12#[cfg(feature = "glium")]
13#[cfg_attr(docsrs, doc(cfg(feature = "glium")))]
14pub mod glium;
15
16use std::slice;
17
18use crate::{
19    bitmap::{Bitmap, OwnedBitmap},
20    platform::GPUDRIVER,
21    rect::Rect,
22};
23
24#[derive(Debug)]
25/// RenderBuffer description. (See [`GpuDriver::create_render_buffer`]).
26pub struct RenderBuffer {
27    /// The backing texture id for this render buffer.
28    pub texture_id: u32,
29    /// The width of the backing texture.
30    pub width: u32,
31    /// The height of the backing texture.
32    pub height: u32,
33    /// Does the backing texture contain stencil buffer. (currently unused always false).
34    pub has_stencil_buffer: bool,
35    /// Does the backing texture contain depth buffer. (currently unused always false).
36    pub has_depth_buffer: bool,
37}
38
39impl From<ul_sys::ULRenderBuffer> for RenderBuffer {
40    fn from(rb: ul_sys::ULRenderBuffer) -> Self {
41        RenderBuffer {
42            texture_id: rb.texture_id,
43            width: rb.width,
44            height: rb.height,
45            has_stencil_buffer: rb.has_stencil_buffer,
46            has_depth_buffer: rb.has_depth_buffer,
47        }
48    }
49}
50
51#[derive(Debug)]
52#[allow(non_camel_case_types)]
53/// Vertex buffer format types
54pub enum VertexBufferFormat {
55    /// Vertex format type for path vertices.
56    Format_2f_4ub_2f = ul_sys::ULVertexBufferFormat_kVertexBufferFormat_2f_4ub_2f as isize,
57    /// Vertex format type for quad vertices.
58    Format_2f_4ub_2f_2f_28f =
59        ul_sys::ULVertexBufferFormat_kVertexBufferFormat_2f_4ub_2f_2f_28f as isize,
60}
61
62impl TryFrom<ul_sys::ULVertexBufferFormat> for VertexBufferFormat {
63    type Error = ();
64
65    fn try_from(vbf: ul_sys::ULVertexBufferFormat) -> Result<Self, Self::Error> {
66        match vbf {
67            ul_sys::ULVertexBufferFormat_kVertexBufferFormat_2f_4ub_2f => {
68                Ok(VertexBufferFormat::Format_2f_4ub_2f)
69            }
70            ul_sys::ULVertexBufferFormat_kVertexBufferFormat_2f_4ub_2f_2f_28f => {
71                Ok(VertexBufferFormat::Format_2f_4ub_2f_2f_28f)
72            }
73            _ => Err(()),
74        }
75    }
76}
77
78// TODO: passing raw `[u8]` is not safe, maybe we should transmute them to
79//       a specific format? like what we did in `glium` gpu_driver.
80/// Vertex buffer, the buffer is used for `quad` or `path` rendering based on
81/// the `format`. (See [`GpuDriver::create_geometry`]).
82pub struct VertexBuffer {
83    /// The format of the raw data. Either path or quad vertices.
84    pub format: VertexBufferFormat,
85    /// The raw vertex buffer data.
86    pub buffer: Vec<u8>,
87}
88
89impl TryFrom<ul_sys::ULVertexBuffer> for VertexBuffer {
90    type Error = ();
91
92    fn try_from(vb: ul_sys::ULVertexBuffer) -> Result<Self, Self::Error> {
93        if vb.data.is_null() {
94            return Err(());
95        }
96        let format = VertexBufferFormat::try_from(vb.format)?;
97        let buffer = unsafe { slice::from_raw_parts(vb.data, vb.size as usize) };
98        Ok(VertexBuffer {
99            format,
100            buffer: buffer.to_vec(),
101        })
102    }
103}
104
105/// Index buffer. (See [`GpuDriver::create_geometry`]).
106pub struct IndexBuffer {
107    pub buffer: Vec<u32>,
108}
109
110impl From<ul_sys::ULIndexBuffer> for IndexBuffer {
111    fn from(vb: ul_sys::ULIndexBuffer) -> Self {
112        assert!(vb.size % 4 == 0);
113        assert!(!vb.data.is_null());
114        let index_slice = unsafe { slice::from_raw_parts(vb.data as _, vb.size as usize / 4) };
115        IndexBuffer {
116            buffer: index_slice.to_vec(),
117        }
118    }
119}
120
121// helper macro to convert arrays
122macro_rules! from_ul_arr {
123    ($arr:expr, $from:ident) => {
124        [
125            $arr[0].$from,
126            $arr[1].$from,
127            $arr[2].$from,
128            $arr[3].$from,
129            $arr[4].$from,
130            $arr[5].$from,
131            $arr[6].$from,
132            $arr[7].$from,
133        ]
134    };
135    (mat $arr:expr, $from:ident) => {
136        [
137            from_ul_arr!(mat $arr[0].$from),
138            from_ul_arr!(mat $arr[1].$from),
139            from_ul_arr!(mat $arr[2].$from),
140            from_ul_arr!(mat $arr[3].$from),
141            from_ul_arr!(mat $arr[4].$from),
142            from_ul_arr!(mat $arr[5].$from),
143            from_ul_arr!(mat $arr[6].$from),
144            from_ul_arr!(mat $arr[7].$from),
145        ]
146    };
147    (mat $arr: expr) => {
148        [
149            [$arr[0], $arr[1], $arr[2], $arr[3]],
150            [$arr[4], $arr[5], $arr[6], $arr[7]],
151            [$arr[8], $arr[9], $arr[10], $arr[11]],
152            [$arr[12], $arr[13], $arr[14], $arr[15]],
153        ]
154    };
155}
156
157#[derive(Debug, Clone)]
158/// Shader types, used by [`GpuState::shader_type`]
159///
160/// Each of these correspond to a vertex/pixel shader pair to be used.
161/// You can find stock shader code for these in the `shaders` folder of the
162/// [`AppCore`](https://github.com/ultralight-ux/AppCore) repo and also in
163/// the [`glium`] custom `gpu_driver` implementation here.
164pub enum ShaderType {
165    /// Shader for the quad geometry.
166    Fill = ul_sys::ULShaderType_kShaderType_Fill as isize,
167    /// Shader for the path geometry.
168    FillPath = ul_sys::ULShaderType_kShaderType_FillPath as isize,
169}
170
171impl TryFrom<ul_sys::ULShaderType> for ShaderType {
172    type Error = ();
173
174    fn try_from(st: ul_sys::ULShaderType) -> Result<Self, Self::Error> {
175        match st {
176            ul_sys::ULShaderType_kShaderType_Fill => Ok(ShaderType::Fill),
177            ul_sys::ULShaderType_kShaderType_FillPath => Ok(ShaderType::FillPath),
178            _ => Err(()),
179        }
180    }
181}
182
183#[derive(Debug, Clone)]
184/// The GPU state description to be used when handling draw command.
185/// (See [`GpuCommand::DrawGeometry`]).
186pub struct GpuState {
187    /// Viewport width in pixels.
188    pub viewport_width: u32,
189    /// Viewport height in pixels.
190    pub viewport_height: u32,
191    /// transformation matrix.
192    ///
193    /// you should multiply this with the screen-space orthographic projection
194    /// matrix then pass to the vertex shader.
195    pub transform: [f32; 16],
196    /// Whether or not we should enable texturing for the current draw command.
197    pub enable_texturing: bool,
198    /// Whether or not we should enable blending for the current draw command.
199    /// If blending is disabled, any drawn pixels should overwrite existing.
200    /// This is mainly used so we can modify alpha values of the RenderBuffer
201    /// during scissored clears.
202    pub enable_blend: bool,
203    /// The vertex/pixel shader program pair to use for the current draw command.
204    pub shader_type: ShaderType,
205    /// The render buffer to use for the current draw command.
206    pub render_buffer_id: u32,
207    /// The texture id to bind to slot #1.
208    pub texture_1_id: Option<u32>,
209    /// The texture id to bind to slot #2.
210    pub texture_2_id: Option<u32>,
211    /// The texture id to bind to slot #3.
212    pub texture_3_id: Option<u32>,
213    /// 8 scalar values to be passed to the shader as uniforms.
214    pub uniform_scalar: [f32; 8],
215    /// 8 vector values to be passed to the shader as uniforms.
216    pub uniform_vector: [[f32; 4]; 8],
217    /// clip size to be passed to the shader as uniforms.
218    pub clip_size: u8,
219    /// 8 clip matrices to be passed to the shader as uniforms.
220    pub clip: [[[f32; 4]; 4]; 8],
221    /// Whether or not scissor testing should be used for the current draw command.
222    pub enable_scissor: bool,
223    /// The scissor rect to use for scissor testing (units in pixels)
224    pub scissor_rect: Rect<i32>,
225}
226
227impl TryFrom<ul_sys::ULGPUState> for GpuState {
228    type Error = ();
229
230    fn try_from(gs: ul_sys::ULGPUState) -> Result<Self, Self::Error> {
231        Ok(GpuState {
232            viewport_width: gs.viewport_width,
233            viewport_height: gs.viewport_height,
234            transform: gs.transform.data,
235            enable_texturing: gs.enable_texturing,
236            enable_blend: gs.enable_blend,
237            shader_type: ShaderType::try_from(gs.shader_type as u32)?,
238            render_buffer_id: gs.render_buffer_id,
239            texture_1_id: if gs.texture_1_id == 0 {
240                None
241            } else {
242                Some(gs.texture_1_id)
243            },
244            texture_2_id: if gs.texture_2_id == 0 {
245                None
246            } else {
247                Some(gs.texture_2_id)
248            },
249            texture_3_id: if gs.texture_3_id == 0 {
250                None
251            } else {
252                Some(gs.texture_3_id)
253            },
254            uniform_scalar: gs.uniform_scalar,
255            uniform_vector: from_ul_arr!(gs.uniform_vector, value),
256            clip_size: gs.clip_size,
257            clip: from_ul_arr!(mat gs.clip, data),
258            enable_scissor: gs.enable_scissor,
259            scissor_rect: Rect::from(gs.scissor_rect),
260        })
261    }
262}
263
264#[derive(Debug, Clone)]
265/// The GPU command to be executed.
266///
267/// This describes a command to be executed on the GPU.
268///
269/// Commands are dispatched to the GPU driver asynchronously via
270/// [`update_command_list`][GpuDriver::update_command_list],
271/// the GPU driver should consume these commands and execute them at an appropriate time.
272pub enum GpuCommand {
273    /// Clear a specific render buffer, to be prepared for drawing.
274    ClearRenderBuffer {
275        /// The render buffer to clear.
276        render_buffer_id: u32,
277    },
278    /// Performs a draw command.
279    DrawGeometry {
280        // `gpu_state` is boxed because its too large, and its not good
281        // to have large difference in size between the two variants in enum.
282        /// The GPU state to use for the draw command. (contain the `render_buffer_id`)
283        gpu_state: Box<GpuState>,
284        /// The geometry (vertex_buffer/index_buffer pair) to be used for the draw command.
285        geometry_id: u32,
286        /// The index offset to start drawing from in the `index_buffer`.
287        indices_offset: u32,
288        /// The number of indices to draw.
289        indices_count: u32,
290    },
291}
292
293impl TryFrom<ul_sys::ULCommand> for GpuCommand {
294    type Error = ();
295
296    fn try_from(gc: ul_sys::ULCommand) -> Result<Self, Self::Error> {
297        match gc.command_type as u32 {
298            ul_sys::ULCommandType_kCommandType_DrawGeometry => Ok(GpuCommand::DrawGeometry {
299                gpu_state: Box::new(GpuState::try_from(gc.gpu_state)?),
300                geometry_id: gc.geometry_id,
301                indices_count: gc.indices_count,
302                indices_offset: gc.indices_offset,
303            }),
304            ul_sys::ULCommandType_kCommandType_ClearRenderBuffer => {
305                Ok(GpuCommand::ClearRenderBuffer {
306                    render_buffer_id: gc.gpu_state.render_buffer_id,
307                })
308            }
309            _ => Err(()),
310        }
311    }
312}
313
314// TODO: we should not return `0` in ids, should we enforce it?
315/// `GpuDriver` trait, dispatches GPU calls to the native driver.
316///
317/// This is automatically provided for you when you use [`App::new`](crate::app::App),
318/// `AppCore` provides platform-specific implementations of `GpuDriver` for each OS.
319///
320/// If you are using [`Renderer::create`](crate::renderer::Renderer::create),
321/// you will need to provide your own implementation of this trait if you
322/// have enabled the GPU renderer in the Config.
323/// (See [`platform::set_gpu_driver`](crate::platform::set_gpu_driver)).
324pub trait GpuDriver {
325    /// Called before any commands are dispatched during a frame.
326    ///
327    /// Called before any state is updated during a call to
328    /// [`Renderer::render`](crate::renderer::Renderer::render).
329    /// This is a good time to prepare the GPU for any state updates.
330    fn begin_synchronize(&mut self);
331    /// Called after any commands are dispatched during a frame.
332    ///
333    /// Called after all state has been updated during a call to
334    /// [`Renderer::render`](crate::renderer::Renderer::render).
335    fn end_synchronize(&mut self);
336    /// Get the next available texture ID. **DO NOT return `0` (reserved)**.
337    ///
338    /// This is used to generate a unique texture ID for each texture created by the library.
339    /// The GPU driver implementation is responsible for mapping these IDs to a native ID.
340    fn next_texture_id(&mut self) -> u32;
341    /// Create a texture with a certain ID and optional bitmap.
342    ///
343    /// **NOTE**: If the Bitmap is empty [`OwnedBitmap::is_empty`],
344    /// then a RTT Texture should be created instead.
345    /// This will be used as a backing texture for a new RenderBuffer.
346    ///
347    /// Even if the bitmap is empty, it will still contain the `width` and `height`
348    /// information, which can be used to know the size of the backing texture.
349    fn create_texture(&mut self, texture_id: u32, bitmap: OwnedBitmap);
350    /// Update an existing non-RTT texture with new bitmap data.
351    fn update_texture(&mut self, texture_id: u32, bitmap: OwnedBitmap);
352    /// Destroy a texture.
353    fn destroy_texture(&mut self, texture_id: u32);
354    /// Generate the next available render buffer ID. **DO NOT return `0` (reserved)**.
355    fn next_render_buffer_id(&mut self) -> u32;
356    /// Create a render buffer with certain ID and buffer description.
357    fn create_render_buffer(&mut self, render_buffer_id: u32, render_buffer: RenderBuffer);
358    /// Destroy a render buffer.
359    fn destroy_render_buffer(&mut self, render_buffer_id: u32);
360    /// Get the next available geometry ID. **DO NOT return `0`**.
361    fn next_geometry_id(&mut self) -> u32;
362    /// Create geometry with certain ID and vertex/index data.
363    fn create_geometry(
364        &mut self,
365        geometry_id: u32,
366        vertex_buffer: VertexBuffer,
367        index_buffer: IndexBuffer,
368    );
369    /// Update existing geometry with new vertex/index data.
370    fn update_geometry(
371        &mut self,
372        geometry_id: u32,
373        vertex_buffer: VertexBuffer,
374        index_buffer: IndexBuffer,
375    );
376    /// Destroy a geometry.
377    fn destroy_geometry(&mut self, geometry_id: u32);
378    /// Update command list (here you should render the commands).
379    fn update_command_list(&mut self, command_list: Vec<GpuCommand>);
380}
381
382platform_set_interface_macro! {
383    #[inline]
384    pub(crate) set_gpu_driver<GpuDriver>(lib, gpu_driver -> GPUDRIVER) -> ulPlatformSetGPUDriver(ULGPUDriver) {
385        begin_synchronize() -> () {}
386        end_synchronize() -> () {}
387        next_texture_id(() -> u32) -> () {}
388        create_texture((texture_id: u32, ul_bitmap: ul_sys::ULBitmap)) -> ((texture_id: u32, bitmap: OwnedBitmap)) {
389            let mut bitmap = Bitmap::from_raw(lib.clone(), ul_bitmap).unwrap();
390            let bitmap = OwnedBitmap::from_bitmap(&mut bitmap).unwrap();
391        }
392        update_texture((texture_id: u32, ul_bitmap: ul_sys::ULBitmap)) -> ((texture_id: u32, bitmap: OwnedBitmap)) {
393            let mut bitmap = Bitmap::from_raw(lib.clone(), ul_bitmap).unwrap();
394            let bitmap = OwnedBitmap::from_bitmap(&mut bitmap).unwrap();
395        }
396        destroy_texture((texture_id: u32)) -> ((texture_id: u32)) {}
397        next_render_buffer_id(() -> u32) -> () {}
398        create_render_buffer((render_buffer_id: u32, ul_render_buffer: ul_sys::ULRenderBuffer))
399            -> ((render_buffer_id: u32, render_buffer: RenderBuffer)) {
400            let render_buffer = RenderBuffer::from(ul_render_buffer);
401        }
402        destroy_render_buffer((render_buffer_id: u32)) -> ((render_buffer_id: u32)) {}
403        next_geometry_id(() -> u32) -> () {}
404        create_geometry((geometry_id: u32, ul_vertex_buffer: ul_sys::ULVertexBuffer,
405            ul_index_buffer: ul_sys::ULIndexBuffer)) -> ((geometry_id: u32, vertex_buffer: VertexBuffer, index_buffer: IndexBuffer)) {
406            let vertex_buffer = VertexBuffer::try_from(ul_vertex_buffer).unwrap();
407            let index_buffer = IndexBuffer::from(ul_index_buffer);
408        }
409        update_geometry((geometry_id: u32, ul_vertex_buffer: ul_sys::ULVertexBuffer,
410            ul_index_buffer: ul_sys::ULIndexBuffer)) -> ((geometry_id: u32, vertex_buffer: VertexBuffer, index_buffer: IndexBuffer)) {
411            let vertex_buffer = VertexBuffer::try_from(ul_vertex_buffer).unwrap();
412            let index_buffer = IndexBuffer::from(ul_index_buffer);
413        }
414        destroy_geometry((geometry_id: u32)) -> ((geometry_id: u32)) {}
415        update_command_list((ul_command_list: ul_sys::ULCommandList)) -> ((commands_list: Vec<GpuCommand>)) {
416            assert!(!ul_command_list.commands.is_null());
417            let commands_slice = slice::from_raw_parts(ul_command_list.commands, ul_command_list.size as usize);
418            let commands_list = commands_slice.iter().map(|gc| GpuCommand::try_from(*gc).unwrap()).collect();
419        }
420    }
421}