#include "shared.h"
#include "renderer_types.h"
#include "lib/frustum_culling.slang"
[[vk::push_constant]]
BuildIndirectPushConstants pc;
// VkDrawIndexedIndirectCommand is 5 x 4-byte fields:
// uint32 indexCount
// uint32 instanceCount
// uint32 firstIndex
// int32 vertexOffset
// uint32 firstInstance
// Total = 20 bytes.
// TODO: size this via a push constant to whatever gfx API
static const u32 DrawIndexedIndirectCommandStride = 20;
static const u32 WG = 64;
groupshared u32 s_vis[WG];
groupshared u32 s_group_base;
[shader("compute")]
[numthreads(64, 1, 1)]
void csMain(uvec3 id: SV_DispatchThreadID, uint gt: SV_GroupIndex) {
u32 visible = 0;
FrameUBO frame = *pc.frame;
Frustum frustum = extract_frustum(frame.view_proj);
set_far_plane(frustum, frame.position_ws.xyz, frame.forward_ws, 1000.0f);
if (id.x < pc.item_count) {
RenderItemGPU item = frame.render_items[id.x];
SubmeshGPU submesh = frame.submeshes[item.submesh_index];
TransformGPU xform = frame.transforms[item.instance_id];
visible = 1;
if (xform.active != 0) {
if (pc.enable_culling == 1) {
if (is_object_visible(submesh, xform, frustum)) {
bool occluded = false;
// UINT64_MAX is the canonical null handle (host-normalized).
Texture2D.Handle hiz_null = reinterpret<Texture2D.Handle>(uint2(0xFFFFFFFFu, 0xFFFFFFFFu));
if (pc.hiz != hiz_null) {
occluded = is_object_occluded(
submesh.local_aabb_min,
submesh.local_aabb_max,
xform,
frame.view_proj,
pc.hiz,
pc.hiz_width,
pc.hiz_height,
pc.hiz_mip_count
);
}
visible = !occluded;
}
} else {
visible = 1;
}
}
}
s_vis[gt] = visible;
GroupMemoryBarrierWithGroupSync();
if (gt == 0) {
u32 total = 0;
[unroll]
for (u32 i = 0; i < WG; ++i) {
u32 c = s_vis[i];
s_vis[i] = total;
total += c;
}
if (total > 0)
InterlockedAdd(pc.draw_count_buffer[0], total, s_group_base);
else
s_group_base = 0;
}
GroupMemoryBarrierWithGroupSync();
if (visible == 0)
return;
if (id.x >= pc.item_count) {
return;
}
RenderItemGPU item = frame.render_items[id.x];
SubmeshGPU submesh = frame.submeshes[item.submesh_index];
u32 slot = s_group_base + s_vis[gt];
pc.indirect_buffer[slot].indexCount = submesh.index_count;
pc.indirect_buffer[slot].instanceCount = 1;
pc.indirect_buffer[slot].firstIndex = submesh.first_index;
pc.indirect_buffer[slot].vertexOffset = submesh.base_vertex;
pc.indirect_buffer[slot].firstInstance = item.instance_id;
pc.visible_items[slot].id = id.x;
pc.visible_items[slot].instance_id = item.instance_id;
}