1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
//! Rendering service and system, pipelines, abstractions for models, transformation, skybox,
//! lights and overlay
mod backend;

use dotrix_math::Mat4;
use backend::Context as Backend;

use crate::{ Pipeline, Color, Assets, Globals, Window };
use crate::assets::{ Mesh, Shader };
use crate::ecs::{ Const, Mut };

pub use backend::{
    Bindings,
    PipelineBackend,
    Sampler,
    ShaderModule,
    TextureBuffer,
    UniformBuffer,
    VertexBuffer,
};

/// Conversion matrix
pub const OPENGL_TO_WGPU_MATRIX: Mat4 = Mat4::new(
    1.0, 0.0, 0.0, 0.0,
    0.0, 1.0, 0.0, 0.0,
    0.0, 0.0, 0.5, 0.0,
    0.0, 0.0, 0.5, 1.0,
);

const RENDERER_STARTUP: &str =
    "Please, use `renderer::startup` as a first system on the `startup` run level";

/// Scissors Rectangle
pub struct ScissorsRect {
    /// Minimal clip size by X axis
    pub clip_min_x: u32,
    /// Minimal clip size by Y axis
    pub clip_min_y: u32,
    /// widget width
    pub width: u32,
    /// widget height
    pub height: u32,
}

/// Pipeline options
#[derive(Default)]
pub struct Options {
    /// Scissors Rectangle
    pub scissors_rect: Option<ScissorsRect>,
}

/// Service providing an interface to `WGPU` and `WINIT`
pub struct Renderer {
    clear_color: Color,
    cycle: usize,
    backend: Option<Backend>,
    loaded: bool,
}

impl Renderer {
    fn backend(&self) -> &Backend {
        self.backend.as_ref().expect(RENDERER_STARTUP)
    }

    fn backend_mut(&mut self) -> &mut Backend {
        self.backend.as_mut().expect(RENDERER_STARTUP)
    }

    /// Returns the rendering cycle number (Experimental)
    pub fn cycle(&self) -> usize {
        self.cycle
    }

    /// Laods the vertex buffer to GPU
    pub fn load_vertex_buffer<'a>(
        &self,
        buffer: &mut VertexBuffer,
        attributes: &'a [u8],
        indices: Option<&'a [u8]>,
        count: usize,
    ) {
        buffer.load(self.backend(), attributes, indices, count as u32);
    }

    /// Loads the texture buffer to GPU
    pub fn load_texture_buffer<'a>(
        &self,
        buffer: &mut TextureBuffer,
        width: u32,
        height: u32,
        layers: &'a[&'a [u8]],
    ) {
        buffer.load(self.backend(), width, height, layers);
    }

    /// Loads the uniform buffer to GPU
    pub fn load_uniform_buffer<'a>(&self, buffer: &mut UniformBuffer, data: &'a [u8]) {
        buffer.load(self.backend(), data);
    }

    /// Loads the sampler to GPU
    pub fn load_sampler(&self, sampler: &mut Sampler) {
        sampler.load(self.backend());
    }

    /// Loads the sahder module to GPU
    pub fn load_shader_module(
        &self,
        shader_module: &mut ShaderModule,
        name: &str,
        code: &str
    ) {
        shader_module.load(self.backend(), name, code);
    }

    /// Forces engine to reload shaders
    pub fn reload(&mut self) {
        self.loaded = false;
    }

    /// Binds uniforms and other data to the pipeline
    pub fn bind(&mut self, pipeline: &mut Pipeline, layout: PipelineLayout) {
        if !self.backend().has_pipeline(pipeline.shader) {
            let pipeline_backend = PipelineBackend::new(self.backend(), &layout);
            self.backend_mut().add_pipeline(pipeline.shader, pipeline_backend);
        }

        let pipeline_backend = self.backend()
            .pipeline(pipeline.shader)
            .unwrap();

        let mut bindings = Bindings::default();
        bindings.load(self.backend(), pipeline_backend, layout.bindings);
        pipeline.bindings = bindings;
    }

    /// Runs the pipeline for a mesh
    pub fn run(&mut self, pipeline: &mut Pipeline, mesh: &Mesh) {
        // TODO: it is not good to copy backend here, find another solution
        // let mut backend = self.backend.take();
        self.backend_mut().run_pipeline(
            pipeline.shader,
            &mesh.vertex_buffer,
            &pipeline.bindings,
            &pipeline.options,
        );
        // self.backend = backend;
    }
}

impl Default for Renderer {
    /// Constructs new instance of the service
    fn default() -> Self {
        Renderer {
            clear_color: Color::from([0.1, 0.2, 0.3, 1.0]),
            cycle: 1,
            backend: None,
            loaded: false,
        }
    }
}

unsafe impl Send for Renderer {}
unsafe impl Sync for Renderer {}

