pipeline: Refactor square pipeline + add support of indexes

This commit is contained in:
Florian RICHER 2025-05-28 22:39:56 +02:00
parent 122f577a26
commit 131811a539
Signed by: florian.richer
GPG key ID: C73D37CBED7BFC77
9 changed files with 270 additions and 217 deletions

View file

@ -19,7 +19,7 @@ use vulkano_util::context::VulkanoContext;
use vulkano_util::renderer::VulkanoWindowRenderer;
use vulkano_util::window::{VulkanoWindows, WindowDescriptor};
use winit::application::ApplicationHandler;
use winit::event::{DeviceEvent, WindowEvent};
use winit::event::WindowEvent;
use winit::event_loop::ActiveEventLoop;
use winit::window::WindowId;

View file

@ -1,4 +1,3 @@
pub mod pipelines;
pub mod primitives;
pub mod render_context;
pub mod texture;

View file

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

View file

@ -1,140 +0,0 @@
use std::collections::BTreeMap;
use std::error::Error;
use std::sync::Arc;
use vulkano::descriptor_set::layout::{
DescriptorSetLayoutBinding, DescriptorSetLayoutCreateInfo, DescriptorType,
};
use vulkano::device::Device;
use vulkano::format::Format;
use vulkano::pipeline::graphics::GraphicsPipelineCreateInfo;
use vulkano::pipeline::graphics::color_blend::{ColorBlendAttachmentState, ColorBlendState};
use vulkano::pipeline::graphics::input_assembly::{InputAssemblyState, PrimitiveTopology};
use vulkano::pipeline::graphics::multisample::MultisampleState;
use vulkano::pipeline::graphics::rasterization::RasterizationState;
use vulkano::pipeline::graphics::subpass::PipelineRenderingCreateInfo;
use vulkano::pipeline::graphics::vertex_input::{Vertex, VertexDefinition};
use vulkano::pipeline::graphics::viewport::ViewportState;
use vulkano::pipeline::layout::{PipelineDescriptorSetLayoutCreateInfo, PipelineLayoutCreateFlags};
use vulkano::pipeline::{
DynamicState, GraphicsPipeline, PipelineLayout, PipelineShaderStageCreateInfo,
};
use vulkano::shader::{EntryPoint, ShaderStages};
use crate::core::render::primitives::vertex::Vertex2D;
pub mod shaders {
pub mod vs {
vulkano_shaders::shader! {
ty: "vertex",
path: r"res/shaders/vertex.vert",
generate_structs: false,
}
}
pub mod fs {
vulkano_shaders::shader! {
ty: "fragment",
path: r"res/shaders/vertex.frag",
generate_structs: false,
}
}
}
pub fn create_triangle_pipeline(
device: &Arc<Device>,
swapchain_format: Format,
) -> Result<Arc<GraphicsPipeline>, Box<dyn Error>> {
let (vs, fs) = load_shaders(device)?;
let vertex_input_state = Vertex2D::per_vertex().definition(&vs)?;
log::trace!("vertex_input_state: {:#?}", vertex_input_state);
let stages = [
PipelineShaderStageCreateInfo::new(vs),
PipelineShaderStageCreateInfo::new(fs),
];
let vertex_bindings = BTreeMap::<u32, DescriptorSetLayoutBinding>::from_iter([(
0,
DescriptorSetLayoutBinding {
stages: ShaderStages::VERTEX,
..DescriptorSetLayoutBinding::descriptor_type(DescriptorType::UniformBuffer)
},
)]);
let fragment_bindings = BTreeMap::<u32, DescriptorSetLayoutBinding>::from_iter([
(
0,
DescriptorSetLayoutBinding {
stages: ShaderStages::FRAGMENT,
..DescriptorSetLayoutBinding::descriptor_type(DescriptorType::Sampler)
},
),
(
1,
DescriptorSetLayoutBinding {
stages: ShaderStages::FRAGMENT,
..DescriptorSetLayoutBinding::descriptor_type(DescriptorType::SampledImage)
},
),
]);
let vertex_descriptor_set_layout = DescriptorSetLayoutCreateInfo {
bindings: vertex_bindings,
..Default::default()
};
let fragment_descriptor_set_layout = DescriptorSetLayoutCreateInfo {
bindings: fragment_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)],
..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 {
topology: PrimitiveTopology::TriangleStrip,
..Default::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(),
)),
dynamic_state: [DynamicState::Viewport].into_iter().collect(),
subpass: Some(subpass.into()),
..GraphicsPipelineCreateInfo::layout(layout)
},
)?;
Ok(pipeline)
}
fn load_shaders(device: &Arc<Device>) -> Result<(EntryPoint, EntryPoint), 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())?;
Ok((vs, fs))
}

