Refactor texture loading

This commit is contained in:
Florian RICHER 2025-06-08 18:38:51 +02:00
parent f91c0792b2
commit a32cf6c747
Signed by: florian.richer
GPG key ID: C73D37CBED7BFC77
19 changed files with 1499 additions and 337 deletions

10
Cargo.lock generated
View file

@ -2090,6 +2090,7 @@ dependencies = [
"image", "image",
"rand 0.9.1", "rand 0.9.1",
"thiserror 2.0.12", "thiserror 2.0.12",
"tobj",
"tracing", "tracing",
"tracing-log", "tracing-log",
"tracing-subscriber", "tracing-subscriber",
@ -2466,6 +2467,15 @@ dependencies = [
"zerovec", "zerovec",
] ]
[[package]]
name = "tobj"
version = "4.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04aca6092e5978e708ee784e8ab9b5cf3cdb598b28f99a2f257446e7081a7025"
dependencies = [
"ahash",
]
[[package]] [[package]]
name = "toml" name = "toml"
version = "0.8.22" version = "0.8.22"

View file

@ -28,3 +28,6 @@ tracing-tracy = "0.11"
# Random # Random
rand = "0.9" rand = "0.9"
# OBJ loader
tobj = "4.0"

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
res/objects/cube-normal.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

1143
res/objects/cube.obj Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,5 @@
pub mod material_manager; pub mod material_manager;
pub mod primitives; pub mod primitives;
pub mod render_pass_manager; pub mod render_pass_manager;
pub mod texture; pub mod resources;
pub mod vulkan_context; pub mod vulkan_context;

View file

@ -14,10 +14,9 @@ mod command;
pub mod camera; pub mod camera;
pub mod mvp; pub mod mvp;
pub mod resource;
pub mod transform; pub mod transform;
pub mod vertex; pub mod vertex;
pub mod resource;
pub use buffer::{AsBindableBuffer, AsIndexBuffer, AsUniformBuffer, AsVertexBuffer}; pub use buffer::{AsBindableBuffer, AsIndexBuffer, AsUniformBuffer, AsVertexBuffer};
pub use command::{AsRecordable, AsRenderableMesh, AsRenderableMeshInstance}; pub use command::{AsRecordable, AsRenderableMesh, AsRenderableMeshInstance};

View file

@ -0,0 +1,5 @@
mod square;
pub use square::SquareMesh;
mod obj;
pub use obj::ObjMesh;

View file

@ -0,0 +1,76 @@
use std::{error::Error, path::PathBuf, sync::Arc};
use vulkano::{buffer::Subbuffer, memory::allocator::StandardMemoryAllocator};
use crate::core::render::primitives::{
AsIndexBuffer, AsRenderableMesh, AsVertexBuffer, vertex::Vertex3D,
};
pub struct ObjMesh {
vertex_buffer: Subbuffer<[Vertex3D]>,
index_buffer: Subbuffer<[u32]>,
}
impl ObjMesh {
pub fn new(
memory_allocator: &Arc<StandardMemoryAllocator>,
file_path: impl Into<PathBuf>,
) -> Result<Vec<Self>, Box<dyn Error>> {
let (models, _) = tobj::load_obj(
&file_path.into(),
&tobj::LoadOptions {
single_index: true,
triangulate: true,
..Default::default()
},
)?;
let meshes = models
.into_iter()
.map(|model| {
let vertices = (0..model.mesh.positions.len() / 3)
.map(|i| Vertex3D {
position: [
model.mesh.positions[i * 3],
model.mesh.positions[i * 3 + 1],
model.mesh.positions[i * 3 + 2],
],
uv: [model.mesh.texcoords[i * 2], model.mesh.texcoords[i * 2 + 1]],
})
.collect::<Vec<_>>();
let indices = model.mesh.indices;
let vertex_buffer = Vertex3D::create_vertex_buffer(memory_allocator, &vertices)
.expect("Failed to create vertex buffer");
let index_buffer = u32::create_index_buffer(memory_allocator, &indices)
.expect("Failed to create index buffer");
Self {
vertex_buffer,
index_buffer,
}
})
.collect::<Vec<_>>();
Ok(meshes)
}
}
impl AsRenderableMesh<Vertex3D, Subbuffer<[u32]>> for ObjMesh {
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 {
self.vertex_buffer.len() as u32
}
fn index_count(&self) -> u32 {
self.index_buffer.len() as u32
}
}

