use memoffset::offset_of;
use std::cell::{Cell, RefCell};
use std::ffi::CStr;
use std::mem;
use std::os::raw::c_uchar;
use std::slice;
use crate::ash::prelude::VkResult;
use crate::imgui::{FrameSize, ImDrawIdx, ImDrawVert, ImGui, Ui};
use crate::vk_helper::*;
use crate::vk_mem::{AllocationCreateFlags, AllocationCreateInfo};
use glsl_to_spirv_macros::{glsl_fs, glsl_vs};
use glsl_to_spirv_macros_impl::GLSLEmbedImpl;
pub struct Renderer {
pipeline_layout: PipelineLayout,
vertex_shader: ShaderModule,
fragment_shader: ShaderModule,
render_pass: RefCell<Option<RenderPass>>,
pipeline: RefCell<Option<crate::ash::vk::Pipeline>>,
old_pipeline: RefCell<Option<crate::ash::vk::Pipeline>>,
vertex_buffers: [Buffer; Renderer::FRAME_COUNT],
index_buffers: [Buffer; Renderer::FRAME_COUNT],
image_buffer: RefCell<Buffer>,
image_width: u32,
image_height: u32,
image: RefCell<Image>,
image_view: ImageView,
sampler: Sampler,
descriptor_set: DescriptorSet,
frame_index: RefCell<usize>,
image_is_valid: Cell<bool>,
}
impl Renderer {
const QUAD_COUNT_PER_FRAME: usize = 64 * 1024;
const VERTEX_COUNT_PER_FRAME: usize = 4 * Renderer::QUAD_COUNT_PER_FRAME;
const INDEX_COUNT_PER_FRAME: usize = 6 * Renderer::QUAD_COUNT_PER_FRAME;
const PUSH_CONSTANT_SIZE: usize = 8;
const FRAME_COUNT: usize = 2;
pub fn new_simple(
instance: &Instance,
physical_device: crate::ash::vk::PhysicalDevice,
device: &Device,
imgui: &mut ImGui,
) -> VkResult<Self> {
let allocator = Allocator::new(&instance, physical_device, &device).map_err(|e| {
if let vk_mem::ErrorKind::Vulkan(e) = e.kind() {
crate::ash::vk::Result::from_raw(e.as_raw())
} else {
crate::ash::vk::Result::from_raw(-1)
}
})?;
Self::new(device, &allocator, imgui)
}
pub fn new(device: &Device, allocator: &Allocator, imgui: &mut ImGui) -> VkResult<Self> {
use arrayvec::ArrayVec;
let vertex_shader = ShaderModule::new_simple(
&device,
glsl_vs! { r#"#version 430 core
layout(location = 0) in vec2 a_pos;
layout(location = 1) in vec2 a_uv;
layout(location = 2) in vec4 a_col;
layout(push_constant) uniform push_t {
vec2 g_dims_rcp;
};
out gl_PerVertex {
vec4 gl_Position;
};
layout(location = 0) out vec2 v_uv;
layout(location = 1) out vec4 v_col;
void main() {
gl_Position = vec4(a_pos*g_dims_rcp*2.0 - 1.0, 0, 1);
v_uv = a_uv;
v_col = a_col;
}"# },
)?;
let fragment_shader = ShaderModule::new_simple(
&device,
glsl_fs! { r#"#version 430 core
layout(location = 0) in vec2 v_uv;
layout(location = 1) in vec4 v_col;
layout(set = 0, binding = 0) uniform sampler2D g_tex;
layout(location = 0) out vec4 o_col;
void main() {
o_col = v_col*texture(g_tex, v_uv);
}"# },
)?;
let sampler = Sampler::new(
&device,
&crate::ash::vk::SamplerCreateInfo::builder()
.mag_filter(crate::ash::vk::Filter::LINEAR)
.min_filter(crate::ash::vk::Filter::LINEAR),
)?;
let binding = crate::ash::vk::DescriptorSetLayoutBinding::builder()
.descriptor_type(crate::ash::vk::DescriptorType::COMBINED_IMAGE_SAMPLER)
.descriptor_count(1)
.stage_flags(crate::ash::vk::ShaderStageFlags::FRAGMENT)
.immutable_samplers(slice::from_ref(&sampler));
let create_info =
crate::ash::vk::DescriptorSetLayoutCreateInfo::builder().bindings(slice::from_ref(&binding));
let descriptor_set_layout = DescriptorSetLayout::new(&device, &create_info)?;
let push_constant_range = crate::ash::vk::PushConstantRange {
stage_flags: crate::ash::vk::ShaderStageFlags::VERTEX,
offset: 0,
size: Renderer::PUSH_CONSTANT_SIZE as u32,
};
let create_info = crate::ash::vk::PipelineLayoutCreateInfo::builder()
.push_constant_ranges(slice::from_ref(&push_constant_range));
let pipeline_layout =
PipelineLayout::new(&device, slice::from_ref(&descriptor_set_layout), create_info)?;
let vertex_buffers = {
let create_info = crate::ash::vk::BufferCreateInfo::builder()
.size(
(Renderer::VERTEX_COUNT_PER_FRAME * mem::size_of::<ImDrawVert>())
as crate::ash::vk::DeviceSize,
)
.usage(crate::ash::vk::BufferUsageFlags::VERTEX_BUFFER);
let mut buffers = ArrayVec::<[Buffer; Renderer::FRAME_COUNT]>::new();
for buffer in std::iter::repeat_with(|| {
Buffer::new(
&allocator,
&create_info,
&AllocationCreateInfo {
usage: vk_mem::MemoryUsage::CpuOnly,
flags: AllocationCreateFlags::MAPPED,
..Default::default()
},
)
.unwrap()
}).take(Renderer::FRAME_COUNT) {
buffers.push(buffer);
};
buffers.into_inner().unwrap()
};
let index_buffers = {
let create_info = crate::ash::vk::BufferCreateInfo::builder()
.size(
(Renderer::INDEX_COUNT_PER_FRAME * mem::size_of::<ImDrawIdx>())
as crate::ash::vk::DeviceSize,
)
.usage(crate::ash::vk::BufferUsageFlags::INDEX_BUFFER);
let mut buffers = ArrayVec::<[Buffer; Renderer::FRAME_COUNT]>::new();
for buffer in std::iter::repeat_with(|| {
Buffer::new(
&allocator,
&create_info,
&AllocationCreateInfo {
usage: vk_mem::MemoryUsage::CpuOnly,
flags: AllocationCreateFlags::MAPPED,
..Default::default()
},
)
.unwrap()
}).take(Renderer::FRAME_COUNT) {
buffers.push(buffer);
};
buffers.into_inner().unwrap()
};
let (image_width, image_height, image_pixels) =
imgui.prepare_texture(|handle| (handle.width, handle.height, handle.pixels));
let create_info = crate::ash::vk::BufferCreateInfo::builder()
.size((image_width * image_height * 4) as crate::ash::vk::DeviceSize)
.usage(crate::ash::vk::BufferUsageFlags::TRANSFER_SRC);
let image_buffer = RefCell::new(Buffer::new(
&allocator,
&create_info,
&AllocationCreateInfo {
usage: vk_mem::MemoryUsage::CpuOnly,
flags: AllocationCreateFlags::MAPPED,
..Default::default()
},
)?);
let create_info = crate::ash::vk::ImageCreateInfo::builder()
.image_type(crate::ash::vk::ImageType::TYPE_2D)
.format(crate::ash::vk::Format::R8G8B8A8_UNORM)
.extent(crate::ash::vk::Extent3D {
width: image_width,
height: image_height,
depth: 1,
})
.mip_levels(1)
.array_layers(1)
.samples(crate::ash::vk::SampleCountFlags::TYPE_1)
.usage(crate::ash::vk::ImageUsageFlags::SAMPLED | crate::ash::vk::ImageUsageFlags::TRANSFER_DST);
let image = RefCell::new(Image::new(
&allocator,
&create_info,
&AllocationCreateInfo {
usage: vk_mem::MemoryUsage::GpuOnly,
..Default::default()
},
)?);
let descriptor_pool_sizes = [crate::ash::vk::DescriptorPoolSize::builder()
.ty(crate::ash::vk::DescriptorType::COMBINED_IMAGE_SAMPLER)
.descriptor_count(1)
.build()];
let create_info = crate::ash::vk::DescriptorPoolCreateInfo::builder()
.max_sets(1)
.pool_sizes(&descriptor_pool_sizes);
let descriptor_pool = DescriptorPool::new(&device, &create_info)?;
let descriptor_set = DescriptorSet::new(
&descriptor_pool,
slice::from_ref(&descriptor_set_layout),
crate::ash::vk::DescriptorSetAllocateInfo::builder(),
)?[0]
.clone();
let create_info = crate::ash::vk::ImageViewCreateInfo::builder()
.view_type(crate::ash::vk::ImageViewType::TYPE_2D)
.format(crate::ash::vk::Format::R8G8B8A8_UNORM)
.subresource_range(
crate::ash::vk::ImageSubresourceRange::builder()
.aspect_mask(crate::ash::vk::ImageAspectFlags::COLOR)
.level_count(1)
.layer_count(1)
.build(),
);
let image_view = ImageView::new(&image.borrow(), create_info)?;
{
use crate::ash::version::DeviceV1_0;
let image_info = crate::ash::vk::DescriptorImageInfo::builder()
.sampler(**sampler)
.image_view(**image_view)
.image_layout(crate::ash::vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL)
.build();
let write_descriptor_set = crate::ash::vk::WriteDescriptorSet::builder()
.dst_set(**descriptor_set)
.descriptor_type(crate::ash::vk::DescriptorType::COMBINED_IMAGE_SAMPLER)
.image_info(slice::from_ref(&image_info));
unsafe { device.update_descriptor_sets(slice::from_ref(&write_descriptor_set), &[]) };
}
let image_base = image_buffer.borrow().allocation_info.get_mapped_data() as *mut c_uchar;
assert_eq!((image_width * image_height * 4) as usize, image_pixels.len());
unsafe {
image_base.copy_from_nonoverlapping(image_pixels.as_ptr(), image_pixels.len());
};
image_buffer.borrow().flush(0, 0).unwrap();
Ok(Self {
pipeline_layout,
vertex_shader,
fragment_shader,
render_pass: Default::default(),
pipeline: Default::default(),
old_pipeline: Default::default(),
vertex_buffers,
index_buffers,
image_buffer,
image_width,
image_height,
image,
image_view,
sampler,
descriptor_set,
frame_index: Default::default(),
image_is_valid: Default::default(),
})
}
pub fn begin_frame(&self, command_buffer: &CommandBuffer) {
if !self.image_is_valid.get() {
use crate::ash::{version::DeviceV1_0, vk};
let transfer_from_undef = vk::ImageMemoryBarrier::builder()
.dst_access_mask(vk::AccessFlags::TRANSFER_WRITE)
.new_layout(vk::ImageLayout::TRANSFER_DST_OPTIMAL)
.src_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
.dst_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
.image(***self.image.borrow())
.subresource_range(
vk::ImageSubresourceRange::builder()
.aspect_mask(vk::ImageAspectFlags::COLOR)
.level_count(1)
.layer_count(1)
.build(),
);
unsafe {
command_buffer.command_pool.device.cmd_pipeline_barrier(
***command_buffer,
vk::PipelineStageFlags::HOST,
vk::PipelineStageFlags::TRANSFER,
vk::DependencyFlags::empty(),
&[],
&[],
slice::from_ref(&transfer_from_undef),
)
};
let buffer_image_copy = vk::BufferImageCopy::builder()
.image_subresource(
vk::ImageSubresourceLayers::builder()
.aspect_mask(vk::ImageAspectFlags::COLOR)
.layer_count(1)
.build(),
)
.image_extent(vk::Extent3D {
width: self.image_width,
height: self.image_height,
depth: 1,
});
unsafe {
command_buffer.command_pool.device.cmd_copy_buffer_to_image(
***command_buffer,
***self.image_buffer.borrow(),
***self.image.borrow(),
vk::ImageLayout::TRANSFER_DST_OPTIMAL,
slice::from_ref(&buffer_image_copy),
)
};
let shader_from_transfer = vk::ImageMemoryBarrier::builder()
.src_access_mask(vk::AccessFlags::TRANSFER_WRITE)
.dst_access_mask(vk::AccessFlags::SHADER_READ)
.old_layout(vk::ImageLayout::TRANSFER_DST_OPTIMAL)
.new_layout(vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL)
.src_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
.dst_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
.image(***self.image.borrow())
.subresource_range(
vk::ImageSubresourceRange::builder()
.aspect_mask(vk::ImageAspectFlags::COLOR)
.level_count(1)
.layer_count(1)
.build(),
);
unsafe {
command_buffer.command_pool.device.cmd_pipeline_barrier(
***command_buffer,
vk::PipelineStageFlags::TRANSFER,
vk::PipelineStageFlags::FRAGMENT_SHADER,
vk::DependencyFlags::empty(),
&[],
&[],
slice::from_ref(&shader_from_transfer),
)
};
self.image_is_valid.set(true);
}
if self.old_pipeline.borrow().is_some() {
self.old_pipeline.replace(None);
}
}
pub fn set_renderpass(&self, render_pass: &RenderPass) {
use crate::ash::{version::DeviceV1_0, vk};
let device = &render_pass.device;
self.render_pass.replace(Some(render_pass.clone()));
let shader_entry_name = CStr::from_bytes_with_nul(b"main\0").unwrap();
let shader_stage_create_info = [
vk::PipelineShaderStageCreateInfo::builder()
.stage(vk::ShaderStageFlags::VERTEX)
.module(**self.vertex_shader)
.name(&shader_entry_name)
.build(),
vk::PipelineShaderStageCreateInfo::builder()
.stage(vk::ShaderStageFlags::FRAGMENT)
.module(**self.fragment_shader)
.name(&shader_entry_name)
.build(),
];
let vertex_input_binding = vk::VertexInputBindingDescription::builder()
.binding(0)
.stride(mem::size_of::<ImDrawVert>() as u32)
.input_rate(vk::VertexInputRate::VERTEX);
let vertex_input_attributes = [
vk::VertexInputAttributeDescription::builder()
.location(0)
.binding(0)
.format(vk::Format::R32G32_SFLOAT)
.offset(offset_of!(ImDrawVert, pos) as u32)
.build(),
vk::VertexInputAttributeDescription::builder()
.location(1)
.binding(0)
.format(vk::Format::R32G32_SFLOAT)
.offset(offset_of!(ImDrawVert, uv) as u32)
.build(),
vk::VertexInputAttributeDescription::builder()
.location(2)
.binding(0)
.format(vk::Format::R8G8B8A8_UNORM)
.offset(offset_of!(ImDrawVert, col) as u32)
.build(),
];
let vertex_input_state_create_info = vk::PipelineVertexInputStateCreateInfo::builder()
.vertex_binding_descriptions(slice::from_ref(&vertex_input_binding))
.vertex_attribute_descriptions(&vertex_input_attributes);
let input_assembly_state_create_info = vk::PipelineInputAssemblyStateCreateInfo::builder()
.topology(vk::PrimitiveTopology::TRIANGLE_LIST);
let viewport_state_create_info = vk::PipelineViewportStateCreateInfo::builder()
.viewport_count(1)
.scissor_count(1);
let rasterization_state_create_info = vk::PipelineRasterizationStateCreateInfo::builder()
.polygon_mode(vk::PolygonMode::FILL)
.cull_mode(vk::CullModeFlags::NONE)
.front_face(vk::FrontFace::CLOCKWISE)
.line_width(1.0);
let multisample_state_create_info = vk::PipelineMultisampleStateCreateInfo::builder()
.rasterization_samples(vk::SampleCountFlags::TYPE_1);
let color_blend_attachment_state = vk::PipelineColorBlendAttachmentState {
blend_enable: vk::TRUE,
src_color_blend_factor: vk::BlendFactor::SRC_ALPHA,
dst_color_blend_factor: vk::BlendFactor::ONE_MINUS_SRC_ALPHA,
color_blend_op: vk::BlendOp::ADD,
src_alpha_blend_factor: vk::BlendFactor::ONE_MINUS_SRC_ALPHA,
dst_alpha_blend_factor: vk::BlendFactor::ZERO,
alpha_blend_op: vk::BlendOp::ADD,
color_write_mask: vk::ColorComponentFlags::all(),
};
let color_blend_state_create_info = vk::PipelineColorBlendStateCreateInfo::builder()
.attachments(slice::from_ref(&color_blend_attachment_state));
let dynamic_states = [vk::DynamicState::VIEWPORT, vk::DynamicState::SCISSOR];
let pipeline_dynamic_state_create_info =
vk::PipelineDynamicStateCreateInfo::builder().dynamic_states(&dynamic_states);
let pipeline_create_info = vk::GraphicsPipelineCreateInfo::builder()
.stages(&shader_stage_create_info)
.vertex_input_state(&vertex_input_state_create_info)
.input_assembly_state(&input_assembly_state_create_info)
.viewport_state(&viewport_state_create_info)
.rasterization_state(&rasterization_state_create_info)
.multisample_state(&multisample_state_create_info)
.color_blend_state(&color_blend_state_create_info)
.dynamic_state(&pipeline_dynamic_state_create_info)
.layout(**self.pipeline_layout)
.render_pass(***render_pass);
self.old_pipeline.replace(Some(
unsafe {
device.create_graphics_pipelines(
vk::PipelineCache::null(),
slice::from_ref(&pipeline_create_info),
None,
)
}
.unwrap()[0],
));
self.old_pipeline.swap(&self.pipeline);
}
pub fn render(&self, ui: Ui, command_buffer: &CommandBuffer) -> Result<(), ()> {
if self.render_pass.borrow().is_none() {
return Err(());
};
let FrameSize {
logical_size: (width, height),
..
} = ui.frame_size();
let width = width as f32;
let height = height as f32;
ui.render(|ui, mut draw_data| -> Result<_, u32> {
use crate::ash::{version::DeviceV1_0, vk};
draw_data.scale_clip_rects(ui.imgui().display_framebuffer_scale());
let vertex_buffer = &self.vertex_buffers[*self.frame_index.borrow()];
let index_buffer = &self.index_buffers[*self.frame_index.borrow()];
unsafe {
command_buffer.command_pool.device.cmd_bind_pipeline(
***command_buffer,
vk::PipelineBindPoint::GRAPHICS,
*self.pipeline.borrow().as_ref().unwrap(),
);
command_buffer.command_pool.device.cmd_bind_descriptor_sets(
***command_buffer,
vk::PipelineBindPoint::GRAPHICS,
**self.pipeline_layout,
0,
slice::from_ref(&self.descriptor_set),
&[],
);
}
let dims_rcp = [1.0 / width, 1.0 / height];
unsafe {
command_buffer.command_pool.device.cmd_push_constants(
***command_buffer,
**self.pipeline_layout,
vk::ShaderStageFlags::VERTEX,
0,
std::slice::from_raw_parts(
dims_rcp.as_ptr() as *const u8,
dims_rcp.len() * std::mem::size_of::<f32>() / std::mem::size_of::<u8>(),
),
)
};
let viewport = vk::Viewport {
width,
height,
max_depth: 1.0,
..Default::default()
};
unsafe {
command_buffer.command_pool.device.cmd_set_viewport(
***command_buffer,
0,
slice::from_ref(&viewport),
)
};
unsafe {
command_buffer.command_pool.device.cmd_bind_vertex_buffers(
***command_buffer,
0,
slice::from_ref(&***vertex_buffer),
&[0],
);
command_buffer.command_pool.device.cmd_bind_index_buffer(
***command_buffer,
***index_buffer,
0,
vk::IndexType::UINT16,
);
}
let vertex_base = vertex_buffer.allocation_info.get_mapped_data() as *mut ImDrawVert;
let index_base = index_buffer.allocation_info.get_mapped_data() as *mut ImDrawIdx;
let mut vertex_offset = 0;
let mut index_offset = 0;
for draw_list in &draw_data {
let next_vertex_offset = vertex_offset + draw_list.vtx_buffer.len();
let next_index_offset = index_offset + draw_list.idx_buffer.len();
if next_vertex_offset > Renderer::VERTEX_COUNT_PER_FRAME
|| next_index_offset > Renderer::INDEX_COUNT_PER_FRAME
{
break;
}
unsafe {
vertex_base
.add(vertex_offset)
.copy_from_nonoverlapping(draw_list.vtx_buffer.as_ptr(), draw_list.vtx_buffer.len());
index_base
.add(index_offset)
.copy_from_nonoverlapping(draw_list.idx_buffer.as_ptr(), draw_list.idx_buffer.len());
}
for cmd in draw_list.cmd_buffer {
let scissor = vk::Rect2D {
offset: vk::Offset2D {
x: cmd.clip_rect.x as i32,
y: cmd.clip_rect.y as i32,
},
extent: vk::Extent2D {
width: (cmd.clip_rect.z - cmd.clip_rect.x) as u32,
height: (cmd.clip_rect.w - cmd.clip_rect.y) as u32,
},
};
unsafe {
command_buffer.command_pool.device.cmd_set_scissor(
***command_buffer,
0,
slice::from_ref(&scissor),
);
command_buffer.command_pool.device.cmd_draw_indexed(
***command_buffer,
cmd.elem_count,
1,
index_offset as u32,
vertex_offset as i32,
0,
);
}
index_offset += cmd.elem_count as usize;
}
vertex_offset = next_vertex_offset;
assert_eq!(index_offset, next_index_offset);
}
vertex_buffer.flush(0, 0).unwrap();
index_buffer.flush(0, 0).unwrap();
Ok(())
})
.unwrap();
self.frame_index
.replace_with(|frame_index| (1 + *frame_index) % Renderer::FRAME_COUNT);
Ok(())
}
}