use ash::vk;
use concinnity_core::gfx::transform::mat4_inverse;
use crate::vulkan::owned::{OwnedDescriptorPool, OwnedFramebuffer, OwnedRenderPass, VkDevice};
use super::allocator::{DeviceAllocator, PooledBuffer};
use super::context::{HDR_FORMAT, VkContext};
use super::draw::ViewUniforms;
use super::resources::alloc_descriptor_sets;
use super::texture::{GpuImage, ImageSpec, create_image, create_image_view};
use concinnity_core::gfx::transform::mat4_mul;
pub(in crate::vulkan) const MAX_PLANAR_PLANES: usize =
crate::gfx::planar_reflection::MAX_PLANAR_PLANES;
const PLANAR_CLIP_BIAS: f32 = 0.02;
const PLANAR_DEPTH_FORMAT: vk::Format = vk::Format::D32_SFLOAT;
pub(in crate::vulkan) fn pane_plane(normal: [f32; 3], centre: [f32; 3]) -> [f32; 4] {
[
normal[0],
normal[1],
normal[2],
-(normal[0] * centre[0] + normal[1] * centre[1] + normal[2] * centre[2]),
]
}
pub(in crate::vulkan) struct PlanarReflectionSet {
planes: Vec<[f32; 4]>,
frames: usize,
sample_count: vk::SampleCountFlags,
width: u32,
height: u32,
main_render_pass: vk::RenderPass,
color: Option<GpuImage>,
depth: GpuImage,
targets: Vec<GpuImage>,
framebuffers: Vec<OwnedFramebuffer>,
view_bufs: Vec<PooledBuffer>,
global_sets: Vec<vk::DescriptorSet>,
probeset_buf: PooledBuffer,
cull_indirect_bufs: Vec<PooledBuffer>,
cull_status_bufs: Vec<PooledBuffer>,
cull_sets: Vec<vk::DescriptorSet>,
hiz_set: Option<vk::DescriptorSet>,
hiz_ubo: Option<PooledBuffer>,
_pool: OwnedDescriptorPool,
}
pub(in crate::vulkan) struct PlanarCullSources<'a> {
pub(in crate::vulkan) frame_object_buffers: &'a [PooledBuffer],
pub(in crate::vulkan) frame_draw_args_buffers: &'a [PooledBuffer],
pub(in crate::vulkan) cull_set_layout: vk::DescriptorSetLayout,
pub(in crate::vulkan) cull_count: usize,
pub(in crate::vulkan) hiz: Option<(vk::DescriptorSetLayout, vk::ImageView, vk::Sampler)>,
}
unsafe impl Send for PlanarReflectionSet {}
unsafe impl Sync for PlanarReflectionSet {}
#[derive(Clone, Copy)]
pub(in crate::vulkan) struct PlanarDevice<'a> {
pub(in crate::vulkan) alloc: &'a DeviceAllocator,
pub(in crate::vulkan) device: &'a VkDevice,
}
#[derive(Clone, Copy)]
struct PlanarTargetDims {
sample_count: vk::SampleCountFlags,
width: u32,
height: u32,
plane_count: usize,
}
fn create_targets(
gpu: PlanarDevice<'_>,
dims: PlanarTargetDims,
) -> Result<(Option<GpuImage>, GpuImage, Vec<GpuImage>), String> {
let PlanarDevice { alloc, device } = gpu;
let PlanarTargetDims {
sample_count,
width,
height,
plane_count,
} = dims;
let msaa = sample_count != vk::SampleCountFlags::TYPE_1;
let w = width.max(1);
let h = height.max(1);
let color = if msaa {
let pooled = create_image(
alloc,
&ImageSpec {
width: w,
height: h,
format: HDR_FORMAT,
tiling: vk::ImageTiling::OPTIMAL,
usage: vk::ImageUsageFlags::COLOR_ATTACHMENT,
mem_props: vk::MemoryPropertyFlags::DEVICE_LOCAL,
samples: sample_count,
},
)?;
let img = pooled.image();
let view = create_image_view(device, img, HDR_FORMAT, vk::ImageAspectFlags::COLOR)?;
Some(GpuImage::from_pooled(pooled, view))
} else {
None
};
let pooled = create_image(
alloc,
&ImageSpec {
width: w,
height: h,
format: PLANAR_DEPTH_FORMAT,
tiling: vk::ImageTiling::OPTIMAL,
usage: vk::ImageUsageFlags::DEPTH_STENCIL_ATTACHMENT,
mem_props: vk::MemoryPropertyFlags::DEVICE_LOCAL,
samples: sample_count,
},
)?;
let depth_img = pooled.image();
let depth_view = create_image_view(
device,
depth_img,
PLANAR_DEPTH_FORMAT,
vk::ImageAspectFlags::DEPTH,
)?;
let depth = GpuImage::from_pooled(pooled, depth_view);
let mut targets = Vec::with_capacity(plane_count);
for _ in 0..plane_count {
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,
mem_props: vk::MemoryPropertyFlags::DEVICE_LOCAL,
samples: vk::SampleCountFlags::TYPE_1,
},
)?;
let img = pooled.image();
let view = create_image_view(device, img, HDR_FORMAT, vk::ImageAspectFlags::COLOR)?;
targets.push(GpuImage::from_pooled(pooled, view));
}
Ok((color, depth, targets))
}
struct PlanarFramebufferInputs<'a> {
main_render_pass: vk::RenderPass,
sample_count: vk::SampleCountFlags,
color: Option<&'a GpuImage>,
depth: &'a GpuImage,
targets: &'a [GpuImage],
width: u32,
height: u32,
}
fn create_framebuffers(
device: &VkDevice,
inputs: PlanarFramebufferInputs<'_>,
) -> Result<Vec<OwnedFramebuffer>, String> {
let PlanarFramebufferInputs {
main_render_pass,
sample_count,
color,
depth,
targets,
width,
height,
} = inputs;
let msaa = sample_count != vk::SampleCountFlags::TYPE_1;
let mut out = Vec::with_capacity(targets.len());
for target in targets {
let attachments: Vec<vk::ImageView> = if msaa {
vec![
color
.expect("a multisampled planar target has a colour image")
.view,
depth.view,
target.view,
]
} else {
vec![target.view, depth.view]
};
let info = vk::FramebufferCreateInfo::default()
.render_pass(main_render_pass)
.attachments(&attachments)
.width(width.max(1))
.height(height.max(1))
.layers(1);
let fb = device
.create_framebuffer(&info)
.map_err(|e| format!("planar framebuffer: {e}"))?;
out.push(fb);
}
Ok(out)
}
#[derive(Clone, Copy)]
pub(in crate::vulkan) struct PlanarConfig {
pub(in crate::vulkan) frames: usize,
pub(in crate::vulkan) sample_count: vk::SampleCountFlags,
pub(in crate::vulkan) width: u32,
pub(in crate::vulkan) height: u32,
}
#[derive(Clone, Copy)]
pub(in crate::vulkan) struct PlanarGlobalSet {
pub(in crate::vulkan) layout: vk::DescriptorSetLayout,
pub(in crate::vulkan) probe_cube_count: u32,
pub(in crate::vulkan) update_after_bind: bool,
}
#[derive(Clone, Copy)]
pub(in crate::vulkan) struct PlanarLightingBindings<'a> {
pub(in crate::vulkan) light_ubos: &'a [PooledBuffer],
pub(in crate::vulkan) light_size: u64,
pub(in crate::vulkan) local_light_buffer: vk::Buffer,
pub(in crate::vulkan) local_light_size: u64,
pub(in crate::vulkan) cluster_params_ubo: vk::Buffer,
pub(in crate::vulkan) cluster_list_buffer: vk::Buffer,
pub(in crate::vulkan) spot_shadow_map_view: vk::ImageView,
pub(in crate::vulkan) spot_shadow_data_buffer: vk::Buffer,
pub(in crate::vulkan) area_light_buffer: vk::Buffer,
pub(in crate::vulkan) ltc_matrix_view: vk::ImageView,
pub(in crate::vulkan) ltc_magnitude_view: vk::ImageView,
pub(in crate::vulkan) ltc_sampler: vk::Sampler,
pub(in crate::vulkan) shadow_ubos: &'a [PooledBuffer],
pub(in crate::vulkan) shadow_size: u64,
pub(in crate::vulkan) shadow_map_view: vk::ImageView,
pub(in crate::vulkan) shadow_sampler: vk::Sampler,
pub(in crate::vulkan) irradiance_view: vk::ImageView,
pub(in crate::vulkan) prefilter_view: vk::ImageView,
pub(in crate::vulkan) cube_sampler: vk::Sampler,
pub(in crate::vulkan) ssao_white_view: vk::ImageView,
pub(in crate::vulkan) linear_sampler: vk::Sampler,
}
impl PlanarReflectionSet {
pub(in crate::vulkan) fn new(
gpu: PlanarDevice<'_>,
config: PlanarConfig,
planes: &[[f32; 4]],
main_render_pass: &OwnedRenderPass,
global_set: PlanarGlobalSet,
lighting: PlanarLightingBindings,
cull: PlanarCullSources<'_>,
) -> Result<Self, String> {
use concinnity_core::render::uniforms::ProbeSet;
let PlanarGlobalSet {
layout: global_set_layout,
probe_cube_count,
update_after_bind: global_update_after_bind,
} = global_set;
let PlanarDevice { alloc, device } = gpu;
let PlanarConfig {
frames,
sample_count,
width,
height,
} = config;
let PlanarLightingBindings {
light_ubos,
light_size,
local_light_buffer,
local_light_size,
cluster_params_ubo,
cluster_list_buffer,
spot_shadow_map_view,
spot_shadow_data_buffer,
area_light_buffer,
ltc_matrix_view,
ltc_magnitude_view,
ltc_sampler,
shadow_ubos,
shadow_size,
shadow_map_view,
shadow_sampler,
irradiance_view,
prefilter_view,
cube_sampler,
ssao_white_view,
linear_sampler,
} = lighting;
let plane_count = planes.len();
let (color, depth, targets) = create_targets(
gpu,
PlanarTargetDims {
sample_count,
width,
height,
plane_count,
},
)?;
let framebuffers = create_framebuffers(
device,
PlanarFramebufferInputs {
main_render_pass: main_render_pass.handle(),
sample_count,
color: color.as_ref(),
depth: &depth,
targets: &targets,
width,
height,
},
)?;
let empty = ProbeSet::EMPTY;
let probeset_size = std::mem::size_of::<ProbeSet>() as u64;
let host = vk::MemoryPropertyFlags::HOST_VISIBLE | vk::MemoryPropertyFlags::HOST_COHERENT;
let probeset_buf =
alloc.create_buffer(probeset_size, vk::BufferUsageFlags::UNIFORM_BUFFER, host)?;
probeset_buf.write_val(0, &empty);
let view_size = std::mem::size_of::<ViewUniforms>() as u64;
let ring = plane_count * frames;
let mut view_bufs = Vec::with_capacity(ring);
for _ in 0..ring {
view_bufs.push(alloc.create_buffer(
view_size,
vk::BufferUsageFlags::UNIFORM_BUFFER,
host,
)?);
}
use crate::gfx::render_types::{GpuDrawArgs, GpuObjectData};
let object_range = (cull.cull_count * std::mem::size_of::<GpuObjectData>()).max(4) as u64;
let args_range = (cull.cull_count * std::mem::size_of::<GpuDrawArgs>()).max(4) as u64;
let indirect_size =
(cull.cull_count * std::mem::size_of::<vk::DrawIndexedIndirectCommand>()).max(4) as u64;
let status_size = (cull.cull_count * std::mem::size_of::<u32>()).max(4) as u64;
let mut cull_indirect_bufs = Vec::with_capacity(ring);
let mut cull_status_bufs = Vec::with_capacity(ring);
for _ in 0..ring {
cull_indirect_bufs.push(alloc.create_buffer(
indirect_size,
vk::BufferUsageFlags::STORAGE_BUFFER | vk::BufferUsageFlags::INDIRECT_BUFFER,
vk::MemoryPropertyFlags::DEVICE_LOCAL,
)?);
cull_status_bufs.push(alloc.create_buffer(
status_size,
vk::BufferUsageFlags::STORAGE_BUFFER,
vk::MemoryPropertyFlags::DEVICE_LOCAL,
)?);
}
let has_hiz = cull.hiz.is_some();
let pool_sizes = [
vk::DescriptorPoolSize::default()
.ty(vk::DescriptorType::UNIFORM_BUFFER)
.descriptor_count((ring * 5 + usize::from(has_hiz)).max(1) as u32),
vk::DescriptorPoolSize::default()
.ty(vk::DescriptorType::COMBINED_IMAGE_SAMPLER)
.descriptor_count(
(ring * (7 + probe_cube_count as usize) + usize::from(has_hiz)).max(1) as u32,
),
vk::DescriptorPoolSize::default()
.ty(vk::DescriptorType::STORAGE_BUFFER)
.descriptor_count((ring * 4 + ring + ring + ring + ring).max(1) as u32),
];
let mut pool_info = vk::DescriptorPoolCreateInfo::default()
.pool_sizes(&pool_sizes)
.max_sets((ring * 2 + usize::from(has_hiz)).max(1) as u32);
if global_update_after_bind {
pool_info = pool_info.flags(vk::DescriptorPoolCreateFlags::UPDATE_AFTER_BIND);
}
let pool = device
.create_descriptor_pool(&pool_info)
.map_err(|e| format!("planar descriptor pool: {e}"))?;
let layouts: Vec<_> = (0..ring).map(|_| global_set_layout).collect();
let global_sets = alloc_descriptor_sets(device, pool.handle(), &layouts)?;
let probe_cube_sky: Vec<vk::DescriptorImageInfo> = (0..probe_cube_count)
.map(|_| {
vk::DescriptorImageInfo::default()
.image_layout(vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL)
.image_view(prefilter_view)
.sampler(cube_sampler)
})
.collect();
for (i, &set) in global_sets.iter().enumerate() {
let view_info = buf_info(view_bufs[i].buffer(), view_size);
let light_info = buf_info(light_ubos[i % frames].buffer(), light_size);
let shadow_info = buf_info(shadow_ubos[i % frames].buffer(), shadow_size);
let probeset_info = buf_info(probeset_buf.buffer(), probeset_size);
let shadow_img = img_info(shadow_map_view, shadow_sampler);
let irr_img = img_info(irradiance_view, cube_sampler);
let pre_img = img_info(prefilter_view, cube_sampler);
let ssao_img = img_info(ssao_white_view, linear_sampler);
let writes = [
ubo_write(set, 0, &view_info),
ubo_write(set, 1, &light_info),
ubo_write(set, 2, &shadow_info),
sampler_write(set, 3, &shadow_img),
sampler_write(set, 4, &irr_img),
sampler_write(set, 5, &pre_img),
sampler_write(set, 6, &ssao_img),
ubo_write(set, 7, &probeset_info),
vk::WriteDescriptorSet::default()
.dst_set(set)
.dst_binding(super::descriptor_layout::PROBE_CUBE_ARRAY_BINDING)
.dst_array_element(0)
.descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER)
.image_info(&probe_cube_sky),
];
unsafe { device.update_descriptor_sets(&writes, &[]) };
write_storage(
device,
set,
super::descriptor_layout::LOCAL_LIGHT_SSBO_BINDING,
local_light_buffer,
local_light_size,
);
let cluster_params_info = vk::DescriptorBufferInfo::default()
.buffer(cluster_params_ubo)
.offset(0)
.range(std::mem::size_of::<crate::gfx::render_types::ClusterParams>() as u64);
let cluster_write = vk::WriteDescriptorSet::default()
.dst_set(set)
.dst_binding(super::descriptor_layout::CLUSTER_PARAMS_UBO_BINDING)
.descriptor_type(vk::DescriptorType::UNIFORM_BUFFER)
.buffer_info(std::slice::from_ref(&cluster_params_info));
unsafe { device.update_descriptor_sets(std::slice::from_ref(&cluster_write), &[]) };
write_storage(
device,
set,
super::descriptor_layout::CLUSTER_LIGHT_LIST_SSBO_BINDING,
cluster_list_buffer,
super::light_cull::cluster_list_size(),
);
let spot_img = img_info(spot_shadow_map_view, shadow_sampler);
let spot_write = sampler_write(
set,
super::descriptor_layout::SPOT_SHADOW_MAP_BINDING,
&spot_img,
);
unsafe { device.update_descriptor_sets(std::slice::from_ref(&spot_write), &[]) };
write_storage(
device,
set,
super::descriptor_layout::SPOT_SHADOW_DATA_SSBO_BINDING,
spot_shadow_data_buffer,
vk::WHOLE_SIZE,
);
write_storage(
device,
set,
super::descriptor_layout::AREA_LIGHT_SSBO_BINDING,
area_light_buffer,
vk::WHOLE_SIZE,
);
let ltc_m = img_info(ltc_matrix_view, ltc_sampler);
let ltc_g = img_info(ltc_magnitude_view, ltc_sampler);
let ltc_writes = [
sampler_write(set, super::descriptor_layout::LTC_MATRIX_BINDING, <c_m),
sampler_write(set, super::descriptor_layout::LTC_MAGNITUDE_BINDING, <c_g),
];
unsafe { device.update_descriptor_sets(<c_writes, &[]) };
}
let cull_layouts: Vec<_> = (0..ring).map(|_| cull.cull_set_layout).collect();
let cull_sets = alloc_descriptor_sets(device, pool.handle(), &cull_layouts)?;
for (i, &set) in cull_sets.iter().enumerate() {
let frame = i % frames;
write_storage(
device,
set,
0,
cull.frame_object_buffers[frame].buffer(),
object_range,
);
write_storage(
device,
set,
1,
cull.frame_draw_args_buffers[frame].buffer(),
args_range,
);
write_storage(
device,
set,
2,
cull_indirect_bufs[i].buffer(),
indirect_size,
);
write_storage(device, set, 3, cull_status_bufs[i].buffer(), status_size);
}
let (hiz_set, hiz_ubo) = if let Some((hiz_layout, hiz_view, hiz_sampler)) = cull.hiz {
use super::hiz::CullHizParams;
let params = CullHizParams {
prev_view_proj: [[0.0; 4]; 4],
hiz_size: [1.0, 1.0],
hiz_mip_count: 1,
hiz_enabled: 0,
};
let params_size = std::mem::size_of::<CullHizParams>() as u64;
let ubo =
alloc.create_buffer(params_size, vk::BufferUsageFlags::UNIFORM_BUFFER, host)?;
ubo.write_val(0, ¶ms);
let set =
alloc_descriptor_sets(device, pool.handle(), std::slice::from_ref(&hiz_layout))?[0];
let img = img_info(hiz_view, hiz_sampler);
let ubo_info = buf_info(ubo.buffer(), params_size);
let writes = [sampler_write(set, 0, &img), ubo_write(set, 1, &ubo_info)];
unsafe { device.update_descriptor_sets(&writes, &[]) };
(Some(set), Some(ubo))
} else {
(None, None)
};
Ok(Self {
planes: planes.to_vec(),
frames,
sample_count,
width,
height,
main_render_pass: main_render_pass.handle(),
color,
depth,
targets,
framebuffers,
view_bufs,
global_sets,
probeset_buf,
cull_indirect_bufs,
cull_status_bufs,
cull_sets,
hiz_set,
hiz_ubo,
_pool: pool,
})
}
pub(in crate::vulkan) fn plane_count(&self) -> usize {
self.planes.len()
}
pub(in crate::vulkan) fn target_view(&self, slot: usize) -> vk::ImageView {
self.targets[slot].view
}
pub(in crate::vulkan) fn rewrite_hiz_view(
&self,
device: &VkDevice,
view: vk::ImageView,
sampler: vk::Sampler,
) {
let Some(set) = self.hiz_set else {
return;
};
let img = img_info(view, sampler);
let write = sampler_write(set, 0, &img);
unsafe { device.update_descriptor_sets(std::slice::from_ref(&write), &[]) };
}
pub(in crate::vulkan) fn rebuild(
&mut self,
alloc: &DeviceAllocator,
device: &VkDevice,
width: u32,
height: u32,
) -> Result<(), String> {
let (color, depth, targets) = create_targets(
PlanarDevice { alloc, device },
PlanarTargetDims {
sample_count: self.sample_count,
width,
height,
plane_count: self.planes.len(),
},
)?;
let framebuffers = create_framebuffers(
device,
PlanarFramebufferInputs {
main_render_pass: self.main_render_pass,
sample_count: self.sample_count,
color: color.as_ref(),
depth: &depth,
targets: &targets,
width,
height,
},
)?;
self.color = color;
self.depth = depth;
self.targets = targets;
self.framebuffers = framebuffers;
self.width = width;
self.height = height;
Ok(())
}
pub(in crate::vulkan) fn destroy(&mut self, _device: &VkDevice) {
self.color = None;
self.depth = GpuImage::null();
self.framebuffers.clear();
self.targets.clear();
self.view_bufs.clear();
self.cull_indirect_bufs.clear();
self.cull_status_bufs.clear();
self.hiz_ubo = None;
self.probeset_buf = PooledBuffer::null();
self.global_sets.clear();
self.cull_sets.clear();
}
}
fn buf_info(buffer: vk::Buffer, range: u64) -> vk::DescriptorBufferInfo {
vk::DescriptorBufferInfo::default()
.buffer(buffer)
.offset(0)
.range(range)
}
fn write_storage(
device: &VkDevice,
set: vk::DescriptorSet,
binding: u32,
buffer: vk::Buffer,
range: u64,
) {
let info = buf_info(buffer, range);
let write = vk::WriteDescriptorSet::default()
.dst_set(set)
.dst_binding(binding)
.descriptor_type(vk::DescriptorType::STORAGE_BUFFER)
.buffer_info(std::slice::from_ref(&info));
unsafe { device.update_descriptor_sets(std::slice::from_ref(&write), &[]) };
}
fn img_info(view: vk::ImageView, sampler: vk::Sampler) -> vk::DescriptorImageInfo {
vk::DescriptorImageInfo::default()
.image_layout(vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL)
.image_view(view)
.sampler(sampler)
}
fn ubo_write<'a>(
set: vk::DescriptorSet,
binding: u32,
info: &'a vk::DescriptorBufferInfo,
) -> vk::WriteDescriptorSet<'a> {
vk::WriteDescriptorSet::default()
.dst_set(set)
.dst_binding(binding)
.descriptor_type(vk::DescriptorType::UNIFORM_BUFFER)
.buffer_info(std::slice::from_ref(info))
}
fn sampler_write<'a>(
set: vk::DescriptorSet,
binding: u32,
info: &'a vk::DescriptorImageInfo,
) -> vk::WriteDescriptorSet<'a> {
vk::WriteDescriptorSet::default()
.dst_set(set)
.dst_binding(binding)
.descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER)
.image_info(std::slice::from_ref(info))
}
impl VkContext {
pub(in crate::vulkan) fn encode_planar_reflections(
&self,
cmd: vk::CommandBuffer,
frame_idx: usize,
vp_mat: [[f32; 4]; 4],
cam_pos: [f32; 3],
elapsed: f32,
) -> Result<(), String> {
let Some(set) = self.planar_reflection.as_ref() else {
return Ok(());
};
let Some(&bindless_set) = self.cull.bindless_sets.get(frame_idx) else {
return Ok(());
};
let proj = mat4_mul(vp_mat, mat4_inverse(self.view.matrix));
let prefilter_mip_count = self.prefilter_mip_count as f32;
let extent = vk::Extent2D {
width: set.width,
height: set.height,
};
for slot in 0..set.plane_count() {
let oriented =
crate::gfx::planar_reflection::orient_plane_toward(set.planes[slot], cam_pos);
let m = crate::gfx::planar_reflection::planar_matrices(
self.view.matrix,
proj,
cam_pos,
oriented,
PLANAR_CLIP_BIAS,
);
let view = ViewUniforms {
vp: m.view_proj,
view: m.view,
elapsed,
reflections_enabled: 0.0,
cam_pos: [m.eye[0], m.eye[1], m.eye[2]],
prefilter_mip_count,
shade_mode: 0.0,
_end_pad: 0.0,
sky_rot: self.view.sky_rot,
};
let ring = slot * set.frames + frame_idx;
set.view_bufs[ring].write_val(0, &view);
let frustum = crate::gfx::frustum::Frustum::from_view_projection(m.view_proj);
self.encode_probe_cull(cmd, set.cull_sets[ring], set.hiz_set, &frustum, m.eye);
let attachment_waw = vk::MemoryBarrier::default()
.src_access_mask(
vk::AccessFlags::COLOR_ATTACHMENT_WRITE
| vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_WRITE,
)
.dst_access_mask(
vk::AccessFlags::COLOR_ATTACHMENT_WRITE
| vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_WRITE,
);
unsafe {
self.device.cmd_pipeline_barrier(
cmd,
vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT
| vk::PipelineStageFlags::LATE_FRAGMENT_TESTS,
vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT
| vk::PipelineStageFlags::EARLY_FRAGMENT_TESTS,
vk::DependencyFlags::empty(),
std::slice::from_ref(&attachment_waw),
&[],
&[],
);
}
self.encode_main_into_face(
cmd,
set.framebuffers[slot].handle(),
extent,
set.global_sets[ring],
bindless_set,
set.cull_indirect_bufs[ring].buffer(),
);
}
let barriers: Vec<vk::ImageMemoryBarrier> = set
.targets
.iter()
.map(|t| {
vk::ImageMemoryBarrier::default()
.src_access_mask(vk::AccessFlags::COLOR_ATTACHMENT_WRITE)
.dst_access_mask(vk::AccessFlags::SHADER_READ)
.old_layout(vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL)
.new_layout(vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL)
.src_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
.dst_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
.image(t.image)
.subresource_range(vk::ImageSubresourceRange {
aspect_mask: vk::ImageAspectFlags::COLOR,
base_mip_level: 0,
level_count: 1,
base_array_layer: 0,
layer_count: 1,
})
})
.collect();
unsafe {
self.device.cmd_pipeline_barrier(
cmd,
vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT,
vk::PipelineStageFlags::FRAGMENT_SHADER,
vk::DependencyFlags::empty(),
&[],
&[],
&barriers,
);
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn pane_plane_passes_through_centre_with_unit_normal() {
let p = pane_plane([0.0, 0.0, 1.0], [1.0, 2.0, 3.0]);
assert_eq!([p[0], p[1], p[2]], [0.0, 0.0, 1.0]);
let signed = p[0] * 1.0 + p[1] * 2.0 + p[2] * 3.0 + p[3];
assert!(signed.abs() < 1e-5, "centre lies on the plane");
}
#[test]
fn pane_plane_offset_is_negative_normal_dot_centre() {
let n = [0.6, 0.0, 0.8];
let c = [2.0, 5.0, -1.0];
let p = pane_plane(n, c);
let expect_d = -(n[0] * c[0] + n[1] * c[1] + n[2] * c[2]);
assert!((p[3] - expect_d).abs() < 1e-5);
}
#[test]
fn planar_capacity_is_four() {
assert_eq!(MAX_PLANAR_PLANES, 4);
assert_eq!(
MAX_PLANAR_PLANES,
crate::gfx::planar_reflection::MAX_PLANAR_PLANES
);
}
}