(&self) {
- let type_id = TypeId::of::();
- let pipeline = Arc::new(RwLock::new(P::default()));
-
- let mut pipelines_id = self.pipelines_id.write().unwrap();
- let mut pipelines_state = self.pipelines_state.write().unwrap();
- let mut pipelines = self.pipelines.write().unwrap();
-
- pipelines_id.push(type_id);
- pipelines_state.push(Arc::new(RwLock::new(MaterialState::Loading)));
- pipelines.push(pipeline.clone());
- }
-
- pub fn add_material(&self) -> Result<(), MaterialError> {
- let pipeline_id = M::pipeline_type_id();
-
- let pipeline_result = {
- let pipelines_id = self.pipelines_id.read().unwrap();
- let pipelines_state = self.pipelines_state.read().unwrap();
- let pipelines = self.pipelines.read().unwrap();
-
- pipelines_id
- .iter()
- .zip(pipelines.iter())
- .zip(pipelines_state.iter())
- .find(|((id, _), _)| *id == &pipeline_id)
- .map(|((_, pipeline), state)| (pipeline.clone(), state.clone()))
- };
-
- let (pipeline, pipeline_state) = match pipeline_result {
- Some(pipeline) => pipeline,
- None => {
- tracing::error!(
- "Pipeline with id {pipeline_id:?} not found, please add it before adding a material"
- );
- return Err(MaterialError::PipelineNotFound);
- }
- };
-
- let type_id = TypeId::of::();
-
- let mut materials_id = self.materials_id.write().unwrap();
- let mut materials_pipeline_id = self.materials_pipeline_id.write().unwrap();
- let mut materials_pipeline = self.materials_pipeline.write().unwrap();
- let mut materials_pipeline_state = self.materials_pipeline_state.write().unwrap();
- let mut materials_state = self.materials_state.write().unwrap();
- let mut materials = self.materials.write().unwrap();
-
- materials_id.push(type_id);
- materials_pipeline_id.push(pipeline_id);
- materials_pipeline.push(pipeline);
- materials_pipeline_state.push(pipeline_state);
- materials_state.push(Arc::new(RwLock::new(MaterialState::Loading)));
- materials.push(Arc::new(RwLock::new(M::default())));
-
- Ok(())
- }
-
- pub fn update_swapchain_format(&mut self, swapchain_format: Format) {
- if self.swapchain_format == swapchain_format {
- return;
- }
-
- self.swapchain_format = swapchain_format;
- self.mark_all_pipelines_as_loading();
- }
-
- fn mark_all_pipelines_as_loading(&self) {
- let pipelines_state = self.pipelines_state.write().unwrap();
-
- for state in pipelines_state.iter() {
- let mut state = state.write().unwrap();
- *state = MaterialState::Loading;
- }
- }
-
- fn load_pipelines(&self) {
- let pipelines_state = self.pipelines_state.read().unwrap();
- let pipelines = self.pipelines.read().unwrap();
-
- let iter = pipelines_state
- .iter()
- .zip(pipelines.iter())
- .filter(|(state, _)| {
- let state = state.read().unwrap();
- matches!(*state, MaterialState::Loading)
- });
-
- for (state, pipeline) in iter {
- let mut pipeline = pipeline.write().unwrap();
- let result = pipeline.load(
- &self.device,
- &self.memory_allocator,
- self.swapchain_format,
- self.depth_format,
- );
-
- match result {
- Ok(_) => {
- let mut state = state.write().unwrap();
- *state = MaterialState::Loaded;
- }
- Err(e) => {
- tracing::error!("Failed to load pipeline: {e}");
- }
- }
- }
- }
-
- fn load_materials(&self) {
- let materials_state = self.materials_state.read().unwrap();
- let materials = self.materials.read().unwrap();
-
- let iter = materials_state
- .iter()
- .zip(materials.iter())
- .filter(|(state, _)| {
- let state = state.read().unwrap();
- matches!(*state, MaterialState::Loading)
- });
-
- for (state, material) in iter {
- let mut material = material.write().unwrap();
- let result = material.load(&self.device, &self.memory_allocator);
-
- match result {
- Ok(_) => {
- let mut state = state.write().unwrap();
- *state = MaterialState::Loaded;
- }
- Err(e) => {
- tracing::error!("Failed to load material: {e}");
- }
- }
- }
- }
-
- fn render_materials(&self, f: F)
- where
- F: Fn(RwLockReadGuard<'_, dyn Material>, RwLockReadGuard<'_, dyn Pipeline>),
- {
- let materials = self.materials.read().unwrap();
- let materials_state = self.materials_state.read().unwrap();
- let materials_pipeline = self.materials_pipeline.read().unwrap();
- let materials_pipeline_state = self.materials_pipeline_state.read().unwrap();
-
- materials
- .iter()
- .zip(materials_state.iter())
- .zip(materials_pipeline.iter())
- .zip(materials_pipeline_state.iter())
- .filter(|(((_, material_state), _), pipeline_state)| {
- let material_state = material_state.read().unwrap();
- let pipeline_state = pipeline_state.read().unwrap();
- matches!(*material_state, MaterialState::Loaded)
- && matches!(*pipeline_state, MaterialState::Loaded)
- })
- .for_each(|(((material, _), pipeline), _)| {
- let material = material.read().unwrap();
- let pipeline = pipeline.read().unwrap();
-
- f(material, pipeline);
- });
- }
-}
diff --git a/src/core/render/mod.rs b/src/core/render/mod.rs
deleted file mode 100644
index ec7ac54..0000000
--- a/src/core/render/mod.rs
+++ /dev/null
@@ -1,5 +0,0 @@
-pub mod material_manager;
-pub mod primitives;
-pub mod render_pass_manager;
-pub mod texture;
-pub mod vulkan_context;
diff --git a/src/core/render/primitives/camera.rs b/src/core/render/primitives/camera.rs
deleted file mode 100644
index f27622a..0000000
--- a/src/core/render/primitives/camera.rs
+++ /dev/null
@@ -1,122 +0,0 @@
-use std::{f32::consts::FRAC_PI_2, sync::Arc};
-
-use glam::{Mat4, Vec3, Vec4};
-use vulkano::{
- Validated,
- buffer::{AllocateBufferError, Subbuffer},
- memory::allocator::StandardMemoryAllocator,
-};
-
-use crate::core::{input::InputManager, timer::Timer};
-
-use super::mvp::Mvp;
-
-// See docs/OPENGL_VULKAN_DIFF.md
-const OPENGL_TO_VULKAN_Y_AXIS_FLIP: Mat4 = Mat4 {
- x_axis: Vec4::new(1.0, 0.0, 0.0, 0.0),
- y_axis: Vec4::new(0.0, -1.0, 0.0, 0.0),
- z_axis: Vec4::new(0.0, 0.0, 1.0, 0.0),
- w_axis: Vec4::new(0.0, 0.0, 0.0, 1.0),
-};
-
-#[derive(Default)]
-pub struct Camera3D {
- projection: Mat4,
-
- position: Vec3,
- rotation: Vec3,
- aspect_ratio: f32,
- fov: f32,
- near: f32,
- far: f32,
-}
-
-impl Camera3D {
- pub fn new(aspect_ratio: f32, fov: f32, near: f32, far: f32) -> Self {
- Self {
- projection: Mat4::perspective_rh(fov, aspect_ratio, near, far),
- position: Vec3::ZERO,
- rotation: Vec3::ZERO,
- aspect_ratio,
- fov,
- near,
- far,
- }
- }
-
- pub fn update(
- &mut self,
- input_manager: &InputManager,
- timer: &Timer,
- movement_speed: f32,
- camera_sensitivity: f32,
- window_aspect_ratio: f32,
- ) {
- // Process camera rotation
- let camera_delta = camera_sensitivity * timer.delta_time();
- self.rotation += Vec3::new(
- (input_manager.get_virtual_input_state("mouse_y") * camera_delta).to_radians(),
- (input_manager.get_virtual_input_state("mouse_x") * camera_delta).to_radians(),
- 0.0,
- );
-
- if self.rotation.x > FRAC_PI_2 {
- self.rotation = Vec3::new(FRAC_PI_2, self.rotation.y, 0.0);
- }
-
- if self.rotation.x < -FRAC_PI_2 {
- self.rotation = Vec3::new(-FRAC_PI_2, self.rotation.y, 0.0);
- }
-
- let movement_delta = movement_speed * timer.delta_time();
-
- let (yaw_sin, yaw_cos) = self.rotation.y.sin_cos();
- let forward = Vec3::new(yaw_cos, 0.0, yaw_sin).normalize();
- let right = Vec3::new(-yaw_sin, 0.0, yaw_cos).normalize();
-
- let tx = input_manager.get_virtual_input_state("move_right") * movement_delta;
- self.position += tx * right;
-
- let tz = input_manager.get_virtual_input_state("move_forward") * movement_delta;
- self.position += tz * forward;
-
- if self.aspect_ratio != window_aspect_ratio {
- self.aspect_ratio = window_aspect_ratio;
- self.projection =
- Mat4::perspective_rh(self.fov, self.aspect_ratio, self.near, self.far);
- }
- }
-
- pub fn set_projection(&mut self, projection: Mat4) {
- self.projection = projection;
- }
-
- pub fn get_position(&self) -> Vec3 {
- self.position
- }
-
- pub fn get_rotation(&self) -> Vec3 {
- self.rotation
- }
-
- pub fn create_buffer(
- &self,
- memory_allocator: &Arc,
- ) -> Result, Validated> {
- let (sin_pitch, cos_pitch) = self.rotation.x.sin_cos();
- let (sin_yaw, cos_yaw) = self.rotation.y.sin_cos();
-
- let view_matrix = Mat4::look_to_rh(
- self.position,
- Vec3::new(cos_pitch * cos_yaw, sin_pitch, cos_pitch * sin_yaw).normalize(),
- Vec3::Y,
- );
-
- Mvp {
- model: OPENGL_TO_VULKAN_Y_AXIS_FLIP.to_cols_array_2d(),
- view: view_matrix.to_cols_array_2d(),
- projection: self.projection.to_cols_array_2d(),
- }
- .into_buffer(memory_allocator)
- }
-}
diff --git a/src/core/render/primitives/mod.rs b/src/core/render/primitives/mod.rs
deleted file mode 100644
index 5ab8266..0000000
--- a/src/core/render/primitives/mod.rs
+++ /dev/null
@@ -1,25 +0,0 @@
-use std::{collections::BTreeMap, sync::Arc};
-
-use vulkano::{
- Validated, VulkanError,
- descriptor_set::{
- DescriptorSet,
- allocator::StandardDescriptorSetAllocator,
- layout::{DescriptorSetLayout, DescriptorSetLayoutBinding},
- },
-};
-
-pub mod camera;
-pub mod mvp;
-pub mod transform;
-pub mod vertex;
-
-pub trait AsBindableDescriptorSet {
- fn as_descriptor_set_layout_bindings() -> BTreeMap;
-
- fn as_descriptor_set(
- descriptor_set_allocator: &Arc,
- layout: &Arc,
- data: &T,
- ) -> Result, Validated>;
-}
diff --git a/src/core/render/primitives/mvp.rs b/src/core/render/primitives/mvp.rs
deleted file mode 100644
index d05307e..0000000
--- a/src/core/render/primitives/mvp.rs
+++ /dev/null
@@ -1,70 +0,0 @@
-use std::collections::BTreeMap;
-use std::sync::Arc;
-
-use vulkano::buffer::{
- AllocateBufferError, Buffer, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer,
-};
-use vulkano::descriptor_set::allocator::StandardDescriptorSetAllocator;
-use vulkano::descriptor_set::layout::{
- DescriptorSetLayout, DescriptorSetLayoutBinding, DescriptorType,
-};
-use vulkano::descriptor_set::{DescriptorSet, WriteDescriptorSet};
-use vulkano::memory::allocator::{AllocationCreateInfo, MemoryTypeFilter, StandardMemoryAllocator};
-use vulkano::shader::ShaderStages;
-use vulkano::{Validated, VulkanError};
-
-use crate::core::render::primitives::AsBindableDescriptorSet;
-
-#[derive(BufferContents, Clone, Copy)]
-#[repr(C)]
-pub struct Mvp {
- pub model: [[f32; 4]; 4],
- pub view: [[f32; 4]; 4],
- pub projection: [[f32; 4]; 4],
-}
-
-impl Mvp {
- pub fn into_buffer(
- self,
- memory_allocator: &Arc,
- ) -> Result, Validated> {
- Buffer::from_iter(
- memory_allocator.clone(),
- BufferCreateInfo {
- usage: BufferUsage::UNIFORM_BUFFER,
- ..Default::default()
- },
- AllocationCreateInfo {
- memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
- | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
- ..Default::default()
- },
- [self],
- )
- }
-}
-
-impl AsBindableDescriptorSet> for Mvp {
- fn as_descriptor_set_layout_bindings() -> BTreeMap {
- BTreeMap::::from_iter([(
- 0,
- DescriptorSetLayoutBinding {
- stages: ShaderStages::VERTEX,
- ..DescriptorSetLayoutBinding::descriptor_type(DescriptorType::UniformBuffer)
- },
- )])
- }
-
- fn as_descriptor_set(
- descriptor_set_allocator: &Arc,
- layout: &Arc,
- data: &Subbuffer<[Mvp]>,
- ) -> Result, Validated> {
- DescriptorSet::new(
- descriptor_set_allocator.clone(),
- layout.clone(),
- [WriteDescriptorSet::buffer(0, data.clone())],
- [],
- )
- }
-}
diff --git a/src/core/render/primitives/transform.rs b/src/core/render/primitives/transform.rs
deleted file mode 100644
index 4d870b0..0000000
--- a/src/core/render/primitives/transform.rs
+++ /dev/null
@@ -1,81 +0,0 @@
-use std::sync::Arc;
-
-use glam::{Mat4, Quat, Vec3};
-use vulkano::{
- Validated,
- buffer::{
- AllocateBufferError, Buffer, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer,
- },
- memory::allocator::{AllocationCreateInfo, MemoryTypeFilter, StandardMemoryAllocator},
- pipeline::graphics::vertex_input::Vertex,
-};
-
-pub struct Transform {
- pub position: Vec3,
- pub rotation: Quat,
- pub scale: Vec3,
-}
-
-#[derive(BufferContents, Vertex)]
-#[repr(C)]
-pub struct TransformRaw {
- #[format(R32G32B32A32_SFLOAT)]
- pub model: [[f32; 4]; 4],
-}
-
-impl Default for Transform {
- fn default() -> Self {
- Self {
- position: Vec3::default(),
- rotation: Quat::default(),
- scale: Vec3::ONE,
- }
- }
-}
-
-impl Transform {
- pub fn rotate(&mut self, rotation: Quat) {
- self.rotation *= rotation;
- }
-
- pub fn translate(&mut self, translation: Vec3) {
- self.position += translation;
- }
-
- pub fn scale(&mut self, scale: Vec3) {
- self.scale *= scale;
- }
-
- pub fn to_raw_tranform(&self) -> TransformRaw {
- TransformRaw {
- model: (Mat4::from_translation(self.position)
- * Mat4::from_quat(self.rotation)
- * Mat4::from_scale(self.scale))
- .to_cols_array_2d(),
- }
- }
-
- pub fn create_buffer(
- memory_allocator: &Arc,
- transforms: &[Transform],
- ) -> Result, Validated> {
- let transform_raws: Vec =
- transforms.iter().map(|t| t.to_raw_tranform()).collect();
-
- let 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()
- },
- transform_raws,
- )?;
-
- Ok(buffer)
- }
-}
diff --git a/src/core/render/primitives/vertex.rs b/src/core/render/primitives/vertex.rs
deleted file mode 100644
index 166ac44..0000000
--- a/src/core/render/primitives/vertex.rs
+++ /dev/null
@@ -1,22 +0,0 @@
-use vulkano::buffer::BufferContents;
-use vulkano::pipeline::graphics::vertex_input::Vertex;
-
-#[derive(BufferContents, Vertex)]
-#[repr(C)]
-pub struct Vertex2D {
- #[format(R32G32_SFLOAT)]
- pub position: [f32; 2],
-
- #[format(R32G32_SFLOAT)]
- pub uv: [f32; 2],
-}
-
-#[derive(BufferContents, Vertex)]
-#[repr(C)]
-pub struct Vertex3D {
- #[format(R32G32B32_SFLOAT)]
- pub position: [f32; 3],
-
- #[format(R32G32_SFLOAT)]
- pub uv: [f32; 2],
-}
diff --git a/src/core/render/render_pass_manager.rs b/src/core/render/render_pass_manager.rs
deleted file mode 100644
index 02c3d7a..0000000
--- a/src/core/render/render_pass_manager.rs
+++ /dev/null
@@ -1,113 +0,0 @@
-use std::sync::Arc;
-use vulkano::{
- command_buffer::{AutoCommandBufferBuilder, RenderingAttachmentInfo, RenderingInfo},
- image::view::ImageView,
- pipeline::graphics::viewport::Viewport,
- render_pass::{AttachmentLoadOp, AttachmentStoreOp},
-};
-
-/// Types de render passes disponibles
-#[derive(Debug, Clone)]
-pub enum RenderPassType {
- Standard,
- ShadowMap,
- PostProcess,
-}
-
-/// Configuration pour un render pass
-#[derive(Debug, Clone)]
-pub struct RenderPassConfig {
- pub pass_type: RenderPassType,
- pub clear_color: Option<[f32; 4]>,
- pub clear_depth: Option,
- pub load_op: AttachmentLoadOp,
- pub store_op: AttachmentStoreOp,
-}
-
-impl Default for RenderPassConfig {
- fn default() -> Self {
- Self {
- pass_type: RenderPassType::Standard,
- clear_color: Some([0.0, 0.0, 0.0, 1.0]),
- clear_depth: Some(1.0),
- load_op: AttachmentLoadOp::Clear,
- store_op: AttachmentStoreOp::Store,
- }
- }
-}
-
-/// Gestionnaire de render passes réutilisable
-pub struct RenderPassManager;
-
-impl RenderPassManager {
- /// Commence un render pass standard avec les paramètres donnés
- pub fn begin_standard_rendering(
- builder: &mut AutoCommandBufferBuilder,
- config: &RenderPassConfig,
- color_attachment: Arc,
- depth_attachment: Option>,
- window_size: [f32; 2],
- ) -> Result<(), Box> {
- let viewport = Viewport {
- offset: [0.0, 0.0],
- extent: window_size,
- depth_range: 0.0..=1.0,
- };
-
- let mut rendering_info = RenderingInfo {
- color_attachments: vec![Some(RenderingAttachmentInfo {
- load_op: config.load_op,
- store_op: config.store_op,
- clear_value: config.clear_color.map(|c| c.into()),
- ..RenderingAttachmentInfo::image_view(color_attachment)
- })],
- depth_attachment: None,
- ..Default::default()
- };
-
- if let Some(depth_view) = depth_attachment {
- rendering_info.depth_attachment = Some(RenderingAttachmentInfo {
- load_op: AttachmentLoadOp::Clear,
- store_op: AttachmentStoreOp::DontCare,
- clear_value: config.clear_depth.map(|d| [d].into()),
- ..RenderingAttachmentInfo::image_view(depth_view)
- });
- }
-
- builder
- .begin_rendering(rendering_info)?
- .set_viewport(0, [viewport].into_iter().collect())?;
-
- Ok(())
- }
-
- /// Termine le render pass actuel
- pub fn end_rendering(
- builder: &mut AutoCommandBufferBuilder,
- ) -> Result<(), Box> {
- builder.end_rendering()?;
- Ok(())
- }
-
- /// Crée une configuration pour un render pass shadow map
- pub fn shadow_map_config() -> RenderPassConfig {
- RenderPassConfig {
- pass_type: RenderPassType::ShadowMap,
- clear_color: None,
- clear_depth: Some(1.0),
- load_op: AttachmentLoadOp::Clear,
- store_op: AttachmentStoreOp::Store,
- }
- }
-
- /// Crée une configuration pour un render pass de post-processing
- pub fn post_process_config() -> RenderPassConfig {
- RenderPassConfig {
- pass_type: RenderPassType::PostProcess,
- clear_color: Some([0.0, 0.0, 0.0, 1.0]),
- clear_depth: None,
- load_op: AttachmentLoadOp::Load,
- store_op: AttachmentStoreOp::Store,
- }
- }
-}
diff --git a/src/core/render/texture.rs b/src/core/render/texture.rs
deleted file mode 100644
index db0bcc5..0000000
--- a/src/core/render/texture.rs
+++ /dev/null
@@ -1,168 +0,0 @@
-use std::{collections::BTreeMap, error::Error, sync::Arc};
-
-use image::DynamicImage;
-use vulkano::{
- Validated, VulkanError,
- buffer::{Buffer, BufferCreateInfo, BufferUsage},
- command_buffer::{AutoCommandBufferBuilder, CopyBufferToImageInfo, PrimaryAutoCommandBuffer},
- descriptor_set::{
- DescriptorSet, WriteDescriptorSet,
- allocator::StandardDescriptorSetAllocator,
- layout::{DescriptorSetLayout, DescriptorSetLayoutBinding, DescriptorType},
- },
- device::Device,
- format::Format,
- image::{
- Image, ImageCreateInfo, ImageType, ImageUsage,
- sampler::{Filter, Sampler, SamplerAddressMode, SamplerCreateInfo},
- view::ImageView,
- },
- memory::allocator::{AllocationCreateInfo, MemoryTypeFilter, StandardMemoryAllocator},
- shader::ShaderStages,
-};
-
-use crate::core::render::primitives::AsBindableDescriptorSet;
-
-pub struct Texture {
- texture: Arc,
- sampler: Arc,
-}
-
-impl Texture {
- fn new(texture: Arc, sampler: Arc) -> Self {
- Self { texture, sampler }
- }
-
- pub fn from_file(
- device: &Arc,
- memory_allocator: &Arc,
- builder: &mut AutoCommandBufferBuilder,
- path: &str,
- ) -> Result> {
- let _span = tracing::info_span!("texture_load_from_file", path = path);
-
- let bytes = std::fs::read(path)?;
- Self::from_bytes(device, memory_allocator, builder, &bytes)
- }
-
- pub fn from_bytes(
- device: &Arc,
- memory_allocator: &Arc,
- builder: &mut AutoCommandBufferBuilder,
- bytes: &[u8],
- ) -> Result> {
- let image = image::load_from_memory(bytes)?;
- Self::from_dynamic_image(device, memory_allocator, builder, image)
- }
-
- pub fn from_dynamic_image(
- device: &Arc,
- memory_allocator: &Arc,
- builder: &mut AutoCommandBufferBuilder,
- image: DynamicImage,
- ) -> Result> {
- let _span = tracing::info_span!("texture_from_dynamic_image");
-
- let image_data = image.to_rgba8();
- let image_dimensions = image_data.dimensions();
- let image_data = image_data.into_raw();
-
- let upload_buffer = Buffer::new_slice(
- memory_allocator.clone(),
- BufferCreateInfo {
- usage: BufferUsage::TRANSFER_SRC,
- ..Default::default()
- },
- AllocationCreateInfo {
- memory_type_filter: MemoryTypeFilter::PREFER_HOST
- | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
- ..Default::default()
- },
- image_data.len() as u64,
- )?;
-
- {
- let buffer_data = &mut *upload_buffer.write()?;
- buffer_data.copy_from_slice(&image_data);
- }
-
- let image = Image::new(
- memory_allocator.clone(),
- ImageCreateInfo {
- image_type: ImageType::Dim2d,
- format: Format::R8G8B8A8_SRGB,
- extent: [image_dimensions.0, image_dimensions.1, 1],
- array_layers: 1,
- usage: ImageUsage::TRANSFER_DST | ImageUsage::SAMPLED,
- ..Default::default()
- },
- AllocationCreateInfo::default(),
- )?;
-
- builder.copy_buffer_to_image(CopyBufferToImageInfo::buffer_image(
- upload_buffer,
- image.clone(),
- ))?;
-
- let sampler = Sampler::new(
- device.clone(),
- SamplerCreateInfo {
- mag_filter: Filter::Linear,
- min_filter: Filter::Linear,
- address_mode: [SamplerAddressMode::Repeat; 3],
- ..Default::default()
- },
- )?;
-
- let image_view = ImageView::new_default(image)?;
-
- tracing::trace!("Texture loaded with dimensions {:?}", image_dimensions);
-
- Ok(Self::new(image_view, sampler))
- }
-
- pub fn get_texture(&self) -> &Arc {
- &self.texture
- }
-
- pub fn get_sampler(&self) -> &Arc {
- &self.sampler
- }
-}
-
-impl AsBindableDescriptorSet for Texture {
- fn as_descriptor_set_layout_bindings() -> BTreeMap {
- BTreeMap::::from_iter([
- (
- 0,
- DescriptorSetLayoutBinding {
- stages: ShaderStages::FRAGMENT,
- ..DescriptorSetLayoutBinding::descriptor_type(DescriptorType::Sampler)
- },
- ),
- (
- 1,
- DescriptorSetLayoutBinding {
- stages: ShaderStages::FRAGMENT,
- ..DescriptorSetLayoutBinding::descriptor_type(DescriptorType::SampledImage)
- },
- ),
- ])
- }
-
- fn as_descriptor_set(
- descriptor_set_allocator: &Arc,
- layout: &Arc,
- data: &Texture,
- ) -> Result, Validated> {
- DescriptorSet::new(
- descriptor_set_allocator.clone(),
- layout.clone(),
- [
- WriteDescriptorSet::sampler(0, data.sampler.clone()),
- WriteDescriptorSet::image_view(1, data.texture.clone()),
- ],
- [],
- )
- }
-}
diff --git a/src/core/render/vulkan_context.rs b/src/core/render/vulkan_context.rs
deleted file mode 100644
index 3d91ec3..0000000
--- a/src/core/render/vulkan_context.rs
+++ /dev/null
@@ -1,45 +0,0 @@
-use std::sync::Arc;
-
-use vulkano::{
- command_buffer::allocator::StandardCommandBufferAllocator,
- descriptor_set::allocator::StandardDescriptorSetAllocator,
-};
-use vulkano_util::context::VulkanoContext;
-
-pub struct VulkanContext {
- vulkano_context: VulkanoContext,
- command_buffer_allocator: Arc,
- descriptor_set_allocator: Arc,
-}
-
-impl VulkanContext {
- pub fn new(vulkano_context: VulkanoContext) -> Self {
- let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new(
- vulkano_context.device().clone(),
- Default::default(),
- ));
-
- let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new(
- vulkano_context.device().clone(),
- Default::default(),
- ));
-
- Self {
- vulkano_context,
- command_buffer_allocator,
- descriptor_set_allocator,
- }
- }
-
- pub fn vulkano_context(&self) -> &VulkanoContext {
- &self.vulkano_context
- }
-
- pub fn command_buffer_allocator(&self) -> &Arc {
- &self.command_buffer_allocator
- }
-
- pub fn descriptor_set_allocator(&self) -> &Arc {
- &self.descriptor_set_allocator
- }
-}
diff --git a/src/core/scene/manager.rs b/src/core/scene/manager.rs
deleted file mode 100644
index cf07ebc..0000000
--- a/src/core/scene/manager.rs
+++ /dev/null
@@ -1,65 +0,0 @@
-use std::error::Error;
-
-use crate::core::app::context::WindowContext;
-
-use super::Scene;
-
-pub struct SceneManager {
- scenes: Vec>,
- current_scene_index: Option,
-}
-
-impl SceneManager {
- pub fn new() -> Self {
- Self {
- scenes: Vec::new(),
- current_scene_index: None,
- }
- }
-
- pub fn load_scene(&mut self, scene: Box) {
- self.scenes.push(scene);
- self.current_scene_index = Some(self.scenes.len() - 1);
- }
-
- pub fn replace_current_scene(&mut self, scene: Box) {
- if let Some(index) = self.current_scene_index {
- if index < self.scenes.len() {
- self.scenes[index].unload();
- self.scenes[index] = scene;
- }
- } else {
- self.load_scene(scene);
- }
- }
-
- pub fn current_scene(&self) -> Option<&Box> {
- if let Some(index) = self.current_scene_index {
- self.scenes.get(index)
- } else {
- None
- }
- }
-
- pub fn current_scene_mut(&mut self) -> Option<&mut Box> {
- if let Some(index) = self.current_scene_index {
- self.scenes.get_mut(index)
- } else {
- None
- }
- }
-
- pub fn load_scene_if_not_loaded(
- &mut self,
- app_context: &mut WindowContext,
- ) -> Result<(), Box> {
- if let Some(scene) = self.current_scene_mut() {
- if !scene.loaded() {
- scene.load(app_context)?;
- }
- } else {
- tracing::warn!("No scene found in SceneManager!");
- }
- Ok(())
- }
-}
diff --git a/src/core/scene/mod.rs b/src/core/scene/mod.rs
deleted file mode 100644
index 5dd8a57..0000000
--- a/src/core/scene/mod.rs
+++ /dev/null
@@ -1,19 +0,0 @@
-use std::error::Error;
-
-use vulkano::sync::GpuFuture;
-
-use crate::core::app::context::WindowContext;
-
-pub mod manager;
-
-pub trait Scene {
- fn loaded(&self) -> bool;
- fn load(&mut self, app_context: &mut WindowContext) -> Result<(), Box>;
- fn update(&mut self, app_context: &mut WindowContext) -> Result<(), Box>;
- fn render(
- &mut self,
- acquire_future: Box,
- app_context: &mut WindowContext,
- ) -> Result, Box>;
- fn unload(&mut self);
-}
diff --git a/src/core/timer.rs b/src/core/timer.rs
deleted file mode 100644
index 3245a4c..0000000
--- a/src/core/timer.rs
+++ /dev/null
@@ -1,34 +0,0 @@
-pub struct Timer {
- start_time: std::time::Instant,
- last_time: std::time::Instant,
- current_time: std::time::Instant,
- delta_time: f32,
-}
-
-impl Timer {
- pub fn new() -> Self {
- Self {
- start_time: std::time::Instant::now(),
- last_time: std::time::Instant::now(),
- current_time: std::time::Instant::now(),
- delta_time: 0.0,
- }
- }
-
- pub fn update(&mut self) {
- self.current_time = std::time::Instant::now();
- self.delta_time = self
- .current_time
- .duration_since(self.last_time)
- .as_secs_f32();
- self.last_time = self.current_time;
- }
-
- pub fn delta_time(&self) -> f32 {
- self.delta_time
- }
-
- pub fn start_time(&self) -> std::time::Instant {
- self.start_time
- }
-}
diff --git a/src/game/assets/mod.rs b/src/game/assets/mod.rs
deleted file mode 100644
index d793a66..0000000
--- a/src/game/assets/mod.rs
+++ /dev/null
@@ -1 +0,0 @@
-pub mod square;
diff --git a/src/game/assets/square.rs b/src/game/assets/square.rs
deleted file mode 100644
index c4a33ad..0000000
--- a/src/game/assets/square.rs
+++ /dev/null
@@ -1,231 +0,0 @@
-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},
- depth_stencil::{DepthState, DepthStencilState},
- input_assembly::InputAssemblyState,
- multisample::MultisampleState,
- rasterization::RasterizationState,
- subpass::PipelineRenderingCreateInfo,
- vertex_input::{Vertex, VertexDefinition},
- viewport::ViewportState,
- },
- layout::{PipelineDescriptorSetLayoutCreateInfo, PipelineLayoutCreateFlags},
- },
- shader::ShaderStages,
-};
-
-use crate::core::render::{
- primitives::{AsBindableDescriptorSet, mvp::Mvp, transform::TransformRaw, vertex::Vertex3D},
- texture::Texture,
-};
-
-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 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<[Vertex3D]>,
- index_buffer: Subbuffer<[u32]>,
- pipeline: Arc,
-}
-
-impl Square {
- pub fn new(
- device: &Arc,
- memory_allocator: &Arc,
- swapchain_format: Format,
- depth_format: Format,
- ) -> Result> {
- 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 =
- [Vertex3D::per_vertex(), TransformRaw::per_instance()].definition(&vs)?;
-
- let stages = [
- PipelineShaderStageCreateInfo::new(vs),
- PipelineShaderStageCreateInfo::new(fs),
- ];
-
- let vertex_bindings = Mvp::as_descriptor_set_layout_bindings();
- let texture_bindings = Texture::as_descriptor_set_layout_bindings();
-
- let vertex_descriptor_set_layout = DescriptorSetLayoutCreateInfo {
- bindings: vertex_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 {
- vertex_buffer,
- index_buffer,
- pipeline,
- })
- }
-
- pub fn render(
- &self,
- command_buffer: &mut AutoCommandBufferBuilder,
- descriptor_set_allocator: &Arc,
- mvp_uniform: &Subbuffer<[Mvp]>,
- transform_uniform: &Subbuffer<[TransformRaw]>,
- texture: &Texture,
- ) -> Result<(), Box> {
- let layouts = self.pipeline.layout().set_layouts();
-
- let uniform_descriptor_set =
- Mvp::as_descriptor_set(descriptor_set_allocator, &layouts[0], mvp_uniform)?;
-
- let texture_descriptor_set =
- Texture::as_descriptor_set(descriptor_set_allocator, &layouts[1], texture)?;
-
- 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(), transform_uniform.clone()))?;
- command_buffer.bind_index_buffer(self.index_buffer.clone())?;
-
- unsafe {
- command_buffer.draw_indexed(
- INDICES.len() as u32,
- transform_uniform.len() as u32,
- 0,
- 0,
- 0,
- )?;
- }
-
- Ok(())
- }
-}
diff --git a/src/game/mod.rs b/src/game/mod.rs
deleted file mode 100644
index 065e8e4..0000000
--- a/src/game/mod.rs
+++ /dev/null
@@ -1,2 +0,0 @@
-pub mod assets;
-pub mod scenes;
diff --git a/src/game/scenes/main_scene.rs b/src/game/scenes/main_scene.rs
deleted file mode 100644
index 37fd4ed..0000000
--- a/src/game/scenes/main_scene.rs
+++ /dev/null
@@ -1,285 +0,0 @@
-use std::error::Error;
-
-use super::settings_scene::SettingsScene;
-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::transform::Transform;
-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 egui_winit_vulkano::egui;
-use glam::EulerRot;
-use glam::Quat;
-use glam::Vec3;
-use vulkano::{
- command_buffer::{AutoCommandBufferBuilder, CommandBufferUsage, PrimaryCommandBufferAbstract},
- sync::GpuFuture,
-};
-use winit::window::CursorGrabMode;
-
-pub struct MainSceneState {
- square: Square,
- instances: Vec,
- camera: Camera3D,
- texture: Texture,
- speed: f32,
-}
-
-#[derive(Default)]
-pub struct MainScene {
- state: Option,
-}
-
-impl Scene for MainScene {
- fn loaded(&self) -> bool {
- self.state.is_some()
- }
-
- fn load(&mut self, app_context: &mut WindowContext) -> Result<(), Box> {
- let depth_image_view = app_context.with_renderer_mut(|renderer| {
- renderer.get_additional_image_view(DEPTH_IMAGE_ID).clone()
- });
-
- let swapchain_image_view =
- app_context.with_renderer(|renderer| renderer.swapchain_image_view().clone());
-
- let square = Square::new(
- &app_context.device,
- &app_context.memory_allocator,
- swapchain_image_view.format(),
- depth_image_view.format(),
- )?;
-
- let num_instances = 100;
- let instance_size = 10.0;
- let instance_spacing = 10.0;
- let num_instances_per_row = (num_instances as f32 / instance_spacing).ceil() as u32;
- let instances: Vec = (0..num_instances)
- .map(|i| Transform {
- position: Vec3::new(
- (i % num_instances_per_row) as f32 * (instance_spacing + instance_size),
- 0.0,
- (i / num_instances_per_row) as f32 * (instance_spacing + instance_size),
- ),
- rotation: Quat::from_euler(
- EulerRot::XYZ,
- 0.0,
- rand::random_range(0.0..=360.0),
- 0.0,
- ),
- scale: Vec3::new(instance_size, instance_size, instance_size),
- })
- .collect();
-
- let texture = {
- let mut uploads = AutoCommandBufferBuilder::primary(
- app_context.command_buffer_allocator.clone(),
- app_context.graphics_queue.queue_family_index(),
- CommandBufferUsage::OneTimeSubmit,
- )?;
-
- let texture = Texture::from_file(
- &app_context.device,
- &app_context.memory_allocator,
- &mut uploads,
- "res/textures/wooden-crate.jpg",
- )?;
-
- let _ = uploads
- .build()?
- .execute(app_context.graphics_queue.clone())?;
-
- texture
- };
-
- let camera = app_context.with_renderer(|renderer| {
- Camera3D::new(
- renderer.aspect_ratio(),
- std::f32::consts::FRAC_PI_2,
- 0.01,
- 1000.0,
- )
- });
-
- self.state = Some(MainSceneState {
- square,
- instances,
- camera,
- texture,
- speed: 50.0,
- });
-
- Ok(())
- }
-
- fn update(&mut self, app_context: &mut WindowContext) -> Result<(), Box> {
- let state = self.state.as_mut().unwrap();
- app_context.with_input_manager(|input_manager| {
- app_context.with_timer(|timer| {
- state.camera.update(
- input_manager,
- timer,
- state.speed,
- 10.0,
- app_context.get_aspect_ratio(),
- );
- });
- });
-
- if app_context
- .with_input_manager(|input_manager| input_manager.get_virtual_input_state("mouse_left"))
- > 0.0
- {
- let _ = app_context
- .event_loop_proxy
- .send_event(UserEvent::CursorVisible(app_context.window_id, false));
- let _ = app_context
- .event_loop_proxy
- .send_event(UserEvent::CursorGrabMode(
- app_context.window_id,
- CursorGrabMode::Locked,
- ));
- }
-
- if app_context.with_input_manager(|input_manager| {
- input_manager.get_virtual_input_state("mouse_right")
- }) > 0.0
- {
- let _ = app_context
- .event_loop_proxy
- .send_event(UserEvent::CursorVisible(app_context.window_id, true));
- let _ = app_context
- .event_loop_proxy
- .send_event(UserEvent::CursorGrabMode(
- app_context.window_id,
- CursorGrabMode::None,
- ));
- }
-
- Ok(())
- }
-
- fn render(
- &mut self,
- before_future: Box,
- app_context: &mut WindowContext,
- ) -> Result, Box> {
- let state = self.state.as_ref().ok_or("State not loaded")?;
-
- let mut builder = AutoCommandBufferBuilder::primary(
- app_context.command_buffer_allocator.clone(),
- app_context.graphics_queue.queue_family_index(),
- CommandBufferUsage::OneTimeSubmit,
- )?;
-
- {
- let swapchain_image_view =
- app_context.with_renderer(|renderer| renderer.swapchain_image_view().clone());
- let depth_image_view = app_context.with_renderer_mut(|renderer| {
- renderer.get_additional_image_view(DEPTH_IMAGE_ID).clone()
- });
- let config = RenderPassConfig::default();
- RenderPassManager::begin_standard_rendering(
- &mut builder,
- &config,
- swapchain_image_view,
- Some(depth_image_view),
- app_context.get_window_size(),
- )?;
- }
-
- // 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();
-
- RenderPassManager::end_rendering(&mut builder)?;
-
- let command_buffer = builder.build()?;
-
- let render_future =
- before_future.then_execute(app_context.graphics_queue.clone(), command_buffer)?;
-
- let swapchain_image_view =
- app_context.with_renderer(|renderer| renderer.swapchain_image_view().clone());
- let input_manager_status =
- app_context.with_input_manager(|input_manager| format!("{:#?}", input_manager));
- let event_loop_proxy = app_context.event_loop_proxy.clone();
- let delta_time = app_context.get_delta_time();
- let window_id = app_context.window_id;
- let window_size = app_context.get_window_size();
-
- let render_future = app_context.with_gui_mut(|gui| {
- gui.immediate_ui(|gui| {
- let ctx = gui.context();
- egui::TopBottomPanel::top("top_panel").show(&ctx, |ui| {
- ui.horizontal(|ui| {
- ui.heading("Vulkan Test - Moteur 3D");
- ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
- if ui.button("Paramètres").clicked() {
- let _ = event_loop_proxy.send_event(UserEvent::ChangeScene(
- window_id,
- Box::new(SettingsScene::default()),
- ));
- }
- if ui.button("Quitter").clicked() {
- let _ = event_loop_proxy.send_event(UserEvent::Exit(window_id));
- }
- });
- });
- });
-
- egui::SidePanel::left("side_panel").show(&ctx, |ui| {
- ui.heading("Informations");
-
- ui.separator();
-
- ui.label(format!("Résolution: {:?}", window_size));
- ui.label(format!("Delta Time: {:.2}ms", delta_time * 1000.0));
-
- ui.separator();
-
- ui.label("Position caméra:");
- let position = state.camera.get_position();
- ui.label(format!(" X: {:.2}", position[0]));
- ui.label(format!(" Y: {:.2}", position[1]));
- ui.label(format!(" Z: {:.2}", position[2]));
-
- ui.separator();
-
- ui.label("Rotation caméra:");
- let rotation = state.camera.get_rotation();
- ui.label(format!(" Yaw: {:.2}°", rotation.y.to_degrees()));
- ui.label(format!(" Pitch: {:.2}°", rotation.x.to_degrees()));
-
- ui.separator();
-
- ui.label(input_manager_status);
- });
- });
-
- gui.draw_on_image(render_future, swapchain_image_view.clone())
- });
-
- Ok(render_future)
- }
-
- fn unload(&mut self) {
- self.state = None;
- }
-}
diff --git a/src/game/scenes/mod.rs b/src/game/scenes/mod.rs
deleted file mode 100644
index 516fa6f..0000000
--- a/src/game/scenes/mod.rs
+++ /dev/null
@@ -1,2 +0,0 @@
-pub mod main_scene;
-pub mod settings_scene;
diff --git a/src/game/scenes/settings_scene.rs b/src/game/scenes/settings_scene.rs
deleted file mode 100644
index 466652a..0000000
--- a/src/game/scenes/settings_scene.rs
+++ /dev/null
@@ -1,136 +0,0 @@
-use std::error::Error;
-
-use crate::core::app::DEPTH_IMAGE_ID;
-use crate::core::app::context::WindowContext;
-use crate::core::app::user_event::UserEvent;
-use crate::core::render::render_pass_manager::{RenderPassConfig, RenderPassManager};
-use crate::core::scene::Scene;
-use egui_winit_vulkano::egui;
-use vulkano::{
- command_buffer::AutoCommandBufferBuilder, command_buffer::CommandBufferUsage, sync::GpuFuture,
-};
-
-use super::main_scene::MainScene;
-
-pub struct SettingsSceneState {
- current_resolution: [f32; 2],
- available_resolutions: Vec<(u32, u32)>,
-}
-
-#[derive(Default)]
-pub struct SettingsScene {
- state: Option,
-}
-
-impl Scene for SettingsScene {
- fn loaded(&self) -> bool {
- self.state.is_some()
- }
-
- fn load(&mut self, app_context: &mut WindowContext) -> Result<(), Box> {
- let current_resolution = app_context.get_window_size();
- let available_resolutions = app_context.get_available_resolutions();
-
- self.state = Some(SettingsSceneState {
- current_resolution,
- available_resolutions,
- });
-
- Ok(())
- }
-
- fn update(&mut self, _app_context: &mut WindowContext) -> Result<(), Box> {
- Ok(())
- }
-
- fn render(
- &mut self,
- before_future: Box,
- app_context: &mut WindowContext,
- ) -> Result, Box> {
- let state = self.state.as_ref().ok_or("State not found")?;
-
- let mut builder = AutoCommandBufferBuilder::primary(
- app_context.command_buffer_allocator.clone(),
- app_context.graphics_queue.queue_family_index(),
- CommandBufferUsage::OneTimeSubmit,
- )?;
-
- // Utiliser le RenderPassManager
- {
- let swapchain_image_view =
- app_context.with_renderer(|renderer| renderer.swapchain_image_view().clone());
- let depth_stencil_image_view = app_context.with_renderer_mut(|renderer| {
- renderer.get_additional_image_view(DEPTH_IMAGE_ID).clone()
- });
-
- let config = RenderPassConfig::default();
- RenderPassManager::begin_standard_rendering(
- &mut builder,
- &config,
- swapchain_image_view,
- Some(depth_stencil_image_view),
- app_context.get_window_size(),
- )?;
- }
-
- // Pas de géométrie dans cette scène - juste un écran de paramètres
- RenderPassManager::end_rendering(&mut builder)?;
-
- let command_buffer = builder.build()?;
-
- let render_future =
- before_future.then_execute(app_context.graphics_queue.clone(), command_buffer)?;
-
- let swapchain_image_view =
- app_context.with_renderer(|renderer| renderer.swapchain_image_view().clone());
-
- let event_loop_proxy = app_context.event_loop_proxy.clone();
- let window_id = app_context.window_id;
-
- let render_future = app_context.with_gui_mut(|gui| {
- gui.immediate_ui(|gui| {
- let ctx = gui.context();
-
- egui::CentralPanel::default().show(&ctx, |ui| {
- ui.heading("Paramètres");
-
- ui.separator();
-
- ui.label(format!(
- "Résolution actuelle: {:?}",
- state.current_resolution
- ));
-
- ui.separator();
- ui.label("Changer la résolution:");
-
- for &(width, height) in &state.available_resolutions {
- if ui.button(format!("{}x{}", width, height)).clicked() {
- let _ = event_loop_proxy.send_event(UserEvent::ChangeResolution(
- window_id,
- width as f32,
- height as f32,
- ));
- }
- }
-
- ui.separator();
-
- if ui.button("Retour au jeu").clicked() {
- let _ = event_loop_proxy.send_event(UserEvent::ChangeScene(
- window_id,
- Box::new(MainScene::default()),
- ));
- }
- });
- });
-
- gui.draw_on_image(render_future, swapchain_image_view.clone())
- });
-
- Ok(render_future)
- }
-
- fn unload(&mut self) {}
-}
diff --git a/src/main.rs b/src/main.rs
index 998f725..ac0d7c9 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,108 +1,14 @@
-use core::input::{AxisDirection, InputManager, VirtualBinding};
-use std::collections::HashMap;
+use winit::event_loop::EventLoop;
-use vulkano::device::{DeviceExtensions, DeviceFeatures};
-use vulkano_util::context::{VulkanoConfig, VulkanoContext};
-use winit::{
- event::MouseButton,
- event_loop::{ControlFlow, EventLoop},
- keyboard::{KeyCode, PhysicalKey},
-};
+mod renderer;
+mod vulkan;
-mod core;
-mod game;
+use renderer::app::App;
+use vulkan::context::VulkanContext;
fn main() {
- use tracing_subscriber::layer::SubscriberExt;
- use tracing_subscriber::util::SubscriberInitExt;
-
- tracing_subscriber::registry()
- .with(
- tracing_subscriber::fmt::layer()
- .with_target(true)
- .with_thread_ids(true)
- .with_file(true)
- .with_line_number(true),
- )
- .with(tracing_tracy::TracyLayer::default())
- .with(
- tracing_subscriber::EnvFilter::try_from_default_env()
- .unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("info")),
- )
- .init();
-
- let input_manager = InputManager::new(HashMap::from([
- (
- "move_forward".to_string(),
- vec![
- VirtualBinding::Keyboard(PhysicalKey::Code(KeyCode::KeyW), AxisDirection::Normal),
- VirtualBinding::Keyboard(PhysicalKey::Code(KeyCode::KeyS), AxisDirection::Invert),
- ],
- ),
- (
- "move_right".to_string(),
- vec![
- VirtualBinding::Keyboard(PhysicalKey::Code(KeyCode::KeyD), AxisDirection::Normal),
- VirtualBinding::Keyboard(PhysicalKey::Code(KeyCode::KeyA), AxisDirection::Invert),
- ],
- ),
- (
- "mouse_x".to_string(),
- vec![VirtualBinding::MouseX(AxisDirection::Normal)],
- ),
- (
- "mouse_y".to_string(),
- vec![VirtualBinding::MouseY(AxisDirection::Normal)],
- ),
- (
- "mouse_wheel".to_string(),
- vec![VirtualBinding::MouseWheelY(AxisDirection::Normal)],
- ),
- (
- "mouse_left".to_string(),
- vec![VirtualBinding::MouseButton(
- MouseButton::Left,
- AxisDirection::Normal,
- )],
- ),
- (
- "mouse_right".to_string(),
- vec![VirtualBinding::MouseButton(
- MouseButton::Right,
- AxisDirection::Normal,
- )],
- ),
- ]));
-
- let device_extensions = DeviceExtensions {
- khr_swapchain: true,
- ..Default::default()
- };
-
- let device_features = DeviceFeatures {
- dynamic_rendering: true,
- ..Default::default()
- };
-
- let vulkano_config = VulkanoConfig {
- print_device_name: true,
- device_extensions,
- device_features,
- ..Default::default()
- };
-
- let vulkano_context = VulkanoContext::new(vulkano_config);
-
- let event_loop = EventLoop::with_user_event().build().unwrap();
- event_loop.set_control_flow(ControlFlow::Poll);
- let proxy = event_loop.create_proxy();
-
- let mut app = core::app::App::new(vulkano_context, input_manager, proxy);
-
- match event_loop.run_app(&mut app) {
- Ok(_) => {}
- Err(e) => {
- tracing::error!("Error running old app: {e}");
- }
- }
+ let event_loop = EventLoop::new().unwrap();
+ let vulkan_context = VulkanContext::new(&event_loop).unwrap();
+ let mut app = App::new(vulkan_context);
+ event_loop.run_app(&mut app).unwrap();
}
diff --git a/src/renderer/app.rs b/src/renderer/app.rs
new file mode 100644
index 0000000..923d70c
--- /dev/null
+++ b/src/renderer/app.rs
@@ -0,0 +1,262 @@
+use crate::renderer::components::{Entity, Material, Mesh, Transform};
+use crate::vulkan::context::VulkanContext;
+use crate::vulkan::pipeline::{Pipeline, triangle::TrianglePipeline};
+use crate::vulkan::renderer::VulkanRenderer;
+use crate::vulkan::resources::vertex::{MVPData, Vertex2D};
+use std::error::Error;
+use std::sync::Arc;
+use vulkano::VulkanError;
+use vulkano::buffer::{Buffer, BufferCreateInfo, BufferUsage};
+use vulkano::command_buffer::{
+ AutoCommandBufferBuilder, CommandBufferUsage, PrimaryAutoCommandBuffer,
+ RenderingAttachmentInfo, RenderingInfo,
+};
+use vulkano::descriptor_set::{DescriptorSet, WriteDescriptorSet};
+use vulkano::memory::allocator::{AllocationCreateInfo, MemoryTypeFilter};
+use vulkano::pipeline::{Pipeline as VulkanPipeline, PipelineBindPoint};
+use vulkano::render_pass::{AttachmentLoadOp, AttachmentStoreOp};
+use vulkano::swapchain::Surface;
+use winit::application::ApplicationHandler;
+use winit::event::WindowEvent;
+use winit::event_loop::ActiveEventLoop;
+use winit::window::WindowId;
+
+pub struct App {
+ pub vulkan_context: VulkanContext,
+ pub renderer: Option,
+ pub entities: Vec,
+}
+
+impl App {
+ pub fn new(vulkan_context: VulkanContext) -> Self {
+ Self {
+ vulkan_context,
+ renderer: None,
+ entities: Vec::new(),
+ }
+ }
+
+ pub fn setup_test_entities(&mut self) -> Result<(), Box> {
+ // Créer un pipeline de test
+ let pipeline = TrianglePipeline::new(&self.vulkan_context.device);
+
+ // Créer un buffer de vertex pour un triangle
+ let vertices = [
+ Vertex2D {
+ position: [-0.5, -0.5],
+ color: [1.0, 0.0, 0.0], // Rouge
+ },
+ Vertex2D {
+ position: [0.5, -0.5],
+ color: [0.0, 1.0, 0.0], // Vert
+ },
+ Vertex2D {
+ position: [0.0, 0.5],
+ color: [0.0, 0.0, 1.0], // Bleu
+ },
+ ];
+
+ let vertex_buffer =
+ Vertex2D::create_buffer(vertices.to_vec(), &self.vulkan_context.memory_allocator)
+ .unwrap();
+
+ // Créer un buffer uniform pour les matrices MVP
+ let mvp_data = MVPData {
+ world: [
+ [1.0, 0.0, 0.0, 0.0],
+ [0.0, 1.0, 0.0, 0.0],
+ [0.0, 0.0, 1.0, 0.0],
+ [0.0, 0.0, 0.0, 1.0],
+ ],
+ view: [
+ [1.0, 0.0, 0.0, 0.0],
+ [0.0, 1.0, 0.0, 0.0],
+ [0.0, 0.0, 1.0, 0.0],
+ [0.0, 0.0, 0.0, 1.0],
+ ],
+ projection: [
+ [1.0, 0.0, 0.0, 0.0],
+ [0.0, 1.0, 0.0, 0.0],
+ [0.0, 0.0, 1.0, 0.0],
+ [0.0, 0.0, 0.0, 1.0],
+ ],
+ };
+
+ let uniform_buffer = Buffer::from_data(
+ self.vulkan_context.memory_allocator.clone(),
+ BufferCreateInfo {
+ usage: BufferUsage::UNIFORM_BUFFER,
+ ..Default::default()
+ },
+ AllocationCreateInfo {
+ memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
+ | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
+ ..Default::default()
+ },
+ mvp_data,
+ )
+ .unwrap();
+
+ // Créer un descriptor set de test
+ let descriptor_set = DescriptorSet::new(
+ self.vulkan_context.descriptor_set_allocator.clone(),
+ pipeline
+ .get_pipeline()
+ .layout()
+ .set_layouts()
+ .get(0)
+ .unwrap()
+ .clone(),
+ [WriteDescriptorSet::buffer(0, uniform_buffer)],
+ [],
+ )?;
+
+ let material = Material {
+ pipeline: pipeline.get_pipeline().clone(),
+ descriptor_set,
+ };
+
+ // Créer quelques entités de test
+ let mut entities = Vec::new();
+ for i in 0..3 {
+ entities.push(Entity {
+ mesh: Mesh {
+ vertex_buffer: vertex_buffer.clone(),
+ vertex_count: 3,
+ instance_count: 1,
+ },
+ material: material.clone(),
+ transform: Transform {
+ position: [i as f32 * 0.5 - 0.5, 0.0, 0.0],
+ rotation: [0.0, 0.0, 0.0],
+ scale: [1.0, 1.0, 1.0],
+ },
+ });
+ }
+ self.entities = entities;
+
+ Ok(())
+ }
+
+ pub fn render(
+ &self,
+ command_buffer: &mut AutoCommandBufferBuilder,
+ ) -> Result<(), Box> {
+ for entity in &self.entities {
+ command_buffer
+ .bind_pipeline_graphics(entity.material.pipeline.clone())
+ .unwrap()
+ .bind_descriptor_sets(
+ PipelineBindPoint::Graphics,
+ entity.material.pipeline.layout().clone(),
+ 0,
+ entity.material.descriptor_set.clone(),
+ )
+ .unwrap()
+ .bind_vertex_buffers(0, entity.mesh.vertex_buffer.clone())
+ .unwrap();
+ unsafe {
+ command_buffer
+ .draw(entity.mesh.vertex_count, 1, 0, 0)
+ .unwrap();
+ }
+ }
+ Ok(())
+ }
+}
+
+impl ApplicationHandler for App {
+ fn resumed(&mut self, event_loop: &ActiveEventLoop) {
+ let window_attributes = winit::window::Window::default_attributes()
+ .with_title("Rust ASH Test")
+ .with_inner_size(winit::dpi::PhysicalSize::new(
+ f64::from(800),
+ f64::from(600),
+ ));
+
+ let window = Arc::new(event_loop.create_window(window_attributes).unwrap());
+
+ let surface =
+ Surface::from_window(self.vulkan_context.instance.clone(), window.clone()).unwrap();
+
+ self.renderer = Some(VulkanRenderer::new(
+ window,
+ surface,
+ self.vulkan_context.device.clone(),
+ self.vulkan_context.queue.clone(),
+ ));
+
+ self.setup_test_entities().unwrap();
+ }
+
+ fn window_event(&mut self, event_loop: &ActiveEventLoop, _id: WindowId, event: WindowEvent) {
+ match event {
+ WindowEvent::CloseRequested => {
+ log::debug!("The close button was pressed; stopping");
+ event_loop.exit();
+ }
+ WindowEvent::Resized(_) => {
+ let renderer = self.renderer.as_mut().unwrap();
+ renderer.recreate_swapchain = true;
+ }
+ WindowEvent::RedrawRequested => {
+ let (image_index, acquire_future) =
+ match self.renderer.as_mut().unwrap().begin_frame() {
+ Ok(r) => r,
+ Err(VulkanError::OutOfDate) => return,
+ Err(e) => panic!("failed to acquire next image: {e}"),
+ };
+
+ let mut builder = AutoCommandBufferBuilder::primary(
+ self.vulkan_context.command_buffer_allocator.clone(),
+ self.vulkan_context.queue.queue_family_index(),
+ CommandBufferUsage::OneTimeSubmit,
+ )
+ .unwrap();
+
+ {
+ builder
+ .begin_rendering(RenderingInfo {
+ color_attachments: vec![Some(RenderingAttachmentInfo {
+ load_op: AttachmentLoadOp::Clear,
+ store_op: AttachmentStoreOp::Store,
+ clear_value: Some([0.0, 0.0, 0.0, 1.0].into()),
+ ..RenderingAttachmentInfo::image_view(
+ self.renderer.as_ref().unwrap().attachment_image_views
+ [image_index as usize]
+ .clone(),
+ )
+ })],
+ ..Default::default()
+ })
+ .unwrap()
+ .set_viewport(
+ 0,
+ [self.renderer.as_ref().unwrap().viewport.clone()]
+ .into_iter()
+ .collect(),
+ )
+ .unwrap();
+ }
+
+ self.render(&mut builder).unwrap();
+
+ {
+ builder.end_rendering().unwrap();
+ }
+
+ self.renderer
+ .as_mut()
+ .unwrap()
+ .end_frame(image_index, acquire_future, builder)
+ .unwrap();
+ }
+ _ => {}
+ }
+ }
+
+ fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) {
+ let renderer = self.renderer.as_mut().unwrap();
+ renderer.window.request_redraw();
+ }
+}
diff --git a/src/renderer/components.rs b/src/renderer/components.rs
new file mode 100644
index 0000000..968539e
--- /dev/null
+++ b/src/renderer/components.rs
@@ -0,0 +1,71 @@
+use std::sync::Arc;
+use vulkano::buffer::Subbuffer;
+use vulkano::buffer::allocator::SubbufferAllocator;
+use vulkano::command_buffer::allocator::StandardCommandBufferAllocator;
+use vulkano::command_buffer::{AutoCommandBufferBuilder, PrimaryAutoCommandBuffer};
+use vulkano::descriptor_set::DescriptorSet;
+use vulkano::descriptor_set::allocator::StandardDescriptorSetAllocator;
+use vulkano::device::Device;
+use vulkano::memory::allocator::StandardMemoryAllocator;
+use vulkano::pipeline::GraphicsPipeline;
+use vulkano::pipeline::Pipeline;
+
+use crate::vulkan::resources::vertex::Vertex2D;
+
+#[derive(Clone)]
+pub struct Mesh {
+ pub vertex_buffer: Subbuffer<[Vertex2D]>,
+ pub vertex_count: u32,
+ pub instance_count: u32,
+}
+
+#[derive(Clone)]
+pub struct Material {
+ pub pipeline: Arc,
+ pub descriptor_set: Arc,
+}
+
+#[derive(Clone)]
+pub struct Transform {
+ pub position: [f32; 3],
+ pub rotation: [f32; 3],
+ pub scale: [f32; 3],
+}
+
+#[derive(Clone)]
+pub struct RenderResources {
+ pub device: Arc,
+ pub memory_allocator: Arc,
+ pub command_buffer_allocator: Arc,
+ pub uniform_buffer_allocator: Arc,
+ pub descriptor_set_allocator: Arc,
+}
+
+pub struct Entity {
+ pub mesh: Mesh,
+ pub material: Material,
+ pub transform: Transform,
+}
+
+pub fn render_system(
+ _resources: &RenderResources,
+ builder: &mut AutoCommandBufferBuilder,
+ entities: &[Entity],
+) -> Result<(), Box> {
+ for entity in entities {
+ unsafe {
+ builder
+ .bind_pipeline_graphics(entity.material.pipeline.clone())?
+ .bind_descriptor_sets(
+ vulkano::pipeline::PipelineBindPoint::Graphics,
+ entity.material.pipeline.layout().clone(),
+ 0,
+ entity.material.descriptor_set.clone(),
+ )?
+ .bind_vertex_buffers(0, entity.mesh.vertex_buffer.clone())?
+ .draw(entity.mesh.vertex_count, entity.mesh.instance_count, 0, 0)?;
+ }
+ }
+
+ Ok(())
+}
diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs
new file mode 100644
index 0000000..13545c4
--- /dev/null
+++ b/src/renderer/mod.rs
@@ -0,0 +1,2 @@
+pub mod app;
+pub mod components;
diff --git a/src/vulkan/context.rs b/src/vulkan/context.rs
new file mode 100644
index 0000000..bd1aeba
--- /dev/null
+++ b/src/vulkan/context.rs
@@ -0,0 +1,135 @@
+use std::sync::Arc;
+use vulkano::buffer::BufferUsage;
+use vulkano::buffer::allocator::{SubbufferAllocator, SubbufferAllocatorCreateInfo};
+use vulkano::command_buffer::allocator::StandardCommandBufferAllocator;
+use vulkano::descriptor_set::allocator::StandardDescriptorSetAllocator;
+use vulkano::device::physical::PhysicalDeviceType;
+use vulkano::device::{
+ Device, DeviceCreateInfo, DeviceExtensions, DeviceFeatures, Queue, QueueCreateInfo, QueueFlags,
+};
+use vulkano::instance::{Instance, InstanceCreateFlags, InstanceCreateInfo};
+use vulkano::memory::allocator::{MemoryTypeFilter, StandardMemoryAllocator};
+use vulkano::swapchain::Surface;
+use vulkano::{Version, VulkanLibrary};
+use winit::event_loop::EventLoop;
+
+pub struct VulkanContext {
+ pub instance: Arc,
+ pub device: Arc,
+ pub queue: Arc,
+ pub memory_allocator: Arc,
+ pub command_buffer_allocator: Arc,
+ pub uniform_buffer_allocator: Arc,
+ pub descriptor_set_allocator: Arc,
+}
+
+impl VulkanContext {
+ pub fn new(event_loop: &EventLoop<()>) -> Result> {
+ let library = VulkanLibrary::new().unwrap();
+ for layer in library.layer_properties().unwrap() {
+ log::debug!("Available layer: {}", layer.name());
+ }
+
+ let required_extensions = Surface::required_extensions(event_loop).unwrap();
+ let instance = Instance::new(
+ library,
+ InstanceCreateInfo {
+ flags: InstanceCreateFlags::ENUMERATE_PORTABILITY,
+ enabled_extensions: required_extensions,
+ enabled_layers: vec![String::from("VK_LAYER_KHRONOS_validation")],
+ ..Default::default()
+ },
+ )
+ .unwrap();
+
+ let mut device_extensions = DeviceExtensions {
+ khr_swapchain: true,
+ ..DeviceExtensions::empty()
+ };
+
+ let (physical_device, queue_family_index) = instance
+ .enumerate_physical_devices()
+ .unwrap()
+ .filter(|p| {
+ p.api_version() >= Version::V1_3 || p.supported_extensions().khr_dynamic_rendering
+ })
+ .filter(|p| p.supported_extensions().contains(&device_extensions))
+ .filter_map(|p| {
+ p.queue_family_properties()
+ .iter()
+ .enumerate()
+ .position(|(_i, q)| q.queue_flags.intersects(QueueFlags::GRAPHICS))
+ .map(|i| (p, i as u32))
+ })
+ .min_by_key(|(p, _)| match p.properties().device_type {
+ PhysicalDeviceType::DiscreteGpu => 0,
+ PhysicalDeviceType::IntegratedGpu => 1,
+ PhysicalDeviceType::VirtualGpu => 2,
+ PhysicalDeviceType::Cpu => 3,
+ PhysicalDeviceType::Other => 4,
+ _ => 5,
+ })
+ .expect("no suitable physical device found");
+
+ log::debug!(
+ "Using device: {} (type: {:?})",
+ physical_device.properties().device_name,
+ physical_device.properties().device_type,
+ );
+
+ if physical_device.api_version() < Version::V1_3 {
+ device_extensions.khr_dynamic_rendering = true;
+ }
+
+ log::debug!("Using device extensions: {:#?}", device_extensions);
+
+ let (device, mut queues) = Device::new(
+ physical_device,
+ DeviceCreateInfo {
+ queue_create_infos: vec![QueueCreateInfo {
+ queue_family_index,
+ ..Default::default()
+ }],
+ enabled_extensions: device_extensions,
+ enabled_features: DeviceFeatures {
+ dynamic_rendering: true,
+ ..DeviceFeatures::empty()
+ },
+ ..Default::default()
+ },
+ )
+ .unwrap();
+
+ let queue = queues.next().unwrap();
+ let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone()));
+ let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new(
+ device.clone(),
+ Default::default(),
+ ));
+
+ let uniform_buffer_allocator = Arc::new(SubbufferAllocator::new(
+ memory_allocator.clone(),
+ SubbufferAllocatorCreateInfo {
+ buffer_usage: BufferUsage::UNIFORM_BUFFER,
+ memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
+ | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
+ ..Default::default()
+ },
+ ));
+
+ let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new(
+ device.clone(),
+ Default::default(),
+ ));
+
+ Ok(Self {
+ instance,
+ device,
+ queue,
+ memory_allocator,
+ command_buffer_allocator,
+ uniform_buffer_allocator,
+ descriptor_set_allocator,
+ })
+ }
+}
diff --git a/src/vulkan/mod.rs b/src/vulkan/mod.rs
new file mode 100644
index 0000000..8a4bdd9
--- /dev/null
+++ b/src/vulkan/mod.rs
@@ -0,0 +1,4 @@
+pub mod context;
+pub mod pipeline;
+pub mod renderer;
+pub mod resources;
diff --git a/src/vulkan/pipeline/mod.rs b/src/vulkan/pipeline/mod.rs
new file mode 100644
index 0000000..f9ba6d5
--- /dev/null
+++ b/src/vulkan/pipeline/mod.rs
@@ -0,0 +1,30 @@
+pub mod triangle;
+
+use std::sync::Arc;
+use vulkano::device::Device;
+use vulkano::pipeline::GraphicsPipeline;
+
+pub trait Pipeline {
+ fn create_pipeline(device: &Arc) -> Arc;
+ fn get_pipeline(&self) -> &Arc;
+}
+
+pub struct PipelineManager {
+ pipelines: std::collections::HashMap>,
+}
+
+impl PipelineManager {
+ pub fn new() -> Self {
+ Self {
+ pipelines: std::collections::HashMap::new(),
+ }
+ }
+
+ pub fn register_pipeline(&mut self, name: String, pipeline: Arc) {
+ self.pipelines.insert(name, pipeline);
+ }
+
+ pub fn get_pipeline(&self, name: &str) -> Option<&Arc> {
+ self.pipelines.get(name)
+ }
+}
diff --git a/src/vulkan/pipeline/triangle.rs b/src/vulkan/pipeline/triangle.rs
new file mode 100644
index 0000000..c625e66
--- /dev/null
+++ b/src/vulkan/pipeline/triangle.rs
@@ -0,0 +1,127 @@
+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::pipeline::graphics::GraphicsPipelineCreateInfo;
+use vulkano::pipeline::graphics::color_blend::{ColorBlendAttachmentState, ColorBlendState};
+use vulkano::pipeline::graphics::input_assembly::InputAssemblyState;
+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::vulkan::resources::vertex::Vertex2D;
+
+use super::Pipeline;
+
+mod shaders {
+ pub mod vs {
+ vulkano_shaders::shader! {
+ ty: "vertex",
+ path: r"res/shaders/vertex.vert",
+ }
+ }
+
+ pub mod fs {
+ vulkano_shaders::shader! {
+ ty: "fragment",
+ path: r"res/shaders/vertex.frag",
+ }
+ }
+}
+
+pub struct TrianglePipeline {
+ pipeline: Arc,
+}
+
+impl super::Pipeline for TrianglePipeline {
+ fn create_pipeline(device: &Arc) -> Arc {
+ let (vs, fs) = load_shaders(device).unwrap();
+ let vertex_input_state = Vertex2D::per_vertex().definition(&vs).unwrap();
+
+ let stages = [
+ PipelineShaderStageCreateInfo::new(vs),
+ PipelineShaderStageCreateInfo::new(fs),
+ ];
+
+ let mut bindings = BTreeMap::::new();
+ let mut descriptor_set_layout_binding =
+ DescriptorSetLayoutBinding::descriptor_type(DescriptorType::UniformBuffer);
+ descriptor_set_layout_binding.stages = ShaderStages::VERTEX;
+ bindings.insert(0, descriptor_set_layout_binding);
+
+ let descriptor_set_layout = DescriptorSetLayoutCreateInfo {
+ bindings,
+ ..Default::default()
+ };
+
+ let create_info = PipelineDescriptorSetLayoutCreateInfo {
+ set_layouts: vec![descriptor_set_layout],
+ flags: PipelineLayoutCreateFlags::default(),
+ push_constant_ranges: vec![],
+ }
+ .into_pipeline_layout_create_info(device.clone())
+ .unwrap();
+
+ let layout = PipelineLayout::new(device.clone(), create_info).unwrap();
+
+ let subpass = PipelineRenderingCreateInfo {
+ color_attachment_formats: vec![Some(vulkano::format::Format::B8G8R8A8_UNORM)],
+ ..Default::default()
+ };
+
+ 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)
+ },
+ )
+ .unwrap()
+ }
+
+ fn get_pipeline(&self) -> &Arc {
+ &self.pipeline
+ }
+}
+
+impl TrianglePipeline {
+ pub fn new(device: &Arc) -> Self {
+ Self {
+ pipeline: Self::create_pipeline(device),
+ }
+ }
+}
+
+fn load_shaders(device: &Arc) -> Result<(EntryPoint, EntryPoint), Box> {
+ 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))
+}
diff --git a/src/vulkan/renderer.rs b/src/vulkan/renderer.rs
new file mode 100644
index 0000000..079276b
--- /dev/null
+++ b/src/vulkan/renderer.rs
@@ -0,0 +1,167 @@
+use std::sync::Arc;
+use vulkano::command_buffer::{AutoCommandBufferBuilder, PrimaryAutoCommandBuffer};
+use vulkano::device::Queue;
+use vulkano::image::view::ImageView;
+use vulkano::pipeline::graphics::viewport::Viewport;
+use vulkano::swapchain::{Surface, Swapchain, SwapchainCreateInfo, SwapchainPresentInfo};
+use vulkano::sync::{self, GpuFuture};
+use vulkano::{Validated, VulkanError};
+use winit::window::Window;
+
+pub struct VulkanRenderer {
+ pub window: Arc,
+ pub surface: Arc,
+ pub swapchain: Arc,
+ pub queue: Arc,
+ pub attachment_image_views: Vec>,
+ pub previous_frame_end: Option>,
+ pub recreate_swapchain: bool,
+ pub viewport: Viewport,
+}
+
+impl VulkanRenderer {
+ pub fn new(
+ window: Arc,
+ surface: Arc,
+ device: Arc,
+ queue: Arc,
+ ) -> Self {
+ let window_size = window.inner_size();
+ let surface_formats = device
+ .physical_device()
+ .surface_formats(&surface, Default::default())
+ .unwrap();
+ let surface_format = surface_formats[0];
+
+ let (swapchain, images) = Swapchain::new(
+ device.clone(),
+ surface.clone(),
+ SwapchainCreateInfo {
+ image_extent: window_size.into(),
+ image_usage: vulkano::image::ImageUsage::COLOR_ATTACHMENT,
+ image_format: surface_format.0,
+ ..Default::default()
+ },
+ )
+ .unwrap();
+
+ let attachment_image_views = images
+ .into_iter()
+ .map(|image| ImageView::new_default(image).unwrap())
+ .collect();
+
+ let viewport = Viewport {
+ offset: [0.0, 0.0],
+ extent: window_size.into(),
+ depth_range: 0.0..=1.0,
+ };
+
+ Self {
+ window,
+ surface,
+ swapchain,
+ queue,
+ attachment_image_views,
+ previous_frame_end: Some(sync::now(device).boxed()),
+ recreate_swapchain: false,
+ viewport,
+ }
+ }
+
+ pub fn begin_frame(&mut self) -> Result<(u32, Box), VulkanError> {
+ self.previous_frame_end.as_mut().unwrap().cleanup_finished();
+
+ if self.recreate_swapchain {
+ self.recreate_swapchain();
+ self.recreate_swapchain = false;
+ }
+
+ let (image_index, suboptimal, acquire_future) =
+ match vulkano::swapchain::acquire_next_image(self.swapchain.clone(), None)
+ .map_err(Validated::unwrap)
+ {
+ Ok(r) => r,
+ Err(VulkanError::OutOfDate) => {
+ self.recreate_swapchain = true;
+ return Err(VulkanError::OutOfDate);
+ }
+ Err(e) => panic!("failed to acquire next image: {e}"),
+ };
+
+ if suboptimal {
+ self.recreate_swapchain = true;
+ }
+
+ Ok((image_index, acquire_future.boxed()))
+ }
+
+ pub fn end_frame(
+ &mut self,
+ image_index: u32,
+ acquire_future: Box,
+ command_buffer: AutoCommandBufferBuilder,
+ ) -> Result<(), VulkanError> {
+ let command_buffer = command_buffer.build().unwrap();
+ let future = self
+ .previous_frame_end
+ .take()
+ .unwrap()
+ .join(acquire_future)
+ .then_execute(self.queue.clone(), command_buffer)
+ .unwrap()
+ .then_swapchain_present(
+ self.queue.clone(),
+ SwapchainPresentInfo::swapchain_image_index(self.swapchain.clone(), image_index),
+ )
+ .then_signal_fence_and_flush();
+
+ match future.map_err(Validated::unwrap) {
+ Ok(future) => {
+ self.previous_frame_end = Some(future.boxed());
+ Ok(())
+ }
+ Err(VulkanError::OutOfDate) => {
+ self.recreate_swapchain = true;
+ self.previous_frame_end = Some(sync::now(self.queue.device().clone()).boxed());
+ Ok(())
+ }
+ Err(e) => {
+ println!("failed to flush future: {e}");
+ self.previous_frame_end = Some(sync::now(self.queue.device().clone()).boxed());
+ Ok(())
+ }
+ }
+ }
+
+ fn recreate_swapchain(&mut self) {
+ let image_extent: [u32; 2] = self.window.inner_size().into();
+ if image_extent.contains(&0) {
+ return;
+ }
+
+ let surface_formats = self
+ .queue
+ .device()
+ .physical_device()
+ .surface_formats(&self.surface, Default::default())
+ .unwrap();
+ let surface_format = surface_formats[0];
+
+ let (new_swapchain, new_images) = self
+ .swapchain
+ .recreate(SwapchainCreateInfo {
+ image_extent,
+ image_usage: vulkano::image::ImageUsage::COLOR_ATTACHMENT,
+ image_format: surface_format.0,
+ ..self.swapchain.create_info()
+ })
+ .expect("failed to recreate swapchain");
+
+ self.swapchain = new_swapchain;
+ self.attachment_image_views = new_images
+ .into_iter()
+ .map(|image| ImageView::new_default(image).unwrap())
+ .collect();
+ self.viewport.extent = [image_extent[0] as f32, image_extent[1] as f32];
+ }
+}
diff --git a/src/vulkan/resources/buffer.rs b/src/vulkan/resources/buffer.rs
new file mode 100644
index 0000000..318d96e
--- /dev/null
+++ b/src/vulkan/resources/buffer.rs
@@ -0,0 +1,65 @@
+use std::sync::Arc;
+use vulkano::buffer::BufferContents;
+use vulkano::buffer::{Buffer, BufferCreateInfo, BufferUsage, Subbuffer};
+use vulkano::memory::allocator::{AllocationCreateInfo, MemoryTypeFilter, StandardMemoryAllocator};
+
+pub struct BufferManager {
+ memory_allocator: Arc,
+}
+
+impl BufferManager {
+ pub fn new(memory_allocator: Arc) -> Self {
+ Self { memory_allocator }
+ }
+
+ pub fn create_vertex_buffer(&self, data: &[T]) -> Subbuffer<[T]> {
+ Buffer::from_iter(
+ self.memory_allocator.clone(),
+ BufferCreateInfo {
+ usage: BufferUsage::VERTEX_BUFFER,
+ ..Default::default()
+ },
+ AllocationCreateInfo {
+ memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
+ | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
+ ..Default::default()
+ },
+ data.iter().cloned(),
+ )
+ .unwrap()
+ }
+
+ pub fn create_index_buffer(&self, data: &[u32]) -> Subbuffer<[u32]> {
+ Buffer::from_iter(
+ self.memory_allocator.clone(),
+ BufferCreateInfo {
+ usage: BufferUsage::INDEX_BUFFER,
+ ..Default::default()
+ },
+ AllocationCreateInfo {
+ memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
+ | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
+ ..Default::default()
+ },
+ data.iter().cloned(),
+ )
+ .unwrap()
+ }
+
+ pub fn create_uniform_buffer(&self, data: &T) -> Subbuffer {
+ Buffer::from_data(
+ self.memory_allocator.clone(),
+ BufferCreateInfo {
+ usage: BufferUsage::UNIFORM_BUFFER,
+ ..Default::default()
+ },
+ AllocationCreateInfo {
+ memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
+ | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
+ ..Default::default()
+ },
+ *data,
+ )
+ .unwrap()
+ }
+}
diff --git a/src/vulkan/resources/descriptor.rs b/src/vulkan/resources/descriptor.rs
new file mode 100644
index 0000000..6b7af8a
--- /dev/null
+++ b/src/vulkan/resources/descriptor.rs
@@ -0,0 +1,47 @@
+use std::sync::Arc;
+use vulkano::descriptor_set::DescriptorSet;
+use vulkano::descriptor_set::WriteDescriptorSet;
+use vulkano::descriptor_set::allocator::StandardDescriptorSetAllocator;
+use vulkano::descriptor_set::layout::{
+ DescriptorSetLayout, DescriptorSetLayoutBinding, DescriptorSetLayoutCreateInfo, DescriptorType,
+};
+use vulkano::device::Device;
+use vulkano::shader::ShaderStages;
+
+pub struct DescriptorManager {
+ device: Arc,
+ allocator: Arc,
+}
+
+impl DescriptorManager {
+ pub fn new(device: Arc, allocator: Arc) -> Self {
+ Self { device, allocator }
+ }
+
+ pub fn create_descriptor_set_layout(
+ &self,
+ bindings: &[(u32, DescriptorType, u32)],
+ ) -> Arc {
+ let mut bindings_map = std::collections::BTreeMap::new();
+ for (binding_index, ty, _count) in bindings {
+ let mut binding = DescriptorSetLayoutBinding::descriptor_type(*ty);
+ binding.stages = ShaderStages::all_graphics();
+ bindings_map.insert(*binding_index, binding);
+ }
+
+ let create_info = DescriptorSetLayoutCreateInfo {
+ bindings: bindings_map,
+ ..Default::default()
+ };
+
+ DescriptorSetLayout::new(self.device.clone(), create_info).unwrap()
+ }
+
+ pub fn create_descriptor_set(
+ &self,
+ layout: &Arc,
+ writes: Vec,
+ ) -> Arc {
+ DescriptorSet::new(self.allocator.clone(), layout.clone(), writes, []).unwrap()
+ }
+}
diff --git a/src/vulkan/resources/mod.rs b/src/vulkan/resources/mod.rs
new file mode 100644
index 0000000..0c2b51c
--- /dev/null
+++ b/src/vulkan/resources/mod.rs
@@ -0,0 +1,3 @@
+pub mod buffer;
+pub mod descriptor;
+pub mod vertex;
diff --git a/src/vulkan/resources/vertex.rs b/src/vulkan/resources/vertex.rs
new file mode 100644
index 0000000..cc25b1b
--- /dev/null
+++ b/src/vulkan/resources/vertex.rs
@@ -0,0 +1,46 @@
+use std::sync::Arc;
+use vulkano::Validated;
+use vulkano::buffer::{
+ AllocateBufferError, Buffer, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer,
+};
+use vulkano::memory::allocator::{AllocationCreateInfo, MemoryTypeFilter, StandardMemoryAllocator};
+use vulkano::pipeline::graphics::vertex_input::Vertex;
+
+#[derive(BufferContents, Vertex, Clone)]
+#[repr(C)]
+pub struct Vertex2D {
+ #[format(R32G32_SFLOAT)]
+ pub position: [f32; 2],
+
+ #[format(R32G32B32_SFLOAT)]
+ pub color: [f32; 3],
+}
+
+#[derive(BufferContents, Clone)]
+#[repr(C)]
+pub struct MVPData {
+ pub world: [[f32; 4]; 4],
+ pub view: [[f32; 4]; 4],
+ pub projection: [[f32; 4]; 4],
+}
+
+impl Vertex2D {
+ pub fn create_buffer(
+ vertices: Vec,
+ memory_allocator: &Arc,
+ ) -> Result, Validated> {
+ 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()
+ },
+ vertices.into_iter(),
+ )
+ }
+}