Created
October 3, 2023 15:39
-
-
Save OniDaito/eba56584df119673fe7b6c02d1e91213 to your computer and use it in GitHub Desktop.
Vulkan Ash Render to Image Snippet
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Command Pool Functions | |
pub fn create_command_pool( | |
device: &ash::Device, | |
queue_families: &QueueFamilyIndices, | |
) -> vk::CommandPool { | |
let command_pool_create_info = vk::CommandPoolCreateInfo { | |
s_type: vk::StructureType::COMMAND_POOL_CREATE_INFO, | |
p_next: ptr::null(), | |
flags: vk::CommandPoolCreateFlags::empty(), | |
queue_family_index: queue_families.graphics_family.unwrap(), | |
}; | |
unsafe { | |
device | |
.create_command_pool(&command_pool_create_info, None) | |
.expect("Failed to create Command Pool!") | |
} | |
} | |
pub fn create_command_buffers( | |
device: &ash::Device, | |
command_pool: vk::CommandPool, | |
graphics_pipeline: vk::Pipeline, | |
framebuffer: vk::Framebuffer, | |
render_pass: vk::RenderPass, | |
vertex_buffer: vk::Buffer, | |
index_buffer: vk::Buffer, | |
pipeline_layout: vk::PipelineLayout, | |
descriptor_sets: &Vec<vk::DescriptorSet>, | |
indices_data_len: u32, | |
image_width: u32, | |
image_height: u32, | |
) -> Vec<vk::CommandBuffer> { | |
let command_buffer_allocate_info = vk::CommandBufferAllocateInfo { | |
s_type: vk::StructureType::COMMAND_BUFFER_ALLOCATE_INFO, | |
p_next: ptr::null(), | |
command_buffer_count: 1, | |
command_pool, | |
level: vk::CommandBufferLevel::PRIMARY, | |
}; | |
let command_buffers = unsafe { | |
device | |
.allocate_command_buffers(&command_buffer_allocate_info) | |
.expect("Failed to allocate Command Buffers!") | |
}; | |
for (i, &command_buffer) in command_buffers.iter().enumerate() { | |
let command_buffer_begin_info = vk::CommandBufferBeginInfo { | |
s_type: vk::StructureType::COMMAND_BUFFER_BEGIN_INFO, | |
p_next: ptr::null(), | |
p_inheritance_info: ptr::null(), | |
flags: vk::CommandBufferUsageFlags::SIMULTANEOUS_USE, | |
}; | |
unsafe { | |
device | |
.begin_command_buffer(command_buffer, &command_buffer_begin_info) | |
.expect("Failed to begin recording Command Buffer at beginning!"); | |
} | |
let clear_values = [vk::ClearValue { | |
color: vk::ClearColorValue { | |
float32: [0.0, 0.0, 0.0, 1.0], | |
}, | |
}]; | |
let render_pass_begin_info = vk::RenderPassBeginInfo { | |
s_type: vk::StructureType::RENDER_PASS_BEGIN_INFO, | |
p_next: ptr::null(), | |
render_pass, | |
framebuffer: framebuffer, | |
render_area: vk::Rect2D { | |
offset: vk::Offset2D { x: 0, y: 0 }, | |
extent: vk::Extent2D { | |
width: image_width, | |
height: image_height, | |
}, | |
}, | |
clear_value_count: clear_values.len() as u32, // replace eventually with 1 as we'll just have luminance | |
p_clear_values: clear_values.as_ptr(), | |
}; | |
unsafe { | |
device.cmd_begin_render_pass( | |
command_buffer, | |
&render_pass_begin_info, | |
vk::SubpassContents::INLINE, | |
); | |
device.cmd_bind_pipeline( | |
command_buffer, | |
vk::PipelineBindPoint::GRAPHICS, | |
graphics_pipeline, | |
); | |
let vertex_buffers = [vertex_buffer]; | |
let offsets = [0_u64]; | |
let descriptor_sets_to_bind = [descriptor_sets[i]]; | |
device.cmd_bind_vertex_buffers(command_buffer, 0, &vertex_buffers, &offsets); | |
device.cmd_bind_index_buffer(command_buffer, index_buffer, 0, vk::IndexType::UINT32); | |
device.cmd_bind_descriptor_sets( | |
command_buffer, | |
vk::PipelineBindPoint::GRAPHICS, | |
pipeline_layout, | |
0, | |
&descriptor_sets_to_bind, | |
&[], | |
); | |
device.cmd_draw_indexed(command_buffer, indices_data_len, 1, 0, 0, 0); | |
device.cmd_end_render_pass(command_buffer); | |
device | |
.end_command_buffer(command_buffer) | |
.expect("Failed to record Command Buffer at Ending!"); | |
} | |
} | |
command_buffers | |
} | |
pub fn create_copy_command( | |
device: &ash::Device, | |
command_pool: vk::CommandPool, | |
src_image: vk::Image, | |
dest_image: vk::Image, | |
image_width: u32, | |
image_height: u32, | |
) -> vk::CommandBuffer { | |
let command_buffer_allocate_info = vk::CommandBufferAllocateInfo { | |
s_type: vk::StructureType::COMMAND_BUFFER_ALLOCATE_INFO, | |
p_next: ptr::null(), | |
command_buffer_count: 1, | |
command_pool, | |
level: vk::CommandBufferLevel::PRIMARY, | |
}; | |
let command_buffer = unsafe { | |
device | |
.allocate_command_buffers(&command_buffer_allocate_info) | |
.expect("Failed to allocate Copy Command Buffer!") | |
}[0]; | |
let command_buffer_begin_info = vk::CommandBufferBeginInfo { | |
s_type: vk::StructureType::COMMAND_BUFFER_BEGIN_INFO, | |
p_next: ptr::null(), | |
p_inheritance_info: ptr::null(), | |
flags: vk::CommandBufferUsageFlags::SIMULTANEOUS_USE, | |
}; | |
unsafe { | |
device | |
.begin_command_buffer(command_buffer, &command_buffer_begin_info) | |
.expect("Failed to begin recording Command Buffer at beginning!"); | |
let ssr = vk::ImageSubresourceRange { | |
aspect_mask: vk::ImageAspectFlags::COLOR, | |
base_mip_level: 0, | |
level_count: 1, | |
base_array_layer: 0, | |
layer_count: 1, | |
}; | |
let img_barrier_first = vk::ImageMemoryBarrier { | |
s_type: vk::StructureType::IMAGE_MEMORY_BARRIER, | |
p_next: ptr::null(), | |
src_access_mask: vk::AccessFlags::empty(), | |
dst_access_mask: vk::AccessFlags::TRANSFER_WRITE, | |
old_layout: vk::ImageLayout::UNDEFINED, | |
new_layout: vk::ImageLayout::TRANSFER_DST_OPTIMAL, | |
src_queue_family_index: 0, | |
dst_queue_family_index: 0, | |
image: dest_image, | |
subresource_range: ssr, | |
}; | |
let src_stage_mask = vk::PipelineStageFlags::TRANSFER; | |
let dst_stage_mask = vk::PipelineStageFlags::TRANSFER; | |
// First barrier | |
device.cmd_pipeline_barrier( | |
command_buffer, | |
src_stage_mask, | |
dst_stage_mask, | |
vk::DependencyFlags::empty(), | |
&[], | |
&[], | |
&[img_barrier_first], | |
); | |
let image_copy_region = vk::ImageCopy { | |
src_subresource: vk::ImageSubresourceLayers { | |
aspect_mask: vk::ImageAspectFlags::COLOR, | |
mip_level: 0, | |
base_array_layer: 0, | |
layer_count: 1, | |
}, | |
src_offset: vk::Offset3D { x: 0, y: 0, z: 0 }, | |
dst_subresource: vk::ImageSubresourceLayers { | |
aspect_mask: vk::ImageAspectFlags::COLOR, | |
mip_level: 0, | |
base_array_layer: 0, | |
layer_count: 1, | |
}, | |
dst_offset: vk::Offset3D { x: 0, y: 0, z: 0 }, | |
extent: vk::Extent3D { | |
width: image_width, | |
height: image_height, | |
depth: 1, | |
}, | |
}; | |
device.cmd_copy_image( | |
command_buffer, | |
src_image, | |
vk::ImageLayout::TRANSFER_SRC_OPTIMAL, | |
dest_image, | |
vk::ImageLayout::TRANSFER_DST_OPTIMAL, | |
&[image_copy_region], | |
); | |
let img_barrier_second = vk::ImageMemoryBarrier { | |
s_type: vk::StructureType::IMAGE_MEMORY_BARRIER, | |
p_next: ptr::null(), | |
src_access_mask: vk::AccessFlags::TRANSFER_WRITE, | |
dst_access_mask: vk::AccessFlags::MEMORY_READ, | |
old_layout: vk::ImageLayout::TRANSFER_DST_OPTIMAL, | |
new_layout: vk::ImageLayout::GENERAL, | |
src_queue_family_index: 0, | |
dst_queue_family_index: 0, | |
image: dest_image, | |
subresource_range: ssr, | |
}; | |
// Insert another barrier | |
device.cmd_pipeline_barrier( | |
command_buffer, | |
src_stage_mask, | |
dst_stage_mask, | |
vk::DependencyFlags::empty(), | |
&[], | |
&[], | |
&[img_barrier_second], | |
); | |
device | |
.end_command_buffer(command_buffer) | |
.expect("Failed to record Command Buffer at Ending!"); | |
} | |
command_buffer | |
} | |
// Buffer Functions | |
pub fn create_vertex_buffer( | |
instance: &ash::Instance, | |
device: &ash::Device, | |
physical_device: vk::PhysicalDevice, | |
command_pool: vk::CommandPool, | |
submit_queue: vk::Queue, | |
vertices_data: &[VertexV2; 1025], | |
) -> (vk::Buffer, vk::DeviceMemory) { | |
let buffer_size = std::mem::size_of_val(vertices_data) as vk::DeviceSize; | |
let device_memory_properties = | |
unsafe { instance.get_physical_device_memory_properties(physical_device) }; | |
let (staging_buffer, staging_buffer_memory) = create_buffer( | |
device, | |
buffer_size, | |
vk::BufferUsageFlags::TRANSFER_SRC, | |
vk::MemoryPropertyFlags::HOST_VISIBLE | vk::MemoryPropertyFlags::HOST_COHERENT, | |
&device_memory_properties, | |
); | |
unsafe { | |
let data_ptr = device | |
.map_memory( | |
staging_buffer_memory, | |
0, | |
buffer_size, | |
vk::MemoryMapFlags::empty(), | |
) | |
.expect("Failed to Map Memory") as *mut VertexV2; | |
data_ptr.copy_from_nonoverlapping(vertices_data.as_ptr(), vertices_data.len()); | |
device.unmap_memory(staging_buffer_memory); | |
} | |
let (vertex_buffer, vertex_buffer_memory) = create_buffer( | |
device, | |
buffer_size, | |
vk::BufferUsageFlags::TRANSFER_DST | vk::BufferUsageFlags::VERTEX_BUFFER, | |
vk::MemoryPropertyFlags::DEVICE_LOCAL, | |
&device_memory_properties, | |
); | |
copy_buffer( | |
device, | |
submit_queue, | |
command_pool, | |
staging_buffer, | |
vertex_buffer, | |
buffer_size, | |
); | |
unsafe { | |
device.destroy_buffer(staging_buffer, None); | |
device.free_memory(staging_buffer_memory, None); | |
} | |
(vertex_buffer, vertex_buffer_memory) | |
} | |
pub fn create_index_buffer( | |
instance: &ash::Instance, | |
device: &ash::Device, | |
physical_device: vk::PhysicalDevice, | |
command_pool: vk::CommandPool, | |
submit_queue: vk::Queue, | |
indices_data: &[u32; 1025], | |
) -> (vk::Buffer, vk::DeviceMemory) { | |
let buffer_size = std::mem::size_of_val(indices_data) as vk::DeviceSize; | |
let device_memory_properties = | |
unsafe { instance.get_physical_device_memory_properties(physical_device) }; | |
let (staging_buffer, staging_buffer_memory) = create_buffer( | |
device, | |
buffer_size, | |
vk::BufferUsageFlags::TRANSFER_SRC, | |
vk::MemoryPropertyFlags::HOST_VISIBLE | vk::MemoryPropertyFlags::HOST_COHERENT, | |
&device_memory_properties, | |
); | |
unsafe { | |
let data_ptr = device | |
.map_memory( | |
staging_buffer_memory, | |
0, | |
buffer_size, | |
vk::MemoryMapFlags::empty(), | |
) | |
.expect("Failed to Map Memory") as *mut u32; | |
data_ptr.copy_from_nonoverlapping(indices_data.as_ptr(), indices_data.len()); | |
device.unmap_memory(staging_buffer_memory); | |
} | |
let (index_buffer, index_buffer_memory) = create_buffer( | |
device, | |
buffer_size, | |
vk::BufferUsageFlags::TRANSFER_DST | vk::BufferUsageFlags::INDEX_BUFFER, | |
vk::MemoryPropertyFlags::DEVICE_LOCAL, | |
&device_memory_properties, | |
); | |
copy_buffer( | |
device, | |
submit_queue, | |
command_pool, | |
staging_buffer, | |
index_buffer, | |
buffer_size, | |
); | |
unsafe { | |
device.destroy_buffer(staging_buffer, None); | |
device.free_memory(staging_buffer_memory, None); | |
} | |
(index_buffer, index_buffer_memory) | |
} | |
pub fn find_memory_type( | |
type_filter: u32, | |
required_properties: vk::MemoryPropertyFlags, | |
mem_properties: &vk::PhysicalDeviceMemoryProperties, | |
) -> u32 { | |
for (i, memory_type) in mem_properties.memory_types.iter().enumerate() { | |
//if (type_filter & (1 << i)) > 0 && (memory_type.property_flags & required_properties) == required_properties { | |
// return i as u32 | |
// } | |
// same implementation | |
if (type_filter & (1 << i)) > 0 | |
&& memory_type.property_flags.contains(required_properties) | |
{ | |
return i as u32; | |
} | |
} | |
panic!("Failed to find suitable memory type!") | |
} | |
pub fn create_buffer( | |
device: &ash::Device, | |
size: vk::DeviceSize, | |
usage: vk::BufferUsageFlags, | |
required_memory_properties: vk::MemoryPropertyFlags, | |
device_memory_properties: &vk::PhysicalDeviceMemoryProperties, | |
) -> (vk::Buffer, vk::DeviceMemory) { | |
let buffer_create_info = vk::BufferCreateInfo { | |
s_type: vk::StructureType::BUFFER_CREATE_INFO, | |
p_next: ptr::null(), | |
flags: vk::BufferCreateFlags::empty(), | |
size, | |
usage, | |
sharing_mode: vk::SharingMode::EXCLUSIVE, | |
queue_family_index_count: 0, | |
p_queue_family_indices: ptr::null(), | |
}; | |
let buffer = unsafe { | |
device | |
.create_buffer(&buffer_create_info, None) | |
.expect("Failed to create Vertex Buffer") | |
}; | |
let mem_requirements = unsafe { device.get_buffer_memory_requirements(buffer) }; | |
let memory_type = find_memory_type( | |
mem_requirements.memory_type_bits, | |
required_memory_properties, | |
device_memory_properties, | |
); | |
let allocate_info = vk::MemoryAllocateInfo { | |
s_type: vk::StructureType::MEMORY_ALLOCATE_INFO, | |
p_next: ptr::null(), | |
allocation_size: mem_requirements.size, | |
memory_type_index: memory_type, | |
}; | |
let buffer_memory = unsafe { | |
device | |
.allocate_memory(&allocate_info, None) | |
.expect("Failed to allocate vertex buffer memory!") | |
}; | |
unsafe { | |
device | |
.bind_buffer_memory(buffer, buffer_memory, 0) | |
.expect("Failed to bind Buffer"); | |
} | |
(buffer, buffer_memory) | |
} | |
fn copy_buffer( | |
device: &ash::Device, | |
submit_queue: vk::Queue, | |
command_pool: vk::CommandPool, | |
src_buffer: vk::Buffer, | |
dst_buffer: vk::Buffer, | |
size: vk::DeviceSize, | |
) { | |
let allocate_info = vk::CommandBufferAllocateInfo { | |
s_type: vk::StructureType::COMMAND_BUFFER_ALLOCATE_INFO, | |
p_next: ptr::null(), | |
command_buffer_count: 1, | |
command_pool, | |
level: vk::CommandBufferLevel::PRIMARY, | |
}; | |
let command_buffers = unsafe { | |
device | |
.allocate_command_buffers(&allocate_info) | |
.expect("Failed to allocate Command Buffer") | |
}; | |
let command_buffer = command_buffers[0]; | |
let begin_info = vk::CommandBufferBeginInfo { | |
s_type: vk::StructureType::COMMAND_BUFFER_BEGIN_INFO, | |
p_next: ptr::null(), | |
flags: vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT, | |
p_inheritance_info: ptr::null(), | |
}; | |
unsafe { | |
device | |
.begin_command_buffer(command_buffer, &begin_info) | |
.expect("Failed to begin Command Buffer"); | |
let copy_regions = [vk::BufferCopy { | |
src_offset: 0, | |
dst_offset: 0, | |
size, | |
}]; | |
device.cmd_copy_buffer(command_buffer, src_buffer, dst_buffer, ©_regions); | |
device | |
.end_command_buffer(command_buffer) | |
.expect("Failed to end Command Buffer"); | |
} | |
let submit_info = [vk::SubmitInfo { | |
s_type: vk::StructureType::SUBMIT_INFO, | |
p_next: ptr::null(), | |
wait_semaphore_count: 0, | |
p_wait_semaphores: ptr::null(), | |
p_wait_dst_stage_mask: ptr::null(), | |
command_buffer_count: 1, | |
p_command_buffers: &command_buffer, | |
signal_semaphore_count: 0, | |
p_signal_semaphores: ptr::null(), | |
}]; | |
unsafe { | |
device | |
.queue_submit(submit_queue, &submit_info, vk::Fence::null()) | |
.expect("Failed to Submit Queue."); | |
device | |
.queue_wait_idle(submit_queue) | |
.expect("Failed to wait Queue idle"); | |
device.free_command_buffers(command_pool, &command_buffers); | |
} | |
} | |
pub fn create_uniform_buffers( | |
device: &ash::Device, | |
device_memory_properties: &vk::PhysicalDeviceMemoryProperties, | |
) -> (Vec<vk::Buffer>, Vec<vk::DeviceMemory>) { | |
let buffer_size = std::mem::size_of::<ubo::UniformBufferObject>(); | |
let mut uniform_buffers = vec![]; | |
let mut uniform_buffers_memory = vec![]; | |
let (uniform_buffer, uniform_buffer_memory) = create_buffer( | |
device, | |
buffer_size as u64, | |
vk::BufferUsageFlags::UNIFORM_BUFFER, | |
vk::MemoryPropertyFlags::HOST_VISIBLE | vk::MemoryPropertyFlags::HOST_COHERENT, | |
device_memory_properties, | |
); | |
uniform_buffers.push(uniform_buffer); | |
uniform_buffers_memory.push(uniform_buffer_memory); | |
(uniform_buffers, uniform_buffers_memory) | |
} | |
// Pipeline function | |
pub fn create_graphics_pipeline( | |
device: &ash::Device, | |
render_pass: vk::RenderPass, | |
ubo_set_layout: vk::DescriptorSetLayout, | |
vert_shader_code: &Vec<u8>, | |
frag_shader_code: &Vec<u8>, | |
image_width: u32, | |
image_height: u32 | |
) -> (vk::Pipeline, vk::PipelineLayout) { | |
let vert_shader_module = shader::create_shader_module(device, vert_shader_code.to_vec()); | |
let frag_shader_module = shader::create_shader_module(device, frag_shader_code.to_vec()); | |
let main_function_name = CString::new("main").unwrap(); // the beginning function name in shader code. | |
let shader_stages = [ | |
vk::PipelineShaderStageCreateInfo { | |
// Vertex Shader | |
s_type: vk::StructureType::PIPELINE_SHADER_STAGE_CREATE_INFO, | |
p_next: ptr::null(), | |
flags: vk::PipelineShaderStageCreateFlags::empty(), | |
module: vert_shader_module, | |
p_name: main_function_name.as_ptr(), | |
p_specialization_info: ptr::null(), | |
stage: vk::ShaderStageFlags::VERTEX, | |
}, | |
vk::PipelineShaderStageCreateInfo { | |
// Fragment Shader | |
s_type: vk::StructureType::PIPELINE_SHADER_STAGE_CREATE_INFO, | |
p_next: ptr::null(), | |
flags: vk::PipelineShaderStageCreateFlags::empty(), | |
module: frag_shader_module, | |
p_name: main_function_name.as_ptr(), | |
p_specialization_info: ptr::null(), | |
stage: vk::ShaderStageFlags::FRAGMENT, | |
}, | |
]; | |
let binding_description = VertexV2::get_binding_descriptions(); | |
let attribute_description = VertexV2::get_attribute_descriptions(); | |
let vertex_input_state_create_info = vk::PipelineVertexInputStateCreateInfo { | |
s_type: vk::StructureType::PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, | |
p_next: ptr::null(), | |
flags: vk::PipelineVertexInputStateCreateFlags::empty(), | |
vertex_attribute_description_count: attribute_description.len() as u32, | |
p_vertex_attribute_descriptions: attribute_description.as_ptr(), | |
vertex_binding_description_count: binding_description.len() as u32, | |
p_vertex_binding_descriptions: binding_description.as_ptr(), | |
}; | |
let vertex_input_assembly_state_info = vk::PipelineInputAssemblyStateCreateInfo { | |
s_type: vk::StructureType::PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, | |
flags: vk::PipelineInputAssemblyStateCreateFlags::empty(), | |
p_next: ptr::null(), | |
primitive_restart_enable: vk::FALSE, | |
topology: vk::PrimitiveTopology::TRIANGLE_FAN, | |
}; | |
let viewports = [vk::Viewport { | |
x: 0.0, | |
y: 0.0, | |
width: image_width as f32, | |
height: image_height as f32, | |
min_depth: 0.0, | |
max_depth: 1.0, | |
}]; | |
let scissors = [vk::Rect2D { | |
offset: vk::Offset2D { x: 0, y: 0 }, | |
extent: vk::Extent2D {width: image_width, height: image_height}, | |
}]; | |
let viewport_state_create_info = vk::PipelineViewportStateCreateInfo { | |
s_type: vk::StructureType::PIPELINE_VIEWPORT_STATE_CREATE_INFO, | |
p_next: ptr::null(), | |
flags: vk::PipelineViewportStateCreateFlags::empty(), | |
scissor_count: scissors.len() as u32, | |
p_scissors: scissors.as_ptr(), | |
viewport_count: viewports.len() as u32, | |
p_viewports: viewports.as_ptr(), | |
}; | |
let rasterization_statue_create_info = vk::PipelineRasterizationStateCreateInfo { | |
s_type: vk::StructureType::PIPELINE_RASTERIZATION_STATE_CREATE_INFO, | |
p_next: ptr::null(), | |
flags: vk::PipelineRasterizationStateCreateFlags::empty(), | |
depth_clamp_enable: vk::FALSE, | |
cull_mode: vk::CullModeFlags::NONE, | |
front_face: vk::FrontFace::CLOCKWISE, | |
line_width: 1.0, | |
polygon_mode: vk::PolygonMode::FILL, | |
rasterizer_discard_enable: vk::FALSE, | |
depth_bias_clamp: 0.0, | |
depth_bias_constant_factor: 0.0, | |
depth_bias_enable: vk::FALSE, | |
depth_bias_slope_factor: 0.0, | |
}; | |
let multisample_state_create_info = vk::PipelineMultisampleStateCreateInfo { | |
s_type: vk::StructureType::PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, | |
flags: vk::PipelineMultisampleStateCreateFlags::empty(), | |
p_next: ptr::null(), | |
rasterization_samples: vk::SampleCountFlags::TYPE_1, | |
sample_shading_enable: vk::FALSE, | |
min_sample_shading: 0.0, | |
p_sample_mask: ptr::null(), | |
alpha_to_one_enable: vk::FALSE, | |
alpha_to_coverage_enable: vk::FALSE, | |
}; | |
let stencil_state = vk::StencilOpState { | |
fail_op: vk::StencilOp::KEEP, | |
pass_op: vk::StencilOp::KEEP, | |
depth_fail_op: vk::StencilOp::KEEP, | |
compare_op: vk::CompareOp::ALWAYS, | |
compare_mask: 0, | |
write_mask: 0, | |
reference: 0, | |
}; | |
let depth_state_create_info = vk::PipelineDepthStencilStateCreateInfo { | |
s_type: vk::StructureType::PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, | |
p_next: ptr::null(), | |
flags: vk::PipelineDepthStencilStateCreateFlags::empty(), | |
depth_test_enable: vk::FALSE, | |
depth_write_enable: vk::FALSE, | |
depth_compare_op: vk::CompareOp::LESS_OR_EQUAL, | |
depth_bounds_test_enable: vk::FALSE, | |
stencil_test_enable: vk::FALSE, | |
front: stencil_state, | |
back: stencil_state, | |
max_depth_bounds: 1.0, | |
min_depth_bounds: 0.0, | |
}; | |
let color_blend_attachment_states = [vk::PipelineColorBlendAttachmentState { | |
blend_enable: vk::FALSE, | |
color_write_mask: vk::ColorComponentFlags::RGBA, | |
src_color_blend_factor: vk::BlendFactor::ONE, | |
dst_color_blend_factor: vk::BlendFactor::ZERO, | |
color_blend_op: vk::BlendOp::ADD, | |
src_alpha_blend_factor: vk::BlendFactor::ONE, | |
dst_alpha_blend_factor: vk::BlendFactor::ZERO, | |
alpha_blend_op: vk::BlendOp::ADD, | |
}]; | |
let color_blend_state = vk::PipelineColorBlendStateCreateInfo { | |
s_type: vk::StructureType::PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, | |
p_next: ptr::null(), | |
flags: vk::PipelineColorBlendStateCreateFlags::empty(), | |
logic_op_enable: vk::FALSE, | |
logic_op: vk::LogicOp::COPY, | |
attachment_count: color_blend_attachment_states.len() as u32, | |
p_attachments: color_blend_attachment_states.as_ptr(), | |
blend_constants: [0.0, 0.0, 0.0, 0.0], | |
}; | |
let set_layouts = [ubo_set_layout]; | |
let pipeline_layout_create_info = vk::PipelineLayoutCreateInfo { | |
s_type: vk::StructureType::PIPELINE_LAYOUT_CREATE_INFO, | |
p_next: ptr::null(), | |
flags: vk::PipelineLayoutCreateFlags::empty(), | |
set_layout_count: set_layouts.len() as u32, | |
p_set_layouts: set_layouts.as_ptr(), | |
push_constant_range_count: 0, | |
p_push_constant_ranges: ptr::null(), | |
}; | |
let pipeline_layout = unsafe { | |
device | |
.create_pipeline_layout(&pipeline_layout_create_info, None) | |
.expect("Failed to create pipeline layout!") | |
}; | |
let graphic_pipeline_create_infos = [vk::GraphicsPipelineCreateInfo { | |
s_type: vk::StructureType::GRAPHICS_PIPELINE_CREATE_INFO, | |
p_next: ptr::null(), | |
flags: vk::PipelineCreateFlags::empty(), | |
stage_count: shader_stages.len() as u32, | |
p_stages: shader_stages.as_ptr(), | |
p_vertex_input_state: &vertex_input_state_create_info, | |
p_input_assembly_state: &vertex_input_assembly_state_info, | |
p_tessellation_state: ptr::null(), | |
p_viewport_state: &viewport_state_create_info, | |
p_rasterization_state: &rasterization_statue_create_info, | |
p_multisample_state: &multisample_state_create_info, | |
p_depth_stencil_state: &depth_state_create_info, | |
p_color_blend_state: &color_blend_state, | |
p_dynamic_state: ptr::null(), | |
layout: pipeline_layout, | |
render_pass, | |
subpass: 0, | |
base_pipeline_handle: vk::Pipeline::null(), | |
base_pipeline_index: -1, | |
}]; | |
let graphics_pipelines = unsafe { | |
device | |
.create_graphics_pipelines( | |
vk::PipelineCache::null(), | |
&graphic_pipeline_create_infos, | |
None, | |
) | |
.expect("Failed to create Graphics Pipeline!.") | |
}; | |
unsafe { | |
device.destroy_shader_module(vert_shader_module, None); | |
device.destroy_shader_module(frag_shader_module, None); | |
} | |
(graphics_pipelines[0], pipeline_layout) | |
} | |
// Renderpass | |
pub fn create_framebuffer(device: &ash::Device, render_pass: vk::RenderPass, views:[vk::ImageView; 1], image_width: u32, image_height: u32) -> vk::Framebuffer { | |
let framebuffer_create_info: vk::FramebufferCreateInfo = vk::FramebufferCreateInfo { | |
attachment_count: 1, | |
p_attachments: views.as_ptr(), | |
width: image_width, | |
height: image_height, | |
layers: 1, | |
s_type: vk::StructureType::FRAMEBUFFER_CREATE_INFO, | |
p_next: ptr::null(), | |
flags: vk::FramebufferCreateFlags::empty(), //vk::FramebufferCreateFlags::IMAGELESS, // This seems to have fixed the crash at least! | |
render_pass: render_pass | |
}; | |
unsafe { | |
device | |
.create_framebuffer(&framebuffer_create_info, None) | |
.expect("Failed to create framebuffer!") | |
} | |
} | |
pub fn create_render_pass(device: &ash::Device) -> vk::RenderPass { | |
let colour_attachment = vk::AttachmentDescription { | |
flags: vk::AttachmentDescriptionFlags::empty(), | |
format: vk::Format::R8G8B8A8_UNORM, // TODO - change to Luminance only eventually | |
samples: vk::SampleCountFlags::TYPE_1, | |
load_op: vk::AttachmentLoadOp::CLEAR, | |
store_op: vk::AttachmentStoreOp::STORE, | |
stencil_load_op: vk::AttachmentLoadOp::DONT_CARE, | |
stencil_store_op: vk::AttachmentStoreOp::DONT_CARE, | |
initial_layout: vk::ImageLayout::UNDEFINED, | |
final_layout: vk::ImageLayout::PRESENT_SRC_KHR, | |
}; | |
let colour_attachment_ref = vk::AttachmentReference { | |
attachment: 0, | |
layout: vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL, | |
}; | |
let subpass = vk::SubpassDescription { | |
flags: vk::SubpassDescriptionFlags::empty(), | |
pipeline_bind_point: vk::PipelineBindPoint::GRAPHICS, | |
input_attachment_count: 0, | |
p_input_attachments: ptr::null(), | |
color_attachment_count: 1, | |
p_color_attachments: &colour_attachment_ref, | |
p_resolve_attachments: ptr::null(), | |
p_depth_stencil_attachment: ptr::null(), | |
preserve_attachment_count: 0, | |
p_preserve_attachments: ptr::null(), | |
}; | |
let render_pass_attachments = [colour_attachment]; | |
let renderpass_create_info = vk::RenderPassCreateInfo { | |
s_type: vk::StructureType::RENDER_PASS_CREATE_INFO, | |
flags: vk::RenderPassCreateFlags::empty(), | |
p_next: ptr::null(), | |
attachment_count: render_pass_attachments.len() as u32, | |
p_attachments: render_pass_attachments.as_ptr(), | |
subpass_count: 1, | |
p_subpasses: &subpass, | |
dependency_count: 0, | |
p_dependencies: ptr::null(), | |
}; | |
unsafe { | |
device | |
.create_render_pass(&renderpass_create_info, None) | |
.expect("Failed to create render pass!") | |
} | |
// Main Vulkan Application Functions | |
pub struct VulkanApp { | |
_entry: ash::Entry, | |
instance: ash::Instance, | |
debug_utils_loader: ash::extensions::ext::DebugUtils, | |
debug_messenger: vk::DebugUtilsMessengerEXT, | |
physical_device: vk::PhysicalDevice, | |
device: ash::Device, | |
image_width: u32, | |
image_height: u32, | |
queue_family: QueueFamilyIndices, | |
graphics_queue: vk::Queue, | |
ubo_layout: vk::DescriptorSetLayout, | |
pipeline_layout: vk::PipelineLayout, | |
render_pass: vk::RenderPass, | |
graphics_pipeline: vk::Pipeline, | |
framebuffer: vk::Framebuffer, | |
framebuffer_image: vk::Image, | |
framebuffer_memory: vk::DeviceMemory, | |
framebuffer_views: [vk::ImageView; 1], | |
texture_image: vk::Image, | |
texture_image_view: vk::ImageView, | |
texture_sampler: vk::Sampler, | |
texture_image_memory: vk::DeviceMemory, | |
dest_image: vk::Image, | |
dest_image_view: vk::ImageView, | |
dest_image_memory: vk::DeviceMemory, | |
vertex_buffer: vk::Buffer, | |
vertex_buffer_memory: vk::DeviceMemory, | |
index_buffer: vk::Buffer, | |
index_buffer_memory: vk::DeviceMemory, | |
uniform_transform: ubo::UniformBufferObject, | |
uniform_buffers: Vec<vk::Buffer>, | |
uniform_buffers_memory: Vec<vk::DeviceMemory>, | |
descriptor_pool: vk::DescriptorPool, | |
descriptor_sets: Vec<vk::DescriptorSet>, | |
command_pool: vk::CommandPool, | |
command_buffers: Vec<vk::CommandBuffer>, | |
copy_command: vk::CommandBuffer, | |
vert_shader_code: Vec<u8>, | |
frag_shader_code: Vec<u8>, | |
} | |
impl VulkanApp { | |
pub fn new() -> VulkanApp { | |
let image_width = 692; | |
let image_height = 400; | |
let entry = unsafe { ash::Entry::load().unwrap() }; | |
let instance = VulkanApp::create_instance(&entry); | |
let (debug_utils_loader, debug_messenger) = setup_debug_utils(true, &entry, &instance); | |
let physical_device = pick_physical_device(&instance); | |
let physical_device_memory_properties = | |
unsafe { instance.get_physical_device_memory_properties(physical_device) }; | |
let (device, queue_family) = create_logical_device(&instance, physical_device, &VALIDATION); | |
let graphics_queue = | |
unsafe { device.get_device_queue(queue_family.graphics_family.unwrap(), 0) }; | |
let vert_spv_path = Path::new("shaders/25-shader-textures.vert.spv"); | |
let frag_spv_path = Path::new("shaders/25-shader-textures.frag.spv"); | |
let vert_shader_code = shader::read_shader_code(vert_spv_path); | |
let frag_shader_code = shader::read_shader_code(frag_spv_path); | |
let (framebuffer_image, framebuffer_memory) = texture::create_image( | |
&device, | |
image_width, | |
image_height, | |
vk::Format::R8G8B8A8_UNORM, | |
vk::ImageTiling::OPTIMAL, | |
vk::ImageUsageFlags::COLOR_ATTACHMENT | vk::ImageUsageFlags::TRANSFER_SRC, | |
vk::MemoryPropertyFlags::DEVICE_LOCAL, | |
&physical_device_memory_properties, | |
); | |
let fv: vk::ImageView = | |
texture::create_image_view(&device, framebuffer_image, vk::Format::R8G8B8A8_UNORM); | |
let framebuffer_views: [vk::ImageView; 1] = [fv]; | |
let render_pass = renderpass::create_render_pass(&device); | |
let framebuffer: vk::Framebuffer = renderpass::create_framebuffer( | |
&device, | |
render_pass, | |
framebuffer_views, | |
image_width, | |
image_height, | |
); | |
let ubo_layout = ubo::create_descriptor_set_layout(&device); | |
let (graphics_pipeline, pipeline_layout) = pipeline::create_graphics_pipeline( | |
&device, | |
render_pass, | |
ubo_layout, | |
&vert_shader_code, | |
&frag_shader_code, | |
image_width, | |
image_height, | |
); | |
let command_pool = command::create_command_pool(&device, &queue_family); | |
let (texture_image, texture_image_memory) = texture::create_texture_image( | |
&device, | |
command_pool, | |
graphics_queue, | |
&physical_device_memory_properties, | |
&Path::new(TEXTURE_PATH), | |
); | |
let texture_image_view = texture::create_texture_image_view(&device, texture_image); | |
let texture_sampler = texture::create_texture_sampler(&device); | |
let (vertex_buffer, vertex_buffer_memory) = buffers::create_vertex_buffer( | |
&instance, | |
&device, | |
physical_device, | |
command_pool, | |
graphics_queue, | |
&vertex::RECT_TEX_COORD_VERTICES_DATA, | |
); | |
// Build our destination image buffer we shall copy the framebuffer to. | |
let (dest_image, dest_image_memory) = texture::create_image( | |
&device, | |
image_width, | |
image_height, | |
vk::Format::R8G8B8A8_UNORM, | |
vk::ImageTiling::LINEAR, | |
vk::ImageUsageFlags::TRANSFER_DST, | |
vk::MemoryPropertyFlags::HOST_VISIBLE | vk::MemoryPropertyFlags::HOST_COHERENT, | |
&physical_device_memory_properties, | |
); | |
let dest_image_view = texture::create_texture_image_view(&device, dest_image); | |
let (index_buffer, index_buffer_memory) = buffers::create_index_buffer( | |
&instance, | |
&device, | |
physical_device, | |
command_pool, | |
graphics_queue, | |
&vertex::INDICES_DATA, | |
); | |
let (uniform_buffers, uniform_buffers_memory) = | |
buffers::create_uniform_buffers(&device, &physical_device_memory_properties); | |
let descriptor_pool: vk::DescriptorPool = descriptors::create_descriptor_pool(&device); | |
let descriptor_sets = descriptors::create_descriptor_sets( | |
&device, | |
descriptor_pool, | |
ubo_layout, | |
&uniform_buffers, | |
texture_image_view, | |
texture_sampler, | |
); | |
let command_buffers = command::create_command_buffers( | |
&device, | |
command_pool, | |
graphics_pipeline, | |
framebuffer, | |
render_pass, | |
vertex_buffer, | |
index_buffer, | |
pipeline_layout, | |
&descriptor_sets, | |
vertex::INDICES_DATA.len() as u32, | |
image_width, | |
image_height, | |
); | |
// TODO - example has memory barriers that Ive not seen elsewhere yet. | |
let copy_command = create_copy_command( | |
&device, | |
command_pool, | |
framebuffer_image, | |
dest_image, | |
image_width, | |
image_height, | |
); | |
// cleanup(); the 'drop' function will take care of it. | |
VulkanApp { | |
_entry: entry, | |
instance, | |
debug_utils_loader, | |
debug_messenger, | |
physical_device: physical_device, | |
device, | |
image_width, | |
image_height, | |
queue_family: queue_family, | |
graphics_queue: graphics_queue, | |
ubo_layout, | |
pipeline_layout, | |
render_pass, | |
graphics_pipeline, | |
framebuffer, | |
framebuffer_image, | |
framebuffer_memory, | |
framebuffer_views, | |
texture_image, | |
texture_image_view, | |
texture_sampler, | |
texture_image_memory, | |
dest_image, | |
dest_image_view, | |
dest_image_memory, | |
vertex_buffer, | |
vertex_buffer_memory, | |
index_buffer, | |
index_buffer_memory, | |
uniform_transform: ubo::UniformBufferObject { | |
//model: Matrix4::<f32>::identity(), | |
model: Matrix4::from_translation(Vector3 { | |
x: 0.0, | |
y: 0.5, | |
z: 0.0, | |
}) * Matrix4::from_angle_z(Deg(180.0)), | |
view: Matrix4::look_at( | |
Point3::new(0.0, 0.0, 1.0), | |
Point3::new(0.0, 0.0, 0.0), | |
Vector3::new(0.0, 1.0, 0.0), | |
), | |
proj: cgmath::perspective( | |
Deg(45.0), | |
image_width as f32 / image_height as f32, | |
0.1, | |
10.0, | |
), | |
}, | |
uniform_buffers, | |
uniform_buffers_memory, | |
descriptor_pool, | |
descriptor_sets, | |
command_pool, | |
command_buffers, | |
copy_command, | |
vert_shader_code: vert_shader_code, | |
frag_shader_code: frag_shader_code, | |
} | |
} | |
fn create_instance(entry: &ash::Entry) -> ash::Instance { | |
if VALIDATION.is_enable | |
&& check_validation_layer_support( | |
entry, | |
&VALIDATION.required_validation_layers.to_vec(), | |
) == false | |
{ | |
panic!("Validation layers requested, but not available!"); | |
} | |
let app_name = CString::new("fan render").unwrap(); | |
let engine_name = CString::new("Vulkan Engine").unwrap(); | |
let app_info = vk::ApplicationInfo { | |
s_type: vk::StructureType::APPLICATION_INFO, | |
p_next: ptr::null(), | |
p_application_name: app_name.as_ptr(), | |
application_version: APPLICATION_VERSION, | |
p_engine_name: engine_name.as_ptr(), | |
engine_version: ENGINE_VERSION, | |
api_version: API_VERSION, | |
}; | |
let extension_names = platforms::required_extension_names(); | |
let create_info = vk::InstanceCreateInfo { | |
s_type: vk::StructureType::INSTANCE_CREATE_INFO, | |
p_next: ptr::null(), | |
flags: vk::InstanceCreateFlags::empty(), | |
p_application_info: &app_info, | |
pp_enabled_layer_names: ptr::null(), | |
enabled_layer_count: 0, | |
pp_enabled_extension_names: extension_names.as_ptr(), | |
enabled_extension_count: extension_names.len() as u32, | |
}; | |
let instance: ash::Instance = unsafe { | |
entry | |
.create_instance(&create_info, None) | |
.expect("Failed to create instance!") | |
}; | |
instance | |
} | |
pub fn go(&self) { | |
unsafe{ | |
// Make sure the ubo is set | |
let buffer_size = (std::mem::size_of::<ubo::UniformBufferObject>() ) as u64; | |
let data_ptr = | |
self.device | |
.map_memory( | |
self.uniform_buffers_memory[0], | |
0, | |
buffer_size, | |
vk::MemoryMapFlags::empty(), | |
) | |
.expect("Failed to Map Memory") as *mut ubo::UniformBufferObject; | |
data_ptr.copy_from_nonoverlapping(&self.uniform_transform, self.uniform_buffers.len()); | |
self.device | |
.unmap_memory(self.uniform_buffers_memory[0]); | |
let submit_infos = [vk::SubmitInfo { | |
s_type: vk::StructureType::SUBMIT_INFO, | |
p_next: ptr::null(), | |
wait_semaphore_count: 0, | |
p_wait_semaphores: null(), | |
p_wait_dst_stage_mask: null(), | |
command_buffer_count: 1, | |
p_command_buffers: &self.command_buffers[0], | |
signal_semaphore_count: 0, | |
p_signal_semaphores: null(), | |
}]; | |
let fence_info = vk::FenceCreateInfo{ | |
s_type: vk::StructureType::FENCE_CREATE_INFO, | |
p_next: null(), | |
flags: vk::FenceCreateFlags::empty(), | |
}; | |
let fence_draw = self.device.create_fence(&fence_info, None ).expect("Failed to create fence"); | |
self.device | |
.queue_submit( | |
self.graphics_queue, | |
&submit_infos, | |
fence_draw, | |
) | |
.expect("Failed to execute queue submit."); | |
self.device.wait_for_fences(&[fence_draw], true, u64::MAX).expect("Failed to wait for fence_draw"); | |
self.device.destroy_fence(fence_draw, None); | |
let fence_copy = self.device.create_fence(&fence_info, None ).expect("Failed to create fence"); | |
let submit_infos2 = [vk::SubmitInfo { | |
s_type: vk::StructureType::SUBMIT_INFO, | |
p_next: ptr::null(), | |
wait_semaphore_count: 0, | |
p_wait_semaphores: null(), | |
p_wait_dst_stage_mask: null(), | |
command_buffer_count: 1, | |
p_command_buffers: &self.copy_command, | |
signal_semaphore_count: 0, | |
p_signal_semaphores: null(), | |
}]; | |
self.device | |
.queue_submit( | |
self.graphics_queue, | |
&submit_infos2, | |
fence_copy, | |
) | |
.expect("Failed to execute queue submit."); | |
self.device.wait_for_fences(&[fence_copy], true, u64::MAX).expect("Failed to wait for fence_copy"); | |
self.device.destroy_fence(fence_draw, None); | |
self.device.device_wait_idle().expect("Wait Idle fail."); | |
// Now copy out the data to an image | |
let sub_resource = vk::ImageSubresource { | |
aspect_mask: vk::ImageAspectFlags::COLOR, | |
mip_level: 0, | |
array_layer: 0, | |
}; | |
let sub_resource_layout = self.device.get_image_subresource_layout(self.dest_image, sub_resource); | |
let mut image_data = self.device.map_memory(self.dest_image_memory, 0, vk::WHOLE_SIZE, vk::MemoryMapFlags::empty()).unwrap(); | |
// TODO - do we need this offset? - doesn't seem to affect things | |
image_data = image_data.add(sub_resource_layout.offset as usize); | |
let mut file = File::create("test.ppm").unwrap(); | |
let ppm_header = format!("P6\n{} {}\n255\n",self.image_width, self.image_height); | |
file.write_all(ppm_header.as_bytes()).unwrap(); | |
let pitch = 4; | |
let row_size = self.image_width * pitch; // TODO - change when we aren't doing RGBA | |
for y in 0..self.image_height { | |
for x in 0..self.image_width{ | |
let r = *(image_data.wrapping_offset ((y * row_size + (x * pitch) + 0) as isize) as *const c_uchar); | |
let g = *(image_data.wrapping_offset ((y * row_size + (x * pitch) + 1) as isize) as *const c_uchar); | |
let b = *(image_data.wrapping_offset ((y * row_size + (x * pitch) + 2) as isize) as *const c_uchar); | |
//let a = *(image_data.wrapping_offset ((y * row_size + (x * pitch) + 3) as isize) as *const c_uchar); | |
//println!("offset {:?}", image_data.wrapping_offset ((y * row_size + (x * pitch)) as isize)); | |
//println!("Alpha {:?}", a ); | |
file.write(&[r]).unwrap(); | |
file.write(&[g]).unwrap(); | |
file.write(&[b]).unwrap(); | |
} | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment