| // Copyright (c) 2022-2026 The Khronos Group Inc. |
| // Copyright (c) 2022-2026 Valve Corporation |
| // Copyright (c) 2022-2026 LunarG, Inc. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #version 460 |
| #extension GL_GOOGLE_include_directive : enable |
| |
| |
| #include "common.h" |
| #include "build_acceleration_structures.h" |
| |
| layout(push_constant, scalar) |
| uniform PushConstants { |
| TLASValidationShaderPushData pc; |
| }; |
| |
| bool RangesInclude(Range r1, Range r2) { |
| return (r1.begin <= r2.begin) && (r2.end <= r1.end); |
| } |
| bool RangesOverlap(Range r1, Range r2) { |
| return RangesInclude(r1, r2) || RangesInclude(r2, r1); |
| } |
| |
| layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; |
| void main() { |
| uint as_instance_i = 0; |
| if (pc.validation_mode == kBuildASValidationMode_invalid_AS) { |
| as_instance_i = gl_GlobalInvocationID.x * (gl_NumWorkGroups.y * gl_WorkGroupSize.y) + gl_GlobalInvocationID.y; |
| } else if (pc.validation_mode == kBuildASValidationMode_memory_overlaps) { |
| as_instance_i = gl_GlobalInvocationID.x; |
| } |
| if (as_instance_i >= pc.blas_array_size) { |
| return; |
| } |
| const uint blas_built_in_cmd_i = gl_GlobalInvocationID.y; |
| |
| // Address of BLAS to validate |
| uint64_t blas_addr = 0; |
| if (pc.is_array_of_pointers == 0) { |
| blas_addr = pc.blas_array_start_addr.blas_array[as_instance_i].accelerationStructureReference; |
| } else { |
| blas_addr = pc.blas_ptr_array_start_addr.blas_ptr_array[as_instance_i].as_instance.accelerationStructureReference; |
| } |
| |
| // At least mentioned in VUID 12281: |
| // A BLAS address of 0 is valid |
| if (blas_addr == 0) { |
| return; |
| } |
| |
| uint error_subcode = 0; |
| |
| if ((blas_addr % 16u ) != 0) { |
| error_subcode = kErrorSubCode_PreBuildAccelerationStructures_BlasAddrAlignment; |
| } else if (pc.validation_mode == kBuildASValidationMode_invalid_AS) { |
| error_subcode = kErrorSubCode_PreBuildAccelerationStructures_InvalidAS; |
| const uint as_count = pc.ptr_to_ptr_to_accel_structs_arrays.as_arrays_ptrs.addresses_ptr.count; |
| for (uint as_i = 0; as_i < as_count; ++as_i) { |
| uint64_t valid_as_addr = pc.ptr_to_ptr_to_accel_structs_arrays.as_arrays_ptrs.addresses_ptr.array[as_i]; |
| |
| if (valid_as_addr == blas_addr) { |
| const uint as_metadata = pc.ptr_to_ptr_to_accel_structs_arrays.as_arrays_ptrs.metadata_ptr.array[as_i]; |
| |
| // found AS, first assume it is valid |
| error_subcode = 0; |
| |
| if (GET_BUILD_AS_METADATA_BUFFER_STATUS(as_metadata) != BUILD_AS_METADATA_VALID_BUFFER) { |
| error_subcode = kErrorSubCode_PreBuildAccelerationStructures_DestroyedASBuffer; |
| break; |
| } |
| |
| if (GET_BUILD_AS_METADATA_AS_TYPE(as_metadata) != BUILD_AS_METADATA_AS_TYPE_BLAS) { |
| error_subcode = kErrorSubCode_PreBuildAccelerationStructures_InvalidASType; |
| break; |
| } |
| |
| if (GET_BUILD_AS_METADATA_BUFFER_MEMORY_STATUS(as_metadata) != BUILD_AS_METADATA_VALID_BUFFER_MEMORY) { |
| error_subcode = kErrorSubCode_PreBuildAccelerationStructures_DestroyedASMemory; |
| break; |
| } |
| |
| break; |
| } |
| } |
| } else if (pc.validation_mode == kBuildASValidationMode_memory_overlaps) { |
| if (blas_built_in_cmd_i >= pc.blas_built_in_cmd_array_size) { |
| return; |
| } |
| // Try to find buffer range associated to BLAS to validate |
| Range blas_buffer_range = {0,0}; |
| const uint as_count = pc.ptr_to_ptr_to_accel_structs_arrays.as_arrays_ptrs.addresses_ptr.count; |
| for (uint as_i = 0; as_i < as_count; ++as_i) { |
| uint64_t valid_as_addr = pc.ptr_to_ptr_to_accel_structs_arrays.as_arrays_ptrs.addresses_ptr.array[as_i]; |
| |
| if (valid_as_addr == blas_addr) { |
| blas_buffer_range = pc.ptr_to_ptr_to_accel_structs_arrays.as_arrays_ptrs.buffer_ranges_ptr.array[as_i]; |
| break; |
| } |
| } |
| // Does BLAS buffer range, referenced in a TLAS build, |
| // overlaps with another BLAS buffer range referenced in the same build command? |
| if (blas_buffer_range.begin != 0) { |
| Range blas_built_in_cmd_buffer_range = pc.blas_built_in_cmd_array_ptr.buffer_ranges[blas_built_in_cmd_i]; |
| if (RangesOverlap(blas_buffer_range, blas_built_in_cmd_buffer_range)) { |
| error_subcode = kErrorSubCode_PreBuildAccelerationStructures_BlasMemoryOverlap; |
| } |
| } |
| } |
| |
| if (error_subcode != 0) { |
| pc.blas_array_start_addr.blas_array[as_instance_i].instanceCustomIndex_and_mask &= 0xffffff;// Set mask to 0 (upper 8 bits), so instance cannot get hit |
| pc.blas_array_start_addr.blas_array[as_instance_i].accelerationStructureReference = pc.valid_dummy_blas_addr;// Point to a valid dummy blas |
| |
| const uint error_dword_0 = uint(blas_addr); |
| const uint error_dword_1 = uint(blas_addr >> 32u); |
| const uint error_dword_2 = as_instance_i; |
| const uint error_dword_3 = pc.blas_array_i;// #ARNO_TODO swap dwords 2 and 3 |
| const uint error_dword_4 = blas_built_in_cmd_i; |
| GpuavLogError5(kErrorGroup_GpuPreBuildAccelerationStructures, error_subcode, error_dword_0, error_dword_1, error_dword_2, error_dword_3, error_dword_4); |
| } |
| } |