View file

@ -1,4 +1,4 @@
pub mod camera;
mod mvp;
pub mod mvp;
pub mod transform;
pub mod vertex;

1
src/game/assets/mod.rs Normal file
View file

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

247
src/game/assets/square.rs Normal file
View file

@ -0,0 +1,247 @@
use std::{collections::BTreeMap, error::Error, sync::Arc};
use vulkano::{
buffer::{Buffer, BufferCreateInfo, BufferUsage, Subbuffer},
command_buffer::{AutoCommandBufferBuilder, PrimaryAutoCommandBuffer},
descriptor_set::{
DescriptorSet, WriteDescriptorSet,
allocator::StandardDescriptorSetAllocator,
layout::{DescriptorSetLayoutBinding, DescriptorSetLayoutCreateInfo, DescriptorType},
},
device::Device,
format::Format,
memory::allocator::{AllocationCreateInfo, MemoryTypeFilter, StandardMemoryAllocator},
pipeline::{
DynamicState, GraphicsPipeline, Pipeline, PipelineBindPoint, PipelineLayout,
PipelineShaderStageCreateInfo,
graphics::{
GraphicsPipelineCreateInfo,
color_blend::{ColorBlendAttachmentState, ColorBlendState},
input_assembly::{InputAssemblyState, PrimitiveTopology},
multisample::MultisampleState,
rasterization::RasterizationState,
subpass::PipelineRenderingCreateInfo,
vertex_input::{Vertex, VertexDefinition},
viewport::ViewportState,
},
layout::{PipelineDescriptorSetLayoutCreateInfo, PipelineLayoutCreateFlags},
},
shader::ShaderStages,
};
use crate::core::render::{
primitives::{mvp::MVP, vertex::Vertex2D},
texture::Texture,
};
const VERTICES: [Vertex2D; 4] = [
Vertex2D {
position: [0.0, 0.0],
uv: [0.0, 0.0],
},
Vertex2D {
position: [0.0, 5.0],
uv: [0.0, 0.5],
},
Vertex2D {
position: [10.0, 0.0],
uv: [1.0, 0.0],
},
Vertex2D {
position: [10.0, 5.0],
uv: [1.0, 0.5],
},
];
const INDICES: [u32; 6] = [0, 1, 2, 2, 1, 3];
pub mod shaders {
pub mod vs {
vulkano_shaders::shader! {
ty: "vertex",
path: r"res/shaders/vertex.vert",
generate_structs: false,
}
}
pub mod fs {
vulkano_shaders::shader! {
ty: "fragment",
path: r"res/shaders/vertex.frag",
generate_structs: false,
}
}
}
pub struct Square {
vertex_buffer: Subbuffer<[Vertex2D]>,
index_buffer: Subbuffer<[u32]>,
pipeline: Arc<GraphicsPipeline>,
}
impl Square {
pub fn new(
device: &Arc<Device>,
memory_allocator: &Arc<StandardMemoryAllocator>,
swapchain_format: Format,
) -> Result<Self, Box<dyn Error>> {
let vertex_buffer = Buffer::from_iter(
memory_allocator.clone(),
BufferCreateInfo {
usage: BufferUsage::VERTEX_BUFFER,
..Default::default()
},
AllocationCreateInfo {
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
..Default::default()
},
Vec::from_iter(VERTICES),
)?;
let index_buffer = Buffer::from_iter(
memory_allocator.clone(),
BufferCreateInfo {
usage: BufferUsage::INDEX_BUFFER,
..Default::default()
},
AllocationCreateInfo {
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
..Default::default()
},
Vec::from_iter(INDICES),
)?;
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 = Vertex2D::per_vertex().definition(&vs)?;
let stages = [
PipelineShaderStageCreateInfo::new(vs),
PipelineShaderStageCreateInfo::new(fs),
];
let vertex_bindings = BTreeMap::<u32, DescriptorSetLayoutBinding>::from_iter([(
0,
DescriptorSetLayoutBinding {
stages: ShaderStages::VERTEX,
..DescriptorSetLayoutBinding::descriptor_type(DescriptorType::UniformBuffer)
},
)]);
let fragment_bindings = BTreeMap::<u32, DescriptorSetLayoutBinding>::from_iter([
(
0,
DescriptorSetLayoutBinding {
stages: ShaderStages::FRAGMENT,
..DescriptorSetLayoutBinding::descriptor_type(DescriptorType::Sampler)
},
),
(
1,
DescriptorSetLayoutBinding {
stages: ShaderStages::FRAGMENT,
..DescriptorSetLayoutBinding::descriptor_type(DescriptorType::SampledImage)
},
),
]);
let vertex_descriptor_set_layout = DescriptorSetLayoutCreateInfo {
bindings: vertex_bindings,
..Default::default()
};
let fragment_descriptor_set_layout = DescriptorSetLayoutCreateInfo {
bindings: fragment_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)],
..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(),
)),
dynamic_state: [DynamicState::Viewport].into_iter().collect(),
subpass: Some(subpass.into()),
..GraphicsPipelineCreateInfo::layout(layout)
},
)?;
Ok(Self {
vertex_buffer,
index_buffer,
pipeline,
})
}
pub fn render(
&self,
command_buffer: &mut AutoCommandBufferBuilder<PrimaryAutoCommandBuffer>,
descriptor_set_allocator: &Arc<StandardDescriptorSetAllocator>,
mvp_uniform: &Subbuffer<[MVP]>,
texture: &Texture,
) -> Result<(), Box<dyn Error>> {
let layouts = self.pipeline.layout().set_layouts();
let uniform_descriptor_set = DescriptorSet::new(
descriptor_set_allocator.clone(),
layouts[0].clone(),
[WriteDescriptorSet::buffer(0, mvp_uniform.clone())],
[],
)?;
let texture_descriptor_set = DescriptorSet::new(
descriptor_set_allocator.clone(),
layouts[1].clone(),
[
WriteDescriptorSet::sampler(0, texture.get_sampler().clone()),
WriteDescriptorSet::image_view(1, texture.get_texture().clone()),
],
[],
)?;
command_buffer.bind_pipeline_graphics(self.pipeline.clone())?;
command_buffer.bind_descriptor_sets(
PipelineBindPoint::Graphics,
self.pipeline.layout().clone(),
0,
vec![uniform_descriptor_set, texture_descriptor_set],
)?;
command_buffer.bind_vertex_buffers(0, self.vertex_buffer.clone())?;
command_buffer.bind_index_buffer(self.index_buffer.clone())?;
unsafe {
command_buffer.draw_indexed(INDICES.len() as u32, 1, 0, 0, 0)?;
}
Ok(())
}
}

