From 40bd889e67d6f9fb4ce837fcda4ddf5a34c54b47 Mon Sep 17 00:00:00 2001 From: Irene Knapp Date: Sat, 18 Jul 2026 03:15:56 -0700 Subject: draw the image wow yay :) Force-Push: yes Change-Id: I7053ec3a7ed7b46e9c9d1260fd0f73b398b798af --- .gitignore | 1 + shaders/shader.frag | 11 +++- shaders/shader.vert | 7 ++- src/graphics_permanent.rs | 77 +++++++++++++++++++++++--- src/graphics_window_dressing.rs | 107 ++++++++++++++++++++++++------------- src/linear_algebra.rs | 53 +++++++++++------- textures/forest_leaves_04_diff.png | Bin 5925987 -> 2364188 bytes 7 files changed, 191 insertions(+), 65 deletions(-) diff --git a/.gitignore b/.gitignore index 3a6e63d..cdbfe51 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ /target /.direnv *.swp +*~ diff --git a/shaders/shader.frag b/shaders/shader.frag index f554b9b..20550ba 100644 --- a/shaders/shader.frag +++ b/shaders/shader.frag @@ -1,8 +1,15 @@ #version 460 -layout(location = 0) in vec3 vertexColor; +layout(binding = 1) uniform sampler2D textureSampler; + +layout(location = 0) in vec3 fragmentColor; +layout(location = 1) in vec2 fragmentTextureCoordinate; + layout(location = 0) out vec4 outColor; void main() { - outColor = vec4(vertexColor, 1.0); + outColor = vec4(fragmentColor + * texture(textureSampler, + fragmentTextureCoordinate).rgb, + 1.0); } diff --git a/shaders/shader.vert b/shaders/shader.vert index d72c6a8..b14c94b 100644 --- a/shaders/shader.vert +++ b/shaders/shader.vert @@ -14,8 +14,10 @@ layout(binding = 0) uniform UniformBlock { layout(location = 0) in vec2 inPosition; layout(location = 1) in vec3 inColor; +layout(location = 2) in vec2 inTextureCoordinate; -layout(location = 0) out vec3 vertexColor; +layout(location = 0) out vec3 fragmentColor; +layout(location = 1) out vec2 fragmentTextureCoordinate; // ///////////////// @@ -84,6 +86,7 @@ vec4 apply_all_transforms(vec3 position) { void main() { gl_Position = apply_all_transforms(vec3(inPosition, 0.0)); - vertexColor = inColor; + fragmentColor = inColor; + fragmentTextureCoordinate = inTextureCoordinate; } diff --git a/src/graphics_permanent.rs b/src/graphics_permanent.rs index b58e8a4..63e8994 100644 --- a/src/graphics_permanent.rs +++ b/src/graphics_permanent.rs @@ -62,6 +62,8 @@ pub struct PermanentGraphicsState { // follow Vulkan's lead and let it have a short variable name. pub device: Device, + pub sampler: vk::Sampler, + // Vulkan has a first-class concept of command queues. We have two of // them, one for graphics drawing commands and one for presentation. // @@ -88,6 +90,7 @@ pub struct QueueFamilyIndices { // for accidentally passing or returning one boolean as if it's another. struct EnablePortability(bool); struct EnableValidation(bool); +struct EnableAnisotropy(bool); pub struct EnableSwapchain(pub bool); @@ -123,14 +126,16 @@ impl PermanentGraphicsState { let (physical_device, device, indices, graphics_queue, presentation_queue, - enable_swapchain) + enable_anisotropy, enable_swapchain) = init_vulkan_device(&instance, &surface, enable_validation, enable_portability)?; + let sampler = init_sampler(&device, &enable_anisotropy)?; + let descriptor_set_layout = init_descriptor_set_layout(&device)?; Ok((PermanentGraphicsState { - window, entry, instance, debug_messager, surface, device, + window, entry, instance, debug_messager, surface, device, sampler, graphics_queue, presentation_queue }, GraphicsStateForReinit { physical_device, indices, descriptor_set_layout @@ -139,6 +144,8 @@ impl PermanentGraphicsState { #[allow(unsafe_code)] pub fn destroy(self) -> () { + unsafe { self.device.destroy_sampler(self.sampler, None) }; + unsafe { self.device.destroy_device(None) }; unsafe { self.instance.destroy_surface_khr(self.surface, None) }; @@ -415,7 +422,7 @@ fn init_vulkan_device(instance: &Instance, surface: &vk::SurfaceKHR, enable_validation: EnableValidation, enable_portability: EnablePortability) -> Result<(vk::PhysicalDevice, Device, QueueFamilyIndices, vk::Queue, - vk::Queue, EnableSwapchain)> + vk::Queue, EnableAnisotropy, EnableSwapchain)> { let (physical_device, indices) = pick_vulkan_device(instance, surface)?; @@ -433,7 +440,10 @@ fn init_vulkan_device(instance: &Instance, surface: &vk::SurfaceKHR, // Old versions of Vulkan want layers to be enabled at the device // level as well. Newer ones will ignore this and just use the instance // layers. - let features = vk::PhysicalDeviceFeatures::builder(); + let available_features = unsafe { + instance.get_physical_device_features(physical_device) + }; + let mut features = vk::PhysicalDeviceFeatures::builder(); let mut extensions = Vec::new(); let mut layers = Vec::new(); @@ -482,6 +492,16 @@ fn init_vulkan_device(instance: &Instance, surface: &vk::SurfaceKHR, EnableSwapchain(false) }; + let enable_anisotropy = if available_features.sampler_anisotropy + == vk::TRUE + { + features = features.sampler_anisotropy(true); + + EnableAnisotropy(true) + } else { + EnableAnisotropy(false) + }; + // We have one or more queue family indices; we don't know a priori // how many, because it's possible some of them are the same. We only // want to create one queue per distinct family, so we find the unique @@ -526,7 +546,39 @@ fn init_vulkan_device(instance: &Instance, surface: &vk::SurfaceKHR, Ok((physical_device, device, indices, graphics_queue, presentation_queue, - enable_swapchain)) + enable_anisotropy, enable_swapchain)) +} + + +#[allow(unsafe_code)] +fn init_sampler(device: &Device, enable_anisotropy: &EnableAnisotropy) + -> Result +{ + let mut sampler_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) + .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); + sampler_info = if enable_anisotropy.0 { + sampler_info.anisotropy_enable(true) + .max_anisotropy(16.0) + } else { + sampler_info.anisotropy_enable(false) + .max_anisotropy(1.0) + }; + + let sampler = unsafe { device.create_sampler(&sampler_info, None) }?; + + Ok(sampler) } @@ -540,7 +592,13 @@ fn init_descriptor_set_layout(device: &Device) .descriptor_count(1) .stage_flags(vk::ShaderStageFlags::VERTEX); - let bindings = [uniform_block_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 = [uniform_block_binding, sampler_binding]; let descriptor_set_layout_info = vk::DescriptorSetLayoutCreateInfo::builder() .bindings(&bindings); @@ -698,6 +756,13 @@ fn score_vulkan_device(instance: &Instance, surface: &vk::SurfaceKHR, // selected. } + let features = unsafe { + instance.get_physical_device_features(*physical_device) + }; + if features.sampler_anisotropy == vk::TRUE { + score += 1; + } + Ok(Acceptable::Accepted((score, indices))) } diff --git a/src/graphics_window_dressing.rs b/src/graphics_window_dressing.rs index 4dddb35..27390e4 100644 --- a/src/graphics_window_dressing.rs +++ b/src/graphics_window_dressing.rs @@ -48,6 +48,7 @@ pub struct WindowDressing { texture_image: vk::Image, texture_image_memory: vk::DeviceMemory, + texture_image_view: vk::ImageView, uniform_buffers: Vec, pub uniform_buffer_memory: Vec, @@ -104,6 +105,7 @@ impl WindowDressing { let instance = &permanent.instance; let surface = &permanent.surface; let device = &permanent.device; + let sampler = &permanent.sampler; let graphics_queue = &permanent.graphics_queue; let physical_device = &for_reinit.physical_device; let indices = &for_reinit.indices; @@ -131,7 +133,7 @@ impl WindowDressing { = init_index_buffer(instance, physical_device, device, graphics_queue, &transient_command_pool)?; - let (texture_image, texture_image_memory) + let (texture_image, texture_image_memory, texture_image_view) = init_texture(instance, physical_device, device, graphics_queue, &transient_command_pool)?; @@ -144,7 +146,8 @@ impl WindowDressing { let descriptor_sets = init_descriptor_sets(device, descriptor_set_layout, &uniform_buffers, &descriptor_pool, - swapchain.images.len())?; + swapchain.images.len(), + &texture_image_view, sampler)?; let command_buffers = init_commands( device, &swapchain.extent, &framebuffers, &render_pass, @@ -165,6 +168,7 @@ impl WindowDressing { index_buffer_memory, texture_image, texture_image_memory, + texture_image_view, uniform_buffers, uniform_buffer_memory, descriptor_pool, @@ -186,6 +190,7 @@ impl WindowDressing { let instance = &permanent.instance; let surface = &permanent.surface; let device = &permanent.device; + let sampler = &permanent.sampler; let physical_device = &for_reinit.physical_device; let indices = &for_reinit.indices; let descriptor_set_layout = &for_reinit.descriptor_set_layout; @@ -217,7 +222,8 @@ impl WindowDressing { let descriptor_sets = init_descriptor_sets(device, descriptor_set_layout, &uniform_buffers, &descriptor_pool, - swapchain.images.len())?; + swapchain.images.len(), + &self.texture_image_view, sampler)?; // Notice that we reused the command pool. let command_buffers = init_commands( @@ -256,6 +262,7 @@ impl WindowDressing { unsafe { device.free_memory(self.index_buffer_memory, None) }; unsafe { device.destroy_image(self.texture_image, None) }; unsafe { device.free_memory(self.texture_image_memory, None) }; + unsafe { device.destroy_image_view(self.texture_image_view, None) }; for semaphore in self.concurrency.image_available_semaphores { unsafe { device.destroy_semaphore(semaphore, None) }; @@ -384,30 +391,7 @@ fn init_swapchain(window: &Window, instance: &Instance, let mut image_views = Vec::new(); for image in &images { - 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 view_info = vk::ImageViewCreateInfo::builder() - .image(*image) - .view_type(vk::ImageViewType::_2D) - .format(format.format) - .components(components) - .subresource_range(subresource_range); - - let view = unsafe { - device.create_image_view(&view_info, None) - }?; - + let view = init_image_view(device, image, format.format)?; image_views.push(view); } @@ -649,7 +633,7 @@ fn init_index_buffer(instance: &Instance, fn init_texture(instance: &Instance, physical_device: &vk::PhysicalDevice, device: &Device, queue: &vk::Queue, command_pool: &vk::CommandPool) - -> Result<(vk::Image, vk::DeviceMemory)> + -> Result<(vk::Image, vk::DeviceMemory, vk::ImageView)> { let png = include_bytes!("../textures/forest_leaves_04_diff.png"); @@ -661,7 +645,7 @@ fn init_texture(instance: &Instance, let mut pixels = vec![0; reader.info().raw_bytes()]; reader.next_frame(&mut pixels)?; - let (staging_buffer, staging_memory, byte_size) + let (staging_buffer, staging_memory, _byte_size) = stage_in_buffer(instance, physical_device, device, &pixels)?; let (image, image_memory) @@ -685,10 +669,12 @@ fn init_texture(instance: &Instance, vk::ImageLayout::TRANSFER_DST_OPTIMAL, vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL)?; + let view = init_image_view(device, &image, vk::Format::R8G8B8A8_SRGB)?; + unsafe { device.destroy_buffer(staging_buffer, None) }; unsafe { device.free_memory(staging_memory, None) }; - Ok((image, image_memory)) + Ok((image, image_memory, view)) } @@ -750,7 +736,11 @@ fn init_descriptor_pool(device: &Device, count: usize) .type_(vk::DescriptorType::UNIFORM_BUFFER) .descriptor_count(count as u32); - let sizes = [uniform_block_size]; + let sampler_size = vk::DescriptorPoolSize::builder() + .type_(vk::DescriptorType::COMBINED_IMAGE_SAMPLER) + .descriptor_count(count as u32); + + let sizes = [uniform_block_size, sampler_size]; let pool_info = vk::DescriptorPoolCreateInfo::builder() .pool_sizes(&sizes) .max_sets(count as u32); @@ -763,7 +753,8 @@ fn init_descriptor_pool(device: &Device, count: usize) #[allow(unsafe_code)] fn init_descriptor_sets(device: &Device, layout: &vk::DescriptorSetLayout, buffers: &Vec, pool: &vk::DescriptorPool, - count: usize) + count: usize, texture_image_view: &vk::ImageView, + sampler: &vk::Sampler) -> Result> { let layouts = vec![*layout; count]; @@ -779,13 +770,26 @@ fn init_descriptor_sets(device: &Device, layout: &vk::DescriptorSetLayout, .range(size_of::>() as vk::DeviceSize); let buffer_info_list = [buffer_info]; - let descriptor_write_info = vk::WriteDescriptorSet::builder() + let uniform_block_write_info = vk::WriteDescriptorSet::builder() .dst_set(sets[index]) .dst_binding(0) .dst_array_element(0) .descriptor_type(vk::DescriptorType::UNIFORM_BUFFER) .buffer_info(&buffer_info_list); - let write_info_list = [descriptor_write_info]; + + let image_info = vk::DescriptorImageInfo::builder() + .image_layout(vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL) + .image_view(*texture_image_view) + .sampler(*sampler); + let image_info_list = [image_info]; + let sampler_write_info = vk::WriteDescriptorSet::builder() + .dst_set(sets[index]) + .dst_binding(1) + .dst_array_element(0) + .descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER) + .image_info(&image_info_list); + + let write_info_list = [uniform_block_write_info, sampler_write_info]; let copy_info_list: [vk::CopyDescriptorSet; 0] = []; unsafe { @@ -954,6 +958,38 @@ fn init_concurrency(device: &Device, } +#[allow(unsafe_code)] +fn init_image_view(device: &Device, image: &vk::Image, format: vk::Format) + -> Result +{ + 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 view_info = vk::ImageViewCreateInfo::builder() + .image(*image) + .view_type(vk::ImageViewType::_2D) + .format(format) + .components(components) + .subresource_range(subresource_range); + + let view = unsafe { + device.create_image_view(&view_info, None) + }?; + + Ok(view) +} + + fn pick_surface_format(available_formats: &Vec) -> Result { @@ -1144,8 +1180,7 @@ fn allocate_image(instance: &Instance, physical_device: &vk::PhysicalDevice, let requirements = unsafe { device.get_image_memory_requirements(image) }; let type_index = pick_memory_type(instance, physical_device, - &vk::MemoryPropertyFlags::DEVICE_LOCAL, - &requirements)?; + &memory_flags, &requirements)?; let image_memory_info = vk::MemoryAllocateInfo::builder() .allocation_size(requirements.size) diff --git a/src/linear_algebra.rs b/src/linear_algebra.rs index 0051fa8..08e9306 100644 --- a/src/linear_algebra.rs +++ b/src/linear_algebra.rs @@ -30,10 +30,14 @@ impl Real for f32 { pub static VERTICES: [Vertex; 4] = [ - Vertex::new(Vec2::new(-0.5, -0.5), Vec3::new(1.0, 0.0, 0.0)), - Vertex::new(Vec2::new( 0.5, -0.5), Vec3::new(0.0, 1.0, 0.0)), - Vertex::new(Vec2::new( 0.5, 0.5), Vec3::new(0.0, 0.0, 1.0)), - Vertex::new(Vec2::new(-0.5, 0.5), Vec3::new(1.0, 1.0, 1.0)), + Vertex::new(Vec2::new(-0.5, -0.5), Vec3::new(1.0, 0.0, 0.0), + Vec2::new(1.0, 0.0)), + Vertex::new(Vec2::new( 0.5, -0.5), Vec3::new(0.0, 1.0, 0.0), + Vec2::new(0.0, 0.0)), + Vertex::new(Vec2::new( 0.5, 0.5), Vec3::new(0.0, 0.0, 1.0), + Vec2::new(0.0, 1.0)), + Vertex::new(Vec2::new(-0.5, 0.5), Vec3::new(1.0, 1.0, 1.0), + Vec2::new(1.0, 1.0)), ]; pub const INDICES: &[u16] = &[0, 1, 2, 2, 3, 0]; @@ -621,6 +625,7 @@ impl Transformation { pub struct Vertex { pub position: Vec2, pub color: Vec3, + pub texture_coordinates: Vec2, } #[repr(C)] @@ -633,8 +638,11 @@ pub struct UniformBlock { } impl Vertex { - const fn new(position: Vec2, color: Vec3) -> Self { - Self { position, color } + const fn new(position: Vec2, color: Vec3, + texture_coordinates: Vec2) + -> Self + { + Self { position, color, texture_coordinates } } pub fn binding_description() -> vk::VertexInputBindingDescription { @@ -646,23 +654,30 @@ impl Vertex { } pub fn attribute_descriptions() - -> [vk::VertexInputAttributeDescription; 2] + -> [vk::VertexInputAttributeDescription; 3] { let position = vk::VertexInputAttributeDescription::builder() - .binding(0) - .location(0) - .format(vk::Format::R32G32_SFLOAT) - .offset(0) - .build(); + .binding(0) + .location(0) + .format(vk::Format::R32G32_SFLOAT) + .offset(0) + .build(); let color = vk::VertexInputAttributeDescription::builder() - .binding(0) - .location(1) - .format(vk::Format::R32G32B32_SFLOAT) - .offset(size_of::>() as u32) - .build(); - - [position, color] + .binding(0) + .location(1) + .format(vk::Format::R32G32B32_SFLOAT) + .offset(size_of::>() as u32) + .build(); + + let texture_coordinates = vk::VertexInputAttributeDescription::builder() + .binding(0) + .location(2) + .format(vk::Format::R32G32_SFLOAT) + .offset((size_of::>() + size_of::>()) as u32) + .build(); + + [position, color, texture_coordinates] } } diff --git a/textures/forest_leaves_04_diff.png b/textures/forest_leaves_04_diff.png index a87b2e6..e991076 100644 Binary files a/textures/forest_leaves_04_diff.png and b/textures/forest_leaves_04_diff.png differ -- cgit 1.4.1