View file

@ -27,12 +27,12 @@ const VERTICES: [Vertex3D; 4] = [
const INDICES: [u32; 6] = [0, 2, 1, 2, 3, 1]; const INDICES: [u32; 6] = [0, 2, 1, 2, 3, 1];
pub struct Square { pub struct SquareMesh {
vertex_buffer: Subbuffer<[Vertex3D]>, vertex_buffer: Subbuffer<[Vertex3D]>,
index_buffer: Subbuffer<[u32]>, index_buffer: Subbuffer<[u32]>,
} }
impl Square { impl SquareMesh {
pub fn new(memory_allocator: &Arc<StandardMemoryAllocator>) -> Result<Self, Box<dyn Error>> { pub fn new(memory_allocator: &Arc<StandardMemoryAllocator>) -> Result<Self, Box<dyn Error>> {
let vertex_buffer = Vertex3D::create_vertex_buffer(memory_allocator, &VERTICES)?; let vertex_buffer = Vertex3D::create_vertex_buffer(memory_allocator, &VERTICES)?;
let index_buffer = u32::create_index_buffer(memory_allocator, &INDICES)?; let index_buffer = u32::create_index_buffer(memory_allocator, &INDICES)?;
@ -44,7 +44,7 @@ impl Square {
} }
} }
impl AsRenderableMesh<Vertex3D, Subbuffer<[u32]>> for Square { impl AsRenderableMesh<Vertex3D, Subbuffer<[u32]>> for SquareMesh {
fn vertex_buffer(&self) -> &Subbuffer<[Vertex3D]> { fn vertex_buffer(&self) -> &Subbuffer<[Vertex3D]> {
&self.vertex_buffer &self.vertex_buffer
} }

View file

@ -0,0 +1,2 @@
pub mod meshes;
pub mod texture;

View file

@ -0,0 +1,127 @@
use std::{collections::HashMap, error::Error, sync::Arc};
use vulkano::{
command_buffer::{
AutoCommandBufferBuilder, CommandBufferUsage, PrimaryCommandBufferAbstract,
allocator::StandardCommandBufferAllocator,
},
device::{Device, Queue},
format::Format,
image::sampler::SamplerCreateInfo,
memory::allocator::StandardMemoryAllocator,
};
use crate::core::app::context::WindowContext;
use super::Texture;
pub enum TextureSourceKind {
File(String),
Buffer(Vec<u8>),
}
pub struct TextureLoadInfo {
pub source: TextureSourceKind,
pub sampler_create_info: SamplerCreateInfo,
pub image_format: Format,
}
pub struct TextureLoader {
loaded_textures: HashMap<String, Texture>,
pending_textures: HashMap<String, TextureLoadInfo>,
device: Arc<Device>,
command_buffer_allocator: Arc<StandardCommandBufferAllocator>,
memory_allocator: Arc<StandardMemoryAllocator>,
queue: Arc<Queue>,
}
impl TextureLoader {
pub fn new(app_context: &WindowContext) -> Self {
Self {
loaded_textures: HashMap::new(),
pending_textures: HashMap::new(),
device: app_context.device.clone(),
command_buffer_allocator: app_context.command_buffer_allocator.clone(),
memory_allocator: app_context.memory_allocator.clone(),
queue: Self::select_best_suitable_queue(app_context),
}
}
fn select_best_suitable_queue(app_context: &WindowContext) -> Arc<Queue> {
app_context
.transfer_queue
.as_ref()
.map(|queue| {
tracing::trace!(
"Selected transfer queue for texture loading with family index: {:?}",
queue.queue_family_index()
);
queue.clone()
})
.or_else(|| {
tracing::trace!(
"Selected graphics queue for texture loading with family index: {:?}",
app_context.graphics_queue.queue_family_index()
);
Some(app_context.graphics_queue.clone())
})
.unwrap()
}
pub fn add_texture(&mut self, name: String, load_info: TextureLoadInfo) {
self.pending_textures.insert(name, load_info);
}
pub fn load_pending_textures(&mut self) -> Result<(), Box<dyn Error>> {
let _span = tracing::info_span!("load_pending_textures");
let mut uploads = AutoCommandBufferBuilder::primary(
self.command_buffer_allocator.clone(),
self.queue.queue_family_index(),
CommandBufferUsage::OneTimeSubmit,
)?;
let mut loading_textures = HashMap::new();
tracing::trace!("Pending textures count: {}", self.pending_textures.len());
for (name, info) in self.pending_textures.iter() {
let texture = match &info.source {
TextureSourceKind::File(path) => Texture::from_file(
&self.device,
&self.memory_allocator,
&mut uploads,
path.as_str(),
info,
)?,
TextureSourceKind::Buffer(buffer) => Texture::from_bytes(
&self.device,
&self.memory_allocator,
&mut uploads,
&buffer,
info,
)?,
};
loading_textures.insert(name.clone(), texture);
tracing::trace!("Loaded texture: {}", name);
}
let _ = uploads.build()?.execute(self.queue.clone())?;
self.loaded_textures.extend(loading_textures);
Ok(())
}
pub fn get_texture(&self, name: &str) -> Option<&Texture> {
self.loaded_textures.get(name)
}
pub fn pending_textures_count(&self) -> usize {
self.pending_textures.len()
}
pub fn loaded_textures_count(&self) -> usize {
self.loaded_textures.len()
}
}

View file

@ -0,0 +1,5 @@
mod texture;
pub use texture::Texture;
mod loader;
pub use loader::{TextureLoadInfo, TextureLoader, TextureSourceKind};

View file

@ -7,22 +7,20 @@ use vulkano::{
command_buffer::{AutoCommandBufferBuilder, CopyBufferToImageInfo, PrimaryAutoCommandBuffer}, command_buffer::{AutoCommandBufferBuilder, CopyBufferToImageInfo, PrimaryAutoCommandBuffer},
descriptor_set::{ descriptor_set::{
DescriptorSet, WriteDescriptorSet, DescriptorSet, WriteDescriptorSet,
allocator::{DescriptorSetAllocator, StandardDescriptorSetAllocator}, allocator::StandardDescriptorSetAllocator,
layout::{DescriptorSetLayout, DescriptorSetLayoutBinding, DescriptorType}, layout::{DescriptorSetLayout, DescriptorSetLayoutBinding, DescriptorType},
}, },
device::Device, device::Device,
format::Format, format::Format,
image::{ image::{Image, ImageCreateInfo, ImageType, ImageUsage, sampler::Sampler, view::ImageView},
Image, ImageCreateInfo, ImageType, ImageUsage,
sampler::{Filter, Sampler, SamplerAddressMode, SamplerCreateInfo},
view::ImageView,
},
memory::allocator::{AllocationCreateInfo, MemoryTypeFilter, StandardMemoryAllocator}, memory::allocator::{AllocationCreateInfo, MemoryTypeFilter, StandardMemoryAllocator},
shader::ShaderStages, shader::ShaderStages,
}; };
use crate::core::render::primitives::AsBindableDescriptorSet; use crate::core::render::primitives::AsBindableDescriptorSet;
use super::TextureLoadInfo;
pub struct Texture { pub struct Texture {
texture: Arc<ImageView>, texture: Arc<ImageView>,
sampler: Arc<Sampler>, sampler: Arc<Sampler>,
@ -33,37 +31,39 @@ impl Texture {
Self { texture, sampler } Self { texture, sampler }
} }
pub fn from_file( pub(super) fn from_file(
device: &Arc<Device>, device: &Arc<Device>,
memory_allocator: &Arc<StandardMemoryAllocator>, memory_allocator: &Arc<StandardMemoryAllocator>,
builder: &mut AutoCommandBufferBuilder<PrimaryAutoCommandBuffer>, builder: &mut AutoCommandBufferBuilder<PrimaryAutoCommandBuffer>,
path: &str, path: &str,
load_info: &TextureLoadInfo,
) -> Result<Self, Box<dyn Error>> { ) -> Result<Self, Box<dyn Error>> {
let _span = tracing::info_span!("texture_load_from_file", path = path);
let bytes = std::fs::read(path)?; let bytes = std::fs::read(path)?;
Self::from_bytes(device, memory_allocator, builder, &bytes) Self::from_bytes(device, memory_allocator, builder, &bytes, load_info)
} }
pub fn from_bytes( pub(super) fn from_bytes(
device: &Arc<Device>, device: &Arc<Device>,
memory_allocator: &Arc<StandardMemoryAllocator>, memory_allocator: &Arc<StandardMemoryAllocator>,
builder: &mut AutoCommandBufferBuilder<PrimaryAutoCommandBuffer>, builder: &mut AutoCommandBufferBuilder<PrimaryAutoCommandBuffer>,
bytes: &[u8], bytes: &[u8],
load_info: &TextureLoadInfo,
) -> Result<Self, Box<dyn Error>> { ) -> Result<Self, Box<dyn Error>> {
let image = image::load_from_memory(bytes)?; let image = image::load_from_memory(bytes)?;
Self::from_dynamic_image(device, memory_allocator, builder, image) Self::from_dynamic_image(device, memory_allocator, builder, image, load_info)
} }
pub fn from_dynamic_image( fn from_dynamic_image(
device: &Arc<Device>, device: &Arc<Device>,
memory_allocator: &Arc<StandardMemoryAllocator>, memory_allocator: &Arc<StandardMemoryAllocator>,
builder: &mut AutoCommandBufferBuilder<PrimaryAutoCommandBuffer>, builder: &mut AutoCommandBufferBuilder<PrimaryAutoCommandBuffer>,
image: DynamicImage, image: DynamicImage,
load_info: &TextureLoadInfo,
) -> Result<Self, Box<dyn Error>> { ) -> Result<Self, Box<dyn Error>> {
let _span = tracing::info_span!("texture_from_dynamic_image"); let image_data = match load_info.image_format {
Format::R8G8B8A8_SRGB => image.to_rgba8(),
let image_data = image.to_rgba8(); _ => return Err("Unsupported format".into()),
};
let image_dimensions = image_data.dimensions(); let image_dimensions = image_data.dimensions();
let image_data = image_data.into_raw(); let image_data = image_data.into_raw();
@ -90,7 +90,7 @@ impl Texture {
memory_allocator.clone(), memory_allocator.clone(),
ImageCreateInfo { ImageCreateInfo {
image_type: ImageType::Dim2d, image_type: ImageType::Dim2d,
format: Format::R8G8B8A8_SRGB, format: load_info.image_format,
extent: [image_dimensions.0, image_dimensions.1, 1], extent: [image_dimensions.0, image_dimensions.1, 1],
array_layers: 1, array_layers: 1,
usage: ImageUsage::TRANSFER_DST | ImageUsage::SAMPLED, usage: ImageUsage::TRANSFER_DST | ImageUsage::SAMPLED,
@ -104,30 +104,12 @@ impl Texture {
image.clone(), image.clone(),
))?; ))?;
let sampler = Sampler::new( let sampler = Sampler::new(device.clone(), load_info.sampler_create_info.clone())?;
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)?; let image_view = ImageView::new_default(image)?;
tracing::trace!("Texture loaded with dimensions {:?}", image_dimensions);
Ok(Self::new(image_view, sampler)) Ok(Self::new(image_view, sampler))
} }
pub fn get_texture(&self) -> &Arc<ImageView> {
&self.texture
}
pub fn get_sampler(&self) -> &Arc<Sampler> {
&self.sampler
}
} }
impl AsBindableDescriptorSet<Texture> for Texture { impl AsBindableDescriptorSet<Texture> for Texture {

View file

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

View file

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

View file

@ -31,7 +31,7 @@ use crate::core::render::{
AsBindableDescriptorSet, AsRecordable, AsRenderableMesh, AsRenderableMeshInstance, AsBindableDescriptorSet, AsRecordable, AsRenderableMesh, AsRenderableMeshInstance,
mvp::Mvp, transform::TransformRaw, vertex::Vertex3D, mvp::Mvp, transform::TransformRaw, vertex::Vertex3D,
}, },
texture::Texture, resources::texture::Texture,
}; };
pub struct SimplePipelineRenderData<'a> { pub struct SimplePipelineRenderData<'a> {

View file

@ -1,255 +0,0 @@
use std::{error::Error, sync::Arc};
use vulkano::{
buffer::Subbuffer,
command_buffer::{AutoCommandBufferBuilder, PrimaryAutoCommandBuffer},
descriptor_set::{
DescriptorSet, allocator::StandardDescriptorSetAllocator,
layout::DescriptorSetLayoutCreateInfo,
},
device::Device,
format::Format,
memory::allocator::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},
},
};
use crate::core::render::{
primitives::{
AsBindableDescriptorSet, AsDrawable, AsIndexBuffer, AsRecordable, AsVertexBuffer, 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<GraphicsPipeline>,
}
/// Structure pour encapsuler les données de rendu du Square
pub struct SquareRenderData<'a> {
pub square: &'a Square,
pub mvp_uniform: &'a Subbuffer<[Mvp]>,
pub transform_uniform: &'a Subbuffer<[TransformRaw]>,
pub descriptor_sets: &'a [Arc<DescriptorSet>],
pub texture: &'a Texture,
}
impl Square {
pub fn new(
device: &Arc<Device>,
memory_allocator: &Arc<StandardMemoryAllocator>,
swapchain_format: Format,
depth_format: Format,
) -> 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)?;
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<PrimaryAutoCommandBuffer>,
descriptor_set_allocator: &Arc<StandardDescriptorSetAllocator>,
mvp_uniform: &Subbuffer<[Mvp]>,
transform_uniform: &Subbuffer<[TransformRaw]>,
texture: &Texture,
) -> Result<(), Box<dyn Error>> {
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)?;
let render_data = SquareRenderData {
square: self,
mvp_uniform,
transform_uniform,
texture,
descriptor_sets: &[uniform_descriptor_set, texture_descriptor_set],
};
// Utiliser les nouveaux traits pour le rendu
Self::record_render_commands(command_buffer, &self.pipeline, &render_data)?;
Ok(())
}
}
impl<'a> AsRecordable<SquareRenderData<'a>> for Square {
fn record_render_commands(
builder: &mut AutoCommandBufferBuilder<PrimaryAutoCommandBuffer>,
pipeline: &Arc<GraphicsPipeline>,
data: &SquareRenderData<'a>,
) -> Result<(), Box<dyn Error>> {
// Bind pipeline
builder.bind_pipeline_graphics(pipeline.clone())?;
// Bind descriptor sets
builder.bind_descriptor_sets(
PipelineBindPoint::Graphics,
pipeline.layout().clone(),
0,
data.descriptor_sets.iter().cloned().collect::<Vec<_>>(),
)?;
// Bind buffers
builder.bind_vertex_buffers(
0,
(
Self::vertex_buffer(data).clone(),
data.transform_uniform.clone(),
),
)?;
if let Some(index_buffer) = Self::index_buffer(data) {
builder.bind_index_buffer(index_buffer.clone())?;
unsafe {
builder.draw_indexed(
Self::index_count(data),
Self::instance_count(data),
0,
0,
0,
)?;
}
} else {
unsafe {
builder.draw(Self::vertex_count(data), Self::instance_count(data), 0, 0)?;
}
}
Ok(())
}
}

