syrillian_render 0.7.1

Renderer of the Syrillian Game Engine
Documentation
use crate::cache::AssetCache;
use crate::passes::post_process::{PostProcessData, ScreenSpaceReflectionRenderPass};
use crate::passes::ui_pass::UiRenderPass;
use crate::rendering::offscreen_surface::OffscreenSurface;
use crate::rendering::render_data::RenderUniformData;
use crate::rendering::renderer::RenderedFrame;
use crate::rendering::state::State;
use crate::rendering::viewport::RenderViewport;
use crate::strobe::StrobeRenderer;
use syrillian_utils::EngineArgs;
use wgpu::{
    Color, CommandEncoder, Device, Extent3d, LoadOp, Operations, RenderPassColorAttachment,
    RenderPassDescriptor, StoreOp, SurfaceConfiguration, Texture, TextureDescriptor,
    TextureDimension, TextureFormat, TextureUsages, TextureViewDescriptor,
};

pub struct RenderPipeline {
    pub depth_texture: Texture,
    pub offscreen_surface: OffscreenSurface,
    pub ssr_pass: ScreenSpaceReflectionRenderPass,
    pub final_surfaces: [OffscreenSurface; 2],
    pub final_data: PostProcessData,
    pub g_normal: Texture,
    pub g_material: Texture,
}

impl RenderPipeline {
    pub fn new(device: &Device, cache: &AssetCache, config: &SurfaceConfiguration) -> Self {
        let pp_bgl = cache.bgl_post_process();

        let normal_texture = Self::create_g_buffer("GBuffer (Normals)", device, config);
        let material_texture = Self::create_material_texture(device, config);
        let depth_texture = Self::create_depth_texture(device, config);
        let depth_view = depth_texture.create_view(&TextureViewDescriptor::default());
        let normal_view = normal_texture.create_view(&TextureViewDescriptor::default());
        let material_view = material_texture.create_view(&TextureViewDescriptor::default());

        let offscreen_surface = OffscreenSurface::new(device, config);
        let final_surfaces = [
            OffscreenSurface::new(device, config),
            OffscreenSurface::new(device, config),
        ];

        let ssr_pass = ScreenSpaceReflectionRenderPass::new(
            device,
            config,
            pp_bgl.clone(),
            &offscreen_surface,
            depth_view.clone(),
            normal_view.clone(),
            material_view.clone(),
        );

        let post_process_final = if EngineArgs::get().no_ssr {
            PostProcessData::new(
                device,
                pp_bgl.clone(),
                offscreen_surface.view().clone(),
                depth_view.clone(),
                normal_view.clone(),
                material_view.clone(),
            )
        } else {
            PostProcessData::new(
                device,
                pp_bgl.clone(),
                ssr_pass.output.view().clone(),
                depth_view.clone(),
                normal_view.clone(),
                material_view.clone(),
            )
        };

        Self {
            depth_texture,
            offscreen_surface,
            ssr_pass,
            final_surfaces,
            final_data: post_process_final,
            g_normal: normal_texture,
            g_material: material_texture,
        }
    }

    pub fn recreate(&mut self, device: &Device, cache: &AssetCache, config: &SurfaceConfiguration) {
        *self = Self::new(device, cache, config);
    }

    fn create_depth_texture(device: &Device, config: &SurfaceConfiguration) -> Texture {
        device.create_texture(&TextureDescriptor {
            label: Some("Depth Texture"),
            size: Extent3d {
                width: config.width.max(1),
                height: config.height.max(1),
                depth_or_array_layers: 1,
            },
            mip_level_count: 1,
            sample_count: 1,
            dimension: TextureDimension::D2,
            format: TextureFormat::Depth32Float,
            usage: TextureUsages::COPY_SRC
                | TextureUsages::RENDER_ATTACHMENT
                | TextureUsages::TEXTURE_BINDING,
            view_formats: &[],
        })
    }

    fn create_g_buffer(
        which: &'static str,
        device: &Device,
        config: &SurfaceConfiguration,
    ) -> Texture {
        device.create_texture(&TextureDescriptor {
            label: Some(which),
            size: Extent3d {
                width: config.width.max(1),
                height: config.height.max(1),
                depth_or_array_layers: 1,
            },
            mip_level_count: 1,
            sample_count: 1,
            dimension: TextureDimension::D2,
            format: TextureFormat::Rg16Float,
            usage: TextureUsages::COPY_SRC
                | TextureUsages::RENDER_ATTACHMENT
                | TextureUsages::TEXTURE_BINDING,
            view_formats: &[],
        })
    }

    fn create_material_texture(device: &Device, config: &SurfaceConfiguration) -> Texture {
        device.create_texture(&TextureDescriptor {
            label: Some("Material Property Texture"),
            size: Extent3d {
                width: config.width.max(1),
                height: config.height.max(1),
                depth_or_array_layers: 1,
            },
            mip_level_count: 1,
            sample_count: 1,
            dimension: TextureDimension::D2,
            format: TextureFormat::Bgra8Unorm,
            usage: TextureUsages::COPY_SRC
                | TextureUsages::RENDER_ATTACHMENT
                | TextureUsages::TEXTURE_BINDING,
            view_formats: &[],
        })
    }

    pub fn render_post_process(
        &self,
        camera_render_data: &RenderUniformData,
        encoder: &mut CommandEncoder,
        cache: &AssetCache,
    ) {
        if !EngineArgs::get().no_ssr {
            self.ssr_pass.render(camera_render_data, encoder, cache);
        }
    }

    pub fn render_ui_onto_final_frame(
        &self,
        encoder: &mut CommandEncoder,
        strobe: &mut StrobeRenderer,
        viewport: &RenderViewport,
        cache: &AssetCache,
        state: &State,
    ) {
        let final_color = &self.final_surfaces[viewport.frame_count() % 2];

        UiRenderPass::render(encoder, strobe, final_color.view(), viewport, cache, state);
    }

    pub fn finalize_frame(
        &self,
        encoder: &mut CommandEncoder,
        viewport: &RenderViewport,
        cache: &AssetCache,
    ) -> RenderedFrame {
        let final_color = &self.final_surfaces[viewport.frame_count() % 2];
        let mut pass = encoder.begin_render_pass(&RenderPassDescriptor {
            label: Some("Final Render Pass"),
            color_attachments: &[Some(RenderPassColorAttachment {
                view: final_color.view(),
                depth_slice: None,
                resolve_target: None,
                ops: Operations {
                    load: LoadOp::Clear(Color::BLACK),
                    store: StoreOp::Store,
                },
            })],
            depth_stencil_attachment: None,
            ..RenderPassDescriptor::default()
        });

        let post_shader = cache.shader_post_process();
        let groups = post_shader.bind_groups();
        pass.set_pipeline(post_shader.solid_pipeline());
        pass.set_bind_group(
            groups.render,
            viewport.render_data.uniform.bind_group(),
            &[],
        );
        if let Some(idx) = groups.post_process {
            pass.set_bind_group(idx, self.final_data.uniform.bind_group(), &[]);
        }
        pass.draw(0..6, 0..1);

        RenderedFrame {
            target: viewport.id,
            frame: final_color.texture().clone(),
            size: viewport.size(),
            format: viewport.config.format,
        }
    }
}