dotrix_core/
renderer.rs

1//! Rendering service and system, pipelines, abstractions for models, transformation, skybox,
2//! lights and overlay
3mod backend;
4
5use dotrix_math::Mat4;
6use backend::Context as Backend;
7
8use crate::{ Pipeline, Color, Assets, Globals, Window };
9use crate::assets::{ Mesh, Shader };
10use crate::ecs::{ Const, Mut };
11
12pub use backend::{
13    Bindings,
14    PipelineBackend,
15    Sampler,
16    ShaderModule,
17    TextureBuffer,
18    UniformBuffer,
19    VertexBuffer,
20};
21
22/// Conversion matrix
23pub const OPENGL_TO_WGPU_MATRIX: Mat4 = Mat4::new(
24    1.0, 0.0, 0.0, 0.0,
25    0.0, 1.0, 0.0, 0.0,
26    0.0, 0.0, 0.5, 0.0,
27    0.0, 0.0, 0.5, 1.0,
28);
29
30const RENDERER_STARTUP: &str =
31    "Please, use `renderer::startup` as a first system on the `startup` run level";
32
33/// Scissors Rectangle
34pub struct ScissorsRect {
35    /// Minimal clip size by X axis
36    pub clip_min_x: u32,
37    /// Minimal clip size by Y axis
38    pub clip_min_y: u32,
39    /// widget width
40    pub width: u32,
41    /// widget height
42    pub height: u32,
43}
44
45/// Pipeline options
46#[derive(Default)]
47pub struct Options {
48    /// Scissors Rectangle
49    pub scissors_rect: Option<ScissorsRect>,
50}
51
52/// Service providing an interface to `WGPU` and `WINIT`
53pub struct Renderer {
54    clear_color: Color,
55    cycle: usize,
56    backend: Option<Backend>,
57    loaded: bool,
58}
59
60impl Renderer {
61    fn backend(&self) -> &Backend {
62        self.backend.as_ref().expect(RENDERER_STARTUP)
63    }
64
65    fn backend_mut(&mut self) -> &mut Backend {
66        self.backend.as_mut().expect(RENDERER_STARTUP)
67    }
68
69    /// Returns the rendering cycle number (Experimental)
70    pub fn cycle(&self) -> usize {
71        self.cycle
72    }
73
74    /// Laods the vertex buffer to GPU
75    pub fn load_vertex_buffer<'a>(
76        &self,
77        buffer: &mut VertexBuffer,
78        attributes: &'a [u8],
79        indices: Option<&'a [u8]>,
80        count: usize,
81    ) {
82        buffer.load(self.backend(), attributes, indices, count as u32);
83    }
84
85    /// Loads the texture buffer to GPU
86    pub fn load_texture_buffer<'a>(
87        &self,
88        buffer: &mut TextureBuffer,
89        width: u32,
90        height: u32,
91        layers: &'a[&'a [u8]],
92    ) {
93        buffer.load(self.backend(), width, height, layers);
94    }
95
96    /// Loads the uniform buffer to GPU
97    pub fn load_uniform_buffer<'a>(&self, buffer: &mut UniformBuffer, data: &'a [u8]) {
98        buffer.load(self.backend(), data);
99    }
100
101    /// Loads the sampler to GPU
102    pub fn load_sampler(&self, sampler: &mut Sampler) {
103        sampler.load(self.backend());
104    }
105
106    /// Loads the sahder module to GPU
107    pub fn load_shader_module(
108        &self,
109        shader_module: &mut ShaderModule,
110        name: &str,
111        code: &str
112    ) {
113        shader_module.load(self.backend(), name, code);
114    }
115
116    /// Forces engine to reload shaders
117    pub fn reload(&mut self) {
118        self.loaded = false;
119    }
120
121    /// Binds uniforms and other data to the pipeline
122    pub fn bind(&mut self, pipeline: &mut Pipeline, layout: PipelineLayout) {
123        if !self.backend().has_pipeline(pipeline.shader) {
124            let pipeline_backend = PipelineBackend::new(self.backend(), &layout);
125            self.backend_mut().add_pipeline(pipeline.shader, pipeline_backend);
126        }
127
128        let pipeline_backend = self.backend()
129            .pipeline(pipeline.shader)
130            .unwrap();
131
132        let mut bindings = Bindings::default();
133        bindings.load(self.backend(), pipeline_backend, layout.bindings);
134        pipeline.bindings = bindings;
135    }
136
137    /// Runs the pipeline for a mesh
138    pub fn run(&mut self, pipeline: &mut Pipeline, mesh: &Mesh) {
139        // TODO: it is not good to copy backend here, find another solution
140        // let mut backend = self.backend.take();
141        self.backend_mut().run_pipeline(
142            pipeline.shader,
143            &mesh.vertex_buffer,
144            &pipeline.bindings,
145            &pipeline.options,
146        );
147        // self.backend = backend;
148    }
149}
150
151impl Default for Renderer {
152    /// Constructs new instance of the service
153    fn default() -> Self {
154        Renderer {
155            clear_color: Color::from([0.1, 0.2, 0.3, 1.0]),
156            cycle: 1,
157            backend: None,
158            loaded: false,
159        }
160    }
161}
162
163unsafe impl Send for Renderer {}
164unsafe impl Sync for Renderer {}
165
166/// Startup system
167pub fn startup(mut renderer: Mut<Renderer>, mut globals: Mut<Globals>, window: Mut<Window>) {
168    // Init backend backend
169    if renderer.backend.is_none() {
170        renderer.backend = Some(futures::executor::block_on(backend::init(window.get())));
171    }
172
173    // Create texture sampler and store it with Globals
174    let mut sampler = Sampler::default();
175    renderer.load_sampler(&mut sampler);
176    globals.set(sampler);
177}
178
179/// Frame binding system
180pub fn bind(mut renderer: Mut<Renderer>, mut assets: Mut<Assets>) {
181    let clear_color = renderer.clear_color;
182    renderer.backend_mut().bind_frame(&clear_color);
183
184    if renderer.loaded {
185        return;
186    }
187
188    let mut loaded = true;
189
190    for (_id, shader) in assets.iter_mut::<Shader>() {
191        shader.load(&renderer);
192        if !shader.loaded() {
193            loaded = false;
194        }
195    }
196
197    renderer.loaded = loaded;
198}
199
200/// Frame release system
201pub fn release(mut renderer: Mut<Renderer>) {
202    renderer.backend_mut().release_frame();
203    renderer.cycle += 1;
204    if renderer.cycle == 0 {
205        renderer.cycle = 1;
206    }
207}
208
209/// Resize handling system
210pub fn resize(mut renderer: Mut<Renderer>, window: Const<Window>) {
211    let size = window.inner_size();
212    renderer.backend_mut().resize(size.x, size.y);
213}
214
215/// Pipeline options
216pub struct PipelineOptions {
217    /// Depth buffer mode
218    pub depth_buffer_mode: DepthBufferMode,
219    /// Disable cull mode
220    pub disable_cull_mode: bool,
221}
222
223impl Default for PipelineOptions {
224    fn default() -> Self {
225        Self {
226            depth_buffer_mode: DepthBufferMode::Write,
227            disable_cull_mode: false,
228        }
229    }
230}
231
232/// Pipeline layout
233pub struct PipelineLayout<'a> {
234    /// Name of the Pipeline
235    pub label: String,
236    /// Mesh object to construct the pipeline
237    pub mesh: &'a Mesh,
238    /// Shader module
239    pub shader: &'a Shader,
240    /// Pipeline bindings
241    pub bindings: &'a [BindGroup<'a>],
242    /// Pipeline options
243    pub options: PipelineOptions,
244}
245
246
247/// Mode of the depth buffer
248#[derive(Debug, Eq, PartialEq, Copy, Clone)]
249pub enum DepthBufferMode {
250    /// Read Only mode
251    Read,
252    /// Read + Write mode
253    Write,
254    /// Depth buffer is disabled
255    Disabled
256}
257
258/// Vertex Attribute Format
259#[derive(Debug)]
260pub enum AttributeFormat {
261    /// 32 bit float attribute
262    Float32,
263    /// 2 x 32 bit float attribute
264    Float32x2,
265    /// 3 x 32 bit float attribute
266    Float32x3,
267    /// 4 x 32 bit float attribute
268    Float32x4,
269    /// 2 x 16 bit unsigned integer attribute
270    Uint16x2,
271    /// 4 x 16 bit unsigned integer attribute
272    Uint16x4,
273    /// 32 bit unsigned integer attribute
274    Uint32,
275    /// 2 x 32 bit unsigned integer attribute
276    Uint32x2,
277    /// 3 x 32 bit unsigned integer attribute
278    Uint32x3,
279    /// 4 x 32 bit unsigned integer attribute
280    Uint32x4,
281}
282
283impl AttributeFormat {
284    /// Returns the actual attribute size in bytes
285    pub fn size(&self) -> usize {
286        match self {
287            AttributeFormat::Float32 => 4,
288            AttributeFormat::Float32x2 => 4 * 2,
289            AttributeFormat::Float32x3 => 4 * 3,
290            AttributeFormat::Float32x4 => 4 * 4,
291            AttributeFormat::Uint16x2 => 2 * 2,
292            AttributeFormat::Uint16x4 => 2 * 4,
293            AttributeFormat::Uint32 => 4,
294            AttributeFormat::Uint32x2 => 4 * 2,
295            AttributeFormat::Uint32x3 => 4 * 3,
296            AttributeFormat::Uint32x4 => 4 * 4,
297        }
298    }
299}
300
301/// Binding types (Label, Stage, Buffer)
302pub enum Binding<'a> {
303    /// Uniform binding
304    Uniform(&'a str, Stage, &'a UniformBuffer),
305    /// Texture binding
306    Texture(&'a str, Stage, &'a TextureBuffer),
307    /// 3D Texture binding
308    Texture3D(&'a str, Stage, &'a TextureBuffer),
309    /// Texture sampler binding
310    Sampler(&'a str, Stage, &'a Sampler),
311}
312
313/// Rendering stage
314pub enum Stage {
315    /// Vertex shader stage
316    Vertex,
317    /// Fragment shader stage
318    Fragment,
319    /// Compute shader stage
320    Compute,
321    /// Any stage
322    All
323}
324
325/// Bind Group holding bindings
326pub struct BindGroup<'a> {
327    label: &'a str,
328    bindings: Vec<Binding<'a>>,
329}
330
331impl<'a> BindGroup<'a> {
332    /// Constructs new Bind Group
333    pub fn new(label: &'a str, bindings: Vec<Binding<'a>>) -> Self {
334        Self {
335            label,
336            bindings,
337        }
338    }
339}
340