View file

@ -1,43 +1,19 @@
use crate::core::input::InputManager;
use crate::core::render::pipelines::triangle_pipeline::create_triangle_pipeline;
use crate::core::render::primitives::camera::Camera;
use crate::core::render::primitives::vertex::Vertex2D;
use crate::core::render::render_context::RenderContext;
use crate::core::render::texture::Texture;
use crate::core::scene::Scene;
use crate::core::timer::Timer;
use glam::{Mat4, Vec3};
use std::sync::Arc;
use vulkano::buffer::Subbuffer;
use vulkano::command_buffer::{
AutoCommandBufferBuilder, CommandBufferUsage, PrimaryAutoCommandBuffer,
PrimaryCommandBufferAbstract,
};
use vulkano::descriptor_set::{DescriptorSet, WriteDescriptorSet};
use vulkano::pipeline::{GraphicsPipeline, Pipeline, PipelineBindPoint};
const VERTICES: [Vertex2D; 4] = [
Vertex2D {
position: [0.0, 0.0],
uv: [0.0, 0.0],
},
Vertex2D {
position: [0.0, 5.0],
uv: [0.0, 0.5],
},
Vertex2D {
position: [10.0, 0.0],
uv: [1.0, 0.0],
},
Vertex2D {
position: [10.0, 5.0],
uv: [1.0, 0.5],
},
];
use super::assets::square::Square;
pub struct MainSceneState {
pipeline: Arc<GraphicsPipeline>,
vertex_buffer: Subbuffer<[Vertex2D]>,
square: Square,
camera: Camera,
texture: Texture,
speed: f32,
@ -54,12 +30,12 @@ impl Scene for MainScene {
}
fn load(&mut self, render_context: &RenderContext) {
let pipeline =
create_triangle_pipeline(render_context.device(), render_context.swapchain_format())
.unwrap();
let vertex_buffer =
Vertex2D::create_buffer(Vec::from_iter(VERTICES), render_context.memory_allocator())
.unwrap();
let square = Square::new(
render_context.device(),
render_context.memory_allocator(),
render_context.swapchain_format(),
)
.unwrap();
let camera = Camera::new(
Mat4::look_at_rh(
@ -97,8 +73,7 @@ impl Scene for MainScene {
.unwrap();
self.state = Some(MainSceneState {
pipeline,
vertex_buffer,
square,
camera,
texture,
speed: 50.0,
@ -128,49 +103,20 @@ impl Scene for MainScene {
builder: &mut AutoCommandBufferBuilder<PrimaryAutoCommandBuffer>,
) {
let state = self.state.as_ref().unwrap();
let vertex_count = state.vertex_buffer.len() as u32;
let instance_count = vertex_count / 4;
let layouts = state.pipeline.layout().set_layouts();
let uniform_buffer = state
let camera_uniform = state
.camera
.create_buffer(render_context.memory_allocator())
.unwrap();
let uniform_descriptor_set = DescriptorSet::new(
render_context.descriptor_set_allocator().clone(),
layouts[0].clone(),
[WriteDescriptorSet::buffer(0, uniform_buffer)],
[],
)
.unwrap();
let texture_descriptor_set = DescriptorSet::new(
render_context.descriptor_set_allocator().clone(),
layouts[1].clone(),
[
WriteDescriptorSet::sampler(0, state.texture.get_sampler().clone()),
WriteDescriptorSet::image_view(1, state.texture.get_texture().clone()),
],
[],
)
.unwrap();
unsafe {
builder
.bind_pipeline_graphics(state.pipeline.clone())
.unwrap()
.bind_descriptor_sets(
PipelineBindPoint::Graphics,
state.pipeline.layout().clone(),
0,
vec![uniform_descriptor_set, texture_descriptor_set],
)
.unwrap()
.bind_vertex_buffers(0, state.vertex_buffer.clone())
.unwrap()
.draw(vertex_count, instance_count, 0, 0)
.unwrap();
}
state
.square
.render(
builder,
render_context.descriptor_set_allocator(),
&camera_uniform,
&state.texture,
)
.unwrap();
}
fn unload(&mut self) {}

View file

@ -1 +1,2 @@
pub mod assets;
pub mod main_scene;