Split Mesh data and Pipeline

This commit is contained in:
Florian RICHER 2025-06-08 15:41:22 +02:00
parent 4f96a1e4b5
commit 078e9daba9
Signed by: florian.richer
GPG key ID: C73D37CBED7BFC77
13 changed files with 331 additions and 109 deletions

View file

@ -1,53 +1,51 @@
use std::{error::Error, sync::Arc}; use std::{error::Error, sync::Arc};
use vulkano::{ use vulkano::{
buffer::Subbuffer,
command_buffer::{ command_buffer::{
AutoCommandBufferBuilder, PrimaryAutoCommandBuffer, RenderPassBeginInfo, SubpassBeginInfo, AutoCommandBufferBuilder, PrimaryAutoCommandBuffer, RenderPassBeginInfo, SubpassBeginInfo,
SubpassEndInfo, SubpassEndInfo,
}, },
descriptor_set::DescriptorSet, descriptor_set::allocator::StandardDescriptorSetAllocator,
pipeline::{GraphicsPipeline, Pipeline}, pipeline::GraphicsPipeline,
render_pass::{Framebuffer, RenderPass}, render_pass::{Framebuffer, RenderPass},
}; };
pub trait AsRecordable<T> { pub trait AsRecordable<T> {
fn record_render_commands( fn record_render_commands(
builder: &mut AutoCommandBufferBuilder<PrimaryAutoCommandBuffer>, builder: &mut AutoCommandBufferBuilder<PrimaryAutoCommandBuffer>,
data: &T, descriptor_set_allocator: &Arc<StandardDescriptorSetAllocator>,
pipeline: &Arc<GraphicsPipeline>, pipeline: &Arc<GraphicsPipeline>,
descriptor_sets: &[Arc<DescriptorSet>], data: &T,
) -> Result<(), Box<dyn Error>>; ) -> Result<(), Box<dyn Error>>;
} }
pub trait AsDrawable<T> { pub trait AsRenderableMesh<V, I> {
type VertexBuffer; fn vertex_buffer(&self) -> &Subbuffer<[V]>;
type IndexBuffer;
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 None
} }
fn vertex_count(data: &T) -> u32; fn index_count(&self) -> u32 {
fn index_count(_data: &T) -> u32 {
0 0
} }
fn instance_count(_data: &T) -> u32 { fn first_index(&self) -> u32 {
1
}
fn first_vertex(_data: &T) -> u32 {
0 0
} }
fn first_index(_data: &T) -> u32 { fn index_offset(&self) -> i32 {
0
}
fn first_instance(_data: &T) -> u32 {
0 0
} }
} }
@ -84,27 +82,3 @@ pub trait AsRenderPassRecordable<T> {
data: &T, data: &T,
) -> Result<(), Box<dyn Error>>; ) -> Result<(), Box<dyn Error>>;
} }
pub trait AsRenderableObject<T>: AsRecordable<T> + AsDrawable<T> {
fn record_complete_render(
builder: &mut AutoCommandBufferBuilder<PrimaryAutoCommandBuffer>,
data: &T,
pipeline: &Arc<GraphicsPipeline>,
descriptor_sets: &[Arc<DescriptorSet>],
) -> Result<(), Box<dyn Error>> {
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::<Vec<_>>(),
)?;
}
Self::record_render_commands(builder, data, pipeline, descriptor_sets)?;
Ok(())
}
}

View file

