From 078e9daba955310d5da6e1bc1f9a745180fe3ce8 Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Sun, 8 Jun 2025 15:41:22 +0200 Subject: [PATCH] Split Mesh data and Pipeline --- res/shaders/{vertex.frag => simple.frag} | 0 res/shaders/{vertex.vert => simple.vert} | 0 src/core/render/primitives/command.rs | 66 +++----- src/core/render/primitives/mod.rs | 8 +- src/core/render/primitives/mvp.rs | 2 +- src/core/render/texture.rs | 2 +- src/game/assets/meshs/mod.rs | 1 + src/game/assets/meshs/square.rs | 63 +++++++ src/game/assets/mod.rs | 3 +- src/game/assets/pipelines/mod.rs | 1 + src/game/assets/pipelines/simple.rs | 207 +++++++++++++++++++++++ src/game/assets/square.rs | 54 ++---- src/game/scenes/main_scene.rs | 33 ++-- 13 files changed, 331 insertions(+), 109 deletions(-) rename res/shaders/{vertex.frag => simple.frag} (100%) rename res/shaders/{vertex.vert => simple.vert} (100%) create mode 100644 src/game/assets/meshs/mod.rs create mode 100644 src/game/assets/meshs/square.rs create mode 100644 src/game/assets/pipelines/mod.rs create mode 100644 src/game/assets/pipelines/simple.rs diff --git a/res/shaders/vertex.frag b/res/shaders/simple.frag similarity index 100% rename from res/shaders/vertex.frag rename to res/shaders/simple.frag diff --git a/res/shaders/vertex.vert b/res/shaders/simple.vert similarity index 100% rename from res/shaders/vertex.vert rename to res/shaders/simple.vert diff --git a/src/core/render/primitives/command.rs b/src/core/render/primitives/command.rs index 37970a1..f7a20e4 100644 --- a/src/core/render/primitives/command.rs +++ b/src/core/render/primitives/command.rs @@ -1,53 +1,51 @@ use std::{error::Error, sync::Arc}; use vulkano::{ + buffer::Subbuffer, command_buffer::{ AutoCommandBufferBuilder, PrimaryAutoCommandBuffer, RenderPassBeginInfo, SubpassBeginInfo, SubpassEndInfo, }, - descriptor_set::DescriptorSet, - pipeline::{GraphicsPipeline, Pipeline}, + descriptor_set::allocator::StandardDescriptorSetAllocator, + pipeline::GraphicsPipeline, render_pass::{Framebuffer, RenderPass}, }; pub trait AsRecordable { fn record_render_commands( builder: &mut AutoCommandBufferBuilder, - data: &T, + descriptor_set_allocator: &Arc, pipeline: &Arc, - descriptor_sets: &[Arc], + data: &T, ) -> Result<(), Box>; } -pub trait AsDrawable { - type VertexBuffer; - type IndexBuffer; +pub trait AsRenderableMesh { + fn vertex_buffer(&self) -> &Subbuffer<[V]>; - fn vertex_buffer(data: &T) -> &Self::VertexBuffer; + fn vertex_count(&self) -> u32; - fn index_buffer(_data: &T) -> Option<&Self::IndexBuffer> { + fn first_vertex(&self) -> u32 { + 0 + } + + fn vertex_offset(&self) -> i32 { + 0 + } + + fn index_buffer(&self) -> Option<&Subbuffer<[I]>> { None } - fn vertex_count(data: &T) -> u32; - - fn index_count(_data: &T) -> u32 { + fn index_count(&self) -> u32 { 0 } - fn instance_count(_data: &T) -> u32 { - 1 - } - - fn first_vertex(_data: &T) -> u32 { + fn first_index(&self) -> u32 { 0 } - fn first_index(_data: &T) -> u32 { - 0 - } - - fn first_instance(_data: &T) -> u32 { + fn index_offset(&self) -> i32 { 0 } } @@ -84,27 +82,3 @@ pub trait AsRenderPassRecordable { data: &T, ) -> Result<(), Box>; } - -pub trait AsRenderableObject: AsRecordable + AsDrawable { - fn record_complete_render( - builder: &mut AutoCommandBufferBuilder, - data: &T, - pipeline: &Arc, - descriptor_sets: &[Arc], - ) -> Result<(), Box> { - builder.bind_pipeline_graphics(pipeline.clone())?; - - if !descriptor_sets.is_empty() { - builder.bind_descriptor_sets( - vulkano::pipeline::PipelineBindPoint::Graphics, - pipeline.layout().clone(), - 0, - descriptor_sets.iter().cloned().collect::>(), - )?; - } - - Self::record_render_commands(builder, data, pipeline, descriptor_sets)?; - - Ok(()) - } -} diff --git a/src/core/render/primitives/mod.rs b/src/core/render/primitives/mod.rs index 8ef9dd7..3303d41 100644 --- a/src/core/render/primitives/mod.rs +++ b/src/core/render/primitives/mod.rs @@ -9,17 +9,17 @@ use vulkano::{ }, }; +mod buffer; +mod command; + pub mod camera; pub mod mvp; pub mod transform; pub mod vertex; -pub mod buffer; -pub mod command; pub mod resource; - pub use buffer::{AsBindableBuffer, AsIndexBuffer, AsUniformBuffer, AsVertexBuffer}; -pub use command::{AsDrawable, AsRecordable}; +pub use command::{AsRecordable, AsRenderableMesh}; pub trait AsBindableDescriptorSet { fn as_descriptor_set_layout_bindings() -> BTreeMap; diff --git a/src/core/render/primitives/mvp.rs b/src/core/render/primitives/mvp.rs index 0f47ce3..af5db16 100644 --- a/src/core/render/primitives/mvp.rs +++ b/src/core/render/primitives/mvp.rs @@ -4,7 +4,7 @@ use std::sync::Arc; use vulkano::buffer::{ AllocateBufferError, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer, }; -use vulkano::descriptor_set::allocator::StandardDescriptorSetAllocator; +use vulkano::descriptor_set::allocator::{DescriptorSetAllocator, StandardDescriptorSetAllocator}; use vulkano::descriptor_set::layout::{ DescriptorSetLayout, DescriptorSetLayoutBinding, DescriptorType, }; diff --git a/src/core/render/texture.rs b/src/core/render/texture.rs index db0bcc5..38d8147 100644 --- a/src/core/render/texture.rs +++ b/src/core/render/texture.rs @@ -7,7 +7,7 @@ use vulkano::{ command_buffer::{AutoCommandBufferBuilder, CopyBufferToImageInfo, PrimaryAutoCommandBuffer}, descriptor_set::{ DescriptorSet, WriteDescriptorSet, - allocator::StandardDescriptorSetAllocator, + allocator::{DescriptorSetAllocator, StandardDescriptorSetAllocator}, layout::{DescriptorSetLayout, DescriptorSetLayoutBinding, DescriptorType}, }, device::Device, diff --git a/src/game/assets/meshs/mod.rs b/src/game/assets/meshs/mod.rs new file mode 100644 index 0000000..d793a66 --- /dev/null +++ b/src/game/assets/meshs/mod.rs @@ -0,0 +1 @@ +pub mod square; diff --git a/src/game/assets/meshs/square.rs b/src/game/assets/meshs/square.rs new file mode 100644 index 0000000..d1dc524 --- /dev/null +++ b/src/game/assets/meshs/square.rs @@ -0,0 +1,63 @@ +use std::{error::Error, sync::Arc}; + +use vulkano::{buffer::Subbuffer, memory::allocator::StandardMemoryAllocator}; + +use crate::core::render::primitives::{ + AsIndexBuffer, AsRenderableMesh, AsVertexBuffer, vertex::Vertex3D, +}; + +const VERTICES: [Vertex3D; 4] = [ + Vertex3D { + position: [-0.5, -0.5, 0.0], + uv: [0.0, 0.0], + }, + Vertex3D { + position: [-0.5, 0.5, 0.0], + uv: [0.0, 1.0], + }, + Vertex3D { + position: [0.5, -0.5, 0.0], + uv: [1.0, 0.0], + }, + Vertex3D { + position: [0.5, 0.5, 0.0], + uv: [1.0, 1.0], + }, +]; + +const INDICES: [u32; 6] = [0, 2, 1, 2, 3, 1]; + +pub struct Square { + vertex_buffer: Subbuffer<[Vertex3D]>, + index_buffer: Subbuffer<[u32]>, +} + +impl Square { + pub fn new(memory_allocator: &Arc) -> Result> { + let vertex_buffer = Vertex3D::create_vertex_buffer(memory_allocator, &VERTICES)?; + let index_buffer = u32::create_index_buffer(memory_allocator, &INDICES)?; + + Ok(Self { + vertex_buffer, + index_buffer, + }) + } +} + +impl AsRenderableMesh for Square { + fn vertex_buffer(&self) -> &Subbuffer<[Vertex3D]> { + &self.vertex_buffer + } + + fn index_buffer(&self) -> Option<&Subbuffer<[u32]>> { + Some(&self.index_buffer) + } + + fn vertex_count(&self) -> u32 { + VERTICES.len() as u32 + } + + fn index_count(&self) -> u32 { + INDICES.len() as u32 + } +} diff --git a/src/game/assets/mod.rs b/src/game/assets/mod.rs index d793a66..a0cac96 100644 --- a/src/game/assets/mod.rs +++ b/src/game/assets/mod.rs @@ -1 +1,2 @@ -pub mod square; +pub mod meshs; +pub mod pipelines; diff --git a/src/game/assets/pipelines/mod.rs b/src/game/assets/pipelines/mod.rs new file mode 100644 index 0000000..b252f36 --- /dev/null +++ b/src/game/assets/pipelines/mod.rs @@ -0,0 +1 @@ +pub mod simple; diff --git a/src/game/assets/pipelines/simple.rs b/src/game/assets/pipelines/simple.rs new file mode 100644 index 0000000..b46c993 --- /dev/null +++ b/src/game/assets/pipelines/simple.rs @@ -0,0 +1,207 @@ +use std::{error::Error, sync::Arc}; + +use vulkano::{ + buffer::Subbuffer, + command_buffer::{AutoCommandBufferBuilder, PrimaryAutoCommandBuffer}, + descriptor_set::{ + allocator::StandardDescriptorSetAllocator, layout::DescriptorSetLayoutCreateInfo, + }, + device::Device, + format::Format, + pipeline::{ + DynamicState, GraphicsPipeline, Pipeline, PipelineBindPoint, PipelineLayout, + PipelineShaderStageCreateInfo, + graphics::{ + GraphicsPipelineCreateInfo, + color_blend::{ColorBlendAttachmentState, ColorBlendState}, + depth_stencil::{DepthState, DepthStencilState}, + input_assembly::InputAssemblyState, + multisample::MultisampleState, + rasterization::RasterizationState, + subpass::PipelineRenderingCreateInfo, + vertex_input::{Vertex, VertexDefinition}, + viewport::ViewportState, + }, + layout::{PipelineDescriptorSetLayoutCreateInfo, PipelineLayoutCreateFlags}, + }, +}; + +use crate::core::render::{ + primitives::{ + AsBindableDescriptorSet, AsRecordable, AsRenderableMesh, mvp::Mvp, transform::TransformRaw, + vertex::Vertex3D, + }, + texture::Texture, +}; + +pub struct SimplePipelineRenderData<'a, T> +where + T: AsRenderableMesh, +{ + pub mesh: &'a T, + pub mvp_uniform: &'a Subbuffer<[Mvp]>, + pub texture: &'a Texture, + pub instances: &'a Subbuffer<[TransformRaw]>, +} + +pub struct SimplePipeline { + pipeline: Arc, +} + +impl SimplePipeline { + pub fn new( + device: &Arc, + swapchain_format: Format, + depth_format: Format, + ) -> Result> { + let vs = shaders::vs::load(device.clone())? + .entry_point("main") + .ok_or("Failed find main entry point of vertex shader".to_string())?; + + let fs = shaders::fs::load(device.clone())? + .entry_point("main") + .ok_or("Failed find main entry point of fragment shader".to_string())?; + + let vertex_input_state = + [Vertex3D::per_vertex(), TransformRaw::per_instance()].definition(&vs)?; + + let stages = [ + PipelineShaderStageCreateInfo::new(vs), + PipelineShaderStageCreateInfo::new(fs), + ]; + + let mvp_bindings = Mvp::as_descriptor_set_layout_bindings(); + let texture_bindings = Texture::as_descriptor_set_layout_bindings(); + + let vertex_descriptor_set_layout = DescriptorSetLayoutCreateInfo { + bindings: mvp_bindings, + ..Default::default() + }; + + let fragment_descriptor_set_layout = DescriptorSetLayoutCreateInfo { + bindings: texture_bindings, + ..Default::default() + }; + + let create_info = PipelineDescriptorSetLayoutCreateInfo { + set_layouts: vec![vertex_descriptor_set_layout, fragment_descriptor_set_layout], + flags: PipelineLayoutCreateFlags::default(), + push_constant_ranges: vec![], + } + .into_pipeline_layout_create_info(device.clone())?; + + let layout = PipelineLayout::new(device.clone(), create_info)?; + + let subpass = PipelineRenderingCreateInfo { + color_attachment_formats: vec![Some(swapchain_format)], + depth_attachment_format: Some(depth_format), + ..Default::default() + }; + + let pipeline = GraphicsPipeline::new( + device.clone(), + None, + GraphicsPipelineCreateInfo { + stages: stages.into_iter().collect(), + vertex_input_state: Some(vertex_input_state), + input_assembly_state: Some(InputAssemblyState::default()), + viewport_state: Some(ViewportState::default()), + rasterization_state: Some(RasterizationState::default()), + multisample_state: Some(MultisampleState::default()), + color_blend_state: Some(ColorBlendState::with_attachment_states( + subpass.color_attachment_formats.len() as u32, + ColorBlendAttachmentState::default(), + )), + depth_stencil_state: Some(DepthStencilState { + depth: Some(DepthState::simple()), + ..Default::default() + }), + dynamic_state: [DynamicState::Viewport].into_iter().collect(), + subpass: Some(subpass.into()), + ..GraphicsPipelineCreateInfo::layout(layout) + }, + )?; + + Ok(Self { pipeline }) + } + + pub fn pipeline(&self) -> &Arc { + &self.pipeline + } +} + +impl<'a, T: AsRenderableMesh> AsRecordable> + for SimplePipeline +{ + fn record_render_commands( + builder: &mut AutoCommandBufferBuilder, + descriptor_set_allocator: &Arc, + pipeline: &Arc, + data: &SimplePipelineRenderData<'a, T>, + ) -> Result<(), Box> { + let layouts = pipeline.layout().set_layouts(); + + let uniform_descriptor_set = + Mvp::as_descriptor_set(descriptor_set_allocator, &layouts[0], data.mvp_uniform)?; + + let texture_descriptor_set = + Texture::as_descriptor_set(descriptor_set_allocator, &layouts[1], data.texture)?; + + builder.bind_pipeline_graphics(pipeline.clone())?; + + builder.bind_descriptor_sets( + PipelineBindPoint::Graphics, + pipeline.layout().clone(), + 0, + vec![uniform_descriptor_set, texture_descriptor_set], + )?; + + builder.bind_vertex_buffers( + 0, + (data.mesh.vertex_buffer().clone(), data.instances.clone()), + )?; + + match data.mesh.index_buffer() { + Some(index_buffer) => { + builder.bind_index_buffer(index_buffer.clone())?; + unsafe { + builder.draw_indexed( + data.mesh.index_count(), + data.instances.len() as u32, + data.mesh.first_index(), + data.mesh.vertex_offset(), + 0, + )?; + } + } + None => unsafe { + builder.draw( + data.mesh.vertex_count(), + data.instances.len() as u32, + data.mesh.first_vertex(), + 0, + )?; + }, + } + + Ok(()) + } +} + +pub mod shaders { + pub mod vs { + vulkano_shaders::shader! { + ty: "vertex", + path: r"res/shaders/simple.vert", + generate_structs: false, + } + } + + pub mod fs { + vulkano_shaders::shader! { + ty: "fragment", + path: r"res/shaders/simple.frag", + generate_structs: false, + } + } +} diff --git a/src/game/assets/square.rs b/src/game/assets/square.rs index 57e4138..cea1274 100644 --- a/src/game/assets/square.rs +++ b/src/game/assets/square.rs @@ -86,6 +86,7 @@ pub struct SquareRenderData<'a> { pub square: &'a Square, pub mvp_uniform: &'a Subbuffer<[Mvp]>, pub transform_uniform: &'a Subbuffer<[TransformRaw]>, + pub descriptor_sets: &'a [Arc], pub texture: &'a Texture, } @@ -183,13 +184,6 @@ impl Square { transform_uniform: &Subbuffer<[TransformRaw]>, texture: &Texture, ) -> Result<(), Box> { - let render_data = SquareRenderData { - square: self, - mvp_uniform, - transform_uniform, - texture, - }; - let layouts = self.pipeline.layout().set_layouts(); let uniform_descriptor_set = @@ -198,50 +192,26 @@ impl Square { let texture_descriptor_set = Texture::as_descriptor_set(descriptor_set_allocator, &layouts[1], texture)?; + let render_data = SquareRenderData { + square: self, + mvp_uniform, + transform_uniform, + texture, + descriptor_sets: &[uniform_descriptor_set, texture_descriptor_set], + }; + // Utiliser les nouveaux traits pour le rendu - Self::record_render_commands( - command_buffer, - &render_data, - &self.pipeline, - &[uniform_descriptor_set, texture_descriptor_set], - )?; + Self::record_render_commands(command_buffer, &self.pipeline, &render_data)?; Ok(()) } } -// Implémentation des nouveaux traits pour Square -impl<'a> AsDrawable> for Square { - type VertexBuffer = Subbuffer<[Vertex3D]>; - type IndexBuffer = Subbuffer<[u32]>; - - fn vertex_buffer(data: &SquareRenderData<'a>) -> &'a Self::VertexBuffer { - &data.square.vertex_buffer - } - - fn index_buffer(data: &SquareRenderData<'a>) -> Option<&'a Self::IndexBuffer> { - Some(&data.square.index_buffer) - } - - fn vertex_count(_data: &SquareRenderData<'a>) -> u32 { - VERTICES.len() as u32 - } - - fn index_count(_data: &SquareRenderData<'a>) -> u32 { - INDICES.len() as u32 - } - - fn instance_count(data: &SquareRenderData<'a>) -> u32 { - data.transform_uniform.len() as u32 - } -} - impl<'a> AsRecordable> for Square { fn record_render_commands( builder: &mut AutoCommandBufferBuilder, - data: &SquareRenderData<'a>, pipeline: &Arc, - descriptor_sets: &[Arc], + data: &SquareRenderData<'a>, ) -> Result<(), Box> { // Bind pipeline builder.bind_pipeline_graphics(pipeline.clone())?; @@ -251,7 +221,7 @@ impl<'a> AsRecordable> for Square { PipelineBindPoint::Graphics, pipeline.layout().clone(), 0, - descriptor_sets.iter().cloned().collect::>(), + data.descriptor_sets.iter().cloned().collect::>(), )?; // Bind buffers diff --git a/src/game/scenes/main_scene.rs b/src/game/scenes/main_scene.rs index 933bccb..eb3b6bf 100644 --- a/src/game/scenes/main_scene.rs +++ b/src/game/scenes/main_scene.rs @@ -5,11 +5,14 @@ use crate::core::app::DEPTH_IMAGE_ID; use crate::core::app::context::WindowContext; use crate::core::app::user_event::UserEvent; use crate::core::render::primitives::camera::Camera3D; +use crate::core::render::primitives::mvp::Mvp; use crate::core::render::primitives::transform::Transform; +use crate::core::render::primitives::{AsBindableDescriptorSet, AsRecordable}; use crate::core::render::render_pass_manager::{RenderPassConfig, RenderPassManager}; use crate::core::render::texture::Texture; use crate::core::scene::Scene; -use crate::game::assets::square::Square; +use crate::game::assets::meshs::square::Square; +use crate::game::assets::pipelines::simple::{SimplePipeline, SimplePipelineRenderData}; use egui_winit_vulkano::egui; use glam::EulerRot; use glam::Quat; @@ -22,6 +25,7 @@ use winit::window::CursorGrabMode; pub struct MainSceneState { square: Square, + simple_pipeline: SimplePipeline, instances: Vec, camera: Camera3D, texture: Texture, @@ -46,9 +50,9 @@ impl Scene for MainScene { let swapchain_image_view = app_context.with_renderer(|renderer| renderer.swapchain_image_view().clone()); - let square = Square::new( + let square = Square::new(&app_context.memory_allocator)?; + let simple_pipeline = SimplePipeline::new( &app_context.device, - &app_context.memory_allocator, swapchain_image_view.format(), depth_image_view.format(), )?; @@ -103,6 +107,7 @@ impl Scene for MainScene { self.state = Some(MainSceneState { square, + simple_pipeline, instances, camera, texture, @@ -198,20 +203,20 @@ impl Scene for MainScene { // Create camera uniform using the actual camera let camera_uniform = state.camera.create_buffer(&app_context.memory_allocator)?; - let transform_uniform = Transform::create_buffer(&app_context.memory_allocator, &state.instances)?; - state - .square - .render( - &mut builder, - &app_context.descriptor_set_allocator, - &camera_uniform, - &transform_uniform, - &state.texture, - ) - .unwrap(); + SimplePipeline::record_render_commands( + &mut builder, + &app_context.descriptor_set_allocator, + state.simple_pipeline.pipeline(), + &SimplePipelineRenderData { + mesh: &state.square, + mvp_uniform: &camera_uniform, + texture: &state.texture, + instances: &transform_uniform, + }, + )?; RenderPassManager::end_rendering(&mut builder)?;