use concinnity_core::gfx::transform::mat4_inverse;
use std::cell::Cell;
use std::ffi::CString;
use ash::vk;
use crate::vulkan::owned::{
OwnedDescriptorPool, OwnedFramebuffer, OwnedPipeline, OwnedPipelineLayout, OwnedRenderPass,
OwnedSetLayout, VkDevice,
};
use crate::gfx::decal::DecalRecord;
use super::allocator::{DeviceAllocator, PooledBuffer};
use super::context::VkContext;
use super::pipeline::spv_module;
use super::texture::GpuImage;
use crate::vulkan::slang_builtins::SlangCompile;
pub(in crate::vulkan) const MAX_DECALS: usize = 256;
const CUBE_VERTS: [f32; 24] = [
-0.5, -0.5, -0.5, 0.5, -0.5, -0.5, 0.5, 0.5, -0.5, -0.5, 0.5, -0.5, -0.5, -0.5, 0.5, 0.5, -0.5,
0.5, 0.5, 0.5, 0.5, -0.5, 0.5, 0.5,
];
const CUBE_INDICES: [u16; 36] = [
0, 2, 1, 0, 3, 2, 4, 5, 6, 4, 6, 7, 0, 1, 5, 0, 5, 4, 3, 6, 2, 3, 7, 6, 0, 4, 7, 0, 7, 3, 1, 2, 6, 1, 6, 5,
];
const PARAMS_STRIDE: u64 = 256;
#[derive(Copy, Clone)]
#[repr(C)]
struct DecalView {
vp: [[f32; 4]; 4],
inv_vp: [[f32; 4]; 4],
viewport: [f32; 2],
_pad: [f32; 2],
}
#[derive(Copy, Clone)]
#[repr(C)]
struct DecalParams {
model: [[f32; 4]; 4],
inv_model: [[f32; 4]; 4],
tint: [f32; 4],
fade: [f32; 4], }
pub(in crate::vulkan) struct DecalResources {
pub(in crate::vulkan) render_pass: OwnedRenderPass,
pub(in crate::vulkan) pipeline: OwnedPipeline,
pub(in crate::vulkan) pipeline_layout: OwnedPipelineLayout,
pub(in crate::vulkan) _view_set_layout: OwnedSetLayout,
pub(in crate::vulkan) _albedo_set_layout: OwnedSetLayout,
pub(in crate::vulkan) _descriptor_pool: OwnedDescriptorPool,
pub(in crate::vulkan) vertex_buffer: PooledBuffer,
pub(in crate::vulkan) index_buffer: PooledBuffer,
pub(in crate::vulkan) view_ubos: Vec<PooledBuffer>,
pub(in crate::vulkan) params_ubos: Vec<PooledBuffer>,
pub(in crate::vulkan) view_sets: Vec<vk::DescriptorSet>,
pub(in crate::vulkan) albedo_sets: Vec<vk::DescriptorSet>,
pub(in crate::vulkan) framebuffers: Vec<OwnedFramebuffer>,
pub(in crate::vulkan) sampler: vk::Sampler,
pub(in crate::vulkan) decal_texture_slots: Cell<[usize; MAX_DECALS]>,
}
#[derive(Clone, Copy)]
pub(in crate::vulkan) struct DecalDeviceContext<'a> {
pub(in crate::vulkan) alloc: &'a DeviceAllocator,
pub(in crate::vulkan) device: &'a VkDevice,
pub(in crate::vulkan) command_pool: vk::CommandPool,
pub(in crate::vulkan) queue: vk::Queue,
}
#[derive(Clone, Copy)]
pub(in crate::vulkan) struct DecalPassTargets<'a> {
pub(in crate::vulkan) hdr_format: vk::Format,
pub(in crate::vulkan) hdr_resolve_views: &'a [vk::ImageView],
pub(in crate::vulkan) depth_views: &'a [vk::ImageView],
pub(in crate::vulkan) sampler: vk::Sampler,
pub(in crate::vulkan) extent: vk::Extent2D,
}
impl DecalResources {
pub(in crate::vulkan) fn new(
ctx: DecalDeviceContext,
targets: DecalPassTargets,
frames: usize,
msaa: bool,
hot_reload: bool,
) -> Result<Self, String> {
let DecalDeviceContext {
alloc,
device,
command_pool,
queue,
} = ctx;
let DecalPassTargets {
hdr_format,
hdr_resolve_views,
depth_views,
sampler,
extent,
} = targets;
let render_pass = create_decal_render_pass(device, hdr_format)?;
let (view_set_layout, albedo_set_layout) = create_decal_set_layouts(device)?;
let pipeline_layout = create_decal_pipeline_layout(
device,
view_set_layout.handle(),
albedo_set_layout.handle(),
)?;
let (vert_spv, frag_spv) = compile_decal_shaders(hot_reload, msaa)?;
let pipeline = create_decal_pipeline(
device,
render_pass.handle(),
pipeline_layout.handle(),
&vert_spv,
&frag_spv,
)?;
let vertex_buffer = upload_static_buffer(
alloc,
device,
command_pool,
queue,
bytemuck_cast(&CUBE_VERTS),
vk::BufferUsageFlags::VERTEX_BUFFER,
)?;
let index_buffer = upload_static_buffer(
alloc,
device,
command_pool,
queue,
bytemuck_cast(&CUBE_INDICES),
vk::BufferUsageFlags::INDEX_BUFFER,
)?;
let mut view_ubos = Vec::with_capacity(frames);
for _ in 0..frames {
view_ubos.push(alloc.create_buffer(
std::mem::size_of::<DecalView>() as u64,
vk::BufferUsageFlags::UNIFORM_BUFFER,
vk::MemoryPropertyFlags::HOST_VISIBLE | vk::MemoryPropertyFlags::HOST_COHERENT,
)?);
}
let params_total = PARAMS_STRIDE * MAX_DECALS as u64;
let mut params_ubos = Vec::with_capacity(frames);
for _ in 0..frames {
params_ubos.push(alloc.create_buffer(
params_total,
vk::BufferUsageFlags::UNIFORM_BUFFER,
vk::MemoryPropertyFlags::HOST_VISIBLE | vk::MemoryPropertyFlags::HOST_COHERENT,
)?);
}
let descriptor_pool = create_decal_descriptor_pool(device, frames)?;
let view_layouts: Vec<_> = (0..frames).map(|_| view_set_layout.handle()).collect();
let view_sets = alloc_descriptor_sets(device, descriptor_pool.handle(), &view_layouts)?;
for (i, &set) in view_sets.iter().enumerate() {
write_view_set(
device,
set,
view_ubos[i].buffer(),
params_ubos[i].buffer(),
depth_views[i.min(depth_views.len().saturating_sub(1))],
sampler,
);
}
let albedo_layouts: Vec<_> = (0..MAX_DECALS)
.map(|_| albedo_set_layout.handle())
.collect();
let albedo_sets = alloc_descriptor_sets(device, descriptor_pool.handle(), &albedo_layouts)?;
let mut framebuffers = Vec::with_capacity(frames);
for &view in hdr_resolve_views.iter().take(frames) {
let attachments = [view];
let fb_info = vk::FramebufferCreateInfo::default()
.render_pass(render_pass.handle())
.attachments(&attachments)
.width(extent.width.max(1))
.height(extent.height.max(1))
.layers(1);
let fb = device
.create_framebuffer(&fb_info)
.map_err(|e| format!("decal framebuffer: {e}"))?;
framebuffers.push(fb);
}
Ok(Self {
render_pass,
pipeline,
pipeline_layout,
_view_set_layout: view_set_layout,
_albedo_set_layout: albedo_set_layout,
_descriptor_pool: descriptor_pool,
vertex_buffer,
index_buffer,
view_ubos,
params_ubos,
view_sets,
albedo_sets,
framebuffers,
sampler,
decal_texture_slots: Cell::new([usize::MAX; MAX_DECALS]),
})
}
pub(in crate::vulkan) fn rebuild(
&mut self,
device: &VkDevice,
hdr_resolve_views: &[vk::ImageView],
depth_views: &[vk::ImageView],
extent: vk::Extent2D,
) -> Result<(), String> {
self.framebuffers.clear();
for &view in hdr_resolve_views.iter().take(self.view_ubos.len()) {
let attachments = [view];
let fb_info = vk::FramebufferCreateInfo::default()
.render_pass(self.render_pass.handle())
.attachments(&attachments)
.width(extent.width.max(1))
.height(extent.height.max(1))
.layers(1);
let fb = device
.create_framebuffer(&fb_info)
.map_err(|e| format!("decal framebuffer (rebuild): {e}"))?;
self.framebuffers.push(fb);
}
for (i, &set) in self.view_sets.iter().enumerate() {
let depth_info = vk::DescriptorImageInfo::default()
.image_layout(vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL)
.image_view(depth_views[i.min(depth_views.len().saturating_sub(1))])
.sampler(self.sampler);
let write = vk::WriteDescriptorSet::default()
.dst_set(set)
.dst_binding(2)
.descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER)
.image_info(std::slice::from_ref(&depth_info));
unsafe { device.update_descriptor_sets(std::slice::from_ref(&write), &[]) };
}
Ok(())
}
pub(in crate::vulkan) fn destroy(&mut self, _device: &VkDevice) {
self.framebuffers.clear();
self.view_ubos.clear();
self.params_ubos.clear();
self.vertex_buffer = PooledBuffer::null();
self.index_buffer = PooledBuffer::null();
}
}
fn create_decal_render_pass(
device: &VkDevice,
format: vk::Format,
) -> Result<OwnedRenderPass, String> {
let attachment = vk::AttachmentDescription::default()
.format(format)
.samples(vk::SampleCountFlags::TYPE_1)
.load_op(vk::AttachmentLoadOp::LOAD)
.store_op(vk::AttachmentStoreOp::STORE)
.stencil_load_op(vk::AttachmentLoadOp::DONT_CARE)
.stencil_store_op(vk::AttachmentStoreOp::DONT_CARE)
.initial_layout(vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL)
.final_layout(vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL);
let color_ref = vk::AttachmentReference::default()
.attachment(0)
.layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL);
let subpass = vk::SubpassDescription::default()
.pipeline_bind_point(vk::PipelineBindPoint::GRAPHICS)
.color_attachments(std::slice::from_ref(&color_ref));
let dep_in = vk::SubpassDependency::default()
.src_subpass(vk::SUBPASS_EXTERNAL)
.dst_subpass(0)
.src_stage_mask(
vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT
| vk::PipelineStageFlags::FRAGMENT_SHADER,
)
.src_access_mask(vk::AccessFlags::SHADER_READ | vk::AccessFlags::COLOR_ATTACHMENT_WRITE)
.dst_stage_mask(vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT)
.dst_access_mask(
vk::AccessFlags::COLOR_ATTACHMENT_WRITE | vk::AccessFlags::COLOR_ATTACHMENT_READ,
);
let dep_out = vk::SubpassDependency::default()
.src_subpass(0)
.dst_subpass(vk::SUBPASS_EXTERNAL)
.src_stage_mask(vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT)
.src_access_mask(vk::AccessFlags::COLOR_ATTACHMENT_WRITE)
.dst_stage_mask(vk::PipelineStageFlags::FRAGMENT_SHADER)
.dst_access_mask(vk::AccessFlags::SHADER_READ);
let deps = [dep_in, dep_out];
let info = vk::RenderPassCreateInfo::default()
.attachments(std::slice::from_ref(&attachment))
.subpasses(std::slice::from_ref(&subpass))
.dependencies(&deps);
device
.create_render_pass(&info)
.map_err(|e| format!("decal render pass: {e}"))
}
fn create_decal_set_layouts(device: &VkDevice) -> Result<(OwnedSetLayout, OwnedSetLayout), String> {
let view_bindings = [
vk::DescriptorSetLayoutBinding::default()
.binding(0)
.descriptor_type(vk::DescriptorType::UNIFORM_BUFFER)
.descriptor_count(1)
.stage_flags(vk::ShaderStageFlags::VERTEX | vk::ShaderStageFlags::FRAGMENT),
vk::DescriptorSetLayoutBinding::default()
.binding(1)
.descriptor_type(vk::DescriptorType::UNIFORM_BUFFER_DYNAMIC)
.descriptor_count(1)
.stage_flags(vk::ShaderStageFlags::VERTEX | vk::ShaderStageFlags::FRAGMENT),
vk::DescriptorSetLayoutBinding::default()
.binding(2)
.descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER)
.descriptor_count(1)
.stage_flags(vk::ShaderStageFlags::FRAGMENT),
];
let view_info = vk::DescriptorSetLayoutCreateInfo::default().bindings(&view_bindings);
let view_set_layout = device
.create_descriptor_set_layout(&view_info)
.map_err(|e| format!("decal view set layout: {e}"))?;
let albedo_bindings = [vk::DescriptorSetLayoutBinding::default()
.binding(0)
.descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER)
.descriptor_count(1)
.stage_flags(vk::ShaderStageFlags::FRAGMENT)];
let albedo_info = vk::DescriptorSetLayoutCreateInfo::default().bindings(&albedo_bindings);
let albedo_set_layout = device
.create_descriptor_set_layout(&albedo_info)
.map_err(|e| format!("decal albedo set layout: {e}"))?;
Ok((view_set_layout, albedo_set_layout))
}
fn create_decal_pipeline_layout(
device: &VkDevice,
view_set_layout: vk::DescriptorSetLayout,
albedo_set_layout: vk::DescriptorSetLayout,
) -> Result<OwnedPipelineLayout, String> {
let set_layouts = [view_set_layout, albedo_set_layout];
let info = vk::PipelineLayoutCreateInfo::default().set_layouts(&set_layouts);
device
.create_pipeline_layout(&info)
.map_err(|e| format!("decal pipeline layout: {e}"))
}
fn create_decal_descriptor_pool(
device: &VkDevice,
frames: usize,
) -> Result<OwnedDescriptorPool, String> {
let frames = frames as u32;
let max_decals = MAX_DECALS as u32;
let sizes = [
vk::DescriptorPoolSize {
ty: vk::DescriptorType::UNIFORM_BUFFER,
descriptor_count: frames,
},
vk::DescriptorPoolSize {
ty: vk::DescriptorType::UNIFORM_BUFFER_DYNAMIC,
descriptor_count: frames,
},
vk::DescriptorPoolSize {
ty: vk::DescriptorType::COMBINED_IMAGE_SAMPLER,
descriptor_count: frames + max_decals,
},
];
let info = vk::DescriptorPoolCreateInfo::default()
.max_sets(frames + max_decals)
.pool_sizes(&sizes);
device
.create_descriptor_pool(&info)
.map_err(|e| format!("decal descriptor pool: {e}"))
}
fn alloc_descriptor_sets(
device: &VkDevice,
pool: vk::DescriptorPool,
layouts: &[vk::DescriptorSetLayout],
) -> Result<Vec<vk::DescriptorSet>, String> {
let info = vk::DescriptorSetAllocateInfo::default()
.descriptor_pool(pool)
.set_layouts(layouts);
unsafe { device.allocate_descriptor_sets(&info) }
.map_err(|e| format!("decal descriptor sets: {e}"))
}
fn write_view_set(
device: &VkDevice,
set: vk::DescriptorSet,
view_ubo: vk::Buffer,
params_ubo: vk::Buffer,
depth_view: vk::ImageView,
sampler: vk::Sampler,
) {
let view_info = vk::DescriptorBufferInfo::default()
.buffer(view_ubo)
.offset(0)
.range(std::mem::size_of::<DecalView>() as u64);
let params_info = vk::DescriptorBufferInfo::default()
.buffer(params_ubo)
.offset(0)
.range(PARAMS_STRIDE);
let depth_info = vk::DescriptorImageInfo::default()
.image_layout(vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL)
.image_view(depth_view)
.sampler(sampler);
let writes = [
vk::WriteDescriptorSet::default()
.dst_set(set)
.dst_binding(0)
.descriptor_type(vk::DescriptorType::UNIFORM_BUFFER)
.buffer_info(std::slice::from_ref(&view_info)),
vk::WriteDescriptorSet::default()
.dst_set(set)
.dst_binding(1)
.descriptor_type(vk::DescriptorType::UNIFORM_BUFFER_DYNAMIC)
.buffer_info(std::slice::from_ref(¶ms_info)),
vk::WriteDescriptorSet::default()
.dst_set(set)
.dst_binding(2)
.descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER)
.image_info(std::slice::from_ref(&depth_info)),
];
unsafe { device.update_descriptor_sets(&writes, &[]) };
}
fn compile_decal_shaders(hot_reload: bool, msaa: bool) -> Result<(Vec<u8>, Vec<u8>), String> {
let ctx = super::builtins::Ctx {
msaa,
..super::builtins::Ctx::plain(hot_reload)
};
let vert = super::slang_builtins::DECAL_VERT.compile(&ctx)?;
let frag = super::slang_builtins::DECAL_FRAG.compile(&ctx)?;
Ok((vert, frag))
}
pub(in crate::vulkan) fn rebuild_decal_pipeline(
device: &VkDevice,
decals: &DecalResources,
msaa: bool,
hot_reload: bool,
) -> Result<OwnedPipeline, String> {
let (vert_spv, frag_spv) = compile_decal_shaders(hot_reload, msaa)?;
create_decal_pipeline(
device,
decals.render_pass.handle(),
decals.pipeline_layout.handle(),
&vert_spv,
&frag_spv,
)
}
fn create_decal_pipeline(
device: &VkDevice,
render_pass: vk::RenderPass,
layout: vk::PipelineLayout,
vert_spv: &[u8],
frag_spv: &[u8],
) -> Result<OwnedPipeline, String> {
let vert = spv_module(device, vert_spv)?;
let frag = spv_module(device, frag_spv)?;
let entry = CString::new("main").unwrap();
let stages = [
vk::PipelineShaderStageCreateInfo::default()
.stage(vk::ShaderStageFlags::VERTEX)
.module(vert.handle())
.name(&entry),
vk::PipelineShaderStageCreateInfo::default()
.stage(vk::ShaderStageFlags::FRAGMENT)
.module(frag.handle())
.name(&entry),
];
let bindings = [vk::VertexInputBindingDescription::default()
.binding(0)
.stride(12) .input_rate(vk::VertexInputRate::VERTEX)];
let attrs = [vk::VertexInputAttributeDescription::default()
.location(0)
.binding(0)
.format(vk::Format::R32G32B32_SFLOAT)
.offset(0)];
let vertex_input = vk::PipelineVertexInputStateCreateInfo::default()
.vertex_binding_descriptions(&bindings)
.vertex_attribute_descriptions(&attrs);
let input_assembly = vk::PipelineInputAssemblyStateCreateInfo::default()
.topology(vk::PrimitiveTopology::TRIANGLE_LIST);
let viewport_state = vk::PipelineViewportStateCreateInfo::default()
.viewport_count(1)
.scissor_count(1);
let raster = vk::PipelineRasterizationStateCreateInfo::default()
.polygon_mode(vk::PolygonMode::FILL)
.cull_mode(vk::CullModeFlags::FRONT)
.front_face(vk::FrontFace::COUNTER_CLOCKWISE)
.line_width(1.0);
let multisample = vk::PipelineMultisampleStateCreateInfo::default()
.rasterization_samples(vk::SampleCountFlags::TYPE_1);
let depth_stencil = vk::PipelineDepthStencilStateCreateInfo::default()
.depth_test_enable(false)
.depth_write_enable(false);
let blend_attachment = vk::PipelineColorBlendAttachmentState::default()
.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::SRC_ALPHA)
.dst_alpha_blend_factor(vk::BlendFactor::ONE_MINUS_SRC_ALPHA)
.alpha_blend_op(vk::BlendOp::ADD)
.color_write_mask(vk::ColorComponentFlags::RGBA);
let blend_attachments = [blend_attachment];
let blend_state = vk::PipelineColorBlendStateCreateInfo::default()
.logic_op_enable(false)
.attachments(&blend_attachments);
let dynamic_states = [vk::DynamicState::VIEWPORT, vk::DynamicState::SCISSOR];
let dynamic = vk::PipelineDynamicStateCreateInfo::default().dynamic_states(&dynamic_states);
let info = vk::GraphicsPipelineCreateInfo::default()
.stages(&stages)
.vertex_input_state(&vertex_input)
.input_assembly_state(&input_assembly)
.viewport_state(&viewport_state)
.rasterization_state(&raster)
.multisample_state(&multisample)
.depth_stencil_state(&depth_stencil)
.color_blend_state(&blend_state)
.dynamic_state(&dynamic)
.layout(layout)
.render_pass(render_pass);
let pipeline = crate::vulkan::pipeline_cache::create_graphics_pipeline(device, &info)
.map_err(|e| format!("create decal pipeline: {e}"))?;
Ok(pipeline)
}
fn upload_static_buffer(
alloc: &DeviceAllocator,
device: &VkDevice,
command_pool: vk::CommandPool,
queue: vk::Queue,
data: &[u8],
usage: vk::BufferUsageFlags,
) -> Result<PooledBuffer, String> {
let size = data.len() as vk::DeviceSize;
let staging = alloc.create_buffer(
size,
vk::BufferUsageFlags::TRANSFER_SRC,
vk::MemoryPropertyFlags::HOST_VISIBLE | vk::MemoryPropertyFlags::HOST_COHERENT,
)?;
staging.write_bytes(0, data);
let buf = alloc.create_buffer(
size,
usage | vk::BufferUsageFlags::TRANSFER_DST,
vk::MemoryPropertyFlags::DEVICE_LOCAL,
)?;
super::texture::one_shot_submit(device, command_pool, queue, |cmd| {
let region = vk::BufferCopy::default().size(size);
unsafe {
device.cmd_copy_buffer(
cmd,
staging.buffer(),
buf.buffer(),
std::slice::from_ref(®ion),
)
};
})?;
Ok(buf)
}
fn bytemuck_cast<T: bytemuck::NoUninit>(s: &[T]) -> &[u8] {
bytemuck::cast_slice(s)
}
impl VkContext {
pub(in crate::vulkan) fn encode_decals(
&self,
cmd: vk::CommandBuffer,
frame_idx: usize,
vp: [[f32; 4]; 4],
frustum: &crate::gfx::frustum::Frustum,
) {
let decals = match &self.decal.resources {
Some(s) => s,
None => return,
};
if self.decal.records.iter().all(|slot| slot.is_none()) {
return;
}
let visible_count = self
.decal
.records
.iter()
.filter(|slot| {
slot.as_ref()
.map(|d| {
let (mn, mx) = d.aabb();
frustum.intersects_aabb(mn, mx)
})
.unwrap_or(false)
})
.count();
if visible_count == 0 {
return;
}
let device = &self.device;
let extent = self.render_extent;
let inv_vp = mat4_inverse(vp);
let viewport_pix = [extent.width as f32, extent.height as f32];
let view_uni = DecalView {
vp,
inv_vp,
viewport: viewport_pix,
_pad: [0.0; 2],
};
decals.view_ubos[frame_idx].write_val(0, &view_uni);
for (i, slot) in self.decal.records.iter().enumerate() {
let d = match slot {
Some(d) => d,
None => continue,
};
let params = DecalParams {
model: d.model,
inv_model: d.inv_model,
tint: d.tint,
fade: [2.0, 0.0, 0.0, 0.0],
};
decals.params_ubos[frame_idx].write_val(i * PARAMS_STRIDE as usize, ¶ms);
}
let rp_begin = vk::RenderPassBeginInfo::default()
.render_pass(decals.render_pass.handle())
.framebuffer(decals.framebuffers[frame_idx].handle())
.render_area(vk::Rect2D::default().extent(extent));
let vp_state = vk::Viewport {
x: 0.0,
y: extent.height as f32,
width: extent.width as f32,
height: -(extent.height as f32),
min_depth: 0.0,
max_depth: 1.0,
};
let scissor = vk::Rect2D::default().extent(extent);
unsafe {
device.cmd_begin_render_pass(cmd, &rp_begin, vk::SubpassContents::INLINE);
device.cmd_set_viewport(cmd, 0, std::slice::from_ref(&vp_state));
device.cmd_set_scissor(cmd, 0, std::slice::from_ref(&scissor));
device.cmd_bind_pipeline(
cmd,
vk::PipelineBindPoint::GRAPHICS,
decals.pipeline.handle(),
);
device.cmd_bind_vertex_buffers(cmd, 0, &[decals.vertex_buffer.buffer()], &[0]);
device.cmd_bind_index_buffer(
cmd,
decals.index_buffer.buffer(),
0,
vk::IndexType::UINT16,
);
}
for (i, slot) in self.decal.records.iter().enumerate() {
let d = match slot {
Some(d) => d,
None => continue,
};
let (mn, mx) = d.aabb();
if !frustum.intersects_aabb(mn, mx) {
continue;
}
let dynamic_offset = (i as u64 * PARAMS_STRIDE) as u32;
unsafe {
device.cmd_bind_descriptor_sets(
cmd,
vk::PipelineBindPoint::GRAPHICS,
decals.pipeline_layout.handle(),
0,
std::slice::from_ref(&decals.view_sets[frame_idx]),
std::slice::from_ref(&dynamic_offset),
);
device.cmd_bind_descriptor_sets(
cmd,
vk::PipelineBindPoint::GRAPHICS,
decals.pipeline_layout.handle(),
1,
std::slice::from_ref(&decals.albedo_sets[i]),
&[],
);
device.cmd_draw_indexed(cmd, 36, 1, 0, 0, 0);
}
self.inc_draw_calls(1);
}
unsafe {
device.cmd_end_render_pass(cmd);
}
}
}
impl VkContext {
pub(crate) fn add_decal(&mut self, record: DecalRecord) -> Result<usize, String> {
let last_tex = self.textures.len().saturating_sub(1);
let tex_idx = record.texture_slot.min(last_tex);
let id = if let Some(slot) = self.decal.free_slots.pop() {
self.decal.records[slot] = Some(record);
slot
} else {
if self.decal.records.len() >= MAX_DECALS {
return Err(format!("add_decal: MAX_DECALS ({MAX_DECALS}) exceeded"));
}
self.decal.records.push(Some(record));
self.decal.records.len() - 1
};
let decals = self
.decal
.resources
.as_ref()
.ok_or_else(|| "add_decal: decal pipeline unavailable".to_string())?;
write_albedo_set(
&self.device,
decals.albedo_sets[id],
self.textures[tex_idx].view,
decals.sampler,
);
let mut slots = decals.decal_texture_slots.get();
slots[id] = tex_idx;
decals.decal_texture_slots.set(slots);
Ok(id)
}
pub(crate) fn remove_decal(&mut self, decal_id: usize) -> Result<(), String> {
let slot = self
.decal
.records
.get_mut(decal_id)
.ok_or_else(|| format!("remove_decal: id {decal_id} out of range"))?;
if slot.is_none() {
return Err(format!("remove_decal: id {decal_id} already removed"));
}
*slot = None;
self.decal.free_slots.push(decal_id);
if let Some(decals) = &self.decal.resources {
let mut slots = decals.decal_texture_slots.get();
slots[decal_id] = usize::MAX;
decals.decal_texture_slots.set(slots);
}
Ok(())
}
pub(in crate::vulkan) fn decal_samples_slot(&self, slot: usize) -> bool {
match &self.decal.resources {
Some(s) => s.decal_texture_slots.get().contains(&slot),
None => false,
}
}
pub(in crate::vulkan) fn rewrite_decal_albedo_slot(&self, slot: usize) {
let decals = match &self.decal.resources {
Some(s) => s,
None => return,
};
let slots = decals.decal_texture_slots.get();
let last_tex = self.textures.len().saturating_sub(1);
for (id, &tex_slot) in slots.iter().enumerate() {
if tex_slot == slot {
let view = self.textures[tex_slot.min(last_tex)].view;
write_albedo_set(&self.device, decals.albedo_sets[id], view, decals.sampler);
}
}
}
}
fn write_albedo_set(
device: &VkDevice,
set: vk::DescriptorSet,
view: vk::ImageView,
sampler: vk::Sampler,
) {
let info = vk::DescriptorImageInfo::default()
.image_layout(vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL)
.image_view(view)
.sampler(sampler);
let write = vk::WriteDescriptorSet::default()
.dst_set(set)
.dst_binding(0)
.descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER)
.image_info(std::slice::from_ref(&info));
unsafe { device.update_descriptor_sets(std::slice::from_ref(&write), &[]) };
}
impl VkContext {
pub(in crate::vulkan) fn upload_initial_decals(
&mut self,
records: Vec<DecalRecord>,
) -> Result<(), String> {
if records.len() > MAX_DECALS {
return Err(format!(
"decals: {} authored decals exceed MAX_DECALS ({})",
records.len(),
MAX_DECALS
));
}
for rec in records {
self.add_decal(rec)?;
}
Ok(())
}
}
type _Marker = GpuImage;