use ash::vk;
use crate::vulkan::owned::{
OwnedDescriptorPool, OwnedFramebuffer, OwnedPipeline, OwnedPipelineLayout, OwnedRenderPass,
OwnedSampler, OwnedSetLayout, VkDevice,
};
use crate::gfx::render_types::RtParams;
use crate::gfx::rt_reflections::{RtParamsInputs, RtReflectionSettings};
use super::super::allocator::{DeviceAllocator, PooledBuffer};
use super::super::context::{HDR_FORMAT, VkContext};
use super::super::pipeline::*;
use super::super::resources::{alloc_descriptor_sets, create_descriptor_set_layout};
use super::super::texture::*;
use crate::vulkan::slang_builtins::SlangCompile;
pub(in crate::vulkan) struct RtShaders {
pub vs: Vec<u8>,
pub flat_fs: Vec<u8>,
pub textured_fs: Option<Vec<u8>>,
}
pub(in crate::vulkan) fn compile_rt_shaders(
hot_reload: bool,
pool_size: usize,
probe_cube_count: u32,
) -> Result<RtShaders, String> {
use super::super::{builtins, slang_builtins};
let ctx = builtins::Ctx {
hot_reload,
msaa: false,
pool_size: pool_size.max(1),
probe_count: probe_cube_count as usize,
};
let vs = slang_builtins::FULLSCREEN_VERT.compile(&ctx)?;
let flat_fs = slang_builtins::RT_REFLECTIONS_FRAG.compile(&ctx)?;
let textured_fs = if pool_size > 0 {
Some(slang_builtins::RT_REFLECTIONS_FRAG_TEXTURED.compile(&ctx)?)
} else {
None
};
Ok(RtShaders {
vs,
flat_fs,
textured_fs,
})
}
pub(in crate::vulkan) struct RtReflectionsResources {
pub(in crate::vulkan) settings: RtReflectionSettings,
pub(in crate::vulkan) output: GpuImage,
render_pass: OwnedRenderPass,
framebuffer: OwnedFramebuffer,
_set_layout: OwnedSetLayout,
layout_flat: OwnedPipelineLayout,
layout_textured: Option<OwnedPipelineLayout>,
flat_pso: OwnedPipeline,
textured_pso: Option<OwnedPipeline>,
params_buffers: Vec<PooledBuffer>,
_descriptor_pool: OwnedDescriptorPool,
resolve_sets: Vec<vk::DescriptorSet>,
sampler: OwnedSampler,
dummy_ssbo: PooledBuffer,
pool_size: usize,
probe_cube_count: u32,
}
unsafe impl Send for RtReflectionsResources {}
fn create_rt_render_pass(device: &VkDevice) -> Result<OwnedRenderPass, String> {
let attachment = vk::AttachmentDescription::default()
.format(HDR_FORMAT)
.samples(vk::SampleCountFlags::TYPE_1)
.load_op(vk::AttachmentLoadOp::DONT_CARE)
.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::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 = 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::COLOR_ATTACHMENT_WRITE | vk::AccessFlags::SHADER_READ)
.dst_stage_mask(
vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT
| vk::PipelineStageFlags::FRAGMENT_SHADER,
)
.dst_access_mask(vk::AccessFlags::COLOR_ATTACHMENT_WRITE | vk::AccessFlags::SHADER_READ);
let info = vk::RenderPassCreateInfo::default()
.attachments(std::slice::from_ref(&attachment))
.subpasses(std::slice::from_ref(&subpass))
.dependencies(std::slice::from_ref(&dep));
device
.create_render_pass(&info)
.map_err(|e| format!("RT reflections render pass: {e}"))
}
fn create_rt_pipeline(
device: &VkDevice,
render_pass: vk::RenderPass,
layout: vk::PipelineLayout,
vert_spv: &[u8],
frag_spv: &[u8],
) -> Result<OwnedPipeline, String> {
let vert_mod = spv_module(device, vert_spv)?;
let frag_mod = spv_module(device, frag_spv)?;
let entry = std::ffi::CString::new("main").unwrap();
let stages = [
vk::PipelineShaderStageCreateInfo::default()
.stage(vk::ShaderStageFlags::VERTEX)
.module(vert_mod.handle())
.name(&entry),
vk::PipelineShaderStageCreateInfo::default()
.stage(vk::ShaderStageFlags::FRAGMENT)
.module(frag_mod.handle())
.name(&entry),
];
let vert_input = vk::PipelineVertexInputStateCreateInfo::default();
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)
.line_width(1.0)
.cull_mode(vk::CullModeFlags::NONE)
.front_face(vk::FrontFace::COUNTER_CLOCKWISE);
let multisample = vk::PipelineMultisampleStateCreateInfo::default()
.rasterization_samples(vk::SampleCountFlags::TYPE_1);
let depth = vk::PipelineDepthStencilStateCreateInfo::default()
.depth_test_enable(false)
.depth_write_enable(false)
.depth_compare_op(vk::CompareOp::ALWAYS);
let blend_attach = vk::PipelineColorBlendAttachmentState::default()
.color_write_mask(vk::ColorComponentFlags::RGBA)
.blend_enable(false);
let blend = vk::PipelineColorBlendStateCreateInfo::default()
.attachments(std::slice::from_ref(&blend_attach));
let dyn_states = [vk::DynamicState::VIEWPORT, vk::DynamicState::SCISSOR];
let dynamic = vk::PipelineDynamicStateCreateInfo::default().dynamic_states(&dyn_states);
let info = vk::GraphicsPipelineCreateInfo::default()
.stages(&stages)
.vertex_input_state(&vert_input)
.input_assembly_state(&input_assembly)
.viewport_state(&viewport_state)
.rasterization_state(&raster)
.multisample_state(&multisample)
.depth_stencil_state(&depth)
.color_blend_state(&blend)
.dynamic_state(&dynamic)
.layout(layout)
.render_pass(render_pass)
.subpass(0);
let pipeline = crate::vulkan::pipeline_cache::create_graphics_pipeline(device, &info)
.map_err(|e| format!("create rt reflections pso: {e}"))?;
Ok(pipeline)
}
pub(in crate::vulkan) struct RebuiltRtPipelines {
flat: OwnedPipeline,
textured: Option<OwnedPipeline>,
}
pub(in crate::vulkan) fn rebuild_rt_pipelines(
device: &VkDevice,
rt: &RtReflectionsResources,
hot_reload: bool,
) -> Result<RebuiltRtPipelines, String> {
let shaders = compile_rt_shaders(hot_reload, rt.pool_size, rt.probe_cube_count)?;
let flat = create_rt_pipeline(
device,
rt.render_pass.handle(),
rt.layout_flat.handle(),
&shaders.vs,
&shaders.flat_fs,
)?;
let textured = match (rt.layout_textured.as_ref(), &shaders.textured_fs) {
(Some(layout), Some(fs)) => Some(create_rt_pipeline(
device,
rt.render_pass.handle(),
layout.handle(),
&shaders.vs,
fs,
)?),
_ => None,
};
Ok(RebuiltRtPipelines { flat, textured })
}
pub(in crate::vulkan) struct RtBuild<'a> {
pub alloc: &'a DeviceAllocator,
pub device: &'a VkDevice,
pub width: u32,
pub height: u32,
pub frames: usize,
}
pub(in crate::vulkan) struct RtStaticInputs<'a> {
pub vertex_buffer: vk::Buffer,
pub index_buffer: vk::Buffer,
pub hdr_resolve_views: &'a [vk::ImageView],
pub gbuffer_views: &'a [vk::ImageView],
pub roughness_views: &'a [vk::ImageView],
pub prefilter_view: vk::ImageView,
pub cube_sampler: vk::Sampler,
}
#[derive(Clone, Copy)]
pub(in crate::vulkan) struct RtAccelHandles {
pub tlas: vk::AccelerationStructureKHR,
pub geom_buffer: vk::Buffer,
pub geom_size: vk::DeviceSize,
pub deformed_verts: vk::Buffer,
pub skinned_indices: vk::Buffer,
}
#[derive(Clone, Copy)]
pub(in crate::vulkan) struct RtLayoutConfig {
pub bindless_set_layout: Option<vk::DescriptorSetLayout>,
pub global_set_layout: vk::DescriptorSetLayout,
pub probe_cube_count: u32,
pub pool_size: usize,
pub hot_reload: bool,
}
impl RtReflectionsResources {
pub(in crate::vulkan) fn new(
build: RtBuild,
settings: RtReflectionSettings,
static_inputs: RtStaticInputs,
accel: RtAccelHandles,
layout: RtLayoutConfig,
) -> Result<Self, String> {
let RtBuild {
alloc,
device,
width,
height,
frames,
} = build;
let RtStaticInputs {
vertex_buffer,
index_buffer,
hdr_resolve_views,
gbuffer_views,
roughness_views,
prefilter_view,
cube_sampler,
} = static_inputs;
let RtAccelHandles {
tlas,
geom_buffer,
geom_size,
deformed_verts,
skinned_indices,
} = accel;
let RtLayoutConfig {
bindless_set_layout,
global_set_layout,
probe_cube_count,
pool_size,
hot_reload,
} = layout;
let render_pass = create_rt_render_pass(device)?;
let set_layout = create_descriptor_set_layout(
device,
&[
(
0,
vk::DescriptorType::UNIFORM_BUFFER,
vk::ShaderStageFlags::FRAGMENT,
),
(
1,
vk::DescriptorType::ACCELERATION_STRUCTURE_KHR,
vk::ShaderStageFlags::FRAGMENT,
),
(
2,
vk::DescriptorType::STORAGE_BUFFER,
vk::ShaderStageFlags::FRAGMENT,
),
(
3,
vk::DescriptorType::STORAGE_BUFFER,
vk::ShaderStageFlags::FRAGMENT,
),
(
4,
vk::DescriptorType::STORAGE_BUFFER,
vk::ShaderStageFlags::FRAGMENT,
),
(
5,
vk::DescriptorType::COMBINED_IMAGE_SAMPLER,
vk::ShaderStageFlags::FRAGMENT,
),
(
6,
vk::DescriptorType::COMBINED_IMAGE_SAMPLER,
vk::ShaderStageFlags::FRAGMENT,
),
(
7,
vk::DescriptorType::COMBINED_IMAGE_SAMPLER,
vk::ShaderStageFlags::FRAGMENT,
),
(
8,
vk::DescriptorType::COMBINED_IMAGE_SAMPLER,
vk::ShaderStageFlags::FRAGMENT,
),
(
9,
vk::DescriptorType::STORAGE_BUFFER,
vk::ShaderStageFlags::FRAGMENT,
),
(
10,
vk::DescriptorType::STORAGE_BUFFER,
vk::ShaderStageFlags::FRAGMENT,
),
],
)?;
let flat_layouts = [set_layout.handle(), global_set_layout];
let layout_flat = device
.create_pipeline_layout(
&vk::PipelineLayoutCreateInfo::default().set_layouts(&flat_layouts),
)
.map_err(|e| format!("rt flat pipeline layout: {e}"))?;
let layout_textured = if let Some(bsl) = bindless_set_layout {
let layouts = [set_layout.handle(), global_set_layout, bsl];
Some(
device
.create_pipeline_layout(
&vk::PipelineLayoutCreateInfo::default().set_layouts(&layouts),
)
.map_err(|e| format!("rt textured pipeline layout: {e}"))?,
)
} else {
None
};
let shaders = compile_rt_shaders(hot_reload, pool_size, probe_cube_count)?;
let flat_pso = create_rt_pipeline(
device,
render_pass.handle(),
layout_flat.handle(),
&shaders.vs,
&shaders.flat_fs,
)?;
let textured_pso = match (layout_textured.as_ref(), &shaders.textured_fs) {
(Some(layout), Some(fs)) => Some(create_rt_pipeline(
device,
render_pass.handle(),
layout.handle(),
&shaders.vs,
fs,
)?),
_ => None,
};
let params_size = std::mem::size_of::<RtParams>() as vk::DeviceSize;
let mut params_buffers = Vec::with_capacity(frames);
for _ in 0..frames {
let buf = alloc.create_buffer(
params_size,
vk::BufferUsageFlags::UNIFORM_BUFFER,
vk::MemoryPropertyFlags::HOST_VISIBLE | vk::MemoryPropertyFlags::HOST_COHERENT,
)?;
params_buffers.push(buf);
}
let f = frames as u32;
let pool_sizes = [
vk::DescriptorPoolSize::default()
.ty(vk::DescriptorType::UNIFORM_BUFFER)
.descriptor_count(f),
vk::DescriptorPoolSize::default()
.ty(vk::DescriptorType::ACCELERATION_STRUCTURE_KHR)
.descriptor_count(f),
vk::DescriptorPoolSize::default()
.ty(vk::DescriptorType::STORAGE_BUFFER)
.descriptor_count(f * 5),
vk::DescriptorPoolSize::default()
.ty(vk::DescriptorType::COMBINED_IMAGE_SAMPLER)
.descriptor_count(f * 4),
];
let descriptor_pool = device
.create_descriptor_pool(
&vk::DescriptorPoolCreateInfo::default()
.pool_sizes(&pool_sizes)
.max_sets(f),
)
.map_err(|e| format!("rt descriptor pool: {e}"))?;
let layouts: Vec<_> = (0..frames).map(|_| set_layout.handle()).collect();
let resolve_sets = alloc_descriptor_sets(device, descriptor_pool.handle(), &layouts)?;
let sampler = create_sampler_linear_clamp(device)?;
let dummy_ssbo = alloc.create_buffer(
16,
vk::BufferUsageFlags::STORAGE_BUFFER,
vk::MemoryPropertyFlags::DEVICE_LOCAL,
)?;
let mut me = Self {
settings,
output: GpuImage::null(),
render_pass,
framebuffer: OwnedFramebuffer::null(),
_set_layout: set_layout,
layout_flat,
layout_textured,
flat_pso,
textured_pso,
params_buffers,
_descriptor_pool: descriptor_pool,
resolve_sets,
sampler,
dummy_ssbo,
pool_size,
probe_cube_count,
};
me.build_targets(alloc, device, width, height)?;
me.wire_static(
device,
RtStaticInputs {
vertex_buffer,
index_buffer,
hdr_resolve_views,
gbuffer_views,
roughness_views,
prefilter_view,
cube_sampler,
},
);
for i in 0..frames {
me.wire_dynamic(
device,
i,
RtAccelHandles {
tlas,
geom_buffer,
geom_size,
deformed_verts,
skinned_indices,
},
);
}
Ok(me)
}
fn build_targets(
&mut self,
alloc: &DeviceAllocator,
device: &VkDevice,
width: u32,
height: u32,
) -> Result<(), String> {
let w = width.max(1);
let h = height.max(1);
let pooled = create_image(
alloc,
&ImageSpec {
width: w,
height: h,
format: HDR_FORMAT,
tiling: vk::ImageTiling::OPTIMAL,
usage: vk::ImageUsageFlags::COLOR_ATTACHMENT
| vk::ImageUsageFlags::SAMPLED
| vk::ImageUsageFlags::TRANSFER_SRC,
mem_props: vk::MemoryPropertyFlags::DEVICE_LOCAL,
samples: vk::SampleCountFlags::TYPE_1,
},
)?;
let image = pooled.image();
let view = create_image_view(device, image, HDR_FORMAT, vk::ImageAspectFlags::COLOR)?;
self.output = GpuImage::from_pooled(pooled, view);
self.framebuffer = device
.create_framebuffer(
&vk::FramebufferCreateInfo::default()
.render_pass(self.render_pass.handle())
.attachments(std::slice::from_ref(&self.output.view))
.width(w)
.height(h)
.layers(1),
)
.map_err(|e| format!("rt framebuffer: {e}"))?;
Ok(())
}
pub(in crate::vulkan) fn rewire_geometry(
&self,
device: &VkDevice,
vertex_buffer: vk::Buffer,
index_buffer: vk::Buffer,
) {
let verts_info = vk::DescriptorBufferInfo::default()
.buffer(vertex_buffer)
.offset(0)
.range(vk::WHOLE_SIZE);
let indices_info = vk::DescriptorBufferInfo::default()
.buffer(index_buffer)
.offset(0)
.range(vk::WHOLE_SIZE);
for &set in &self.resolve_sets {
let writes = [
vk::WriteDescriptorSet::default()
.dst_set(set)
.dst_binding(3)
.descriptor_type(vk::DescriptorType::STORAGE_BUFFER)
.buffer_info(std::slice::from_ref(&verts_info)),
vk::WriteDescriptorSet::default()
.dst_set(set)
.dst_binding(4)
.descriptor_type(vk::DescriptorType::STORAGE_BUFFER)
.buffer_info(std::slice::from_ref(&indices_info)),
];
unsafe { device.update_descriptor_sets(&writes, &[]) };
}
}
pub(in crate::vulkan) fn wire_static(&self, device: &VkDevice, inputs: RtStaticInputs) {
let RtStaticInputs {
vertex_buffer,
index_buffer,
hdr_resolve_views,
gbuffer_views,
roughness_views,
prefilter_view,
cube_sampler,
} = inputs;
self.rewire_geometry(device, vertex_buffer, index_buffer);
let cube_info = vk::DescriptorImageInfo::default()
.image_layout(vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL)
.image_view(prefilter_view)
.sampler(cube_sampler);
for (i, &set) in self.resolve_sets.iter().enumerate() {
let gb_info = vk::DescriptorImageInfo::default()
.image_layout(vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL)
.image_view(gbuffer_views[i % gbuffer_views.len().max(1)])
.sampler(self.sampler.handle());
let rough_info = vk::DescriptorImageInfo::default()
.image_layout(vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL)
.image_view(roughness_views[i % roughness_views.len().max(1)])
.sampler(self.sampler.handle());
let ubo_info = vk::DescriptorBufferInfo::default()
.buffer(self.params_buffers[i].buffer())
.offset(0)
.range(std::mem::size_of::<RtParams>() as vk::DeviceSize);
let scene_view = hdr_resolve_views[i % hdr_resolve_views.len().max(1)];
let scene_info = vk::DescriptorImageInfo::default()
.image_layout(vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL)
.image_view(scene_view)
.sampler(self.sampler.handle());
let writes = [
vk::WriteDescriptorSet::default()
.dst_set(set)
.dst_binding(0)
.descriptor_type(vk::DescriptorType::UNIFORM_BUFFER)
.buffer_info(std::slice::from_ref(&ubo_info)),
vk::WriteDescriptorSet::default()
.dst_set(set)
.dst_binding(5)
.descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER)
.image_info(std::slice::from_ref(&scene_info)),
vk::WriteDescriptorSet::default()
.dst_set(set)
.dst_binding(6)
.descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER)
.image_info(std::slice::from_ref(&gb_info)),
vk::WriteDescriptorSet::default()
.dst_set(set)
.dst_binding(7)
.descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER)
.image_info(std::slice::from_ref(&rough_info)),
vk::WriteDescriptorSet::default()
.dst_set(set)
.dst_binding(8)
.descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER)
.image_info(std::slice::from_ref(&cube_info)),
];
unsafe { device.update_descriptor_sets(&writes, &[]) };
}
}
pub(in crate::vulkan) fn wire_dynamic(
&self,
device: &VkDevice,
frame_idx: usize,
accel: RtAccelHandles,
) {
let RtAccelHandles {
tlas,
geom_buffer,
geom_size,
deformed_verts: deformed,
skinned_indices,
} = accel;
let set = self.resolve_sets[frame_idx];
let accels = [tlas];
let mut accel_write = vk::WriteDescriptorSetAccelerationStructureKHR::default()
.acceleration_structures(&accels);
let mut tlas_write = vk::WriteDescriptorSet::default()
.dst_set(set)
.dst_binding(1)
.descriptor_type(vk::DescriptorType::ACCELERATION_STRUCTURE_KHR)
.push_next(&mut accel_write);
tlas_write.descriptor_count = 1;
let geom_info = vk::DescriptorBufferInfo::default()
.buffer(geom_buffer)
.offset(0)
.range(geom_size);
let geom_write = vk::WriteDescriptorSet::default()
.dst_set(set)
.dst_binding(2)
.descriptor_type(vk::DescriptorType::STORAGE_BUFFER)
.buffer_info(std::slice::from_ref(&geom_info));
let deformed_info = vk::DescriptorBufferInfo::default()
.buffer(deformed)
.offset(0)
.range(vk::WHOLE_SIZE);
let deformed_write = vk::WriteDescriptorSet::default()
.dst_set(set)
.dst_binding(9)
.descriptor_type(vk::DescriptorType::STORAGE_BUFFER)
.buffer_info(std::slice::from_ref(&deformed_info));
let sidx_buffer = if skinned_indices != vk::Buffer::null() {
skinned_indices
} else {
self.dummy_ssbo.buffer()
};
let sidx_info = vk::DescriptorBufferInfo::default()
.buffer(sidx_buffer)
.offset(0)
.range(vk::WHOLE_SIZE);
let sidx_write = vk::WriteDescriptorSet::default()
.dst_set(set)
.dst_binding(10)
.descriptor_type(vk::DescriptorType::STORAGE_BUFFER)
.buffer_info(std::slice::from_ref(&sidx_info));
unsafe {
device
.update_descriptor_sets(&[tlas_write, geom_write, deformed_write, sidx_write], &[])
};
}
pub(in crate::vulkan) fn rewire_prefilter(
&self,
device: &VkDevice,
prefilter_view: vk::ImageView,
cube_sampler: vk::Sampler,
) {
let cube_info = vk::DescriptorImageInfo::default()
.image_layout(vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL)
.image_view(prefilter_view)
.sampler(cube_sampler);
for &set in &self.resolve_sets {
let write = vk::WriteDescriptorSet::default()
.dst_set(set)
.dst_binding(8)
.descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER)
.image_info(std::slice::from_ref(&cube_info));
unsafe { device.update_descriptor_sets(std::slice::from_ref(&write), &[]) };
}
}
fn destroy_targets(&mut self, _device: &VkDevice) {
if !self.framebuffer.is_null() {
self.framebuffer = OwnedFramebuffer::null();
}
if self.output.image != vk::Image::null() {
self.output = GpuImage::null();
}
}
pub(in crate::vulkan) fn rebuild(
&mut self,
alloc: &DeviceAllocator,
device: &VkDevice,
width: u32,
height: u32,
inputs: RtStaticInputs,
) -> Result<(), String> {
self.destroy_targets(device);
self.build_targets(alloc, device, width, height)?;
self.wire_static(device, inputs);
Ok(())
}
pub(in crate::vulkan) fn swap_pipelines(&mut self, rebuilt: RebuiltRtPipelines) {
self.flat_pso = rebuilt.flat;
self.textured_pso = rebuilt.textured;
}
pub(in crate::vulkan) fn destroy(&mut self, device: &VkDevice) {
self.destroy_targets(device);
self.dummy_ssbo = PooledBuffer::null();
self.params_buffers.clear();
}
}
impl VkContext {
pub(in crate::vulkan) fn rt_reflections_active(&self) -> bool {
self.rt_reflections.is_some() && self.rt_accel.is_some()
}
pub(in crate::vulkan) fn planar_pass_needed(&self) -> bool {
crate::gfx::planar_reflection::planar_pass_needed(
self.planar_reflection.is_some(),
self.transparent
.as_ref()
.is_some_and(|t| t.water_planar_slot_live()),
self.rt_transparent_active(),
)
}
pub(in crate::vulkan) fn rt_transparent_active(&self) -> bool {
self.rt_reflections_active()
&& self
.transparent
.as_ref()
.is_some_and(|t| t.rt_pipelines_ready())
}
pub(in crate::vulkan) fn rt_dynamic_update(
&mut self,
cmd: vk::CommandBuffer,
frame_idx: usize,
) {
let topology_dirty = std::mem::take(&mut self.rt_topology_dirty);
if self.rt_accel.is_none() || self.rt_reflections.is_none() {
return;
}
let device = self.device.clone();
let instance = self.instance.clone();
let pd = self.physical_device;
let mode = self.rt_dynamic_mode;
let skinned_inputs: Option<(vk::Buffer, vk::Buffer)> = if self.rt_skinned_geometry
&& !self.skinned.draw_objects.is_empty()
&& !self.skinned.vertex_buffer.is_null()
&& !self.skinned.index_buffer.is_null()
{
Some((
self.skinned.vertex_buffer.buffer(),
self.skinned.index_buffer.buffer(),
))
} else {
None
};
let exclude_seethrough = self.seethrough_meshes_enabled();
if let Some(mut accel) = self.rt_accel.take() {
let joint_buffers = self
.skinned
.joint_buffers
.get(frame_idx)
.map(|b| b.as_slice())
.unwrap_or(&[]);
let skinned = skinned_inputs.map(|(vb, ib)| super::super::raytrace::SkinnedRtInputs {
objects: &self.skinned.draw_objects,
vertex_buffer: vb,
index_buffer: ib,
joint_buffers,
});
accel.dynamic_update(
super::super::raytrace::RtDeviceCtx {
alloc: &self.alloc,
instance: &instance,
device: &device,
pd,
},
cmd,
&self.draw.objects,
super::super::raytrace::RtDynamicInputs {
policy: super::super::raytrace::RtRebuildPolicy {
mode,
topology_dirty,
exclude_seethrough,
},
frame_idx,
skinned,
},
);
self.rt_accel = Some(accel);
}
let accel = self
.rt_accel
.as_ref()
.expect("RT acceleration structures are live");
let (geom_buffer, geom_size) = accel.geom_table();
let tlas = accel.tlas();
let deformed = accel.deformed_verts();
let skinned_indices = accel.skinned_indices();
let rt = self
.rt_reflections
.as_ref()
.expect("RT reflection resources are live");
rt.wire_dynamic(
&device,
frame_idx,
RtAccelHandles {
tlas,
geom_buffer,
geom_size,
deformed_verts: deformed,
skinned_indices,
},
);
if let Some(transparent) = self.transparent.as_ref() {
transparent.wire_rt_dynamic(
&device,
frame_idx,
super::super::transparent::TransparentRtDynamic {
tlas,
geom_buffer,
geom_size,
deformed,
skinned_indices,
},
);
}
}
pub(in crate::vulkan) fn encode_rt_reflections(
&self,
cmd: vk::CommandBuffer,
frame_idx: usize,
fov_y_radians: f32,
aspect: f32,
cam_pos: [f32; 3],
) {
let rt = match &self.rt_reflections {
Some(r) => r,
None => return,
};
let device = &self.device;
let extent = self.render_extent;
let v = self.view.matrix;
let inv_view_rot = [
[v[0][0], v[1][0], v[2][0], 0.0],
[v[0][1], v[1][1], v[2][1], 0.0],
[v[0][2], v[1][2], v[2][2], 0.0],
[0.0, 0.0, 0.0, 1.0],
];
let params = rt.settings.params(RtParamsInputs {
fov_y_radians,
aspect,
inv_view_rot,
cam_pos,
sun_dir: self.fog.sun_dir,
sun_color: self.fog.sun_color,
prefilter_mip_count: self.prefilter_mip_count as f32,
sky_rot: self.view.sky_rot,
});
rt.params_buffers[frame_idx].write_val(0, ¶ms);
let textured = self.cull.bindless_pipeline.is_some() && rt.textured_pso.is_some();
let (pso, layout) = match (
textured,
rt.textured_pso.as_ref(),
rt.layout_textured.as_ref(),
) {
(true, Some(pso), Some(layout)) => (pso, layout),
_ => (&rt.flat_pso, &rt.layout_flat),
};
let rp_begin = vk::RenderPassBeginInfo::default()
.render_pass(rt.render_pass.handle())
.framebuffer(rt.framebuffer.handle())
.render_area(vk::Rect2D::default().extent(extent));
let vp = vk::Viewport {
x: 0.0,
y: 0.0,
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));
device.cmd_set_scissor(cmd, 0, std::slice::from_ref(&scissor));
device.cmd_bind_pipeline(cmd, vk::PipelineBindPoint::GRAPHICS, pso.handle());
device.cmd_bind_descriptor_sets(
cmd,
vk::PipelineBindPoint::GRAPHICS,
layout.handle(),
0,
std::slice::from_ref(&rt.resolve_sets[frame_idx]),
&[],
);
device.cmd_bind_descriptor_sets(
cmd,
vk::PipelineBindPoint::GRAPHICS,
layout.handle(),
1,
std::slice::from_ref(&self.descriptors.global_sets[frame_idx]),
&[],
);
if textured {
device.cmd_bind_descriptor_sets(
cmd,
vk::PipelineBindPoint::GRAPHICS,
layout.handle(),
2,
std::slice::from_ref(&self.cull.bindless_sets[frame_idx]),
&[],
);
}
device.cmd_draw(cmd, 3, 1, 0, 0);
device.cmd_end_render_pass(cmd);
}
self.encode_reflection_composite(cmd, rt.output.view, frame_idx);
}
}
#[cfg(test)]
mod tests {
#[test]
fn rt_reflections_shaders_compile() {
if !crate::slangc_gate::slangc_available() {
return;
}
for probes in [1, concinnity_core::render::uniforms::MAX_PROBES as u32] {
let shaders = super::compile_rt_shaders(false, 4, probes).expect("rt shaders compile");
assert!(super::is_spirv(&shaders.vs));
assert!(super::is_spirv(&shaders.flat_fs));
assert!(shaders.textured_fs.is_some(), "pool_size>0 builds textured");
}
let flat_only = super::compile_rt_shaders(false, 0, 4).expect("rt flat compiles");
assert!(flat_only.textured_fs.is_none());
}
}