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,
};
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";
pub struct ScissorsRect {
pub clip_min_x: u32,
pub clip_min_y: u32,
pub width: u32,
pub height: u32,
}
#[derive(Default)]
pub struct Options {
pub scissors_rect: Option<ScissorsRect>,
}
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)
}
pub fn cycle(&self) -> usize {
self.cycle
}
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);
}
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);
}
pub fn load_uniform_buffer<'a>(&self, buffer: &mut UniformBuffer, data: &'a [u8]) {
buffer.load(self.backend(), data);
}
pub fn load_sampler(&self, sampler: &mut Sampler) {
sampler.load(self.backend());
}
pub fn load_shader_module(
&self,
shader_module: &mut ShaderModule,
name: &str,
code: &str
) {
shader_module.load(self.backend(), name, code);
}
pub fn reload(&mut self) {
self.loaded = false;
}
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;
}
pub fn run(&mut self, pipeline: &mut Pipeline, mesh: &Mesh) {
self.backend_mut().run_pipeline(
pipeline.shader,
&mesh.vertex_buffer,
&pipeline.bindings,
&pipeline.options,
);
}
}
impl Default for Renderer {
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 {}
pub fn startup(mut renderer: Mut<Renderer>, mut globals: Mut<Globals>, window: Mut<Window>) {
if renderer.backend.is_none() {
renderer.backend = Some(futures::executor::block_on(backend::init(window.get())));
}
let mut sampler = Sampler::default();
renderer.load_sampler(&mut sampler);
globals.set(sampler);
}
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;
}
pub fn release(mut renderer: Mut<Renderer>) {
renderer.backend_mut().release_frame();
renderer.cycle += 1;
if renderer.cycle == 0 {
renderer.cycle = 1;
}
}
pub fn resize(mut renderer: Mut<Renderer>, window: Const<Window>) {
let size = window.inner_size();
renderer.backend_mut().resize(size.x, size.y);
}
pub struct PipelineOptions {
pub depth_buffer_mode: DepthBufferMode,
pub disable_cull_mode: bool,
}
impl Default for PipelineOptions {
fn default() -> Self {
Self {
depth_buffer_mode: DepthBufferMode::Write,
disable_cull_mode: false,
}
}
}
pub struct PipelineLayout<'a> {
pub label: String,
pub mesh: &'a Mesh,
pub shader: &'a Shader,
pub bindings: &'a [BindGroup<'a>],
pub options: PipelineOptions,
}
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
pub enum DepthBufferMode {
Read,
Write,
Disabled
}
#[derive(Debug)]
pub enum AttributeFormat {
Float32,
Float32x2,
Float32x3,
Float32x4,
Uint16x2,
Uint16x4,
Uint32,
Uint32x2,
Uint32x3,
Uint32x4,
}
impl AttributeFormat {
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,
}
}
}
pub enum Binding<'a> {
Uniform(&'a str, Stage, &'a UniformBuffer),
Texture(&'a str, Stage, &'a TextureBuffer),
Texture3D(&'a str, Stage, &'a TextureBuffer),
Sampler(&'a str, Stage, &'a Sampler),
}
pub enum Stage {
Vertex,
Fragment,
Compute,
All
}
pub struct BindGroup<'a> {
label: &'a str,
bindings: Vec<Binding<'a>>,
}
impl<'a> BindGroup<'a> {
pub fn new(label: &'a str, bindings: Vec<Binding<'a>>) -> Self {
Self {
label,
bindings,
}
}
}