/// Startup system
pub fn startup(mut renderer: Mut<Renderer>, mut globals: Mut<Globals>, window: Mut<Window>) {
    // Init backend backend
    if renderer.backend.is_none() {
        renderer.backend = Some(futures::executor::block_on(backend::init(window.get())));
    }

    // Create texture sampler and store it with Globals
    let mut sampler = Sampler::default();
    renderer.load_sampler(&mut sampler);
    globals.set(sampler);
}

/// Frame binding system
pub fn bind(mut renderer: Mut<Renderer>, mut assets: Mut<Assets>) {
    let clear_color = renderer.clear_color;
    renderer.backend_mut().bind_frame(&clear_color);

    if renderer.loaded {
        return;
    }

    let mut loaded = true;

    for (_id, shader) in assets.iter_mut::<Shader>() {
        shader.load(&renderer);
        if !shader.loaded() {
            loaded = false;
        }
    }

    renderer.loaded = loaded;
}

/// Frame release system
pub fn release(mut renderer: Mut<Renderer>) {
    renderer.backend_mut().release_frame();
    renderer.cycle += 1;
    if renderer.cycle == 0 {
        renderer.cycle = 1;
    }
}

/// Resize handling system
pub fn resize(mut renderer: Mut<Renderer>, window: Const<Window>) {
    let size = window.inner_size();
    renderer.backend_mut().resize(size.x, size.y);
}

/// Pipeline options
pub struct PipelineOptions {
    /// Depth buffer mode
    pub depth_buffer_mode: DepthBufferMode,
    /// Disable cull mode
    pub disable_cull_mode: bool,
}

impl Default for PipelineOptions {
    fn default() -> Self {
        Self {
            depth_buffer_mode: DepthBufferMode::Write,
            disable_cull_mode: false,
        }
    }
}

/// Pipeline layout
pub struct PipelineLayout<'a> {
    /// Name of the Pipeline
    pub label: String,
    /// Mesh object to construct the pipeline
    pub mesh: &'a Mesh,
    /// Shader module
    pub shader: &'a Shader,
    /// Pipeline bindings
    pub bindings: &'a [BindGroup<'a>],
    /// Pipeline options
    pub options: PipelineOptions,
}


/// Mode of the depth buffer
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
pub enum DepthBufferMode {
    /// Read Only mode
    Read,
    /// Read + Write mode
    Write,
    /// Depth buffer is disabled
    Disabled
}

/// Vertex Attribute Format
#[derive(Debug)]
pub enum AttributeFormat {
    /// 32 bit float attribute
    Float32,
    /// 2 x 32 bit float attribute
    Float32x2,
    /// 3 x 32 bit float attribute
    Float32x3,
    /// 4 x 32 bit float attribute
    Float32x4,
    /// 2 x 16 bit unsigned integer attribute
    Uint16x2,
    /// 4 x 16 bit unsigned integer attribute
    Uint16x4,
    /// 32 bit unsigned integer attribute
    Uint32,
    /// 2 x 32 bit unsigned integer attribute
    Uint32x2,
    /// 3 x 32 bit unsigned integer attribute
    Uint32x3,
    /// 4 x 32 bit unsigned integer attribute
    Uint32x4,
}

impl AttributeFormat {
    /// Returns the actual attribute size in bytes
    pub fn size(&self) -> usize {
        match self {
            AttributeFormat::Float32 => 4,
            AttributeFormat::Float32x2 => 4 * 2,
            AttributeFormat::Float32x3 => 4 * 3,
            AttributeFormat::Float32x4 => 4 * 4,
            AttributeFormat::Uint16x2 => 2 * 2,
            AttributeFormat::Uint16x4 => 2 * 4,
            AttributeFormat::Uint32 => 4,
            AttributeFormat::Uint32x2 => 4 * 2,
            AttributeFormat::Uint32x3 => 4 * 3,
            AttributeFormat::Uint32x4 => 4 * 4,
        }
    }
}

/// Binding types (Label, Stage, Buffer)
pub enum Binding<'a> {
    /// Uniform binding
    Uniform(&'a str, Stage, &'a UniformBuffer),
    /// Texture binding
    Texture(&'a str, Stage, &'a TextureBuffer),
    /// 3D Texture binding
    Texture3D(&'a str, Stage, &'a TextureBuffer),
    /// Texture sampler binding
    Sampler(&'a str, Stage, &'a Sampler),
}

/// Rendering stage
pub enum Stage {
    /// Vertex shader stage
    Vertex,
    /// Fragment shader stage
    Fragment,
    /// Compute shader stage
    Compute,
    /// Any stage
    All
}

/// Bind Group holding bindings
pub struct BindGroup<'a> {
    label: &'a str,
    bindings: Vec<Binding<'a>>,
}

impl<'a> BindGroup<'a> {
    /// Constructs new Bind Group
    pub fn new(label: &'a str, bindings: Vec<Binding<'a>>) -> Self {
        Self {
            label,
            bindings,
        }
    }
}