View file

@ -4,19 +4,22 @@ use super::settings_scene::SettingsScene;
use crate::core::app::DEPTH_IMAGE_ID; 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::AsRecordable;
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::resources::meshes::{ObjMesh, SquareMesh};
use crate::core::render::resources::texture::{
Texture, TextureLoadInfo, TextureLoader, TextureSourceKind,
};
use crate::core::scene::Scene; use crate::core::scene::Scene;
use crate::game::assets::meshs::square::Square;
use crate::game::assets::pipelines::simple::{SimplePipeline, SimplePipelineRenderData}; 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;
use glam::Vec3; use glam::Vec3;
use vulkano::format::Format;
use vulkano::image::sampler::{Filter, SamplerAddressMode, SamplerCreateInfo};
use vulkano::{ use vulkano::{
command_buffer::{AutoCommandBufferBuilder, CommandBufferUsage, PrimaryCommandBufferAbstract}, command_buffer::{AutoCommandBufferBuilder, CommandBufferUsage, PrimaryCommandBufferAbstract},
sync::GpuFuture, sync::GpuFuture,
@ -24,11 +27,13 @@ use vulkano::{
use winit::window::CursorGrabMode; use winit::window::CursorGrabMode;
pub struct MainSceneState { pub struct MainSceneState {
square: Square, texture_loader: TextureLoader,
square: SquareMesh,
obj: ObjMesh,
simple_pipeline: SimplePipeline, simple_pipeline: SimplePipeline,
instances: Vec<Transform>, square_instances: Vec<Transform>,
obj_instances: Vec<Transform>,
camera: Camera3D, camera: Camera3D,
texture: Texture,
speed: f32, speed: f32,
} }
@ -50,7 +55,41 @@ 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(&app_context.memory_allocator)?; let mut texture_loader = TextureLoader::new(app_context);
texture_loader.add_texture(
"wooden-crate".to_string(),
TextureLoadInfo {
source: TextureSourceKind::File("res/textures/wooden-crate.jpg".to_string()),
sampler_create_info: SamplerCreateInfo {
mag_filter: Filter::Linear,
min_filter: Filter::Linear,
address_mode: [SamplerAddressMode::Repeat; 3],
..Default::default()
},
image_format: Format::R8G8B8A8_SRGB,
},
);
texture_loader.add_texture(
"cube-diffuse".to_string(),
TextureLoadInfo {
source: TextureSourceKind::File("res/objects/cube-diffuse.jpg".to_string()),
sampler_create_info: SamplerCreateInfo {
mag_filter: Filter::Linear,
min_filter: Filter::Linear,
address_mode: [SamplerAddressMode::Repeat; 3],
..Default::default()
},
image_format: Format::R8G8B8A8_SRGB,
},
);
texture_loader.load_pending_textures()?;
let square = SquareMesh::new(&app_context.memory_allocator)?;
let obj = {
let obj = ObjMesh::new(&app_context.memory_allocator, "res/objects/cube.obj")?;
obj.into_iter().next().unwrap()
};
let simple_pipeline = SimplePipeline::new( let simple_pipeline = SimplePipeline::new(
&app_context.device, &app_context.device,
swapchain_image_view.format(), swapchain_image_view.format(),
@ -61,7 +100,7 @@ impl Scene for MainScene {
let instance_size = 10.0; let instance_size = 10.0;
let instance_spacing = 10.0; let instance_spacing = 10.0;
let num_instances_per_row = (num_instances as f32 / instance_spacing).ceil() as u32; let num_instances_per_row = (num_instances as f32 / instance_spacing).ceil() as u32;
let instances: Vec<Transform> = (0..num_instances) let square_instances: Vec<Transform> = (0..num_instances)
.map(|i| { .map(|i| {
Transform::new( Transform::new(
Vec3::new( Vec3::new(
@ -75,26 +114,26 @@ impl Scene for MainScene {
}) })
.collect(); .collect();
let texture = { let obj_instances: Vec<Transform> = (0..num_instances)
let mut uploads = AutoCommandBufferBuilder::primary( .map(|i| {
app_context.command_buffer_allocator.clone(), Transform::new(
app_context.graphics_queue.queue_family_index(), Vec3::new(
CommandBufferUsage::OneTimeSubmit, (i % num_instances_per_row) as f32 * (instance_spacing + instance_size),
)?; 0.0,
(i / num_instances_per_row) as f32
let texture = Texture::from_file( * (instance_spacing + instance_size)
&app_context.device, * -1.0
&app_context.memory_allocator, - instance_spacing * 2.0,
&mut uploads, ),
"res/textures/wooden-crate.jpg", Quat::from_euler(EulerRot::XYZ, 0.0, rand::random_range(0.0..=360.0), 0.0),
)?; Vec3::new(
instance_size * 0.5,
let _ = uploads instance_size * 0.5,
.build()? instance_size * 0.5,
.execute(app_context.graphics_queue.clone())?; ),
)
texture })
}; .collect();
let camera = app_context.with_renderer(|renderer| { let camera = app_context.with_renderer(|renderer| {
Camera3D::new( Camera3D::new(
@ -107,11 +146,13 @@ impl Scene for MainScene {
self.state = Some(MainSceneState { self.state = Some(MainSceneState {
square, square,
obj,
simple_pipeline, simple_pipeline,
instances, square_instances,
obj_instances,
camera, camera,
texture,
speed: 50.0, speed: 50.0,
texture_loader,
}); });
Ok(()) Ok(())
@ -132,7 +173,14 @@ impl Scene for MainScene {
}); });
let delta_time = app_context.get_delta_time(); let delta_time = app_context.get_delta_time();
for (i, instance) in state.instances.iter_mut().enumerate() { for (i, instance) in state.square_instances.iter_mut().enumerate() {
let rotation_speed = (i % 10) as f32;
let rotation_delta = Quat::from_rotation_y(rotation_speed * delta_time);
instance.rotate(rotation_delta);
}
for (i, instance) in state.obj_instances.iter_mut().enumerate() {
let rotation_speed = (i % 10) as f32; let rotation_speed = (i % 10) as f32;
let rotation_delta = Quat::from_rotation_y(rotation_speed * delta_time); let rotation_delta = Quat::from_rotation_y(rotation_speed * delta_time);
@ -203,21 +251,40 @@ 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 square_transform_uniform =
Transform::create_buffer(&app_context.memory_allocator, &state.instances)?; Transform::create_buffer(&app_context.memory_allocator, &state.square_instances)?;
let obj_transform_uniform =
Transform::create_buffer(&app_context.memory_allocator, &state.obj_instances)?;
SimplePipeline::record_bind_commands( SimplePipeline::record_bind_commands(
&mut builder, &mut builder,
&app_context.descriptor_set_allocator, &app_context.descriptor_set_allocator,
state.simple_pipeline.pipeline(), state.simple_pipeline.pipeline(),
&state.square, &state.square,
&transform_uniform, &square_transform_uniform,
&SimplePipelineRenderData { &SimplePipelineRenderData {
mvp_uniform: &camera_uniform, mvp_uniform: &camera_uniform,
texture: &state.texture, texture: &state.texture_loader.get_texture("wooden-crate").unwrap(),
}, },
)?; )?;
SimplePipeline::record_draw_commands(&mut builder, &state.square, &transform_uniform)?; SimplePipeline::record_draw_commands(
&mut builder,
&state.square,
&square_transform_uniform,
)?;
SimplePipeline::record_bind_commands(
&mut builder,
&app_context.descriptor_set_allocator,
state.simple_pipeline.pipeline(),
&state.obj,
&obj_transform_uniform,
&SimplePipelineRenderData {
mvp_uniform: &camera_uniform,
texture: &state.texture_loader.get_texture("cube-diffuse").unwrap(),
},
)?;
SimplePipeline::record_draw_commands(&mut builder, &state.obj, &obj_transform_uniform)?;
RenderPassManager::end_rendering(&mut builder)?; RenderPassManager::end_rendering(&mut builder)?;