syrillian_render 0.7.1

Renderer of the Syrillian Game Engine
Documentation
use super::StrobeFrame;
use crate::cache::AssetCache;
use crate::model_uniform::ModelUniform;
use crate::proxies::mesh_proxy::{MeshUniformIndex, RuntimeMeshData};
use crate::proxies::text_proxy::TextRenderData;
use crate::rendering::RenderPassType;
use crate::rendering::state::State;
use crate::rendering::uniform::ShaderUniform;
use crate::rendering::viewport::{RenderViewport, ViewportId};
use crate::strobe::ui_element::Rect;
use crate::strobe::{CacheId, ContextWithId, LayoutElement, StrobeRoot};
use delegate::delegate;
use glamx::{Mat4, Vec2};
use parking_lot::RwLock;
use std::collections::HashMap;
use std::mem;
use syrillian_asset::mesh::bone::BoneData;
use web_time::Instant;
use wgpu::{BindGroup, BufferDescriptor, BufferUsages, RenderPass};
use winit::dpi::PhysicalSize;

#[derive(Default)]
pub struct StrobeRenderer {
    strobe_roots: HashMap<ViewportId, Vec<StrobeRoot>>,
    image_cache: HashMap<(CacheId, u64), RuntimeMeshData>,
    text_cache: HashMap<(CacheId, u64), TextRenderData>,
}

pub struct UiGPUContext<'a> {
    pub pass: RwLock<RenderPass<'a>>,
    pub pass_type: RenderPassType,
    pub render_bind_group: &'a BindGroup,
}

pub struct UiDrawContext<'a, 'b, 'c, 'd, 'e> {
    image_cache: &'a mut HashMap<(CacheId, u64), RuntimeMeshData>,
    text_cache: &'a mut HashMap<(CacheId, u64), TextRenderData>,
    gpu_ctx: &'b UiGPUContext<'e>,
    cache: &'c AssetCache,
    cache_id: CacheId,
    pub render_id: u32,
    viewport_size: PhysicalSize<u32>,
    start_time: Instant,
    state: &'d State,
}

impl ContextWithId for UiDrawContext<'_, '_, '_, '_, '_> {
    fn set_id(&mut self, id: u32) {
        self.render_id = id;
    }
}

impl<'a, 'b, 'c, 'd, 'e> UiDrawContext<'a, 'b, 'c, 'd, 'e> {
    delegate! {
        to self {
            #[field]
            pub fn gpu_ctx(&self) -> &UiGPUContext<'e>;

            #[field]
            pub fn cache(&self) -> &AssetCache;

            #[field]
            pub fn cache_id(&self) -> CacheId;

            #[field]
            pub fn viewport_size(&self) -> PhysicalSize<u32>;

            #[field]
            pub fn start_time(&self) -> Instant;

            #[field]
            pub fn state(&self) -> &State;
        }

        to self.gpu_ctx {
            #[field(&)]
            pub fn pass(&self) -> &RwLock<RenderPass<'e>>;

            #[field]
            pub fn pass_type(&self) -> RenderPassType;

            #[field]
            pub fn render_bind_group(&self) -> &'b BindGroup;
        }
    }

    pub fn ui_text_data(&mut self) -> &mut TextRenderData {
        let key = (self.cache_id, self.render_id as u64);
        self.text_cache.entry(key).or_insert_with(|| {
            let model_bgl = self.cache.bgl_model();
            let model = ModelUniform::empty();
            let uniform = ShaderUniform::<MeshUniformIndex>::builder(model_bgl)
                .with_buffer_data(&model)
                .with_buffer_data(&BoneData::DUMMY)
                .build(&self.state.device);

            let glyph_vbo = self.state.device.create_buffer(&BufferDescriptor {
                label: Some("Strobe Text Glyphs"),
                size: 4,
                usage: BufferUsages::VERTEX | BufferUsages::COPY_DST,
                mapped_at_creation: false,
            });

            TextRenderData { uniform, glyph_vbo }
        })
    }

    pub fn ui_image_data(&mut self, model_mat: &Mat4) -> &mut RuntimeMeshData {
        let key = (self.cache_id, self.render_id as u64);
        self.image_cache.entry(key).or_insert_with(|| {
            let model_bgl = self.cache.bgl_model();
            let mesh_data = ModelUniform {
                model_mat: *model_mat,
            };

            let uniform = ShaderUniform::<MeshUniformIndex>::builder(model_bgl)
                .with_buffer_data(&mesh_data)
                .with_buffer_data(&BoneData::DUMMY)
                .build(&self.state.device);

            RuntimeMeshData { mesh_data, uniform }
        })
    }
}

impl StrobeRenderer {
    pub fn update_frame(&mut self, frame: StrobeFrame) {
        self.strobe_roots.clear();

        for root in frame.strobe_roots {
            let target = root.target;
            self.strobe_roots.entry(target).or_default().push(root);
        }
    }

    pub fn has_draws(&self, target: ViewportId) -> bool {
        self.strobe_roots
            .get(&target)
            .is_some_and(|r| !r.is_empty())
    }

    pub fn render(
        &mut self,
        ctx: &UiGPUContext,
        cache: &AssetCache,
        state: &State,
        viewport: &RenderViewport,
    ) {
        let roots_map = mem::take(&mut self.strobe_roots);

        let roots = roots_map.get(&viewport.id);

        if roots.is_none() {
            self.strobe_roots = roots_map;
            return;
        }

        let viewport_size = viewport.size();
        let mut current_context = UiDrawContext {
            image_cache: &mut self.image_cache,
            text_cache: &mut self.text_cache,
            gpu_ctx: ctx,
            cache,
            cache_id: 0,
            render_id: 0,
            viewport_size,
            start_time: viewport.start_time,
            state,
        };

        if let Some(roots) = roots {
            let full_rect = Rect::new(
                Vec2::ZERO,
                Vec2::new(viewport_size.width as f32, viewport_size.height as f32),
            );

            for root in roots {
                current_context.cache_id = root.cache_id;
                current_context.render_id = root.root.id;
                root.root.render_layout(&mut current_context, full_rect);
            }
        }

        self.strobe_roots = roots_map;
    }
}