Skip to content

Instantly share code, notes, and snippets.

@OniDaito
Created October 3, 2023 15:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save OniDaito/eba56584df119673fe7b6c02d1e91213 to your computer and use it in GitHub Desktop.
Save OniDaito/eba56584df119673fe7b6c02d1e91213 to your computer and use it in GitHub Desktop.
Vulkan Ash Render to Image Snippet
// 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, &copy_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