@ -9,17 +9,17 @@ use vulkano::{
}, },
}; };
mod buffer;
mod command;
pub mod camera; pub mod camera;
pub mod mvp; pub mod mvp;
pub mod transform; pub mod transform;
pub mod vertex; pub mod vertex;
pub mod buffer;
pub mod command;
pub mod resource; pub mod resource;
pub use buffer::{AsBindableBuffer, AsIndexBuffer, AsUniformBuffer, AsVertexBuffer}; pub use buffer::{AsBindableBuffer, AsIndexBuffer, AsUniformBuffer, AsVertexBuffer};
pub use command::{AsDrawable, AsRecordable}; pub use command::{AsRecordable, AsRenderableMesh};
pub trait AsBindableDescriptorSet<T> { pub trait AsBindableDescriptorSet<T> {
fn as_descriptor_set_layout_bindings() -> BTreeMap<u32, DescriptorSetLayoutBinding>; fn as_descriptor_set_layout_bindings() -> BTreeMap<u32, DescriptorSetLayoutBinding>;

View file

@ -4,7 +4,7 @@ use std::sync::Arc;
use vulkano::buffer::{ use vulkano::buffer::{
AllocateBufferError, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer, AllocateBufferError, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer,
}; };
use vulkano::descriptor_set::allocator::StandardDescriptorSetAllocator; use vulkano::descriptor_set::allocator::{DescriptorSetAllocator, StandardDescriptorSetAllocator};
use vulkano::descriptor_set::layout::{ use vulkano::descriptor_set::layout::{
DescriptorSetLayout, DescriptorSetLayoutBinding, DescriptorType, DescriptorSetLayout, DescriptorSetLayoutBinding, DescriptorType,
}; };

View file

@ -7,7 +7,7 @@ use vulkano::{
command_buffer::{AutoCommandBufferBuilder, CopyBufferToImageInfo, PrimaryAutoCommandBuffer}, command_buffer::{AutoCommandBufferBuilder, CopyBufferToImageInfo, PrimaryAutoCommandBuffer},
descriptor_set::{ descriptor_set::{
DescriptorSet, WriteDescriptorSet, DescriptorSet, WriteDescriptorSet,
allocator::StandardDescriptorSetAllocator, allocator::{DescriptorSetAllocator, StandardDescriptorSetAllocator},
layout::{DescriptorSetLayout, DescriptorSetLayoutBinding, DescriptorType}, layout::{DescriptorSetLayout, DescriptorSetLayoutBinding, DescriptorType},
}, },
device::Device, device::Device,

View file

@ -0,0 +1 @@
pub mod square;

View file

@ -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<StandardMemoryAllocator>) -> Result<Self, Box<dyn Error>> {
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<Vertex3D, u32> 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
}
}

View file

@ -1 +1,2 @@
pub mod square; pub mod meshs;
pub mod pipelines;

View file

@ -0,0 +1 @@
pub mod simple;

View file

@ -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<Vertex3D, u32>,
{
pub mesh: &'a T,
pub mvp_uniform: &'a Subbuffer<[Mvp]>,
pub texture: &'a Texture,
pub instances: &'a Subbuffer<[TransformRaw]>,
}
pub struct SimplePipeline {
pipeline: Arc<GraphicsPipeline>,
}
impl SimplePipeline {
pub fn new(
device: &Arc<Device>,
swapchain_format: Format,
depth_format: Format,
) -> Result<Self, Box<dyn Error>> {
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<GraphicsPipeline> {
&self.pipeline
}
}
impl<'a, T: AsRenderableMesh<Vertex3D, u32>> AsRecordable<SimplePipelineRenderData<'a, T>>
for SimplePipeline
{
fn record_render_commands(
builder: &mut AutoCommandBufferBuilder<PrimaryAutoCommandBuffer>,
descriptor_set_allocator: &Arc<StandardDescriptorSetAllocator>,
pipeline: &Arc<GraphicsPipeline>,
data: &SimplePipelineRenderData<'a, T>,
) -> Result<(), Box<dyn Error>> {
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,
}
}
}

View file

@ -86,6 +86,7 @@ pub struct SquareRenderData<'a> {
pub square: &'a Square, pub square: &'a Square,
pub mvp_uniform: &'a Subbuffer<[Mvp]>, pub mvp_uniform: &'a Subbuffer<[Mvp]>,
pub transform_uniform: &'a Subbuffer<[TransformRaw]>, pub transform_uniform: &'a Subbuffer<[TransformRaw]>,
pub descriptor_sets: &'a [Arc<DescriptorSet>],
pub texture: &'a Texture, pub texture: &'a Texture,
} }
@ -183,13 +184,6 @@ impl Square {
transform_uniform: &Subbuffer<[TransformRaw]>, transform_uniform: &Subbuffer<[TransformRaw]>,
texture: &Texture, texture: &Texture,
) -> Result<(), Box<dyn Error>> { ) -> Result<(), Box<dyn Error>> {
let render_data = SquareRenderData {
square: self,
mvp_uniform,
transform_uniform,
texture,
};
let layouts = self.pipeline.layout().set_layouts(); let layouts = self.pipeline.layout().set_layouts();
let uniform_descriptor_set = let uniform_descriptor_set =
@ -198,50 +192,26 @@ impl Square {
let texture_descriptor_set = let texture_descriptor_set =
Texture::as_descriptor_set(descriptor_set_allocator, &layouts[1], texture)?; 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 // Utiliser les nouveaux traits pour le rendu
Self::record_render_commands( Self::record_render_commands(command_buffer, &self.pipeline, &render_data)?;
command_buffer,
&render_data,
&self.pipeline,
&[uniform_descriptor_set, texture_descriptor_set],
)?;
Ok(()) Ok(())
} }
} }
// Implémentation des nouveaux traits pour Square
impl<'a> AsDrawable<SquareRenderData<'a>> 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<SquareRenderData<'a>> for Square { impl<'a> AsRecordable<SquareRenderData<'a>> for Square {
fn record_render_commands( fn record_render_commands(
builder: &mut AutoCommandBufferBuilder<PrimaryAutoCommandBuffer>, builder: &mut AutoCommandBufferBuilder<PrimaryAutoCommandBuffer>,
data: &SquareRenderData<'a>,
pipeline: &Arc<GraphicsPipeline>, pipeline: &Arc<GraphicsPipeline>,
descriptor_sets: &[Arc<DescriptorSet>], data: &SquareRenderData<'a>,
) -> Result<(), Box<dyn Error>> { ) -> Result<(), Box<dyn Error>> {
// Bind pipeline // Bind pipeline
builder.bind_pipeline_graphics(pipeline.clone())?; builder.bind_pipeline_graphics(pipeline.clone())?;
@ -251,7 +221,7 @@ impl<'a> AsRecordable<SquareRenderData<'a>> for Square {
PipelineBindPoint::Graphics, PipelineBindPoint::Graphics,
pipeline.layout().clone(), pipeline.layout().clone(),
0, 0,
descriptor_sets.iter().cloned().collect::<Vec<_>>(), data.descriptor_sets.iter().cloned().collect::<Vec<_>>(),
)?; )?;
// Bind buffers // Bind buffers

View file

@ -5,11 +5,14 @@ use crate::core::app::DEPTH_IMAGE_ID;
use crate::core::app::context::WindowContext; use crate::core::app::context::WindowContext;
use crate::core::app::user_event::UserEvent; use crate::core::app::user_event::UserEvent;
use crate::core::render::primitives::camera::Camera3D; 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::transform::Transform;
use crate::core::render::primitives::{AsBindableDescriptorSet, AsRecordable};
use crate::core::render::render_pass_manager::{RenderPassConfig, RenderPassManager}; use crate::core::render::render_pass_manager::{RenderPassConfig, RenderPassManager};
use crate::core::render::texture::Texture; use crate::core::render::texture::Texture;
use crate::core::scene::Scene; 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 egui_winit_vulkano::egui;
use glam::EulerRot; use glam::EulerRot;
use glam::Quat; use glam::Quat;
@ -22,6 +25,7 @@ use winit::window::CursorGrabMode;
pub struct MainSceneState { pub struct MainSceneState {
square: Square, square: Square,
simple_pipeline: SimplePipeline,
instances: Vec<Transform>, instances: Vec<Transform>,
camera: Camera3D, camera: Camera3D,
texture: Texture, texture: Texture,
@ -46,9 +50,9 @@ impl Scene for MainScene {
let swapchain_image_view = let swapchain_image_view =
app_context.with_renderer(|renderer| renderer.swapchain_image_view().clone()); 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.device,
&app_context.memory_allocator,
swapchain_image_view.format(), swapchain_image_view.format(),
depth_image_view.format(), depth_image_view.format(),
)?; )?;
@ -103,6 +107,7 @@ impl Scene for MainScene {
self.state = Some(MainSceneState { self.state = Some(MainSceneState {
square, square,
simple_pipeline,
instances, instances,
camera, camera,
texture, texture,
@ -198,20 +203,20 @@ impl Scene for MainScene {
// Create camera uniform using the actual camera // Create camera uniform using the actual camera
let camera_uniform = state.camera.create_buffer(&app_context.memory_allocator)?; let camera_uniform = state.camera.create_buffer(&app_context.memory_allocator)?;
let transform_uniform = let transform_uniform =
Transform::create_buffer(&app_context.memory_allocator, &state.instances)?; Transform::create_buffer(&app_context.memory_allocator, &state.instances)?;
state SimplePipeline::record_render_commands(
.square &mut builder,
.render( &app_context.descriptor_set_allocator,
&mut builder, state.simple_pipeline.pipeline(),
&app_context.descriptor_set_allocator, &SimplePipelineRenderData {
&camera_uniform, mesh: &state.square,
&transform_uniform, mvp_uniform: &camera_uniform,
&state.texture, texture: &state.texture,
) instances: &transform_uniform,
.unwrap(); },
)?;
RenderPassManager::end_rendering(&mut builder)?; RenderPassManager::end_rendering(&mut builder)?;