syrillian_render 0.7.1

Renderer of the Syrillian Game Engine
Documentation
use crate::cache::generic_cache::CacheType;
use crate::cache::shader::bindings::ShaderBindings;
use crate::cache::{AssetCache, RenderPipelineBuilder};
use crate::rendering::{GPUDrawCtx, RenderPassType};
use std::borrow::Cow;
use std::sync::Arc;
use syrillian_asset::Shader;
use syrillian_asset::shader::{BindGroupMap, ShaderType};
use wgpu::*;

mod bindings;
pub mod builder;

#[derive(Debug, Clone)]
pub struct RuntimeShader {
    name: String,
    pub module: ShaderModule,
    pipeline: RenderPipeline,
    shadow_pipeline: Option<RenderPipeline>,
    pub immediate_size: u32,
    bind_groups: BindGroupMap,
    pub shader_type: ShaderType,
}

impl CacheType for Shader {
    type Hot = Arc<RuntimeShader>;

    fn upload(self, device: &Device, _queue: &Queue, cache: &AssetCache) -> Self::Hot {
        let bind_groups = self.bind_group_map();
        let code = self.gen_code_with_map(&bind_groups);

        debug_assert!(
            code.contains("@fragment"),
            "No fragment entry point in shader {:?}: \n{code}",
            self.name()
        );
        debug_assert!(
            code.contains("@vertex"),
            "No vertex entry point in shader {:?}: \n{code}",
            self.name()
        );

        let module = device.create_shader_module(ShaderModuleDescriptor {
            label: Some(self.name()),
            source: ShaderSource::Wgsl(Cow::Owned(code)),
        });
        let name = self.name().to_string();

        let solid_layout = self.solid_layout(device, cache);
        let solid_builder = RenderPipelineBuilder::builder(&self, &solid_layout, &module);
        let pipeline = solid_builder.build(device);
        let shadow_pipeline = self.shadow_layout(device, cache).and_then(|layout| {
            let shadow_builder = RenderPipelineBuilder::builder(&self, &layout, &module);
            shadow_builder.build_shadow(device)
        });

        Arc::new(RuntimeShader {
            name,
            module,
            pipeline,
            shadow_pipeline,
            immediate_size: self.immediate_size(),
            bind_groups,
            shader_type: self.stage(),
        })
    }
}

impl RuntimeShader {
    pub fn solid_pipeline(&self) -> &RenderPipeline {
        &self.pipeline
    }

    pub fn shadow_pipeline(&self) -> Option<&RenderPipeline> {
        self.shadow_pipeline.as_ref()
    }

    pub fn pipeline(&self, stage: RenderPassType) -> Option<&RenderPipeline> {
        match stage {
            RenderPassType::Color
            | RenderPassType::Color2D
            | RenderPassType::Picking
            | RenderPassType::PickingUi => Some(&self.pipeline),
            RenderPassType::Shadow => self.shadow_pipeline.as_ref(),
        }
    }

    pub fn bind_groups(&self) -> &BindGroupMap {
        &self.bind_groups
    }

    pub fn activate(&self, pass: &mut RenderPass, ctx: &GPUDrawCtx) -> bool {
        crate::must_pipeline!(pipeline = self, ctx.pass_type => return false);

        pass.set_pipeline(pipeline);
        pass.set_bind_group(self.bind_groups.render, ctx.render_bind_group, &[]);
        if let Some(light) = self.bind_groups.light {
            pass.set_bind_group(light, ctx.light_bind_group, &[]);
        }
        if let Some(shadow) = self.bind_groups.shadow {
            pass.set_bind_group(shadow, ctx.shadow_bind_group, &[]);
        }

        true
    }

    pub fn name(&self) -> &str {
        &self.name
    }
}

#[macro_export]
macro_rules! activate_shader {
    ($shader:expr, $pass:expr, $ctx:expr => $( $exit_strat:tt )*) => {
        if !$shader.activate($pass, $ctx) {
            ::syrillian_utils::debug_panic!(
                "Invalid pipeline for specified shader. Cannot activate shader."
            );
            ::tracing::error!("A pipeline for the specified shader could not be found for the current render pass");
            $( $exit_strat )*;
        };
    };
}

#[macro_export]
macro_rules! try_activate_shader {
    ($shader:expr, $pass:expr, $ctx:expr => $( $exit_strat:tt )*) => {
        if !$shader.activate($pass, $ctx) {
            ::tracing::debug!("Tried to activate shader {:?}, but pipeline was not found for the specified render pass of type {:?}", $shader.name(), $ctx.pass_type);
            $( $exit_strat )*;
        };
    };
}

#[macro_export]
macro_rules! must_pipeline {
    ($name:ident = $shader:expr, $pass_type:expr => $( $exit_strat:tt )*) => {
        let Some($name) = $shader.pipeline($pass_type) else {
            ::syrillian_utils::debug_panic!(
                "A 3D Shader was instantiated without a Shadow Pipeline Variant"
            );
            ::tracing::error!("A 3D Shader was instantiated without a Shadow Pipeline Variant");
            $( $exit_strat )*;
        };
    };
}