blob: 1979abe14302b5199c94de4117fe9a7f77319f54 [file] [edit]
/*
* Copyright © 2026 Intel Corporation
* SPDX-License-Identifier: MIT
*/
#version 460
#include "anv_bvh_defines.h"
#define VK_USED_BUILD_FLAGS (VK_BUILD_FLAG_HAS_QUADS)
#include "anv_bvh_helpers.h"
#include "encode.h"
/*
* Update shader is spawned for number of leaves (leaf_node).
*
* 1) Each thread (leaf) will calculate the new bounding box and then write it
* into aabb_scratch space on it's leaf index.
*
* 2) Threads can race to update the parent, so that's why we write updated
* bounding boxes in scratch space to read it later.
*
* 3) Leaf thread will start walking up towards the root node. It does track
* the arrival counter by doing atomic add.
*
* 4) Last thread to win, will read all bounding boxes from scratch except it's
* own which gets passed as parameter to function and update the parent
* bounding box accordingly.
*
* 5) When we hit root node, we compute the final root AABB and update the
* header.
*/
layout(local_size_x = 32, local_size_y = 1, local_size_z = 1) in;
layout(push_constant) uniform CONSTS {
update_args args;
};
vk_ir_triangle_node
anv_load_triangle_node(vk_bvh_geometry_data geom_data, uint32_t local_id, out vk_aabb bounds)
{
triangle_indices indices = load_indices(geom_data.indices, geom_data.index_format, local_id);
triangle_vertices vertices = load_vertices(geom_data.data, indices, geom_data.vertex_format, geom_data.stride);
if (geom_data.transform != NULL) {
mat4 transform = mat4(1.0);
for (uint32_t col = 0; col < 4; col++) {
for (uint32_t row = 0; row < 3; row++) {
transform[col][row] = DEREF(INDEX(float, geom_data.transform, col + row * 4));
}
}
for (uint32_t i = 0; i < 3; i++) {
vertices.vertex[i] = transform * vertices.vertex[i];
}
}
vk_ir_triangle_node node;
bounds.min = vec3(INFINITY);
bounds.max = vec3(-INFINITY);
for (uint32_t coord = 0; coord < 3; coord++) {
for (uint32_t comp = 0; comp < 3; comp++) {
node.coords[coord][comp] = vertices.vertex[coord][comp];
bounds.min[comp] = min(bounds.min[comp], vertices.vertex[coord][comp]);
bounds.max[comp] = max(bounds.max[comp], vertices.vertex[coord][comp]);
}
}
node.triangle_id = local_id;
node.geometry_id_and_flags = geom_data.geometry_id;
return node;
}
void
anv_build_triangle(inout vk_aabb bounds, VOID_REF dst_ptr, vk_bvh_geometry_data geom_data, uint32_t global_id)
{
vk_ir_triangle_node node = anv_load_triangle_node(geom_data, global_id, bounds);
anv_encode_triangle(dst_ptr, node);
}
void
anv_decode_quad_shared_edges(uint32_t prim_index1_delta, uint32_t own_local_id, uint32_t merge_id,
out uint32_t tri0_edge, out uint32_t tri1_edge)
{
uint32_t j[3] = {(prim_index1_delta >> 16) & 0x3, (prim_index1_delta >> 18) & 0x3,
(prim_index1_delta >> 20) & 0x3};
uint32_t idx3 = (j[0] == 3) ? 0 : ((j[1] == 3) ? 1 : 2);
uint32_t tri1_swapped_edge = (idx3 + 1u) % 3u;
uint32_t tri0_swapped_edge = (j[tri1_swapped_edge] + 2u) % 3u;
bool swapped = merge_id < own_local_id;
tri0_edge = swapped ? tri1_swapped_edge : tri0_swapped_edge;
tri1_edge = swapped ? tri0_swapped_edge : tri1_swapped_edge;
}
vk_aabb
anv_build_quad(VOID_REF dst_ptr, vk_bvh_geometry_data geom_data,
uint32_t own_local_id, uint32_t merge_id,
uint32_t prim_index1_delta)
{
vk_aabb own_bounds, merge_bounds;
vk_ir_triangle_node own_node = anv_load_triangle_node(geom_data, own_local_id, own_bounds);
vk_ir_triangle_node merge_node = anv_load_triangle_node(geom_data, merge_id, merge_bounds);
uint32_t tri0_edge, tri1_edge;
anv_decode_quad_shared_edges(prim_index1_delta, own_local_id, merge_id, tri0_edge, tri1_edge);
uint32_t v3_index = (tri1_edge + 2u) % 3u;
vec3 v3 = vec3(merge_node.coords[v3_index][0], merge_node.coords[v3_index][1], merge_node.coords[v3_index][2]);
vk_aabb bounds = own_bounds;
aabb_extend(bounds, merge_bounds);
vk_ir_triangle_node_quad quad;
quad.coords[0] = v3.x;
quad.coords[1] = v3.y;
quad.coords[2] = v3.z;
quad.triangle_id = merge_node.triangle_id | (((tri1_edge << 2) | tri0_edge) << 28);
anv_encode_quad(dst_ptr, own_node, quad);
return bounds;
}
void
anv_build_aabb(inout vk_aabb bounds, VOID_REF src_ptr, VOID_REF dst_ptr, uint32_t geometry_id, uint32_t global_id)
{
for (uint32_t vec = 0; vec < 2; vec++) {
for (uint32_t comp = 0; comp < 3; comp++) {
float coord = DEREF(INDEX(float, src_ptr, comp + vec * 3));
if (vec == 0)
bounds.min[comp] = coord;
else
bounds.max[comp] = coord;
}
}
vk_ir_aabb_node node;
node.base.aabb = bounds;
node.primitive_id = global_id;
node.geometry_id_and_flags = geometry_id;
anv_encode_aabb(dst_ptr, node);
}
vk_aabb
build_and_encode_leaf(uint32_t leaf_local_idx, BLOCK leaf_block)
{
VOID_REF dst = BLOCK_OFFSET(leaf_block);
vk_aabb bounds = vk_aabb(vec3(0.0f), vec3(0.0f));
switch (args.geom_data.geometry_type) {
case VK_GEOMETRY_TYPE_TRIANGLES_KHR: {
if (VK_TEST_BUILD_FLAG_HAS_QUADS) {
REF(anv_quad_leaf_node) quad_leaf = REF(anv_quad_leaf_node)(dst);
uint32_t prim_index0 = DEREF(quad_leaf).prim_index0;
uint32_t prim_index1_delta = DEREF(quad_leaf).prim_index1_delta;
uint32_t delta16 = prim_index1_delta & 0xffff;
if (delta16 != 0) {
uint32_t triangle_id0 = prim_index0;
uint32_t triangle_id1 = prim_index0 + delta16;
uint32_t merge_idx = (leaf_local_idx == triangle_id0) ? triangle_id1 : triangle_id0;
bounds = anv_build_quad(dst, args.geom_data, leaf_local_idx, merge_idx,
prim_index1_delta);
break;
}
}
anv_build_triangle(bounds, dst, args.geom_data, leaf_local_idx);
break;
}
case VK_GEOMETRY_TYPE_AABBS_KHR: {
VOID_REF src_aabb_ptr = OFFSET(args.geom_data.data,
leaf_local_idx * args.geom_data.stride);
anv_build_aabb(bounds, src_aabb_ptr, dst, args.geom_data.geometry_id,
leaf_local_idx);
break;
}
default:
/* instances typically not updated */
break;
}
return bounds;
}
vk_aabb
recompute_parent(BLOCK parent_block, uint32_t updated_idx,
uint32_t child_count, vk_aabb updated_child_bounds)
{
REF(anv_internal_node) parent = REF(anv_internal_node)(BLOCK_OFFSET(parent_block));
vk_aabb box;
box.min = vec3(INFINITY);
box.max = vec3(-INFINITY);
BLOCK current_child_block = parent_block + DEREF(parent).child_block_offset;
vk_aabb cache_aabb[6];
for (uint32_t i = 0; i < child_count; ++i) {
cache_aabb[i] = (i == updated_idx) ? updated_child_bounds :
DEREF(INDEX(vk_aabb, args.aabb_scratch, current_child_block + i));
aabb_extend(box, cache_aabb[i]);
}
vk_aabb conservative_child_aabb = conservative_aabb(box);
float up = 1.0 + ULP;
ivec3 exp;
vec3 len = aabb_size(conservative_child_aabb) * up;
vec3 mant = frexp(len, exp);
exp.x += int((mant.x > (255.0f / 256.0f)));
exp.y += int((mant.y > (255.0f / 256.0f)));
exp.z += int((mant.z > (255.0f / 256.0f)));
i8vec3 exponent_i8 = i8vec3(exp);
i8vec3 exp_i8 = {max(int8_t(-128), exponent_i8.x),
max(int8_t(-128), exponent_i8.y),
max(int8_t(-128), exponent_i8.z)};
DEREF(parent).lower[0] = conservative_child_aabb.min.x;
DEREF(parent).lower[1] = conservative_child_aabb.min.y;
DEREF(parent).lower[2] = conservative_child_aabb.min.z;
DEREF(parent).exp_x = exp_i8[0];
DEREF(parent).exp_y = exp_i8[1];
DEREF(parent).exp_z = exp_i8[2];
vec3 base = conservative_child_aabb.min;
vec3 scale = ldexp(vec3(1.0), exp_i8 - 8);
for (uint32_t i = 0; i < child_count; ++i) {
vk_aabb child_bounds = cache_aabb[i];
vec3 lower = (child_bounds.min - base) / scale;
vec3 upper = (child_bounds.max - base) / scale;
lower = clamp(floor(lower), vec3(0.0), vec3(255.0));
upper = clamp(ceil(upper), vec3(0.0), vec3(255.0));
DEREF(parent).lower_x[i] = uint8_t(lower.x);
DEREF(parent).lower_y[i] = uint8_t(lower.y);
DEREF(parent).lower_z[i] = uint8_t(lower.z);
DEREF(parent).upper_x[i] = uint8_t(upper.x);
DEREF(parent).upper_y[i] = uint8_t(upper.y);
DEREF(parent).upper_z[i] = uint8_t(upper.z);
}
return box;
}
uint32_t
get_parent_id(uint32_t parent_raw)
{
return parent_raw & 0x03FFFFFF;
}
uint32_t
get_child_index(uint32_t parent_raw)
{
return parent_raw >> 26;
}
void main()
{
uint32_t leaf_local = gl_GlobalInvocationID.x;
if (leaf_local >= args.leaf_node_count || leaf_local >= args.primitive_count)
return;
uint32_t leaf_id = args.geom_data.first_id + leaf_local;
BLOCK leaf_block = DEREF(INDEX(uint32_t, args.leaf_block_offset_map, leaf_id));
if (leaf_block == VK_NULL_BVH_OFFSET)
return;
vk_aabb leaf_bounds = build_and_encode_leaf(leaf_local, leaf_block);
DEREF(INDEX(vk_aabb, args.aabb_scratch, leaf_block)) = leaf_bounds;
BLOCK current_block = leaf_block;
vk_aabb current_bounds = leaf_bounds;
while (true) {
BLOCK parent_raw = DEREF(INDEX(uint32_t, args.parent_child_map, current_block));
BLOCK parent = get_parent_id(parent_raw);
uint32_t valid_child_count =
uint32_t(DEREF(INDEX(uint8_t, args.parent_child_count_map, parent)));
if (valid_child_count == 0)
break;
/* Last-child-wins atomic */
uint32_t ready = atomicAdd(
DEREF(INDEX(uint32_t, args.internal_ready_count, parent)), 1,
gl_ScopeDevice, gl_StorageSemanticsBuffer,
gl_SemanticsAcquireRelease | gl_SemanticsMakeAvailable | gl_SemanticsMakeVisible);
/* Not the last child */
if (ready != valid_child_count - 1)
break;
uint32_t child_idx = get_child_index(parent_raw);
/* Encode and quantize parent bounds */
vk_aabb parent_bounds = recompute_parent(parent, child_idx, valid_child_count, current_bounds);
/* Store parent bounds for next level */
DEREF(INDEX(vk_aabb, args.aabb_scratch, parent)) = parent_bounds;
/* Check whether this parent is the root internal node */
uint32_t grandparent_raw = DEREF(INDEX(uint32_t, args.parent_child_map, parent));
if (grandparent_raw == VK_NULL_BVH_OFFSET) {
REF(anv_internal_node) root = REF(anv_internal_node)(BLOCK_OFFSET(parent));
vec3 base = vec3(DEREF(root).lower[0], DEREF(root).lower[1], DEREF(root).lower[2]);
ivec3 exp = ivec3( int(DEREF(root).exp_x), int(DEREF(root).exp_y), int(DEREF(root).exp_z));
vec3 scale = ldexp(vec3(1.0), exp - 8);
vk_aabb root_bounds = vk_aabb(vec3(INFINITY), vec3(-INFINITY));
for (uint32_t i = 0; i < valid_child_count; ++i) {
vec3 lower = vec3(DEREF(root).lower_x[i],
DEREF(root).lower_y[i],
DEREF(root).lower_z[i]);
vec3 upper = vec3(DEREF(root).upper_x[i],
DEREF(root).upper_y[i],
DEREF(root).upper_z[i]);
vk_aabb child;
child.min = base + lower * scale;
child.max = base + upper * scale;
aabb_extend(root_bounds, child);
}
REF(anv_accel_struct_header) hdr =
REF(anv_accel_struct_header)(args.output_bvh - args.output_bvh_offset);
DEREF(hdr).aabb = root_bounds;
break;
}
current_block = parent;
current_bounds = parent_bounds;
}
}