diff --git a/resources/texture.png b/resources/texture.png new file mode 100644 index 0000000..8878bcf Binary files /dev/null and b/resources/texture.png differ diff --git a/shaders/compile.bat b/shaders/compile.bat index 2ab8716..a8d7348 100644 --- a/shaders/compile.bat +++ b/shaders/compile.bat @@ -1,3 +1,2 @@ C:/VulkanSDK/1.3.280.0/Bin/glslc.exe shaders/shader.vert -o shaders/vert.spv -C:/VulkanSDK/1.3.280.0/Bin/glslc.exe shaders/shader.frag -o shaders/frag.spv -pause \ No newline at end of file +C:/VulkanSDK/1.3.280.0/Bin/glslc.exe shaders/shader.frag -o shaders/frag.spv \ No newline at end of file diff --git a/shaders/frag.spv b/shaders/frag.spv index da37f7e..9495412 100644 Binary files a/shaders/frag.spv and b/shaders/frag.spv differ diff --git a/shaders/shader.frag b/shaders/shader.frag index 13009da..87187fa 100644 --- a/shaders/shader.frag +++ b/shaders/shader.frag @@ -1,9 +1,11 @@ #version 450 layout(location = 0) in vec3 fragColor; +layout(location = 1) in vec2 fragTexCoord; layout(location = 0) out vec4 outColor; +layout(binding = 1) uniform sampler2D texSampler; void main() { - outColor = vec4(fragColor, 1.0); + outColor = texture(texSampler, fragTexCoord); } \ No newline at end of file diff --git a/shaders/shader.vert b/shaders/shader.vert index b56a6bc..4cda1bf 100644 --- a/shaders/shader.vert +++ b/shaders/shader.vert @@ -7,12 +7,15 @@ layout(binding = 0) uniform UniformBufferObject { } ubo; -layout(location = 0) in vec2 inPosition; +layout(location = 0) in vec3 inPosition; layout(location = 1) in vec3 inColor; +layout(location = 2) in vec2 inTexCoord; layout(location = 0) out vec3 fragColor; +layout(location = 1) out vec2 fragTexCoord; void main() { - gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 0.0, 1.0); + gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 1.0); fragColor = inColor; + fragTexCoord = inTexCoord; } \ No newline at end of file diff --git a/shaders/vert.spv b/shaders/vert.spv index 2b7afb0..99280dc 100644 Binary files a/shaders/vert.spv and b/shaders/vert.spv differ diff --git a/src/app_data.rs b/src/app_data.rs index 21b9205..36ed0b2 100644 --- a/src/app_data.rs +++ b/src/app_data.rs @@ -34,11 +34,19 @@ pub struct AppData { pub index_buffer: vk::Buffer, pub index_buffer_memory: vk::DeviceMemory, - pub uniform_buffers: Vec, pub uniform_buffers_memory: Vec, pub descriptor_pool: vk::DescriptorPool, pub descriptor_sets: Vec, + + pub texture_image: vk::Image, + pub texture_image_memory: vk::DeviceMemory, + pub texture_image_view: vk::ImageView, + pub texture_sampler: vk::Sampler, + + pub depth_image: vk::Image, + pub depth_image_memory: vk::DeviceMemory, + pub depth_image_view: vk::ImageView, } \ No newline at end of file diff --git a/src/buffer.rs b/src/buffer.rs index 985307c..7a9471c 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -11,15 +11,25 @@ pub type Mat4 = cgmath::Matrix4; use crate::app_data; use crate::vertex; +use crate::command_buffer; -static VERTICES: [vertex::Vertex; 4] = [ - vertex::Vertex::new(vec2(-0.5, -0.5), vec3(1.0, 0.0, 0.0)), - vertex::Vertex::new(vec2(0.5, -0.5), vec3(0.0, 1.0, 0.0)), - vertex::Vertex::new(vec2(0.5, 0.5), vec3(0.0, 0.0, 1.0)), - vertex::Vertex::new(vec2(-0.5, 0.5), vec3(1.0, 1.0, 1.0)), +static VERTICES: [vertex::Vertex; 8] = [ + vertex::Vertex::new(vec3(-0.5, -0.5, 0.0), vec3(1.0, 0.0, 0.0), vec2(1.0, 0.0)), + vertex::Vertex::new(vec3(0.5, -0.5, 0.0), vec3(0.0, 1.0, 0.0), vec2(0.0, 0.0)), + vertex::Vertex::new(vec3(0.5, 0.5, 0.0), vec3(0.0, 0.0, 1.0), vec2(0.0, 1.0)), + vertex::Vertex::new(vec3(-0.5, 0.5, 0.0), vec3(1.0, 1.0, 1.0), vec2(1.0, 1.0)), + vertex::Vertex::new(vec3(-0.5, -0.5, -0.5), vec3(1.0, 0.0, 0.0), vec2(1.0, 0.0)), + vertex::Vertex::new(vec3(0.5, -0.5, -0.5), vec3(0.0, 1.0, 0.0), vec2(0.0, 0.0)), + vertex::Vertex::new(vec3(0.5, 0.5, -0.5), vec3(0.0, 0.0, 1.0), vec2(0.0, 1.0)), + vertex::Vertex::new(vec3(-0.5, 0.5, -0.5), vec3(1.0, 1.0, 1.0), vec2(1.0, 1.0)), ]; -pub const INDICES: &[u16] = &[0, 1, 2, 2, 3, 0]; +pub const INDICES: &[u16] = &[ + 0, 1, 2, + 2, 3, 0, + 4, 5, 6, + 6, 7, 4, +]; pub unsafe fn create_buffer( instance: &Instance, @@ -125,30 +135,12 @@ pub unsafe fn copy_buffer( destination: vk::Buffer, size: vk::DeviceSize, ) -> Result<()> { - let info = vk::CommandBufferAllocateInfo::builder() - .level(vk::CommandBufferLevel::PRIMARY) - .command_pool(data.command_pool) - .command_buffer_count(1); + let command_buffer = command_buffer::begin_single_time_commands(device, data)?; - let command_buffer = device.allocate_command_buffers(&info)?[0]; - - let info = vk::CommandBufferBeginInfo::builder() - .flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT); - - device.begin_command_buffer(command_buffer, &info)?; let regions = vk::BufferCopy::builder().size(size); device.cmd_copy_buffer(command_buffer, source, destination, &[regions]); - device.end_command_buffer(command_buffer)?; - - let command_buffers = &[command_buffer]; - let info = vk::SubmitInfo::builder() - .command_buffers(command_buffers); - - device.queue_submit(data.graphics_queue, &[info], vk::Fence::null())?; - device.queue_wait_idle(data.graphics_queue)?; - - device.free_command_buffers(data.command_pool, &[command_buffer]); + command_buffer::end_single_time_commands(device, data, command_buffer)?; Ok(()) } @@ -218,7 +210,13 @@ pub unsafe fn create_descriptor_set_layout( .descriptor_count(1) .stage_flags(vk::ShaderStageFlags::VERTEX); - let bindings = &[ubo_binding]; + let sampler_binding = vk::DescriptorSetLayoutBinding::builder() + .binding(1) + .descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER) + .descriptor_count(1) + .stage_flags(vk::ShaderStageFlags::FRAGMENT); + + let bindings = &[ubo_binding, sampler_binding]; let info = vk::DescriptorSetLayoutCreateInfo::builder() .bindings(bindings); @@ -256,7 +254,12 @@ pub unsafe fn create_descriptor_pool(device: &Device, data: &mut app_data::AppDa let ubo_size = vk::DescriptorPoolSize::builder() .type_(vk::DescriptorType::UNIFORM_BUFFER) .descriptor_count(data.swapchain_images.len() as u32); - let pool_sizes = &[ubo_size]; + + let sampler_size = vk::DescriptorPoolSize::builder() + .type_(vk::DescriptorType::COMBINED_IMAGE_SAMPLER) + .descriptor_count(data.swapchain_images.len() as u32); + + let pool_sizes = &[ubo_size, sampler_size]; let info = vk::DescriptorPoolCreateInfo::builder() .pool_sizes(pool_sizes) .max_sets(data.swapchain_images.len() as u32); @@ -278,13 +281,31 @@ pub unsafe fn create_descriptor_sets(device: &Device, data: &mut app_data::AppDa .offset(0) .range(size_of::() as u64); let buffer_info = &[info]; + let ubo_write = vk::WriteDescriptorSet::builder() .dst_set(data.descriptor_sets[i]) .dst_binding(0) .dst_array_element(0) .descriptor_type(vk::DescriptorType::UNIFORM_BUFFER) .buffer_info(buffer_info); - device.update_descriptor_sets(&[ubo_write], &[] as &[vk::CopyDescriptorSet]); + + let info = vk::DescriptorImageInfo::builder() + .image_layout(vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL) + .image_view(data.texture_image_view) + .sampler(data.texture_sampler); + + let image_info = &[info]; + let sampler_write = vk::WriteDescriptorSet::builder() + .dst_set(data.descriptor_sets[i]) + .dst_binding(1) + .dst_array_element(0) + .descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER) + .image_info(image_info); + + device.update_descriptor_sets( + &[ubo_write, sampler_write], + &[] as &[vk::CopyDescriptorSet], + ); } Ok(()) diff --git a/src/command_buffer.rs b/src/command_buffer.rs new file mode 100644 index 0000000..d2dca3a --- /dev/null +++ b/src/command_buffer.rs @@ -0,0 +1,131 @@ +use anyhow::Result; + +use vulkanalia::prelude::v1_0::*; + + +use crate::app_data; +use crate::buffer; +use crate::queue_family_indices; + +pub unsafe fn create_command_pool( + instance: &Instance, + device: &Device, + data: &mut app_data::AppData, +) -> Result<()> { + let indices = queue_family_indices::QueueFamilyIndices::get(instance, data, data.physical_device)?; + + let info = vk::CommandPoolCreateInfo::builder() + .flags(vk::CommandPoolCreateFlags::empty()) // Optional. + .queue_family_index(indices.graphics); + + data.command_pool = device.create_command_pool(&info, None)?; + + Ok(()) +} + +pub unsafe fn create_command_buffers(device: &Device, data: &mut app_data::AppData) -> Result<()> { + let allocate_info = vk::CommandBufferAllocateInfo::builder() + .command_pool(data.command_pool) + .level(vk::CommandBufferLevel::PRIMARY) + .command_buffer_count(data.framebuffers.len() as u32); + + data.command_buffers = device.allocate_command_buffers(&allocate_info)?; + + for (i, command_buffer) in data.command_buffers.iter().enumerate() { + let inheritance = vk::CommandBufferInheritanceInfo::builder(); + + let info = vk::CommandBufferBeginInfo::builder() + .flags(vk::CommandBufferUsageFlags::empty()) // Optional. + .inheritance_info(&inheritance); // Optional. + + device.begin_command_buffer(*command_buffer, &info)?; + + let render_area = vk::Rect2D::builder() + .offset(vk::Offset2D::default()) + .extent(data.swapchain_extent); + + let color_clear_value = vk::ClearValue { + color: vk::ClearColorValue { + float32: [0.0, 0.0, 0.0, 1.0], + }, + }; + + let depth_clear_value = vk::ClearValue { + depth_stencil: vk::ClearDepthStencilValue { + depth: 1.0, + stencil: 0, + }, + }; + + let clear_values = &[color_clear_value, depth_clear_value]; + let info = vk::RenderPassBeginInfo::builder() + .render_pass(data.render_pass) + .framebuffer(data.framebuffers[i]) + .render_area(render_area) + .clear_values(clear_values); + + device.cmd_begin_render_pass( + *command_buffer, &info, vk::SubpassContents::INLINE); + + device.cmd_bind_pipeline( + *command_buffer, vk::PipelineBindPoint::GRAPHICS, data.pipeline); + + device.cmd_bind_vertex_buffers(*command_buffer, 0, &[data.vertex_buffer], &[0]); + device.cmd_bind_index_buffer(*command_buffer, data.index_buffer, 0, vk::IndexType::UINT16); + + device.cmd_bind_descriptor_sets( + *command_buffer, + vk::PipelineBindPoint::GRAPHICS, + data.pipeline_layout, + 0, + &[data.descriptor_sets[i]], + &[], + ); + + device.cmd_draw_indexed(*command_buffer, buffer::INDICES.len() as u32, 1, 0, 0, 0); + + device.cmd_end_render_pass(*command_buffer); + + device.end_command_buffer(*command_buffer)?; + } + + Ok(()) +} + +pub unsafe fn begin_single_time_commands( + device: &Device, + data: &app_data::AppData, +) -> Result { + let info = vk::CommandBufferAllocateInfo::builder() + .level(vk::CommandBufferLevel::PRIMARY) + .command_pool(data.command_pool) + .command_buffer_count(1); + + let command_buffer = device.allocate_command_buffers(&info)?[0]; + + let info = vk::CommandBufferBeginInfo::builder() + .flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT); + + device.begin_command_buffer(command_buffer, &info)?; + + Ok(command_buffer) +} + +pub unsafe fn end_single_time_commands( + device: &Device, + data: &app_data::AppData, + command_buffer: vk::CommandBuffer, +) -> Result<()> { + device.end_command_buffer(command_buffer)?; + + let command_buffers = &[command_buffer]; + let info = vk::SubmitInfo::builder() + .command_buffers(command_buffers); + + device.queue_submit(data.graphics_queue, &[info], vk::Fence::null())?; + device.queue_wait_idle(data.graphics_queue)?; + + device.free_command_buffers(data.command_pool, &[command_buffer]); + + Ok(()) +} diff --git a/src/depth_buffer.rs b/src/depth_buffer.rs new file mode 100644 index 0000000..9601ca2 --- /dev/null +++ b/src/depth_buffer.rs @@ -0,0 +1,82 @@ +use anyhow::{anyhow, Result}; +use vulkanalia::prelude::v1_0::*; + +use crate::app_data; +use crate::image; + +pub unsafe fn create_depth_objects( + instance: &Instance, + device: &Device, + data: &mut app_data::AppData, +) -> Result<()> { + let format = get_depth_format(instance, data)?; + let (depth_image, depth_image_memory) = image::create_image( + instance, + device, + data, + data.swapchain_extent.width, + data.swapchain_extent.height, + format, + vk::ImageTiling::OPTIMAL, + vk::ImageUsageFlags::DEPTH_STENCIL_ATTACHMENT, + vk::MemoryPropertyFlags::DEVICE_LOCAL, + )?; + + data.depth_image = depth_image; + data.depth_image_memory = depth_image_memory; + + // Image View + + data.depth_image_view = image::create_image_view(device, data.depth_image, format, vk::ImageAspectFlags::DEPTH)?; + + image::transition_image_layout( + device, + data, + data.depth_image, + format, + vk::ImageLayout::UNDEFINED, + vk::ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL, + )?; + + Ok(()) +} + +unsafe fn get_supported_format( + instance: &Instance, + data: &app_data::AppData, + candidates: &[vk::Format], + tiling: vk::ImageTiling, + features: vk::FormatFeatureFlags, +) -> Result { + candidates + .iter() + .cloned() + .find(|f| { + let properties = instance.get_physical_device_format_properties( + data.physical_device, + *f, + ); + match tiling { + vk::ImageTiling::LINEAR => properties.linear_tiling_features.contains(features), + vk::ImageTiling::OPTIMAL => properties.optimal_tiling_features.contains(features), + _ => false, + } + }) + .ok_or_else(|| anyhow!("Failed to find supported format!")) +} + +pub unsafe fn get_depth_format(instance: &Instance, data: &app_data::AppData) -> Result { + let candidates = &[ + vk::Format::D32_SFLOAT, + vk::Format::D32_SFLOAT_S8_UINT, + vk::Format::D24_UNORM_S8_UINT, + ]; + + get_supported_format( + instance, + data, + candidates, + vk::ImageTiling::OPTIMAL, + vk::FormatFeatureFlags::DEPTH_STENCIL_ATTACHMENT, + ) +} \ No newline at end of file diff --git a/src/image.rs b/src/image.rs new file mode 100644 index 0000000..5709761 --- /dev/null +++ b/src/image.rs @@ -0,0 +1,317 @@ +use anyhow::{anyhow, Result}; + +use vulkanalia::prelude::v1_0::*; + +use std::ptr::copy_nonoverlapping as memcpy; + +use std::fs::File; + +pub type Mat4 = cgmath::Matrix4; + +use crate::app_data; +use crate::buffer; +use crate::command_buffer; + + +pub unsafe fn create_texture_image( + instance: &Instance, + device: &Device, + data: &mut app_data::AppData, +) -> Result<()> { + let image = File::open("resources/texture.png")?; + + let decoder = png::Decoder::new(image); + let mut reader = decoder.read_info()?; + + let mut pixels = vec![0; reader.info().raw_bytes()]; + reader.next_frame(&mut pixels)?; + + let size = reader.info().raw_bytes() as u64; + let (width, height) = reader.info().size(); + + let (staging_buffer, staging_buffer_memory) = buffer::create_buffer( + instance, + device, + data, + size, + vk::BufferUsageFlags::TRANSFER_SRC, + vk::MemoryPropertyFlags::HOST_COHERENT | vk::MemoryPropertyFlags::HOST_VISIBLE, + )?; + + let memory = device.map_memory( + staging_buffer_memory, + 0, + size, + vk::MemoryMapFlags::empty(), + )?; + + memcpy(pixels.as_ptr(), memory.cast(), pixels.len()); + + device.unmap_memory(staging_buffer_memory); + + let (texture_image, texture_image_memory) = create_image( + instance, + device, + data, + width, + height, + vk::Format::R8G8B8A8_SRGB, + vk::ImageTiling::OPTIMAL, + vk::ImageUsageFlags::SAMPLED | vk::ImageUsageFlags::TRANSFER_DST, + vk::MemoryPropertyFlags::DEVICE_LOCAL, + )?; + + data.texture_image = texture_image; + data.texture_image_memory = texture_image_memory; + + transition_image_layout( + device, + data, + data.texture_image, + vk::Format::R8G8B8A8_SRGB, + vk::ImageLayout::UNDEFINED, + vk::ImageLayout::TRANSFER_DST_OPTIMAL, + )?; + + copy_buffer_to_image( + device, + data, + staging_buffer, + data.texture_image, + width, + height, + )?; + + transition_image_layout( + device, + data, + data.texture_image, + vk::Format::R8G8B8A8_SRGB, + vk::ImageLayout::TRANSFER_DST_OPTIMAL, + vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL, + )?; + + device.destroy_buffer(staging_buffer, None); + device.free_memory(staging_buffer_memory, None); + + Ok(()) +} + +pub unsafe fn create_image( + instance: &Instance, + device: &Device, + data: &app_data::AppData, + width: u32, + height: u32, + format: vk::Format, + tiling: vk::ImageTiling, + usage: vk::ImageUsageFlags, + properties: vk::MemoryPropertyFlags, +) -> Result<(vk::Image, vk::DeviceMemory)> { + let info = vk::ImageCreateInfo::builder() + .image_type(vk::ImageType::_2D) + .extent(vk::Extent3D { + width, + height, + depth: 1, + }) + .mip_levels(1) + .array_layers(1) + .format(format) + .tiling(tiling) + .initial_layout(vk::ImageLayout::UNDEFINED) + .usage(usage) + .samples(vk::SampleCountFlags::_1) + .sharing_mode(vk::SharingMode::EXCLUSIVE); + + let image = device.create_image(&info, None)?; + + let requirements = device.get_image_memory_requirements(image); + + let info = vk::MemoryAllocateInfo::builder() + .allocation_size(requirements.size) + .memory_type_index(buffer::get_memory_type_index( + instance, + data, + properties, + requirements, + )?); + + let image_memory = device.allocate_memory(&info, None)?; + + device.bind_image_memory(image, image_memory, 0)?; + + Ok((image, image_memory)) +} + +pub unsafe fn transition_image_layout( + device: &Device, + data: &app_data::AppData, + image: vk::Image, + format: vk::Format, + old_layout: vk::ImageLayout, + new_layout: vk::ImageLayout, +) -> Result<()> { + let command_buffer = command_buffer::begin_single_time_commands(device, data)?; + + let ( + src_access_mask, + dst_access_mask, + src_stage_mask, + dst_stage_mask, + ) = match (old_layout, new_layout) { + (vk::ImageLayout::UNDEFINED, vk::ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL) => ( + vk::AccessFlags::empty(), + vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_READ | vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_WRITE, + vk::PipelineStageFlags::TOP_OF_PIPE, + vk::PipelineStageFlags::EARLY_FRAGMENT_TESTS, + ), + (vk::ImageLayout::UNDEFINED, vk::ImageLayout::TRANSFER_DST_OPTIMAL) => ( + vk::AccessFlags::empty(), + vk::AccessFlags::TRANSFER_WRITE, + vk::PipelineStageFlags::TOP_OF_PIPE, + vk::PipelineStageFlags::TRANSFER, + ), + (vk::ImageLayout::TRANSFER_DST_OPTIMAL, vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL) => ( + vk::AccessFlags::TRANSFER_WRITE, + vk::AccessFlags::SHADER_READ, + vk::PipelineStageFlags::TRANSFER, + vk::PipelineStageFlags::FRAGMENT_SHADER, + ), + _ => return Err(anyhow!("Unsupported image layout transition!")), + }; + + let aspect_mask = if new_layout == vk::ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL { + match format { + vk::Format::D32_SFLOAT_S8_UINT | vk::Format::D24_UNORM_S8_UINT => + vk::ImageAspectFlags::DEPTH | vk::ImageAspectFlags::STENCIL, + _ => vk::ImageAspectFlags::DEPTH + } + } else { + vk::ImageAspectFlags::COLOR + }; + + let subresource = vk::ImageSubresourceRange::builder() + .aspect_mask(aspect_mask) + .base_mip_level(0) + .level_count(1) + .base_array_layer(0) + .layer_count(1); + + let barrier = vk::ImageMemoryBarrier::builder() + .old_layout(old_layout) + .new_layout(new_layout) + .src_queue_family_index(vk::QUEUE_FAMILY_IGNORED) + .dst_queue_family_index(vk::QUEUE_FAMILY_IGNORED) + .image(image) + .subresource_range(subresource) + .src_access_mask(src_access_mask) + .dst_access_mask(dst_access_mask); + + device.cmd_pipeline_barrier( + command_buffer, + src_stage_mask, + dst_stage_mask, + vk::DependencyFlags::empty(), + &[] as &[vk::MemoryBarrier], + &[] as &[vk::BufferMemoryBarrier], + &[barrier], + ); + + command_buffer::end_single_time_commands(device, data, command_buffer)?; + + Ok(()) +} + +unsafe fn copy_buffer_to_image( + device: &Device, + data: &app_data::AppData, + buffer: vk::Buffer, + image: vk::Image, + width: u32, + height: u32, +) -> Result<()> { + let command_buffer = command_buffer::begin_single_time_commands(device, data)?; + + let subresource = vk::ImageSubresourceLayers::builder() + .aspect_mask(vk::ImageAspectFlags::COLOR) + .mip_level(0) + .base_array_layer(0) + .layer_count(1); + + let region = vk::BufferImageCopy::builder() + .buffer_offset(0) + .buffer_row_length(0) + .buffer_image_height(0) + .image_subresource(subresource) + .image_offset(vk::Offset3D { x: 0, y: 0, z: 0 }) + .image_extent(vk::Extent3D { width, height, depth: 1 }); + + device.cmd_copy_buffer_to_image( + command_buffer, + buffer, + image, + vk::ImageLayout::TRANSFER_DST_OPTIMAL, + &[region], + ); + + command_buffer::end_single_time_commands(device, data, command_buffer)?; + + Ok(()) +} + +pub unsafe fn create_image_view( + device: &Device, + image: vk::Image, + format: vk::Format, + aspects: vk::ImageAspectFlags, +) -> Result { + let subresource_range = vk::ImageSubresourceRange::builder() + .aspect_mask(aspects) + .base_mip_level(0) + .level_count(1) + .base_array_layer(0) + .layer_count(1); + + let info = vk::ImageViewCreateInfo::builder() + .image(image) + .view_type(vk::ImageViewType::_2D) + .format(format) + .subresource_range(subresource_range); + + Ok(device.create_image_view(&info, None)?) +} + +pub unsafe fn create_texture_image_view(device: &Device, data: &mut app_data::AppData) -> Result<()> { + data.texture_image_view = create_image_view( + device, + data.texture_image, + vk::Format::R8G8B8A8_SRGB, + vk::ImageAspectFlags::COLOR + )?; + + Ok(()) +} + + +pub unsafe fn create_texture_sampler(device: &Device, data: &mut app_data::AppData) -> Result<()> { + let info = 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); + + data.texture_sampler = device.create_sampler(&info, None)?; + Ok(()) +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index d1e389e..6c22f30 100644 --- a/src/main.rs +++ b/src/main.rs @@ -27,7 +27,7 @@ use vulkanalia::vk::ExtDebugUtilsExtension; use vulkanalia::vk::KhrSurfaceExtension; use vulkanalia::vk::KhrSwapchainExtension; -use cgmath::{vec2, vec3}; +use cgmath::vec3; use std::mem::size_of; use std::ptr::copy_nonoverlapping as memcpy; @@ -39,6 +39,9 @@ pub mod swapchain; pub mod queue_family_indices; pub mod vertex; pub mod buffer; +pub mod image; +pub mod command_buffer; +pub mod depth_buffer; const PORTABILITY_MACOS_VERSION: Version = Version::new(1, 3, 216); const VALIDATION_ENABLED: bool = @@ -123,10 +126,16 @@ impl App { buffer::create_descriptor_set_layout(&device, &mut data)?; create_pipeline(&device, &mut data)?; - + + command_buffer::create_command_pool(&instance, &device, &mut data)?; + + depth_buffer::create_depth_objects(&instance, &device, &mut data)?; create_framebuffers(&device, &mut data)?; - create_command_pool(&instance, &device, &mut data)?; + + image::create_texture_image(&instance, &device, &mut data)?; + image::create_texture_image_view(&device, &mut data)?; + image::create_texture_sampler(&device, &mut data)?; buffer::create_vertex_buffer(&instance, &device, &mut data)?; buffer::create_index_buffer(&instance, &device, &mut data)?; @@ -135,7 +144,7 @@ impl App { buffer::create_descriptor_pool(&device, &mut data)?; buffer::create_descriptor_sets(&device, &mut data)?; - create_command_buffers(&device, &mut data)?; + command_buffer::create_command_buffers(&device, &mut data)?; create_sync_objects(&device, &mut data)?; @@ -210,6 +219,13 @@ impl App { unsafe fn destroy(&mut self) { self.destroy_swapchain(); + self.device.destroy_sampler(self.data.texture_sampler, None); + + self.device.destroy_image_view(self.data.texture_image_view, None); + + self.device.destroy_image(self.data.texture_image, None); + self.device.free_memory(self.data.texture_image_memory, None); + self.device.destroy_descriptor_set_layout(self.data.descriptor_set_layout, None); self.device.destroy_buffer(self.data.index_buffer, None); @@ -245,11 +261,12 @@ impl App { swapchain::create_swapchain_image_views(&self.device, &mut self.data)?; create_render_pass(&self.instance, &self.device, &mut self.data)?; create_pipeline(&self.device, &mut self.data)?; + buffer::create_descriptor_pool(&self.device, &mut self.data)?; + depth_buffer::create_depth_objects(&self.instance, &self.device, &mut self.data)?; create_framebuffers(&self.device, &mut self.data)?; buffer::create_uniform_buffers(&self.instance, &self.device, &mut self.data)?; - buffer::create_descriptor_pool(&self.device, &mut self.data)?; buffer::create_descriptor_sets(&self.device, &mut self.data)?; - create_command_buffers(&self.device, &mut self.data)?; + command_buffer::create_command_buffers(&self.device, &mut self.data)?; self.data .images_in_flight .resize(self.data.swapchain_images.len(), vk::Fence::null()); @@ -257,6 +274,11 @@ impl App { } unsafe fn destroy_swapchain(&mut self) { + self.device.destroy_image_view(self.data.depth_image_view, None); + + self.device.destroy_image(self.data.depth_image, None); + self.device.free_memory(self.data.depth_image_memory, None); + self.device.destroy_descriptor_pool(self.data.descriptor_pool, None); self.data.uniform_buffers .iter() @@ -292,14 +314,21 @@ impl App { vec3(0.0, 0.0, 1.0), ); - let mut proj = cgmath::perspective( - cgmath::Deg(45.0), - self.data.swapchain_extent.width as f32 / self.data.swapchain_extent.height as f32, - 0.1, - 10.0, + let correction = buffer::Mat4::new( //column major order, matrix looks transposed + 1.0, 0.0, 0.0, 0.0, + // We're also flipping the Y-axis with this line's `-1.0`. + 0.0, -1.0, 0.0, 0.0, + 0.0, 0.0, 1.0 / 2.0, 0.0, + 0.0, 0.0, 1.0 / 2.0, 1.0, ); - - proj[1][1] *= -1.0; + + let proj = correction + * cgmath::perspective( + cgmath::Deg(45.0), + self.data.swapchain_extent.width as f32 / self.data.swapchain_extent.height as f32, + 0.1, + 10.0, + ); let ubo = buffer::UniformBufferObject { model, view, proj }; @@ -447,6 +476,9 @@ unsafe fn check_physical_device( if features.geometry_shader != vk::TRUE { return Err(anyhow!(errors::SuitabilityError("Missing geometry shader support."))); } + if features.sampler_anisotropy != vk::TRUE { + return Err(anyhow!(errors::SuitabilityError("No sampler anisotropy."))); + } queue_family_indices::QueueFamilyIndices::get(instance, data, physical_device)?; check_physical_device_extensions(instance, physical_device)?; @@ -504,7 +536,8 @@ unsafe fn create_logical_device( extensions.push(vk::KHR_PORTABILITY_SUBSET_EXTENSION.name.as_ptr()); }; - let features = vk::PhysicalDeviceFeatures::builder(); + let features = vk::PhysicalDeviceFeatures::builder() + .sampler_anisotropy(true); let indices = queue_family_indices::QueueFamilyIndices::get(instance, data, data.physical_device)?; @@ -594,6 +627,15 @@ unsafe fn create_pipeline(device: &Device, data: &mut app_data::AppData) -> Resu .sample_shading_enable(false) .rasterization_samples(vk::SampleCountFlags::_1); + let depth_stencil_state = vk::PipelineDepthStencilStateCreateInfo::builder() + .depth_test_enable(true) + .depth_write_enable(true) + .depth_compare_op(vk::CompareOp::LESS) + .depth_bounds_test_enable(false) + .min_depth_bounds(0.0) // Optional. + .max_depth_bounds(1.0) // Optional. + .stencil_test_enable(false); + let attachment = vk::PipelineColorBlendAttachmentState::builder() .color_write_mask(vk::ColorComponentFlags::all()) .blend_enable(false) @@ -625,6 +667,7 @@ unsafe fn create_pipeline(device: &Device, data: &mut app_data::AppData) -> Resu .viewport_state(&viewport_state) .rasterization_state(&rasterization_state) .multisample_state(&multisample_state) + .depth_stencil_state(&depth_stencil_state) .color_blend_state(&color_blend_state) .layout(data.pipeline_layout) .render_pass(data.render_pass) @@ -670,20 +713,38 @@ unsafe fn create_render_pass( .attachment(0) .layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL); + let depth_stencil_attachment = vk::AttachmentDescription::builder() + .format(depth_buffer::get_depth_format(instance, data)?) + .samples(vk::SampleCountFlags::_1) + .load_op(vk::AttachmentLoadOp::CLEAR) + .store_op(vk::AttachmentStoreOp::DONT_CARE) + .stencil_load_op(vk::AttachmentLoadOp::DONT_CARE) + .stencil_store_op(vk::AttachmentStoreOp::DONT_CARE) + .initial_layout(vk::ImageLayout::UNDEFINED) + .final_layout(vk::ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL); + + let depth_stencil_attachment_ref = vk::AttachmentReference::builder() + .attachment(1) + .layout(vk::ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL); + let color_attachments = &[color_attachment_ref]; let subpass = vk::SubpassDescription::builder() .pipeline_bind_point(vk::PipelineBindPoint::GRAPHICS) - .color_attachments(color_attachments); + .color_attachments(color_attachments) + .depth_stencil_attachment(&depth_stencil_attachment_ref); let dependency = vk::SubpassDependency::builder() .src_subpass(vk::SUBPASS_EXTERNAL) .dst_subpass(0) - .src_stage_mask(vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT) + .src_stage_mask(vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT + | vk::PipelineStageFlags::EARLY_FRAGMENT_TESTS) .src_access_mask(vk::AccessFlags::empty()) - .dst_stage_mask(vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT) - .dst_access_mask(vk::AccessFlags::COLOR_ATTACHMENT_WRITE); + .dst_stage_mask(vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT + | vk::PipelineStageFlags::EARLY_FRAGMENT_TESTS) + .dst_access_mask(vk::AccessFlags::COLOR_ATTACHMENT_WRITE + | vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_WRITE); - let attachments = &[color_attachment]; + let attachments = &[color_attachment, depth_stencil_attachment]; let subpasses = &[subpass]; let dependencies = &[dependency]; let info = vk::RenderPassCreateInfo::builder() @@ -701,7 +762,7 @@ unsafe fn create_framebuffers(device: &Device, data: &mut app_data::AppData) -> .swapchain_image_views .iter() .map(|i| { - let attachments = &[*i]; + let attachments = &[*i, data.depth_image_view]; let create_info = vk::FramebufferCreateInfo::builder() .render_pass(data.render_pass) .attachments(attachments) @@ -716,84 +777,6 @@ unsafe fn create_framebuffers(device: &Device, data: &mut app_data::AppData) -> Ok(()) } -unsafe fn create_command_pool( - instance: &Instance, - device: &Device, - data: &mut app_data::AppData, -) -> Result<()> { - let indices = queue_family_indices::QueueFamilyIndices::get(instance, data, data.physical_device)?; - - let info = vk::CommandPoolCreateInfo::builder() - .flags(vk::CommandPoolCreateFlags::empty()) // Optional. - .queue_family_index(indices.graphics); - - data.command_pool = device.create_command_pool(&info, None)?; - - Ok(()) -} - -unsafe fn create_command_buffers(device: &Device, data: &mut app_data::AppData) -> Result<()> { - let allocate_info = vk::CommandBufferAllocateInfo::builder() - .command_pool(data.command_pool) - .level(vk::CommandBufferLevel::PRIMARY) - .command_buffer_count(data.framebuffers.len() as u32); - - data.command_buffers = device.allocate_command_buffers(&allocate_info)?; - - for (i, command_buffer) in data.command_buffers.iter().enumerate() { - let inheritance = vk::CommandBufferInheritanceInfo::builder(); - - let info = vk::CommandBufferBeginInfo::builder() - .flags(vk::CommandBufferUsageFlags::empty()) // Optional. - .inheritance_info(&inheritance); // Optional. - - device.begin_command_buffer(*command_buffer, &info)?; - - let render_area = vk::Rect2D::builder() - .offset(vk::Offset2D::default()) - .extent(data.swapchain_extent); - - let color_clear_value = vk::ClearValue { - color: vk::ClearColorValue { - float32: [0.0, 0.0, 0.0, 1.0], - }, - }; - - let clear_values = &[color_clear_value]; - let info = vk::RenderPassBeginInfo::builder() - .render_pass(data.render_pass) - .framebuffer(data.framebuffers[i]) - .render_area(render_area) - .clear_values(clear_values); - - device.cmd_begin_render_pass( - *command_buffer, &info, vk::SubpassContents::INLINE); - - device.cmd_bind_pipeline( - *command_buffer, vk::PipelineBindPoint::GRAPHICS, data.pipeline); - - device.cmd_bind_vertex_buffers(*command_buffer, 0, &[data.vertex_buffer], &[0]); - device.cmd_bind_index_buffer(*command_buffer, data.index_buffer, 0, vk::IndexType::UINT16); - - device.cmd_bind_descriptor_sets( - *command_buffer, - vk::PipelineBindPoint::GRAPHICS, - data.pipeline_layout, - 0, - &[data.descriptor_sets[i]], - &[], - ); - - device.cmd_draw_indexed(*command_buffer, buffer::INDICES.len() as u32, 1, 0, 0, 0); - - device.cmd_end_render_pass(*command_buffer); - - device.end_command_buffer(*command_buffer)?; - } - - Ok(()) -} - unsafe fn create_sync_objects(device: &Device, data: &mut app_data::AppData) -> Result<()> { let semaphore_info = vk::SemaphoreCreateInfo::builder(); let fence_info = vk::FenceCreateInfo::builder() diff --git a/src/swapchain.rs b/src/swapchain.rs index 0b0c46e..c4f7400 100644 --- a/src/swapchain.rs +++ b/src/swapchain.rs @@ -10,6 +10,7 @@ use vulkanalia::vk::KhrSwapchainExtension; use crate::app_data; use crate::queue_family_indices; +use crate::image; #[derive(Clone, Debug)] pub struct SwapchainSupport { @@ -140,26 +141,7 @@ pub unsafe fn create_swapchain_image_views( data.swapchain_image_views = data .swapchain_images .iter() - .map(|i| { - let components = vk::ComponentMapping::builder() - .r(vk::ComponentSwizzle::IDENTITY) - .g(vk::ComponentSwizzle::IDENTITY) - .b(vk::ComponentSwizzle::IDENTITY) - .a(vk::ComponentSwizzle::IDENTITY); - let subresource_range = vk::ImageSubresourceRange::builder() - .aspect_mask(vk::ImageAspectFlags::COLOR) - .base_mip_level(0) - .level_count(1) - .base_array_layer(0) - .layer_count(1); - let info = vk::ImageViewCreateInfo::builder() - .image(*i) - .view_type(vk::ImageViewType::_2D) - .format(data.swapchain_format) - .components(components) - .subresource_range(subresource_range); - device.create_image_view(&info, None) - }) + .map(|i| image::create_image_view(device, *i, data.swapchain_format, vk::ImageAspectFlags::COLOR)) .collect::, _>>()?; Ok(()) diff --git a/src/vertex.rs b/src/vertex.rs index bc03ff1..f94329f 100644 --- a/src/vertex.rs +++ b/src/vertex.rs @@ -10,13 +10,14 @@ type Vec3 = cgmath::Vector3; #[repr(C)] #[derive(Copy, Clone, Debug)] pub struct Vertex { - pub pos: Vec2, + pub pos: Vec3, pub color: Vec3, + pub tex_coord: Vec2, } impl Vertex { - pub const fn new(pos: Vec2, color: Vec3) -> Self { - Self { pos, color } + pub const fn new(pos: Vec3, color: Vec3, tex_coord: Vec2) -> Self { + Self { pos, color, tex_coord } } pub fn binding_description() -> vk::VertexInputBindingDescription { @@ -27,11 +28,11 @@ impl Vertex { .build() } - pub fn attribute_descriptions() -> [vk::VertexInputAttributeDescription; 2] { + pub fn attribute_descriptions() -> [vk::VertexInputAttributeDescription; 3] { let pos = vk::VertexInputAttributeDescription::builder() .binding(0) .location(0) - .format(vk::Format::R32G32_SFLOAT) + .format(vk::Format::R32G32B32_SFLOAT) .offset(0) .build(); @@ -41,6 +42,13 @@ impl Vertex { .format(vk::Format::R32G32B32_SFLOAT) .offset(size_of::() as u32) .build(); - [pos, color] + + let tex_coord = vk::VertexInputAttributeDescription::builder() + .binding(0) + .location(2) + .format(vk::Format::R32G32_SFLOAT) + .offset((size_of::() + size_of::()) as u32) + .build(); + [pos, color, tex_coord] } }