use memoffset::offset_of;
use std::cell::RefCell;
use std::ffi::CStr;
use std::fmt::Debug;
use std::mem;
use std::ops::Deref;
use std::os::raw::c_uchar;
use std::rc::Rc;
use std::slice;
use crate::ash::vk;
use crate::imgui::{
Context as ImGui, DrawIdx as ImDrawIdx, DrawVert as ImDrawVert, FontAtlasTexture, Ui,
};
use crate::vk_helper::{Error as VkHelperError, *};
use crate::vk_mem::{AllocationCreateFlags, AllocationCreateInfo, AllocatorCreateInfo};
use glsl_to_spirv_macros::{glsl_fs, glsl_vs};
use glsl_to_spirv_macros_impl::GLSLEmbedImpl;
use snafu::{ResultExt, Snafu};
#[derive(Debug, Snafu)]
pub enum Error {
#[snafu(display("vk_helper::Allocator creation error: {}", source))]
VkHelperAllocatorNew { source: VkHelperError },
#[snafu(display("vk_helper::ShaderModule vertex creation error: {}", source))]
VkHelperVertexShaderModuleNew { source: VkHelperError },
#[snafu(display("vk_helper::ShaderModule fragment creation error: {}", source))]
VkHelperFragmentShaderModuleNew { source: VkHelperError },
#[snafu(display("vk_helper::Sampler creation error: {}", source))]
VkHelperSamplerNew { source: VkHelperError },
#[snafu(display("vk_helper::DescriptorSetLayout creation error: {}", source))]
VkHelperDescriptorSetLayoutNew { source: VkHelperError },
#[snafu(display("vk_helper::PipelineLayout creation error: {}", source))]
VkHelperPipelineLayoutNew { source: VkHelperError },
#[snafu(display("vk_helper::Buffer creation error: {}", source))]
VkHelperBufferNew { source: VkHelperError },
#[snafu(display("vk_helper::Image creation error: {}", source))]
VkHelperImageNew { source: VkHelperError },
#[snafu(display("vk_helper::ImageView creation error: {}", source))]
VkHelperImageViewNew { source: VkHelperError },
#[snafu(display("vk_helper::DescriptorPool creation error: {}", source))]
VkHelperDescriptorPoolNew { source: VkHelperError },
#[snafu(display("vk_helper::DescriptorSet creation error: {}", source))]
VkHelperDescriptorSetNew { source: VkHelperError },
#[snafu(display("vk_helper::Pipeline creation error: {}", source))]
VkHelperPipelineNew { source: VkHelperError },
#[snafu(display("Pipeline not defined in ui renderer"))]
PipelineNone,
#[snafu(display("vk_helper::DescriptorSet write combined images error: {}", source))]
VkHelperDescriptorSetWriteCombinedImages { source: VkHelperError },
#[snafu(display("vk_helper::CommandBuffer bind descriptor sets error: {}", source))]
VkHelperCommandBufferBindDescriptorSets { source: VkHelperError },
#[snafu(display("vk_helper::Buffer flush vertex error: {}", source))]
VkHelperBufferFlushVertex { source: VkHelperError },
#[snafu(display("vk_helper::Buffer flush index error: {}", source))]
VkHelperBufferFlushIndex { source: VkHelperError },
}
pub type Result<T> = std::result::Result<T, Error>;
pub trait UserDataTrait: Clone + Default + Debug {}
impl<T: Clone + Default + Debug> UserDataTrait for T {}
#[derive(Debug, Clone)]
pub struct Texture<T: UserDataTrait>(Rc<RcTexture<T>>);
impl<T: UserDataTrait> Deref for Texture<T> {
type Target = RcTexture<T>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(Debug)]
pub struct RcTexture<T: UserDataTrait> {
pub sampler: Sampler<T>,
pub image_view: ImageView<T>,
pub image_layout: vk::ImageLayout,
}
pub struct Renderer<T: UserDataTrait> {
pipeline_layout: PipelineLayout<T>,
vertex_shader: ShaderModule<T>,
fragment_shader: ShaderModule<T>,
pipeline: RefCell<Option<Pipeline<T>>>,
old_pipeline: RefCell<Option<Pipeline<T>>>,
vertex_buffers: [Buffer<T>; 2],
index_buffers: [Buffer<T>; 2],
fonts_texture_sampler: Sampler<T>,
fonts_texture_image_view: ImageView<T>,
descriptor_set: DescriptorSet<T>,
tex_ids: RefCell<Vec<Option<(usize, u8)>>>,
frame_index: RefCell<usize>,
}
impl<T: UserDataTrait> Renderer<T> {
const QUAD_COUNT_PER_FRAME: usize = 64 * 1024;
const VERTEX_COUNT_PER_FRAME: usize = 4 * Self::QUAD_COUNT_PER_FRAME;
const INDEX_COUNT_PER_FRAME: usize = 6 * Self::QUAD_COUNT_PER_FRAME;
pub fn new_texture(&self, sampler: Sampler<T>, image: ImageView<T>) -> Texture<T> {
Texture(Rc::new(RcTexture {
sampler,
image_view: image,
image_layout: vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL,
}))
}
pub fn new_simple(
instance: Instance<T>,
physical_device: vk::PhysicalDevice,
device: Device<T>,
features: Option<vk::PhysicalDeviceVulkan12Features>,
command_buffer: &CommandBuffer<T>,
imgui: &mut ImGui,
) -> Result<Self> {
let create_info =
AllocatorCreateInfo::new(instance.inner.clone(), device.0.clone(), physical_device);
let allocator = Allocator::new(
instance,
physical_device,
device.clone(),
create_info,
Default::default(),
)
.context(VkHelperAllocatorNewSnafu {})?;
Self::new(device, allocator, features, command_buffer, imgui)
}
pub fn new(
device: Device<T>,
allocator: Allocator<T>,
features: Option<vk::PhysicalDeviceVulkan12Features>,
command_buffer: &CommandBuffer<T>,
imgui: &mut ImGui,
) -> Result<Self> {
use arrayvec::ArrayVec;
let vertex_shader = ShaderModule::new_simple(
device.clone(),
glsl_vs! { r#"#version 450 core
layout(location = 0) in vec2 aPos;
layout(location = 1) in vec2 aUV;
layout(location = 2) in vec4 aColor;
layout(push_constant) uniform uPushConstant { vec2 uScale; vec2 uTranslate; } pc;
out gl_PerVertex { vec4 gl_Position; };
layout(location = 0) out struct { vec4 Color; vec2 UV; } Out;
void main()
{
Out.Color = aColor;
Out.UV = aUV;
gl_Position = vec4(aPos * pc.uScale + pc.uTranslate, 0, 1);
}"# },
Default::default(),
)
.context(VkHelperVertexShaderModuleNewSnafu {})?;
let fragment_shader = ShaderModule::new_simple(
device.clone(),
glsl_fs! { r#"#version 450 core
layout(location = 0) out vec4 fColor;
layout(push_constant) uniform uPushConstant {
layout(offset=16) uint uTextureID;
} pc;
layout(set=0, binding=0) uniform sampler2D sTextures[30];
layout(location = 0) in struct { vec4 Color; vec2 UV; } In;
void main()
{
fColor = In.Color * texture(sTextures[pc.uTextureID], In.UV.st);
}"# },
Default::default(),
)
.context(VkHelperFragmentShaderModuleNewSnafu {})?;
let bindings = vec![*vk::DescriptorSetLayoutBinding::builder()
.descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER)
.descriptor_count(4096)
.stage_flags(vk::ShaderStageFlags::FRAGMENT)];
let descriptor_set_layout = if features
.unwrap_or_default()
.descriptor_binding_partially_bound
== 0
{
DescriptorSetLayout::new(
device.clone(),
bindings,
vk::DescriptorSetLayoutCreateInfo::builder(),
Default::default(),
)
} else {
let mut flags = vk::DescriptorSetLayoutBindingFlagsCreateInfo::builder()
.binding_flags(&[vk::DescriptorBindingFlags::PARTIALLY_BOUND]);
DescriptorSetLayout::new(
device.clone(),
bindings,
vk::DescriptorSetLayoutCreateInfo::builder().push_next(&mut flags),
Default::default(),
)
}
.context(VkHelperDescriptorSetLayoutNewSnafu {})?;
let push_constant_ranges = [
*vk::PushConstantRange::builder()
.stage_flags(vk::ShaderStageFlags::VERTEX)
.size(16),
*vk::PushConstantRange::builder()
.stage_flags(vk::ShaderStageFlags::FRAGMENT)
.offset(16)
.size(4),
];
let create_info =
vk::PipelineLayoutCreateInfo::builder().push_constant_ranges(&push_constant_ranges);
let pipeline_layout = PipelineLayout::new(
device.clone(),
vec![descriptor_set_layout.clone()],
create_info,
Default::default(),
)
.context(VkHelperPipelineLayoutNewSnafu {})?;
let vertex_buffers = {
let create_info = vk::BufferCreateInfo::builder()
.size(
(Self::VERTEX_COUNT_PER_FRAME * mem::size_of::<ImDrawVert>()) as vk::DeviceSize,
)
.usage(vk::BufferUsageFlags::VERTEX_BUFFER);
let mut buffers = ArrayVec::<Buffer<T>, 2>::new();
for buffer in std::iter::repeat_with(|| {
Buffer::new(
allocator.clone(),
&create_info,
&AllocationCreateInfo {
usage: crate::vk_mem::MemoryUsage::Auto,
flags: AllocationCreateFlags::MAPPED
| AllocationCreateFlags::HOST_ACCESS_SEQUENTIAL_WRITE,
..Default::default()
},
Default::default(),
)
})
.take(2)
{
buffers.push(buffer.context(VkHelperBufferNewSnafu)?);
}
buffers.into_inner().unwrap()
};
let index_buffers = {
let create_info = vk::BufferCreateInfo::builder()
.size((Self::INDEX_COUNT_PER_FRAME * mem::size_of::<ImDrawIdx>()) as vk::DeviceSize)
.usage(vk::BufferUsageFlags::INDEX_BUFFER);
let mut buffers = ArrayVec::<Buffer<T>, 2>::new();
for buffer in std::iter::repeat_with(|| {
Buffer::new(
allocator.clone(),
&create_info,
&AllocationCreateInfo {
usage: crate::vk_mem::MemoryUsage::Auto,
flags: AllocationCreateFlags::MAPPED
| AllocationCreateFlags::HOST_ACCESS_SEQUENTIAL_WRITE,
..Default::default()
},
Default::default(),
)
})
.take(2)
{
buffers.push(buffer.context(VkHelperBufferNewSnafu)?);
}
buffers.into_inner().unwrap()
};
let sampler = Sampler::new(
device.clone(),
&vk::SamplerCreateInfo::builder()
.mag_filter(vk::Filter::LINEAR)
.min_filter(vk::Filter::LINEAR),
Default::default(),
)
.context(VkHelperSamplerNewSnafu {})?;
let mut fonts = imgui.fonts();
let image_view = {
let FontAtlasTexture {
width: image_width,
height: image_height,
data: image_pixels,
} = fonts.build_rgba32_texture();
let create_info = vk::BufferCreateInfo::builder()
.size(vk::DeviceSize::from(image_width * image_height * 4))
.usage(vk::BufferUsageFlags::TRANSFER_SRC);
let image_buffer = Buffer::new(
allocator.clone(),
&create_info,
&AllocationCreateInfo {
usage: crate::vk_mem::MemoryUsage::Auto,
flags: AllocationCreateFlags::MAPPED
| AllocationCreateFlags::HOST_ACCESS_SEQUENTIAL_WRITE,
..Default::default()
},
Default::default(),
)
.context(VkHelperBufferNewSnafu {})?;
let create_info = vk::ImageCreateInfo::builder()
.image_type(vk::ImageType::TYPE_2D)
.format(vk::Format::R8G8B8A8_UNORM)
.extent(vk::Extent3D {
width: image_width,
height: image_height,
depth: 1,
})
.mip_levels(1)
.array_layers(1)
.samples(vk::SampleCountFlags::TYPE_1)
.usage(vk::ImageUsageFlags::SAMPLED | vk::ImageUsageFlags::TRANSFER_DST);
let image = Image::new(
allocator,
&create_info,
&AllocationCreateInfo {
usage: crate::vk_mem::MemoryUsage::Auto,
..Default::default()
},
Default::default(),
)
.context(VkHelperImageNewSnafu {})?;
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)
.subresource_range(
*vk::ImageSubresourceRange::builder()
.aspect_mask(vk::ImageAspectFlags::COLOR)
.level_count(1)
.layer_count(1),
);
command_buffer.pipeline_barrier(
vk::PipelineStageFlags::HOST,
vk::PipelineStageFlags::TRANSFER,
vk::DependencyFlags::empty(),
&[],
vec![],
vec![(image.clone(), transfer_from_undef)],
);
let image_base = unsafe {
image_buffer
.allocator
.borrow_mut()
.get_allocation_info(&image_buffer.allocation)
}
.unwrap()
.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());
};
let buffer_image_copy = vk::BufferImageCopy::builder()
.image_subresource(
*vk::ImageSubresourceLayers::builder()
.aspect_mask(vk::ImageAspectFlags::COLOR)
.layer_count(1),
)
.image_extent(vk::Extent3D {
width: image_width,
height: image_height,
depth: 1,
});
command_buffer.copy_buffer_to_image(
image_buffer,
image.clone(),
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)
.subresource_range(
*vk::ImageSubresourceRange::builder()
.aspect_mask(vk::ImageAspectFlags::COLOR)
.level_count(1)
.layer_count(1),
);
command_buffer.pipeline_barrier(
vk::PipelineStageFlags::TRANSFER,
vk::PipelineStageFlags::FRAGMENT_SHADER,
vk::DependencyFlags::empty(),
&[],
vec![],
vec![(image.clone(), shader_from_transfer)],
);
let create_info = vk::ImageViewCreateInfo::builder()
.view_type(vk::ImageViewType::TYPE_2D)
.format(vk::Format::R8G8B8A8_UNORM)
.subresource_range(
*vk::ImageSubresourceRange::builder()
.aspect_mask(vk::ImageAspectFlags::COLOR)
.level_count(1)
.layer_count(1),
);
ImageView::new(image, create_info, Default::default())
.context(VkHelperImageViewNewSnafu {})?
};
let (fonts_texture_sampler, fonts_texture_image_view) =
(sampler.clone(), image_view.clone());
let fonts_texture = Box::new(Texture(Rc::new(RcTexture {
sampler,
image_view,
image_layout: vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL,
})));
fonts.tex_id = (Box::leak(fonts_texture) as *const Texture<T>).into();
let descriptor_pool_sizes = [*vk::DescriptorPoolSize::builder()
.ty(vk::DescriptorType::COMBINED_IMAGE_SAMPLER)
.descriptor_count(4096)];
let create_info = vk::DescriptorPoolCreateInfo::builder()
.flags(vk::DescriptorPoolCreateFlags::FREE_DESCRIPTOR_SET)
.max_sets(1)
.pool_sizes(&descriptor_pool_sizes);
let descriptor_pool = DescriptorPool::new(device, &create_info, Default::default())
.context(VkHelperDescriptorPoolNewSnafu {})?;
let descriptor_set = DescriptorSet::new(
&descriptor_pool,
vec![descriptor_set_layout],
vk::DescriptorSetAllocateInfo::builder(),
Default::default(),
vec![],
)
.context(VkHelperDescriptorSetNewSnafu {})?[0]
.clone();
Ok(Self {
pipeline_layout,
vertex_shader,
fragment_shader,
pipeline: Default::default(),
old_pipeline: Default::default(),
vertex_buffers,
index_buffers,
fonts_texture_sampler,
fonts_texture_image_view,
descriptor_set,
tex_ids: Default::default(),
frame_index: Default::default(),
})
}
pub fn begin_frame(&self) {
if self.old_pipeline.borrow().is_some() {
self.old_pipeline.replace(None);
}
}
pub fn set_render_pass(&self, render_pass: RenderPass<T>) -> Result<()> {
let mut shaders = std::collections::HashMap::new();
shaders.insert(vk::ShaderStageFlags::VERTEX, self.vertex_shader.clone());
shaders.insert(vk::ShaderStageFlags::FRAGMENT, self.fragment_shader.clone());
let shader_entry_name = CStr::from_bytes_with_nul(b"main\0").unwrap();
let shader_stage_create_infos = vec![
vk::PipelineShaderStageCreateInfo::builder()
.stage(vk::ShaderStageFlags::VERTEX)
.name(shader_entry_name),
vk::PipelineShaderStageCreateInfo::builder()
.stage(vk::ShaderStageFlags::FRAGMENT)
.name(shader_entry_name),
];
let vertex_input_binding = vk::VertexInputBindingDescription::builder()
.binding(0)
.stride(mem::size_of::<ImDrawVert>() as u32)
.input_rate(vk::VertexInputRate::VERTEX);
#[allow(clippy::unneeded_field_pattern)]
let vertex_input_attributes = [
*vk::VertexInputAttributeDescription::builder()
.location(0)
.binding(0)
.format(vk::Format::R32G32_SFLOAT)
.offset(offset_of!(ImDrawVert, pos) as u32),
*vk::VertexInputAttributeDescription::builder()
.location(1)
.binding(0)
.format(vk::Format::R32G32_SFLOAT)
.offset(offset_of!(ImDrawVert, uv) as u32),
*vk::VertexInputAttributeDescription::builder()
.location(2)
.binding(0)
.format(vk::Format::R8G8B8A8_UNORM)
.offset(offset_of!(ImDrawVert, col) as u32),
];
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 = vk::Viewport::builder().build();
let scissor = vk::Rect2D::builder().build();
let viewport_state_create_info = vk::PipelineViewportStateCreateInfo::builder()
.viewports(slice::from_ref(&viewport))
.scissors(slice::from_ref(&scissor));
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.);
let depth_stencil_state_create_info = vk::PipelineDepthStencilStateCreateInfo::builder();
let multisample_state_create_info = vk::PipelineMultisampleStateCreateInfo::builder()
.rasterization_samples(vk::SampleCountFlags::TYPE_1);
let color_blend_attachment_state = vk::PipelineColorBlendAttachmentState::builder()
.blend_enable(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::RGBA);
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 create_info = vk::GraphicsPipelineCreateInfo::builder()
.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)
.depth_stencil_state(&depth_stencil_state_create_info)
.multisample_state(&multisample_state_create_info)
.color_blend_state(&color_blend_state_create_info)
.dynamic_state(&pipeline_dynamic_state_create_info);
self.old_pipeline.replace(Some(
Pipeline::new(
self.pipeline_layout.clone(),
render_pass,
shaders,
shader_stage_create_infos,
create_info,
Default::default(),
)
.context(VkHelperPipelineNewSnafu {})?,
));
self.old_pipeline.swap(&self.pipeline);
Ok(())
}
pub fn render(&self, ui: Ui, command_buffer: &CommandBuffer<T>) -> Result<()> {
let draw_data = ui.render();
let x = draw_data.display_pos[0];
let y = draw_data.display_pos[1];
let width = draw_data.display_size[0] * draw_data.framebuffer_scale[0];
let height = draw_data.display_size[1] * draw_data.framebuffer_scale[1];
if !(width > 0. && height > 0.) {
return Ok(());
}
command_buffer.bind_pipeline(
self.pipeline
.borrow()
.as_ref()
.ok_or(Error::PipelineNone)?
.clone(),
);
let mut tex_ids = self.tex_ids.borrow_mut();
tex_ids
.iter()
.filter_map(|&e| e)
.for_each(|(_, ref mut age)| *age -= 1);
let mut tex_ids_dirty = false;
command_buffer
.bind_descriptor_sets(0, vec![self.descriptor_set.clone()], &[])
.context(VkHelperCommandBufferBindDescriptorSetsSnafu {})?;
let scale = [width.recip() * 2., height.recip() * 2.];
let translate = [x.mul_add(-scale[0], -1.), y.mul_add(-scale[1], -1.)];
unsafe {
command_buffer.command_pool.device.cmd_push_constants(
***command_buffer,
**self.pipeline_layout,
vk::ShaderStageFlags::VERTEX,
0,
std::slice::from_raw_parts([scale, translate].as_ptr() as *const u8, 16),
);
};
let viewport = vk::Viewport::builder()
.width(width)
.height(height)
.max_depth(1.);
unsafe {
command_buffer.command_pool.device.cmd_set_viewport(
***command_buffer,
0,
slice::from_ref(&*viewport),
);
};
let vertex_buffer = self.vertex_buffers[*self.frame_index.borrow()].clone();
let index_buffer = self.index_buffers[*self.frame_index.borrow()].clone();
command_buffer.bind_vertex_buffers(0, vec![vertex_buffer.clone()], &[0]);
command_buffer.bind_index_buffer(index_buffer.clone(), 0, vk::IndexType::UINT16);
#[allow(clippy::cast_ptr_alignment)]
let vertex_base = unsafe {
vertex_buffer
.allocator
.borrow_mut()
.get_allocation_info(&vertex_buffer.allocation)
}
.unwrap()
.mapped_data as *mut ImDrawVert;
#[allow(clippy::cast_ptr_alignment)]
let index_base = unsafe {
index_buffer
.allocator
.borrow_mut()
.get_allocation_info(&index_buffer.allocation)
}
.unwrap()
.mapped_data as *mut ImDrawIdx;
let mut global_vertex_offset = 0;
let mut global_index_offset = 0;
let mut current_texture_id = None;
for draw_list in draw_data.draw_lists() {
let vtx_buffer = draw_list.vtx_buffer();
let idx_buffer = draw_list.idx_buffer();
let next_vertex_offset = global_vertex_offset + vtx_buffer.len();
let next_index_offset = global_index_offset + idx_buffer.len();
if next_vertex_offset > Self::VERTEX_COUNT_PER_FRAME
|| next_index_offset > Self::INDEX_COUNT_PER_FRAME
{
break;
}
unsafe {
vertex_base
.add(global_vertex_offset)
.copy_from_nonoverlapping(vtx_buffer.as_ptr(), vtx_buffer.len());
index_base
.add(global_index_offset)
.copy_from_nonoverlapping(idx_buffer.as_ptr(), idx_buffer.len());
}
vertex_buffer
.flush(
global_vertex_offset * mem::size_of::<ImDrawVert>(),
vtx_buffer.len() * mem::size_of::<ImDrawVert>(),
)
.context(VkHelperBufferFlushVertexSnafu {})?;
index_buffer
.flush(
global_index_offset * mem::size_of::<u16>(),
idx_buffer.len() * mem::size_of::<u16>(),
)
.context(VkHelperBufferFlushIndexSnafu {})?;
let clip_off = draw_data.display_pos;
let clip_scale = [1., 1.]; for cmd in draw_list.commands() {
match cmd {
crate::imgui::DrawCmd::Elements {
count,
cmd_params:
crate::imgui::DrawCmdParams {
clip_rect,
texture_id,
vtx_offset: _,
idx_offset: _,
},
} => {
let offset = vk::Offset2D::builder()
.x((((clip_rect[0] - clip_off[0]) * clip_scale[0]) as i32).max(0))
.y((((clip_rect[1] - clip_off[1]) * clip_scale[1]) as i32).max(0));
let extent = vk::Extent2D::builder()
.width(
(((clip_rect[2] - clip_off[0]) * clip_scale[0]) as i32 - offset.x)
as u32,
)
.height(
(((clip_rect[3] - clip_off[1]) * clip_scale[1]) as i32 - offset.y)
as u32,
);
if offset.x < width as i32
&& offset.y < height as i32
&& extent.width as i32 >= -offset.x
&& extent.height as i32 >= -offset.y
{
let texture_id = texture_id.id();
let scissor = vk::Rect2D::builder().offset(*offset).extent(*extent);
unsafe {
command_buffer.command_pool.device.cmd_set_scissor(
***command_buffer,
0,
slice::from_ref(&scissor),
);
};
if current_texture_id != Some(texture_id) {
let index = tex_ids
.iter_mut()
.enumerate()
.filter_map(|(i, x)| x.as_mut().map(|(x, age)| (i, *x, age)))
.find_map(|(i, x, age)| {
if texture_id == x {
*age = 5;
Some(i)
} else {
None
}
})
.unwrap_or_else(|| {
tex_ids_dirty = true;
let add_me = Some((texture_id, 5));
if let Some((id, object)) = tex_ids
.iter_mut()
.enumerate()
.find(|(_, x)| x.is_none())
{
*object = add_me;
id
} else {
let ret = tex_ids.len() as _;
tex_ids.push(add_me);
ret
}
})
.to_ne_bytes();
unsafe {
command_buffer.command_pool.device.cmd_push_constants(
***command_buffer,
**self.pipeline_layout,
vk::ShaderStageFlags::FRAGMENT,
16,
&index,
)
};
current_texture_id = Some(texture_id);
}
unsafe {
command_buffer.command_pool.device.cmd_draw_indexed(
***command_buffer,
count as u32,
1,
(global_index_offset) as u32,
(global_vertex_offset) as i32,
0,
);
};
}
global_index_offset += count;
}
crate::imgui::DrawCmd::ResetRenderState => (), crate::imgui::DrawCmd::RawCallback { .. } => (), };
}
global_vertex_offset = next_vertex_offset;
assert_eq!(global_index_offset, next_index_offset);
}
if tex_ids_dirty {
let images = tex_ids
.iter_mut()
.map(|x| match x {
Some((_, 0)) => {
*x = None;
(
self.fonts_texture_sampler.clone(),
self.fonts_texture_image_view.clone(),
vk::DescriptorImageInfo::builder()
.image_layout(vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL),
)
}
None => (
self.fonts_texture_sampler.clone(),
self.fonts_texture_image_view.clone(),
vk::DescriptorImageInfo::builder()
.image_layout(vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL),
),
Some((texture_id, _)) => unsafe {
let texture = *texture_id as *const Texture<T>;
(
(*texture).sampler.clone(),
(*texture).image_view.clone(),
vk::DescriptorImageInfo::builder()
.image_layout((*texture).image_layout),
)
},
})
.collect::<Vec<_>>();
self.descriptor_set
.write_combined_images(images, vk::WriteDescriptorSet::builder())
.context(VkHelperDescriptorSetWriteCombinedImagesSnafu {})?;
}
self.frame_index
.replace_with(|frame_index| (1 + *frame_index) % 2);
Ok(())
}
}