• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright © 2022 Konstantin Seurer
3  *
4  * SPDX-License-Identifier: MIT
5  */
6 
7 #ifndef BVH_BUILD_HELPERS_H
8 #define BVH_BUILD_HELPERS_H
9 
10 #include "bvh.h"
11 #include "vk_build_helpers.h"
12 
13 TYPE(radv_accel_struct_serialization_header, 8);
14 TYPE(radv_accel_struct_header, 8);
15 TYPE(radv_bvh_triangle_node, 4);
16 TYPE(radv_bvh_aabb_node, 4);
17 TYPE(radv_bvh_instance_node, 8);
18 TYPE(radv_bvh_box16_node, 4);
19 TYPE(radv_bvh_box32_node, 4);
20 
21 uint32_t
id_to_offset(uint32_t id)22 id_to_offset(uint32_t id)
23 {
24    return (id & (~7u)) << 3;
25 }
26 
27 uint32_t
id_to_type(uint32_t id)28 id_to_type(uint32_t id)
29 {
30    return id & 7u;
31 }
32 
33 uint32_t
pack_node_id(uint32_t offset,uint32_t type)34 pack_node_id(uint32_t offset, uint32_t type)
35 {
36    return (offset >> 3) | type;
37 }
38 
39 uint64_t
node_to_addr(uint64_t node)40 node_to_addr(uint64_t node)
41 {
42    node &= ~7ul;
43    node <<= 19;
44    return int64_t(node) >> 16;
45 }
46 
47 uint64_t
addr_to_node(uint64_t addr)48 addr_to_node(uint64_t addr)
49 {
50    return (addr >> 3) & ((1ul << 45) - 1);
51 }
52 
53 uint32_t
ir_type_to_bvh_type(uint32_t type)54 ir_type_to_bvh_type(uint32_t type)
55 {
56    switch (type) {
57    case vk_ir_node_triangle:
58       return radv_bvh_node_triangle;
59    case vk_ir_node_internal:
60       return radv_bvh_node_box32;
61    case vk_ir_node_instance:
62       return radv_bvh_node_instance;
63    case vk_ir_node_aabb:
64       return radv_bvh_node_aabb;
65    }
66    /* unreachable in valid nodes */
67    return RADV_BVH_INVALID_NODE;
68 }
69 
70 /* A GLSL-adapted copy of VkAccelerationStructureInstanceKHR. */
71 struct AccelerationStructureInstance {
72    mat3x4 transform;
73    uint32_t custom_instance_and_mask;
74    uint32_t sbt_offset_and_flags;
75    uint64_t accelerationStructureReference;
76 };
77 TYPE(AccelerationStructureInstance, 8);
78 
79 bool
build_triangle(inout vk_aabb bounds,VOID_REF dst_ptr,vk_bvh_geometry_data geom_data,uint32_t global_id)80 build_triangle(inout vk_aabb bounds, VOID_REF dst_ptr, vk_bvh_geometry_data geom_data, uint32_t global_id)
81 {
82    bool is_valid = true;
83    triangle_indices indices = load_indices(geom_data.indices, geom_data.index_format, global_id);
84 
85    triangle_vertices vertices = load_vertices(geom_data.data, indices, geom_data.vertex_format, geom_data.stride);
86 
87    /* An inactive triangle is one for which the first (X) component of any vertex is NaN. If any
88     * other vertex component is NaN, and the first is not, the behavior is undefined. If the vertex
89     * format does not have a NaN representation, then all triangles are considered active.
90     */
91    if (isnan(vertices.vertex[0].x) || isnan(vertices.vertex[1].x) || isnan(vertices.vertex[2].x))
92 #if ALWAYS_ACTIVE
93       is_valid = false;
94 #else
95       return false;
96 #endif
97 
98    if (geom_data.transform != NULL) {
99       mat4 transform = mat4(1.0);
100 
101       for (uint32_t col = 0; col < 4; col++)
102          for (uint32_t row = 0; row < 3; row++)
103             transform[col][row] = DEREF(INDEX(float, geom_data.transform, col + row * 4));
104 
105       for (uint32_t i = 0; i < 3; i++)
106          vertices.vertex[i] = transform * vertices.vertex[i];
107    }
108 
109    REF(radv_bvh_triangle_node) node = REF(radv_bvh_triangle_node)(dst_ptr);
110 
111    bounds.min = vec3(INFINITY);
112    bounds.max = vec3(-INFINITY);
113 
114    for (uint32_t coord = 0; coord < 3; coord++)
115       for (uint32_t comp = 0; comp < 3; comp++) {
116          DEREF(node).coords[coord][comp] = vertices.vertex[coord][comp];
117          bounds.min[comp] = min(bounds.min[comp], vertices.vertex[coord][comp]);
118          bounds.max[comp] = max(bounds.max[comp], vertices.vertex[coord][comp]);
119       }
120 
121    DEREF(node).triangle_id = global_id;
122    DEREF(node).geometry_id_and_flags = geom_data.geometry_id;
123    DEREF(node).id = 9;
124 
125    return is_valid;
126 }
127 
128 bool
build_aabb(inout vk_aabb bounds,VOID_REF src_ptr,VOID_REF dst_ptr,uint32_t geometry_id,uint32_t global_id)129 build_aabb(inout vk_aabb bounds, VOID_REF src_ptr, VOID_REF dst_ptr, uint32_t geometry_id, uint32_t global_id)
130 {
131    bool is_valid = true;
132    REF(radv_bvh_aabb_node) node = REF(radv_bvh_aabb_node)(dst_ptr);
133 
134    for (uint32_t vec = 0; vec < 2; vec++)
135       for (uint32_t comp = 0; comp < 3; comp++) {
136          float coord = DEREF(INDEX(float, src_ptr, comp + vec * 3));
137 
138          if (vec == 0)
139             bounds.min[comp] = coord;
140          else
141             bounds.max[comp] = coord;
142       }
143 
144    /* An inactive AABB is one for which the minimum X coordinate is NaN. If any other component is
145     * NaN, and the first is not, the behavior is undefined.
146     */
147    if (isnan(bounds.min.x))
148 #if ALWAYS_ACTIVE
149       is_valid = false;
150 #else
151       return false;
152 #endif
153 
154    DEREF(node).primitive_id = global_id;
155    DEREF(node).geometry_id_and_flags = geometry_id;
156 
157    return is_valid;
158 }
159 
160 vk_aabb
calculate_instance_node_bounds(radv_accel_struct_header header,mat3x4 otw_matrix)161 calculate_instance_node_bounds(radv_accel_struct_header header, mat3x4 otw_matrix)
162 {
163    vk_aabb aabb;
164    for (uint32_t comp = 0; comp < 3; ++comp) {
165       aabb.min[comp] = otw_matrix[comp][3];
166       aabb.max[comp] = otw_matrix[comp][3];
167       for (uint32_t col = 0; col < 3; ++col) {
168          aabb.min[comp] +=
169             min(otw_matrix[comp][col] * header.aabb.min[col], otw_matrix[comp][col] * header.aabb.max[col]);
170          aabb.max[comp] +=
171             max(otw_matrix[comp][col] * header.aabb.min[col], otw_matrix[comp][col] * header.aabb.max[col]);
172       }
173    }
174    return aabb;
175 }
176 
177 uint32_t
encode_sbt_offset_and_flags(uint32_t src)178 encode_sbt_offset_and_flags(uint32_t src)
179 {
180    uint32_t flags = src >> 24;
181    uint32_t ret = src & 0xffffffu;
182    if ((flags & VK_GEOMETRY_INSTANCE_FORCE_OPAQUE_BIT_KHR) != 0)
183       ret |= RADV_INSTANCE_FORCE_OPAQUE;
184    if ((flags & VK_GEOMETRY_INSTANCE_FORCE_NO_OPAQUE_BIT_KHR) == 0)
185       ret |= RADV_INSTANCE_NO_FORCE_NOT_OPAQUE;
186    if ((flags & VK_GEOMETRY_INSTANCE_TRIANGLE_FACING_CULL_DISABLE_BIT_KHR) != 0)
187       ret |= RADV_INSTANCE_TRIANGLE_FACING_CULL_DISABLE;
188    if ((flags & VK_GEOMETRY_INSTANCE_TRIANGLE_FLIP_FACING_BIT_KHR) != 0)
189       ret |= RADV_INSTANCE_TRIANGLE_FLIP_FACING;
190    return ret;
191 }
192 
193 bool
build_instance(inout vk_aabb bounds,VOID_REF src_ptr,VOID_REF dst_ptr,uint32_t global_id)194 build_instance(inout vk_aabb bounds, VOID_REF src_ptr, VOID_REF dst_ptr, uint32_t global_id)
195 {
196    REF(radv_bvh_instance_node) node = REF(radv_bvh_instance_node)(dst_ptr);
197 
198    AccelerationStructureInstance instance = DEREF(REF(AccelerationStructureInstance)(src_ptr));
199 
200    /* An inactive instance is one whose acceleration structure handle is VK_NULL_HANDLE. Since the active terminology is
201     * only relevant for BVH updates, which we do not implement, we can also skip instances with mask == 0.
202     */
203    if (instance.accelerationStructureReference == 0 || instance.custom_instance_and_mask < (1u << 24u))
204       return false;
205 
206    radv_accel_struct_header instance_header =
207       DEREF(REF(radv_accel_struct_header)(instance.accelerationStructureReference));
208 
209    DEREF(node).bvh_ptr = addr_to_node(instance.accelerationStructureReference + instance_header.bvh_offset);
210    DEREF(node).bvh_offset = instance_header.bvh_offset;
211 
212    mat4 transform = mat4(instance.transform);
213    mat4 inv_transform = transpose(inverse(transpose(transform)));
214    DEREF(node).wto_matrix = mat3x4(inv_transform);
215    DEREF(node).otw_matrix = mat3x4(transform);
216 
217    bounds = calculate_instance_node_bounds(instance_header, mat3x4(transform));
218 
219    DEREF(node).custom_instance_and_mask = instance.custom_instance_and_mask;
220    DEREF(node).sbt_offset_and_flags = encode_sbt_offset_and_flags(instance.sbt_offset_and_flags);
221    DEREF(node).instance_id = global_id;
222 
223    return true;
224 }
225 
226 /** Compute ceiling of integer quotient of A divided by B.
227     From macros.h */
228 #define DIV_ROUND_UP(A, B) (((A) + (B)-1) / (B))
229 
230 #endif /* BUILD_HELPERS_H */
231