diff --git a/Cargo.lock b/Cargo.lock index cc05c08..f44eb23 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -216,6 +216,12 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + [[package]] name = "bincode" version = "1.3.3" @@ -303,6 +309,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +[[package]] +name = "byteorder-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" + [[package]] name = "bytes" version = "1.9.0" @@ -751,6 +763,15 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" +[[package]] +name = "fdeflate" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" +dependencies = [ + "simd-adler32", +] + [[package]] name = "flate2" version = "1.0.35" @@ -859,6 +880,45 @@ dependencies = [ "libm", ] +[[package]] +name = "gltf" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ce1918195723ce6ac74e80542c5a96a40c2b26162c1957a5cd70799b8cacf7" +dependencies = [ + "base64", + "byteorder", + "gltf-json", + "image", + "lazy_static", + "serde_json", + "urlencoding", +] + +[[package]] +name = "gltf-derive" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14070e711538afba5d6c807edb74bcb84e5dbb9211a3bf5dea0dfab5b24f4c51" +dependencies = [ + "inflections", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "gltf-json" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6176f9d60a7eab0a877e8e96548605dedbde9190a7ae1e80bbcc1c9af03ab14" +dependencies = [ + "gltf-derive", + "serde", + "serde_derive", + "serde_json", +] + [[package]] name = "gpu-allocator" version = "0.25.0" @@ -1066,6 +1126,20 @@ dependencies = [ "icu_properties", ] +[[package]] +name = "image" +version = "0.25.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd6f44aed642f18953a158afeb30206f4d50da59fbc66ecb53c66488de73563b" +dependencies = [ + "bytemuck", + "byteorder-lite", + "num-traits", + "png", + "zune-core", + "zune-jpeg", +] + [[package]] name = "indenter" version = "0.3.3" @@ -1083,6 +1157,12 @@ dependencies = [ "serde", ] +[[package]] +name = "inflections" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a257582fdcde896fd96463bf2d40eefea0580021c0712a0e2b028b60b47a837a" + [[package]] name = "internal-iterator" version = "0.2.3" @@ -1274,6 +1354,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394" dependencies = [ "adler2", + "simd-adler32", ] [[package]] @@ -1723,6 +1804,19 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" +[[package]] +name = "png" +version = "0.17.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide 0.8.2", +] + [[package]] name = "polling" version = "3.7.4" @@ -2136,6 +2230,12 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + [[package]] name = "slab" version = "0.4.9" @@ -2579,6 +2679,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + [[package]] name = "utf16_iter" version = "1.0.5" @@ -2621,6 +2727,7 @@ dependencies = [ "egui", "egui-ash", "glam 0.22.0", + "gltf", "gpu-allocator", "gpu-profiler", "puffin", @@ -3447,3 +3554,18 @@ dependencies = [ "quote", "syn", ] + +[[package]] +name = "zune-core" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" + +[[package]] +name = "zune-jpeg" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99a5bab8d7dedf81405c4bb1f2b83ea057643d9cb28778cea9eecddeedd2e028" +dependencies = [ + "zune-core", +] diff --git a/crates/shaders/src/lib.rs b/crates/shaders/src/lib.rs index 98df01d..6974cbb 100644 --- a/crates/shaders/src/lib.rs +++ b/crates/shaders/src/lib.rs @@ -1,7 +1,7 @@ #![cfg_attr(target_arch = "spirv", no_std)] use shaders_shared::UniformBufferObject; -use spirv_std::spirv; +use spirv_std::{glam::Vec2, image::Image2d, spirv, Image, Sampler}; use glam::{Mat3, Vec3, Vec4, Vec4Swizzles}; @@ -35,10 +35,13 @@ pub fn main_vs( pub fn main_fs( frag_world_position: Vec3, frag_world_normal: Vec3, - out_color: &mut Vec4, + frag_tex_coord: Vec2, #[spirv(uniform, descriptor_set = 0, binding = 0)] ubo: &UniformBufferObject, + #[spirv(descriptor_set = 0, binding = 1)] texture: &Image2d, + #[spirv(descriptor_set = 0, binding = 2)] sampler: &Sampler, + out_color: &mut Vec4, ) { - let base_color = ubo.model_color; + let base_color = texture.sample(*sampler, frag_tex_coord); let light_pos = Vec3::new(2.0, 2.0, -2.0); // Calculate light direction @@ -49,9 +52,13 @@ pub fn main_fs( let lambertian = f32::max(n.dot(l), 0.0); // Ambient lighting - let ambient = Vec3::splat(0.1); + let ambient = Vec3::splat(0.2); + + // Combine texture color with model color + let color_from_texture = Vec3::new(base_color.x, base_color.y, base_color.z); + let combined_color = color_from_texture * ubo.model_color; // Final color calculation with gamma correction - let color = (base_color * lambertian + ambient).powf(2.2); + let color = (combined_color * lambertian + ambient).powf(2.2); *out_color = Vec4::from((color, 1.0)); } diff --git a/crates/vk-rs/Cargo.toml b/crates/vk-rs/Cargo.toml index 2691d6d..c7119e3 100644 --- a/crates/vk-rs/Cargo.toml +++ b/crates/vk-rs/Cargo.toml @@ -22,6 +22,7 @@ puffin_egui = { git = "https://github.com/EmbarkStudios/puffin", rev = "5ac4e541 gpu-profiler = { git = "https://github.com/zackartz/gpu-profiler", features = [ "use-ash", ] } +gltf = { version = "1.4.1", features = ["import"] } [build-dependencies] spirv-builder.workspace = true diff --git a/crates/vk-rs/shaders/main_fs.spv b/crates/vk-rs/shaders/main_fs.spv index 5b57869..611f575 100644 Binary files a/crates/vk-rs/shaders/main_fs.spv and b/crates/vk-rs/shaders/main_fs.spv differ diff --git a/crates/vk-rs/src/renderer.rs b/crates/vk-rs/src/renderer.rs index d6179b7..0c6c9aa 100644 --- a/crates/vk-rs/src/renderer.rs +++ b/crates/vk-rs/src/renderer.rs @@ -1,16 +1,18 @@ use ash::{ extensions::khr::{Surface, Swapchain}, - vk::{self, Buffer}, + vk::{self, Buffer, ImageAspectFlags}, Device, }; -use color_eyre::owo_colors::Color; +use egui::Vec2; use egui_ash::EguiCommand; use glam::{Mat4, Vec3}; -use gpu_allocator::vulkan::{Allocation, Allocator}; +use gltf::json::buffer::View; +use gpu_allocator::vulkan::{Allocation, AllocationCreateDesc, Allocator}; use shaders_shared::UniformBufferObject; use std::{ ffi::CString, mem::ManuallyDrop, + path::{Path, PathBuf}, sync::{Arc, Mutex}, }; @@ -34,6 +36,7 @@ macro_rules! include_spirv { struct Vertex { position: Vec3, normal: Vec3, + tex_coords: Vec2, } impl Vertex { fn get_binding_descriptions() -> [vk::VertexInputBindingDescription; 1] { @@ -44,7 +47,7 @@ impl Vertex { .build()] } - fn get_attribute_descriptions() -> [vk::VertexInputAttributeDescription; 2] { + fn get_attribute_descriptions() -> [vk::VertexInputAttributeDescription; 3] { [ vk::VertexInputAttributeDescription::builder() .binding(0) @@ -58,10 +61,605 @@ impl Vertex { .format(vk::Format::R32G32B32_SFLOAT) .offset(4 * 3) .build(), + vk::VertexInputAttributeDescription::builder() + .binding(0) + .location(2) + .format(vk::Format::R32G32_SFLOAT) + .offset(24) + .build(), ] } } +pub struct Texture { + image: vk::Image, + image_allocation: Option, + image_view: vk::ImageView, + sampler: vk::Sampler, +} + +impl Texture { + fn new( + device: &Device, + allocator: Arc>, + command_pool: vk::CommandPool, + queue: vk::Queue, + width: u32, + height: u32, + data: &[u8], + ) -> Self { + let mut allocator = allocator.lock().unwrap(); + + let buffer_size = data.len() as u64; + let staging_buffer = unsafe { + device + .create_buffer( + &vk::BufferCreateInfo::builder() + .size(buffer_size) + .usage(vk::BufferUsageFlags::TRANSFER_SRC), + None, + ) + .expect("failed to create_buffer") + }; + + let staging_allocation = allocator + .allocate(&gpu_allocator::vulkan::AllocationCreateDesc { + name: "staging_buffer", + requirements: unsafe { device.get_buffer_memory_requirements(staging_buffer) }, + location: gpu_allocator::MemoryLocation::CpuToGpu, + linear: true, + allocation_scheme: gpu_allocator::vulkan::AllocationScheme::GpuAllocatorManaged, + }) + .expect("failed to allocate memory"); + + unsafe { + device + .bind_buffer_memory( + staging_buffer, + staging_allocation.memory(), + staging_allocation.offset(), + ) + .expect("failed to bind_buffer_memory"); + + let ptr = staging_allocation.mapped_ptr().unwrap().as_ptr() as *mut u8; + ptr.copy_from_nonoverlapping(data.as_ptr(), data.len()); + } + + let image = unsafe { + device + .create_image( + &vk::ImageCreateInfo::builder() + .image_type(vk::ImageType::TYPE_2D) + .format(vk::Format::R8G8B8A8_SRGB) + .extent(vk::Extent3D { + width, + height, + depth: 1, + }) + .mip_levels(1) + .array_layers(1) + .samples(vk::SampleCountFlags::TYPE_1) + .tiling(vk::ImageTiling::OPTIMAL) + .usage(vk::ImageUsageFlags::TRANSFER_DST | vk::ImageUsageFlags::SAMPLED) + .initial_layout(vk::ImageLayout::UNDEFINED), + None, + ) + .expect("failed to create image") + }; + + let image_allocation = allocator + .allocate(&AllocationCreateDesc { + name: "texture image", + requirements: unsafe { device.get_image_memory_requirements(image) }, + location: gpu_allocator::MemoryLocation::GpuOnly, + linear: true, + allocation_scheme: gpu_allocator::vulkan::AllocationScheme::GpuAllocatorManaged, + }) + .expect("failed to allocate memory"); + + unsafe { + device + .bind_image_memory(image, image_allocation.memory(), image_allocation.offset()) + .expect("failed to bind image memory") + }; + + let command_buffer = unsafe { + let command_buffer = device + .allocate_command_buffers( + &vk::CommandBufferAllocateInfo::builder() + .command_pool(command_pool) + .level(vk::CommandBufferLevel::PRIMARY) + .command_buffer_count(1), + ) + .expect("failed to allocate command_buffer")[0]; + + device + .begin_command_buffer( + command_buffer, + &vk::CommandBufferBeginInfo::builder() + .flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT), + ) + .expect("failed to begin_command_buffer"); + + let barrier = vk::ImageMemoryBarrier::builder() + .old_layout(vk::ImageLayout::UNDEFINED) + .new_layout(vk::ImageLayout::TRANSFER_DST_OPTIMAL) + .src_queue_family_index(vk::QUEUE_FAMILY_IGNORED) + .dst_queue_family_index(vk::QUEUE_FAMILY_IGNORED) + .image(image) + .subresource_range(vk::ImageSubresourceRange { + aspect_mask: vk::ImageAspectFlags::COLOR, + base_mip_level: 0, + level_count: 1, + base_array_layer: 0, + layer_count: 1, + }) + .src_access_mask(vk::AccessFlags::empty()) + .dst_access_mask(vk::AccessFlags::TRANSFER_WRITE); + + device.cmd_pipeline_barrier( + command_buffer, + vk::PipelineStageFlags::TOP_OF_PIPE, + vk::PipelineStageFlags::TRANSFER, + vk::DependencyFlags::empty(), + &[], + &[], + &[barrier.build()], + ); + + let region = vk::BufferImageCopy::builder() + .image_subresource(vk::ImageSubresourceLayers { + aspect_mask: vk::ImageAspectFlags::COLOR, + mip_level: 0, + base_array_layer: 0, + layer_count: 1, + }) + .image_extent(vk::Extent3D { + width, + height, + depth: 1, + }); + + device.cmd_copy_buffer_to_image( + command_buffer, + staging_buffer, + image, + vk::ImageLayout::TRANSFER_DST_OPTIMAL, + &[region.build()], + ); + + let barrier = vk::ImageMemoryBarrier::builder() + .old_layout(vk::ImageLayout::TRANSFER_DST_OPTIMAL) + .new_layout(vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL) + .src_queue_family_index(vk::QUEUE_FAMILY_IGNORED) + .dst_queue_family_index(vk::QUEUE_FAMILY_IGNORED) + .image(image) + .subresource_range(vk::ImageSubresourceRange { + aspect_mask: vk::ImageAspectFlags::COLOR, + base_mip_level: 0, + level_count: 1, + base_array_layer: 0, + layer_count: 1, + }) + .src_access_mask(vk::AccessFlags::TRANSFER_WRITE) + .dst_access_mask(vk::AccessFlags::SHADER_READ); + + device.cmd_pipeline_barrier( + command_buffer, + vk::PipelineStageFlags::TRANSFER, + vk::PipelineStageFlags::FRAGMENT_SHADER, + vk::DependencyFlags::empty(), + &[], + &[], + &[barrier.build()], + ); + + device + .end_command_buffer(command_buffer) + .expect("failed to end the command buffer"); + + device + .queue_submit( + queue, + &[vk::SubmitInfo::builder() + .command_buffers(&[command_buffer]) + .build()], + vk::Fence::null(), + ) + .expect("failed to submit queue"); + + device.queue_wait_idle(queue).expect("failed to wait queue"); + + device.free_command_buffers(command_pool, &[command_buffer]); + + device.destroy_buffer(staging_buffer, None); + allocator + .free(staging_allocation) + .expect("failed to free memory"); + }; + + let image_view = unsafe { + device + .create_image_view( + &vk::ImageViewCreateInfo::builder() + .image(image) + .view_type(vk::ImageViewType::TYPE_2D) + .format(vk::Format::R8G8B8A8_SRGB) + .subresource_range(vk::ImageSubresourceRange { + aspect_mask: vk::ImageAspectFlags::COLOR, + base_mip_level: 0, + level_count: 1, + base_array_layer: 0, + layer_count: 1, + }), + None, + ) + .expect("failed to create image view") + }; + + let sampler = unsafe { + device + .create_sampler( + &vk::SamplerCreateInfo::builder() + .mag_filter(vk::Filter::LINEAR) + .min_filter(vk::Filter::LINEAR) + .address_mode_u(vk::SamplerAddressMode::REPEAT) + .address_mode_v(vk::SamplerAddressMode::REPEAT) + .address_mode_w(vk::SamplerAddressMode::REPEAT) + .anisotropy_enable(true) + .max_anisotropy(16.0) + .border_color(vk::BorderColor::INT_OPAQUE_BLACK) + .unnormalized_coordinates(false) + .compare_enable(false) + .compare_op(vk::CompareOp::ALWAYS) + .mipmap_mode(vk::SamplerMipmapMode::LINEAR) + .mip_lod_bias(0.0) + .min_lod(0.0) + .max_lod(0.0), + None, + ) + .expect("failed to create sampler") + }; + + Self { + image, + image_allocation: Some(image_allocation), + image_view, + sampler, + } + } + + fn destroy(&mut self, device: &Device, allocator: &mut Allocator) { + unsafe { + device.destroy_sampler(self.sampler, None); + device.destroy_image_view(self.image_view, None); + device.destroy_image(self.image, None); + } + if let Some(allocation) = self.image_allocation.take() { + allocator.free(allocation).expect("failed to free memory"); + } + } +} + +pub struct Mesh { + vertex_buffer: Buffer, + vertex_buffer_allocation: Option, + vertex_count: u32, + transform: Mat4, + texture: Option, +} + +pub struct Model { + meshes: Vec, +} + +fn load_texture_from_gltf( + device: &Device, + allocator: Arc>, + command_pool: vk::CommandPool, + queue: vk::Queue, + texture: &gltf::Texture, + buffers: &[gltf::buffer::Data], + path: &Path, +) -> Option { + let image = texture.source(); + let data = match image.source() { + gltf::image::Source::View { view, .. } => { + // Handle embedded buffer view + match view.buffer().source() { + gltf::buffer::Source::Bin => { + let start = view.offset(); + let end = start + view.length(); + buffers[0][start..end].to_vec() + } + _ => return None, + } + } + gltf::image::Source::Uri { .. } => { + // For URI sources, we'll get the data directly from gltf::image::Data + let img_data = + gltf::image::Data::from_source(image.source(), Some(path), buffers).ok()?; + img_data.pixels.clone().to_vec() + } + }; + + let img_data = gltf::image::Data::from_source(image.source(), Some(path), buffers).ok()?; + + Some(Texture::new( + device, + allocator, + command_pool, + queue, + img_data.width, + img_data.height, + &data, + )) +} + +impl Model { + fn load( + device: &Device, + allocator: Arc>, + command_pool: vk::CommandPool, + queue: vk::Queue, + path: &str, + ) -> Self { + let path = Path::new(path); + let base_path = path.parent(); + let (document, buffers, _) = gltf::import(path).expect("failed to load GLTF"); + let mut meshes = Vec::new(); + + for scene in document.scenes() { + for node in scene.nodes() { + meshes.extend(process_node( + node, + Mat4::IDENTITY, + &buffers, + device, + allocator.clone(), + command_pool, + queue, + base_path.unwrap(), + )); + } + } + + Self { meshes } + } +} + +fn process_node( + node: gltf::Node, + parent_transform: Mat4, + buffers: &[gltf::buffer::Data], + device: &Device, + allocator: Arc>, + command_pool: vk::CommandPool, + queue: vk::Queue, + path: &Path, +) -> Vec { + let mut meshes = Vec::new(); + + let local_transform = { + let (translation, rotation, scale) = node.transform().decomposed(); + Mat4::from_scale_rotation_translation( + Vec3::from(scale), + glam::Quat::from_array(rotation), + Vec3::from(translation), + ) + }; + let world_transform = parent_transform * local_transform; + + if let Some(mesh) = node.mesh() { + for primitive in mesh.primitives() { + let texture = primitive + .material() + .pbr_metallic_roughness() + .base_color_texture() + .and_then(|tex| { + load_texture_from_gltf( + device, + allocator.clone(), + command_pool, + queue, + &tex.texture(), + buffers, + path, + ) + }); + + let reader = primitive.reader(|buffer| Some(&buffers[buffer.index()])); + + if let (Some(positions), Some(normals), Some(tex_coords)) = ( + reader.read_positions(), + reader.read_normals(), + reader.read_tex_coords(0), + ) { + let mut vertices = Vec::new(); + + let indicies = reader + .read_indices() + .map(|indicies| indicies.into_u32().collect::>()) + .unwrap_or_else(|| (0..positions.len() as u32).collect()); + + let positions: Vec<_> = positions.collect(); + let normals: Vec<_> = normals.collect(); + let tex_coords: Vec<_> = tex_coords.into_f32().collect(); + + for &index in indicies.iter() { + let i = index as usize; + let vertex = Vertex { + position: Vec3::new(positions[i][0], positions[i][1], positions[i][2]), + normal: Vec3::new(normals[i][0], normals[i][1], normals[i][2]), + tex_coords: Vec2::new(tex_coords[i][0], tex_coords[i][1]), + }; + vertices.push(vertex); + } + + let (vertex_buffer, vertex_buffer_allocation) = + create_vertex_buffer(device, allocator.clone(), command_pool, queue, &vertices); + + meshes.push(Mesh { + vertex_buffer, + vertex_buffer_allocation: Some(vertex_buffer_allocation), + vertex_count: vertices.len() as u32, + transform: world_transform, + texture, + }); + } + } + } + + for child in node.children() { + meshes.extend(process_node( + child, + world_transform, + buffers, + device, + allocator.clone(), + command_pool, + queue, + path, + )); + } + + meshes +} + +fn create_vertex_buffer( + device: &Device, + allocator: Arc>, + command_pool: vk::CommandPool, + queue: vk::Queue, + vertices: &[Vertex], +) -> (Buffer, Allocation) { + let mut allocator = allocator.lock().unwrap(); + + let vertex_buffer_size = std::mem::size_of_val(vertices) as u64; + + let staging_buffer = unsafe { + device + .create_buffer( + &vk::BufferCreateInfo::builder() + .size(vertex_buffer_size) + .usage(vk::BufferUsageFlags::TRANSFER_SRC), + None, + ) + .expect("failed to create buffer") + }; + + let staging_allocation = allocator + .allocate(&AllocationCreateDesc { + name: "Staging vertex buffer", + requirements: unsafe { device.get_buffer_memory_requirements(staging_buffer) }, + location: gpu_allocator::MemoryLocation::CpuToGpu, + linear: true, + allocation_scheme: gpu_allocator::vulkan::AllocationScheme::GpuAllocatorManaged, + }) + .expect("failed to allocate memory"); + + unsafe { + device + .bind_buffer_memory( + staging_buffer, + staging_allocation.memory(), + staging_allocation.offset(), + ) + .expect("failed to bind buffer memory"); + + let ptr = staging_allocation.mapped_ptr().unwrap().as_ptr() as *mut Vertex; + ptr.copy_from_nonoverlapping(vertices.as_ptr(), vertices.len()); + } + + let vertex_buffer = unsafe { + device + .create_buffer( + &vk::BufferCreateInfo::builder() + .size(vertex_buffer_size) + .usage( + vk::BufferUsageFlags::TRANSFER_DST | vk::BufferUsageFlags::VERTEX_BUFFER, + ), + None, + ) + .expect("failed to create buffer") + }; + + let vertex_allocation = allocator + .allocate(&AllocationCreateDesc { + name: "Vertex Buffer", + requirements: unsafe { device.get_buffer_memory_requirements(vertex_buffer) }, + location: gpu_allocator::MemoryLocation::GpuOnly, + linear: true, + allocation_scheme: gpu_allocator::vulkan::AllocationScheme::GpuAllocatorManaged, + }) + .expect("failed to allocate memory"); + + unsafe { + device + .bind_buffer_memory( + vertex_buffer, + vertex_allocation.memory(), + vertex_allocation.offset(), + ) + .expect("failed to bind buffer memory"); + } + + let command_buffer = unsafe { + device + .allocate_command_buffers( + &vk::CommandBufferAllocateInfo::builder() + .command_pool(command_pool) + .level(vk::CommandBufferLevel::PRIMARY) + .command_buffer_count(1), + ) + .expect("failed to allocate command buffer")[0] + }; + + unsafe { + device + .begin_command_buffer( + command_buffer, + &vk::CommandBufferBeginInfo::builder() + .flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT) + .build(), + ) + .expect("failed to begin command_buffer"); + + device.cmd_copy_buffer( + command_buffer, + staging_buffer, + vertex_buffer, + &[vk::BufferCopy::builder().size(vertex_buffer_size).build()], + ); + + device + .end_command_buffer(command_buffer) + .expect("Failed to end command buffer"); + + device + .queue_submit( + queue, + &[vk::SubmitInfo::builder() + .command_buffers(&[command_buffer]) + .build()], + vk::Fence::null(), + ) + .expect("failed to submit queue"); + + device.queue_wait_idle(queue).expect("failed to wait queue"); + device.free_command_buffers(command_pool, &[command_buffer]); + } + + unsafe { + device.destroy_buffer(staging_buffer, None); + } + allocator + .free(staging_allocation) + .expect("failed to free memory"); + + (vertex_buffer, vertex_allocation) +} + pub struct RendererInner { width: u32, height: u32, @@ -90,9 +688,9 @@ pub struct RendererInner { depth_image_views: Vec, pipeline: vk::Pipeline, pipeline_layout: vk::PipelineLayout, - vertex_buffer: Buffer, - vertex_buffer_allocation: Option, - vertex_count: u32, + + model: Model, + command_buffers: Vec, in_flight_fences: Vec, image_available_semaphores: Vec, @@ -541,7 +1139,7 @@ impl RendererInner { .rasterizer_discard_enable(false) .polygon_mode(vk::PolygonMode::FILL) .cull_mode(vk::CullModeFlags::BACK) - .front_face(vk::FrontFace::CLOCKWISE) + .front_face(vk::FrontFace::COUNTER_CLOCKWISE) .depth_bias_enable(false) .line_width(1.0); let stencil_op = vk::StencilOpState::builder() @@ -603,178 +1201,6 @@ impl RendererInner { (graphics_pipeline, pipeline_layout) } - fn load_model_and_create_vertex_buffer( - device: &Device, - allocator: Arc>, - command_pool: vk::CommandPool, - queue: vk::Queue, - ) -> (Buffer, Allocation, u32) { - let mut allocator = allocator.lock().unwrap(); - let vertices = { - let model_obj = tobj::load_obj( - "./assets/suzanne.obj", - &tobj::LoadOptions { - single_index: true, - triangulate: true, - ignore_points: true, - ignore_lines: true, - }, - ) - .expect("Failed to load model"); - let mut vertices = vec![]; - let (models, _) = model_obj; - for m in models.iter() { - let mesh = &m.mesh; - - for &i in mesh.indices.iter() { - let i = i as usize; - let vertex = Vertex { - position: Vec3::new( - mesh.positions[3 * i], - mesh.positions[3 * i + 1], - mesh.positions[3 * i + 2], - ), - normal: Vec3::new( - mesh.normals[3 * i], - mesh.normals[3 * i + 1], - mesh.normals[3 * i + 2], - ), - }; - vertices.push(vertex); - } - } - - vertices - }; - let vertex_buffer_size = vertices.len() as u64 * std::mem::size_of::() as u64; - let temporary_buffer = unsafe { - device - .create_buffer( - &vk::BufferCreateInfo::builder() - .size(vertex_buffer_size) - .usage(vk::BufferUsageFlags::TRANSFER_SRC), - None, - ) - .expect("Failed to create buffer") - }; - let temporary_buffer_memory_requirements = - unsafe { device.get_buffer_memory_requirements(temporary_buffer) }; - let temporary_buffer_allocation = allocator - .allocate(&gpu_allocator::vulkan::AllocationCreateDesc { - name: "Temporary Vertex Buffer", - requirements: temporary_buffer_memory_requirements, - location: gpu_allocator::MemoryLocation::CpuToGpu, - linear: true, - allocation_scheme: gpu_allocator::vulkan::AllocationScheme::GpuAllocatorManaged, - }) - .expect("Failed to allocate memory"); - unsafe { - device - .bind_buffer_memory( - temporary_buffer, - temporary_buffer_allocation.memory(), - temporary_buffer_allocation.offset(), - ) - .expect("Failed to bind buffer memory") - } - unsafe { - let ptr = temporary_buffer_allocation.mapped_ptr().unwrap().as_ptr() as *mut Vertex; - ptr.copy_from_nonoverlapping(vertices.as_ptr(), vertices.len()); - } - - let vertex_buffer = unsafe { - device - .create_buffer( - &vk::BufferCreateInfo::builder() - .size(vertex_buffer_size) - .usage( - vk::BufferUsageFlags::TRANSFER_DST - | vk::BufferUsageFlags::VERTEX_BUFFER, - ), - None, - ) - .expect("Failed to create buffer") - }; - let vertex_buffer_memory_requirements = - unsafe { device.get_buffer_memory_requirements(vertex_buffer) }; - let vertex_buffer_allocation = allocator - .allocate(&gpu_allocator::vulkan::AllocationCreateDesc { - name: "Vertex Buffer", - requirements: vertex_buffer_memory_requirements, - location: gpu_allocator::MemoryLocation::GpuOnly, - linear: true, - allocation_scheme: gpu_allocator::vulkan::AllocationScheme::GpuAllocatorManaged, - }) - .expect("Failed to allocate memory"); - unsafe { - device - .bind_buffer_memory( - vertex_buffer, - vertex_buffer_allocation.memory(), - vertex_buffer_allocation.offset(), - ) - .expect("Failed to bind buffer memory") - } - - let cmd = unsafe { - device - .allocate_command_buffers( - &vk::CommandBufferAllocateInfo::builder() - .command_pool(command_pool) - .level(vk::CommandBufferLevel::PRIMARY) - .command_buffer_count(1), - ) - .expect("Failed to allocate command buffer")[0] - }; - - unsafe { - device - .begin_command_buffer( - cmd, - &vk::CommandBufferBeginInfo::builder() - .flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT), - ) - .expect("Failed to begin command buffer"); - device.cmd_copy_buffer( - cmd, - temporary_buffer, - vertex_buffer, - &[vk::BufferCopy::builder() - .src_offset(0) - .dst_offset(0) - .size(vertex_buffer_size) - .build()], - ); - device - .end_command_buffer(cmd) - .expect("Failed to end command buffer"); - - device - .queue_submit( - queue, - &[vk::SubmitInfo::builder().command_buffers(&[cmd]).build()], - vk::Fence::null(), - ) - .expect("Failed to submit queue"); - device.queue_wait_idle(queue).expect("Failed to wait queue"); - - device.free_command_buffers(command_pool, &[cmd]); - } - - allocator - .free(temporary_buffer_allocation) - .expect("Failed to free memory"); - unsafe { - device.destroy_buffer(temporary_buffer, None); - } - - ( - vertex_buffer, - vertex_buffer_allocation, - vertices.len() as u32, - ) - } - fn create_command_buffers( device: &Device, command_pool: vk::CommandPool, @@ -1036,13 +1462,15 @@ impl RendererInner { ); let (pipeline, pipeline_layout) = Self::create_graphics_pipeline(&device, &descriptor_set_layouts, render_pass); - let (vertex_buffer, vertex_buffer_allocation, vertex_count) = - Self::load_model_and_create_vertex_buffer( - &device, - allocator.clone(), - command_pool, - queue, - ); + println!("loading model!"); + let model = Model::load( + &device, + allocator.clone(), + command_pool, + queue, + "./sponza/NewSponza_Main_glTF_003.gltf", + ); + println!("loaded!"); let command_buffers = Self::create_command_buffers(&device, command_pool, swapchain_images.len()); let (in_flight_fences, image_available_semaphores, render_finished_semaphores) = @@ -1074,9 +1502,7 @@ impl RendererInner { depth_image_views, pipeline, pipeline_layout, - vertex_buffer, - vertex_buffer_allocation: Some(vertex_buffer_allocation), - vertex_count, + model, command_buffers, in_flight_fences, image_available_semaphores, @@ -1144,22 +1570,6 @@ impl RendererInner { self.recreate_swapchain(width, height, &mut egui_cmd); } - // unsafe { - // self.device - // .wait_for_fences( - // std::slice::from_ref(&self.in_flight_fences[self.current_frame]), - // true, - // u64::MAX, - // ) - // .expect("failed to wait for fences"); - // - // self.device - // .reset_fences(std::slice::from_ref( - // &self.in_flight_fences[self.current_frame], - // )) - // .expect("failed to reset fences"); - // } - unsafe { puffin::profile_scope!("wait_for_fences"); self.device.wait_for_fences( @@ -1210,26 +1620,6 @@ impl RendererInner { ) }; - let ubo = UniformBufferObject { - model: Mat4::from_rotation_y(rotate_y.to_radians()), - view, - proj: Mat4::perspective_rh( - self.camera_fov.to_radians(), - width as f32 / height as f32, - 0.1, - 10.0, - ), - model_color: self.model_color, - }; - - unsafe { - let ptr = self.uniform_buffer_allocations[self.current_frame] - .mapped_ptr() - .unwrap() - .as_ptr() as *mut UniformBufferObject; - ptr.copy_from_nonoverlapping([ubo].as_ptr(), 1); - } - let command_buffer_begin_info = vk::CommandBufferBeginInfo::builder() .flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT); unsafe { @@ -1272,6 +1662,7 @@ impl RendererInner { vk::PipelineBindPoint::GRAPHICS, self.pipeline, ); + self.device.cmd_set_viewport( self.command_buffers[self.current_frame], 0, @@ -1292,28 +1683,51 @@ impl RendererInner { .extent(self.surface_extent), ), ); - self.device.cmd_bind_descriptor_sets( - self.command_buffers[self.current_frame], - vk::PipelineBindPoint::GRAPHICS, - self.pipeline_layout, - 0, - &[self.descriptor_sets[self.current_frame]], - &[], - ); - self.device.cmd_bind_vertex_buffers( - self.command_buffers[self.current_frame], - 0, - &[self.vertex_buffer], - &[0], - ); - self.device.cmd_draw( - self.command_buffers[self.current_frame], - self.vertex_count, - 1, - 0, - 0, - ); + for mesh in &self.model.meshes { + let model_matrix = Mat4::from_rotation_y(rotate_y.to_radians()) * mesh.transform; + + let ubo = UniformBufferObject { + model: model_matrix, + view, + proj: Mat4::perspective_rh( + self.camera_fov.to_radians(), + width as f32 / height as f32, + 0.1, + 1000.0, + ), + model_color: self.model_color, + }; + + let ptr = self.uniform_buffer_allocations[self.current_frame] + .mapped_ptr() + .unwrap() + .as_ptr() as *mut UniformBufferObject; + ptr.copy_from_nonoverlapping(&ubo, 1); + + self.device.cmd_bind_descriptor_sets( + self.command_buffers[self.current_frame], + vk::PipelineBindPoint::GRAPHICS, + self.pipeline_layout, + 0, + &[self.descriptor_sets[self.current_frame]], + &[], + ); + + self.device.cmd_bind_vertex_buffers( + self.command_buffers[self.current_frame], + 0, + &[mesh.vertex_buffer], + &[0], + ); + self.device.cmd_draw( + self.command_buffers[self.current_frame], + mesh.vertex_count, + 1, + 0, + 0, + ); + } self.device .cmd_end_render_pass(self.command_buffers[self.current_frame]); @@ -1393,11 +1807,13 @@ impl RendererInner { for semaphore in self.render_finished_semaphores.drain(..) { self.device.destroy_semaphore(semaphore, None); } - self.device.destroy_buffer(self.vertex_buffer, None); - if let Some(vertex_buffer_allocation) = self.vertex_buffer_allocation.take() { - allocator - .free(vertex_buffer_allocation) - .expect("Failed to free memory"); + for mesh in &mut self.model.meshes { + self.device.destroy_buffer(mesh.vertex_buffer, None); + if let Some(vertex_buffer_allocation) = mesh.vertex_buffer_allocation.take() { + allocator + .free(vertex_buffer_allocation) + .expect("Failed to free memory"); + } } self.device.destroy_pipeline(self.pipeline, None); self.device