use ash::vk;
use concinnity_core::gfx::render_types::RtParams;
use concinnity_core::render::error::RenderResult;
use concinnity_core::render::planar_reflection;
use concinnity_core::render::post::rt_reflections::{RtParamsInputs, RtReflectionSettings};
use super::super::allocator::{DeviceAllocator, PooledBuffer};
use super::super::context::{HDR_FORMAT, VkContext};
use super::super::descriptor_layout::{Binding, PoolSizes};
use super::super::pipeline::*;
use super::super::resources::{
alloc_descriptor_sets, create_descriptor_set_layout, write_samplers,
};
use super::super::texture::*;
use crate::vulkan::builtin_shaders::CompileProgram;
use crate::vulkan::owned::{
OwnedDescriptorPool, OwnedFramebuffer, OwnedPipeline, OwnedPipelineLayout, OwnedRenderPass,
OwnedSampler, OwnedSetLayout, VkDevice,
};
use crate::vulkan::wire_cache::WireCache;
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,
) -> RenderResult<RtShaders> {
use super::super::builtin_shaders;
let vs = builtin_shaders::FULLSCREEN_VERT.compile(hot_reload)?;
let flat_fs = builtin_shaders::RT_REFLECTIONS_FRAG.compile(hot_reload)?;
let textured_fs = if pool_size > 0 {
Some(builtin_shaders::RT_REFLECTIONS_FRAG_TEXTURED.compile(hot_reload)?)
} 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,
extent: vk::Extent2D,
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,
wired_accel: WireCache<RtAccelHandles>,
}
unsafe impl Send for RtReflectionsResources {}
fn resolve_set_bindings() -> [Binding; 13] {
use vk::DescriptorType as T;
let frag = vk::ShaderStageFlags::FRAGMENT;
[
(0, T::UNIFORM_BUFFER, frag),
(1, T::ACCELERATION_STRUCTURE_KHR, frag),
(2, T::STORAGE_BUFFER, frag),
(3, T::STORAGE_BUFFER, frag),
(4, T::STORAGE_BUFFER, frag),
(5, T::SAMPLED_IMAGE, frag),
(6, T::SAMPLED_IMAGE, frag),
(7, T::SAMPLED_IMAGE, frag),
(9, T::STORAGE_BUFFER, frag),
(10, T::STORAGE_BUFFER, frag),
(11, T::SAMPLER, frag),
(12, T::SAMPLER, frag),
(13, T::SAMPLER, frag),
]
}
fn create_rt_render_pass(device: &VkDevice) -> RenderResult<OwnedRenderPass> {
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| crate::vulkan::error::map_vk_result(e, "RT reflections render pass"))
}
fn create_rt_pipeline(
device: &VkDevice,
render_pass: vk::RenderPass,
layout: vk::PipelineLayout,
vert_spv: &[u8],
frag_spv: &[u8],
) -> RenderResult<OwnedPipeline> {
let modules = GraphicsStages::new(device, vert_spv, frag_spv)?;
let stages = modules.infos();
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| crate::vulkan::error::map_vk_result(e, "create rt reflections pso"))?;
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,
) -> RenderResult<RebuiltRtPipelines> {
let shaders = compile_rt_shaders(hot_reload, rt.pool_size)?;
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],
}
#[derive(Clone, Copy, PartialEq, Eq)]
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 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,
) -> RenderResult<Self> {
let RtBuild {
alloc,
device,
width,
height,
frames,
} = build;
let RtStaticInputs {
vertex_buffer,
index_buffer,
hdr_resolve_views,
gbuffer_views,
roughness_views,
} = static_inputs;
let RtAccelHandles {
tlas,
geom_buffer,
geom_size,
deformed_verts,
skinned_indices,
} = accel;
let RtLayoutConfig {
bindless_set_layout,
global_set_layout,
pool_size,
hot_reload,
} = layout;
let render_pass = create_rt_render_pass(device)?;
let set_layout = create_descriptor_set_layout(device, &resolve_set_bindings())?;
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| crate::vulkan::error::map_vk_result(e, "rt flat pipeline layout"))?;
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| {
crate::vulkan::error::map_vk_result(e, "rt textured pipeline layout")
})?,
)
} else {
None
};
let shaders = compile_rt_shaders(hot_reload, pool_size)?;
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 = PoolSizes::default()
.sets(&resolve_set_bindings(), f)
.build();
let descriptor_pool = device
.create_descriptor_pool(
&vk::DescriptorPoolCreateInfo::default()
.pool_sizes(&pool_sizes)
.max_sets(f),
)
.map_err(|e| crate::vulkan::error::map_vk_result(e, "rt descriptor pool"))?;
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 screen = sampler.handle();
for &set in &resolve_sets {
write_samplers(device, set, 11, &[screen, screen, screen]);
}
let dummy_ssbo = alloc.create_buffer(
16,
vk::BufferUsageFlags::STORAGE_BUFFER,
vk::MemoryPropertyFlags::DEVICE_LOCAL,
)?;
let mut me = Self {
settings,
output: GpuImage::null(),
extent: vk::Extent2D::default(),
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: sampler,
dummy_ssbo,
pool_size,
wired_accel: WireCache::new(frames),
};
me.build_targets(alloc, device, width, height)?;
me.wire_static(
device,
RtStaticInputs {
vertex_buffer,
index_buffer,
hdr_resolve_views,
gbuffer_views,
roughness_views,
},
);
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,
) -> RenderResult<()> {
let (w, h) = self.settings.trace_extent(width, height);
self.extent = vk::Extent2D {
width: w,
height: h,
};
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| crate::vulkan::error::map_vk_result(e, "rt framebuffer"))?;
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,
} = inputs;
self.rewire_geometry(device, vertex_buffer, index_buffer);
let frames = self.resolve_sets.len();
debug_assert_eq!(hdr_resolve_views.len(), frames);
debug_assert_eq!(gbuffer_views.len(), frames);
debug_assert_eq!(roughness_views.len(), frames);
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]);
let rough_info = vk::DescriptorImageInfo::default()
.image_layout(vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL)
.image_view(roughness_views[i]);
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];
let scene_info = vk::DescriptorImageInfo::default()
.image_layout(vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL)
.image_view(scene_view);
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::SAMPLED_IMAGE)
.image_info(std::slice::from_ref(&scene_info)),
vk::WriteDescriptorSet::default()
.dst_set(set)
.dst_binding(6)
.descriptor_type(vk::DescriptorType::SAMPLED_IMAGE)
.image_info(std::slice::from_ref(&gb_info)),
vk::WriteDescriptorSet::default()
.dst_set(set)
.dst_binding(7)
.descriptor_type(vk::DescriptorType::SAMPLED_IMAGE)
.image_info(std::slice::from_ref(&rough_info)),
];
unsafe { device.update_descriptor_sets(&writes, &[]) };
}
}
pub(in crate::vulkan) fn wire_dynamic(
&mut self,
device: &VkDevice,
frame_idx: usize,
accel: RtAccelHandles,
) {
if !self.wired_accel.changed(frame_idx, accel) {
return;
}
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], &[])
};
}
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,
) -> RenderResult<()> {
self.destroy_targets(device);
self.build_targets(alloc, device, width, height)?;
self.wire_static(device, inputs);
self.wired_accel.reset();
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 {
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.hw.device.clone();
let instance = self.hw.instance.clone();
let pd = self.hw.physical_device;
let mode = self.rt.dynamic_mode;
let skinned_inputs: Option<(vk::Buffer, vk::Buffer)> = if self.rt.skinned_geometry
&& !self.skinned.slots.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.slots.draw_objects,
vertex_buffer: vb,
index_buffer: ib,
joint_buffers,
});
accel.dynamic_update(
super::super::raytrace::RtDeviceCtx {
alloc: &self.hw.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_mut()
.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_mut() {
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.hw.device;
let extent = rt.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.scene.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 !concinnity_shader::dxc_available() {
return;
}
let shaders = super::compile_rt_shaders(false, 4).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).expect("rt flat compiles");
assert!(flat_only.textured_fs.is_none());
}
}