build_indirect.slang

cross platform rendering playground

src/passes/dispatch_mdi/build_indirect.slang

3.12 KB
#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;
}