From 6099a3e27fc8bdeca0b1ec851d713af121dddf8c Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Mon, 9 Jun 2025 20:42:35 +0200 Subject: [PATCH] Add pipeline loader --- src/core/render/resources/mod.rs | 1 + src/core/render/resources/pipeline/loader.rs | 112 +++++++++++++++++++ src/core/render/resources/pipeline/mod.rs | 2 + src/game/assets/pipelines/simple.rs | 17 ++- src/game/scenes/main_scene.rs | 100 ++++++++++------- 5 files changed, 179 insertions(+), 53 deletions(-) create mode 100644 src/core/render/resources/pipeline/loader.rs create mode 100644 src/core/render/resources/pipeline/mod.rs diff --git a/src/core/render/resources/mod.rs b/src/core/render/resources/mod.rs index 26807af..d900da7 100644 --- a/src/core/render/resources/mod.rs +++ b/src/core/render/resources/mod.rs @@ -1,2 +1,3 @@ pub mod meshes; +pub mod pipeline; pub mod texture; diff --git a/src/core/render/resources/pipeline/loader.rs b/src/core/render/resources/pipeline/loader.rs new file mode 100644 index 0000000..c20a1de --- /dev/null +++ b/src/core/render/resources/pipeline/loader.rs @@ -0,0 +1,112 @@ +use std::{ + any::TypeId, + collections::HashMap, + error::Error, + sync::{Arc, RwLock}, +}; + +use vulkano::{device::Device, format::Format, pipeline::GraphicsPipeline}; + +#[derive(PartialEq, Eq)] +pub enum PipelineState { + NeedBuild, + Loaded, +} + +pub type GraphicsPipelineLoadFn = + fn(&Arc, Format, Format) -> Result, Box>; + +pub struct PipelineLoader { + device: Arc, + swapchain_image_format: Format, + depth_image_format: Format, + + // Arc is used in internal of vulkano. It's not possible to use Arc>> directly. + pipelines_index: HashMap, + pipelines_id: Vec, + pipelines_load_fn: Vec, + // Only content is protected by Arc and RwLock to avoid push in pipeline_loader in multiple threads and just allow to lock each pipeline when is needed as parallel pipelines loading. + // But only the pipeline loader is allowed to load a pipeline when it's needed. + pipelines: Vec>>>>, + pipelines_state: Vec>>, +} + +impl PipelineLoader { + pub fn new( + device: Arc, + swapchain_image_format: Format, + depth_image_format: Format, + ) -> Self { + Self { + device, + swapchain_image_format, + depth_image_format, + pipelines: Vec::new(), + pipelines_load_fn: Vec::new(), + pipelines_id: Vec::new(), + pipelines_state: Vec::new(), + pipelines_index: HashMap::new(), + } + } + + pub fn register( + &mut self, + load_fn: GraphicsPipelineLoadFn, + ) -> Result<(), Box> { + let id = TypeId::of::(); + self.pipelines_index.insert(id, self.pipelines.len()); + self.pipelines_id.push(id); + self.pipelines_load_fn.push(load_fn); + self.pipelines_state + .push(Arc::new(RwLock::new(PipelineState::NeedBuild))); + self.pipelines.push(Arc::new(RwLock::new(None))); + Ok(()) + } + + pub fn load_pipelines(&self) -> Result<(), Box> { + let iter = self + .pipelines_id + .iter() + .zip(self.pipelines.iter()) + .zip(self.pipelines_load_fn.iter()) + .zip(self.pipelines_state.iter()) + .filter(|(_, state)| { + let state = state.read().unwrap(); + *state == PipelineState::NeedBuild + }); + + for (((id, pipeline), load_fn), state) in iter { + let new_pipeline = load_fn( + &self.device, + self.swapchain_image_format, + self.depth_image_format, + )?; + let mut pipeline = pipeline.write().unwrap(); + *pipeline = Some(new_pipeline); + let mut state = state.write().unwrap(); + *state = PipelineState::Loaded; + tracing::trace!("Pipeline {id:?} loaded"); + } + Ok(()) + } + + fn mark_pipelines_as_need_build(&mut self) { + for state in self.pipelines_state.iter() { + let mut state = state.write().unwrap(); + *state = PipelineState::NeedBuild; + } + } + + pub fn with_pipeline(&self, f: F) -> Result<(), Box> + where + F: FnOnce(&Arc) -> Result<(), Box>, + { + let id = TypeId::of::(); + let index = self.pipelines_index.get(&id).ok_or("Pipeline not found")?; + let pipeline_locker = self.pipelines[*index] + .read() + .map_err(|_| "Failed to lock pipeline")?; + let pipeline = pipeline_locker.as_ref().ok_or("Pipeline not loaded")?; + f(pipeline) + } +} diff --git a/src/core/render/resources/pipeline/mod.rs b/src/core/render/resources/pipeline/mod.rs new file mode 100644 index 0000000..3026927 --- /dev/null +++ b/src/core/render/resources/pipeline/mod.rs @@ -0,0 +1,2 @@ +mod loader; +pub use loader::{GraphicsPipelineLoadFn, PipelineLoader}; diff --git a/src/game/assets/pipelines/simple.rs b/src/game/assets/pipelines/simple.rs index 8336a3d..0e9cfcd 100644 --- a/src/game/assets/pipelines/simple.rs +++ b/src/game/assets/pipelines/simple.rs @@ -1,4 +1,7 @@ -use std::{error::Error, sync::Arc}; +use std::{ + error::Error, + sync::{Arc, RwLock}, +}; use vulkano::{ command_buffer::{AutoCommandBufferBuilder, PrimaryAutoCommandBuffer}, @@ -33,16 +36,14 @@ use crate::core::render::{ resources::texture::Texture, }; -pub struct SimplePipeline { - pipeline: Arc, -} +pub struct SimplePipeline; impl SimplePipeline { pub fn new( device: &Arc, swapchain_format: Format, depth_format: Format, - ) -> Result> { + ) -> Result, Box> { let vs = shaders::vs::load(device.clone())? .entry_point("main") .ok_or("Failed find main entry point of vertex shader".to_string())?; @@ -111,11 +112,7 @@ impl SimplePipeline { }, )?; - Ok(Self { pipeline }) - } - - pub fn pipeline(&self) -> &Arc { - &self.pipeline + Ok(pipeline) } } diff --git a/src/game/scenes/main_scene.rs b/src/game/scenes/main_scene.rs index e139e90..4f0b421 100644 --- a/src/game/scenes/main_scene.rs +++ b/src/game/scenes/main_scene.rs @@ -11,6 +11,7 @@ use crate::core::render::primitives::transform::Transform; use crate::core::render::primitives::{AsDescriptorSet, AsRecordable}; use crate::core::render::render_pass_manager::{RenderPassConfig, RenderPassManager}; use crate::core::render::resources::meshes::{ObjMesh, SquareMesh}; +use crate::core::render::resources::pipeline::PipelineLoader; use crate::core::render::resources::texture::{TextureLoadInfo, TextureLoader, TextureSourceKind}; use crate::core::scene::Scene; use crate::game::assets::pipelines::simple::SimplePipeline; @@ -28,9 +29,9 @@ use winit::window::CursorGrabMode; pub struct MainSceneState { texture_loader: TextureLoader, + pipeline_loader: PipelineLoader, square: SquareMesh, obj: ObjMesh, - simple_pipeline: SimplePipeline, square_instances: Vec, obj_instances: Vec, camera: Camera3D, @@ -55,6 +56,14 @@ impl Scene for MainScene { let swapchain_image_view = app_context.with_renderer(|renderer| renderer.swapchain_image_view().clone()); + let mut pipeline_loader = PipelineLoader::new( + app_context.device.clone(), + swapchain_image_view.format(), + depth_image_view.format(), + ); + pipeline_loader.register::(SimplePipeline::new)?; + pipeline_loader.load_pipelines()?; + let mut texture_loader = TextureLoader::new(app_context); texture_loader.add_texture( "wooden-crate".to_string(), @@ -90,11 +99,6 @@ impl Scene for MainScene { let obj = ObjMesh::new(&app_context.memory_allocator, "res/objects/cube.obj")?; obj.into_iter().next().unwrap() }; - let simple_pipeline = SimplePipeline::new( - &app_context.device, - swapchain_image_view.format(), - depth_image_view.format(), - )?; let num_instances = 100; let instance_size = 10.0; @@ -147,7 +151,7 @@ impl Scene for MainScene { self.state = Some(MainSceneState { square, obj, - simple_pipeline, + pipeline_loader, square_instances, obj_instances, camera, @@ -256,43 +260,53 @@ impl Scene for MainScene { let obj_transform_uniform = Transform::create_buffer(&app_context.memory_allocator, &state.obj_instances)?; - SimplePipeline::record_bind_commands( - &mut builder, - &app_context.descriptor_set_allocator, - state.simple_pipeline.pipeline(), - &state.square, - &square_transform_uniform, - vec![ - camera_uniform.clone() as Arc, - state - .texture_loader - .get_texture("wooden-crate") - .unwrap() - .clone(), - ], - )?; - SimplePipeline::record_draw_commands( - &mut builder, - &state.square, - &square_transform_uniform, - )?; + state + .pipeline_loader + .with_pipeline::(|pipeline| { + SimplePipeline::record_bind_commands( + &mut builder, + &app_context.descriptor_set_allocator, + pipeline, + &state.square, + &square_transform_uniform, + vec![ + camera_uniform.clone() as Arc, + state + .texture_loader + .get_texture("wooden-crate") + .unwrap() + .clone(), + ], + )?; + SimplePipeline::record_draw_commands( + &mut builder, + &state.square, + &square_transform_uniform, + )?; - SimplePipeline::record_bind_commands( - &mut builder, - &app_context.descriptor_set_allocator, - state.simple_pipeline.pipeline(), - &state.obj, - &obj_transform_uniform, - vec![ - camera_uniform.clone() as Arc, - state - .texture_loader - .get_texture("cube-diffuse") - .unwrap() - .clone(), - ], - )?; - SimplePipeline::record_draw_commands(&mut builder, &state.obj, &obj_transform_uniform)?; + SimplePipeline::record_bind_commands( + &mut builder, + &app_context.descriptor_set_allocator, + pipeline, + &state.obj, + &obj_transform_uniform, + vec![ + camera_uniform.clone() as Arc, + state + .texture_loader + .get_texture("cube-diffuse") + .unwrap() + .clone(), + ], + )?; + SimplePipeline::record_draw_commands( + &mut builder, + &state.obj, + &obj_transform_uniform, + )?; + + Ok(()) + })?; RenderPassManager::end_rendering(&mut builder)?;