// Copyright (c) 2022 The Khronos Group 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. // Tests ray tracing instructions from SPV_KHR_ray_tracing. #include #include #include "gmock/gmock.h" #include "test/val/val_fixtures.h" namespace spvtools { namespace val { namespace { using ::testing::HasSubstr; using ::testing::Values; using ValidateRayTracing = spvtest::ValidateBase; TEST_F(ValidateRayTracing, IgnoreIntersectionSuccess) { const std::string body = R"( OpCapability RayTracingKHR OpExtension "SPV_KHR_ray_tracing" OpMemoryModel Logical GLSL450 OpEntryPoint AnyHitKHR %main "main" OpName %main "main" %void = OpTypeVoid %func = OpTypeFunction %void %main = OpFunction %void None %func %label = OpLabel OpIgnoreIntersectionKHR OpFunctionEnd )"; CompileSuccessfully(body.c_str()); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } TEST_F(ValidateRayTracing, IgnoreIntersectionExecutionModel) { const std::string body = R"( OpCapability RayTracingKHR OpExtension "SPV_KHR_ray_tracing" OpMemoryModel Logical GLSL450 OpEntryPoint CallableKHR %main "main" OpName %main "main" %void = OpTypeVoid %func = OpTypeFunction %void %main = OpFunction %void None %func %label = OpLabel OpIgnoreIntersectionKHR OpFunctionEnd )"; CompileSuccessfully(body.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), HasSubstr("OpIgnoreIntersectionKHR requires AnyHitKHR execution model")); } TEST_F(ValidateRayTracing, TerminateRaySuccess) { const std::string body = R"( OpCapability RayTracingKHR OpExtension "SPV_KHR_ray_tracing" OpMemoryModel Logical GLSL450 OpEntryPoint AnyHitKHR %main "main" OpName %main "main" %void = OpTypeVoid %func = OpTypeFunction %void %main = OpFunction %void None %func %label = OpLabel OpIgnoreIntersectionKHR OpFunctionEnd )"; CompileSuccessfully(body.c_str()); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } TEST_F(ValidateRayTracing, TerminateRayExecutionModel) { const std::string body = R"( OpCapability RayTracingKHR OpExtension "SPV_KHR_ray_tracing" OpMemoryModel Logical GLSL450 OpEntryPoint MissKHR %main "main" OpName %main "main" %void = OpTypeVoid %func = OpTypeFunction %void %main = OpFunction %void None %func %label = OpLabel OpTerminateRayKHR OpFunctionEnd )"; CompileSuccessfully(body.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), HasSubstr("OpTerminateRayKHR requires AnyHitKHR execution model")); } TEST_F(ValidateRayTracing, ReportIntersectionRaySuccess) { const std::string body = R"( OpCapability RayTracingKHR OpExtension "SPV_KHR_ray_tracing" OpMemoryModel Logical GLSL450 OpEntryPoint IntersectionKHR %main "main" OpName %main "main" %void = OpTypeVoid %func = OpTypeFunction %void %float = OpTypeFloat 32 %float_1 = OpConstant %float 1 %uint = OpTypeInt 32 0 %uint_1 = OpConstant %uint 1 %bool = OpTypeBool %main = OpFunction %void None %func %label = OpLabel %report = OpReportIntersectionKHR %bool %float_1 %uint_1 OpReturn OpFunctionEnd )"; CompileSuccessfully(body.c_str()); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } TEST_F(ValidateRayTracing, ReportIntersectionExecutionModel) { const std::string body = R"( OpCapability RayTracingKHR OpExtension "SPV_KHR_ray_tracing" OpMemoryModel Logical GLSL450 OpEntryPoint MissKHR %main "main" OpName %main "main" %void = OpTypeVoid %func = OpTypeFunction %void %float = OpTypeFloat 32 %float_1 = OpConstant %float 1 %uint = OpTypeInt 32 0 %uint_1 = OpConstant %uint 1 %bool = OpTypeBool %main = OpFunction %void None %func %label = OpLabel %report = OpReportIntersectionKHR %bool %float_1 %uint_1 OpReturn OpFunctionEnd )"; CompileSuccessfully(body.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), HasSubstr( "OpReportIntersectionKHR requires IntersectionKHR execution model")); } TEST_F(ValidateRayTracing, ReportIntersectionReturnType) { const std::string body = R"( OpCapability RayTracingKHR OpExtension "SPV_KHR_ray_tracing" OpMemoryModel Logical GLSL450 OpEntryPoint IntersectionKHR %main "main" OpName %main "main" %void = OpTypeVoid %func = OpTypeFunction %void %float = OpTypeFloat 32 %float_1 = OpConstant %float 1 %uint = OpTypeInt 32 0 %uint_1 = OpConstant %uint 1 %main = OpFunction %void None %func %label = OpLabel %report = OpReportIntersectionKHR %uint %float_1 %uint_1 OpReturn OpFunctionEnd )"; CompileSuccessfully(body.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("expected Result Type to be bool scalar type")); } TEST_F(ValidateRayTracing, ReportIntersectionHit) { const std::string body = R"( OpCapability RayTracingKHR OpCapability Float64 OpExtension "SPV_KHR_ray_tracing" OpMemoryModel Logical GLSL450 OpEntryPoint IntersectionKHR %main "main" OpName %main "main" %void = OpTypeVoid %func = OpTypeFunction %void %float64 = OpTypeFloat 64 %float64_1 = OpConstant %float64 1 %uint = OpTypeInt 32 0 %uint_1 = OpConstant %uint 1 %bool = OpTypeBool %main = OpFunction %void None %func %label = OpLabel %report = OpReportIntersectionKHR %bool %float64_1 %uint_1 OpReturn OpFunctionEnd )"; CompileSuccessfully(body.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("Hit must be a 32-bit int scalar")); } TEST_F(ValidateRayTracing, ReportIntersectionHitKind) { const std::string body = R"( OpCapability RayTracingKHR OpExtension "SPV_KHR_ray_tracing" OpMemoryModel Logical GLSL450 OpEntryPoint IntersectionKHR %main "main" OpName %main "main" %void = OpTypeVoid %func = OpTypeFunction %void %float = OpTypeFloat 32 %float_1 = OpConstant %float 1 %sint = OpTypeInt 32 1 %sint_1 = OpConstant %sint 1 %bool = OpTypeBool %main = OpFunction %void None %func %label = OpLabel %report = OpReportIntersectionKHR %bool %float_1 %sint_1 OpReturn OpFunctionEnd )"; CompileSuccessfully(body.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("Hit Kind must be a 32-bit unsigned int scalar")); } TEST_F(ValidateRayTracing, ExecuteCallableSuccess) { const std::string body = R"( OpCapability RayTracingKHR OpExtension "SPV_KHR_ray_tracing" OpMemoryModel Logical GLSL450 OpEntryPoint CallableKHR %main "main" OpName %main "main" %void = OpTypeVoid %func = OpTypeFunction %void %int = OpTypeInt 32 1 %uint = OpTypeInt 32 0 %uint_0 = OpConstant %uint 0 %data_ptr = OpTypePointer CallableDataKHR %int %data = OpVariable %data_ptr CallableDataKHR %inData_ptr = OpTypePointer IncomingCallableDataKHR %int %inData = OpVariable %inData_ptr IncomingCallableDataKHR %main = OpFunction %void None %func %label = OpLabel OpExecuteCallableKHR %uint_0 %data OpExecuteCallableKHR %uint_0 %inData OpReturn OpFunctionEnd )"; CompileSuccessfully(body.c_str()); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } TEST_F(ValidateRayTracing, ExecuteCallableExecutionModel) { const std::string body = R"( OpCapability RayTracingKHR OpExtension "SPV_KHR_ray_tracing" OpMemoryModel Logical GLSL450 OpEntryPoint AnyHitKHR %main "main" OpName %main "main" %void = OpTypeVoid %func = OpTypeFunction %void %int = OpTypeInt 32 1 %uint = OpTypeInt 32 0 %uint_0 = OpConstant %uint 0 %data_ptr = OpTypePointer CallableDataKHR %int %data = OpVariable %data_ptr CallableDataKHR %inData_ptr = OpTypePointer IncomingCallableDataKHR %int %inData = OpVariable %inData_ptr IncomingCallableDataKHR %main = OpFunction %void None %func %label = OpLabel OpExecuteCallableKHR %uint_0 %data OpExecuteCallableKHR %uint_0 %inData OpReturn OpFunctionEnd )"; CompileSuccessfully(body.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), HasSubstr("OpExecuteCallableKHR requires RayGenerationKHR, " "ClosestHitKHR, MissKHR and CallableKHR execution models")); } TEST_F(ValidateRayTracing, ExecuteCallableStorageClass) { const std::string body = R"( OpCapability RayTracingKHR OpExtension "SPV_KHR_ray_tracing" OpMemoryModel Logical GLSL450 OpEntryPoint RayGenerationKHR %main "main" OpName %main "main" %void = OpTypeVoid %func = OpTypeFunction %void %int = OpTypeInt 32 1 %uint = OpTypeInt 32 0 %uint_0 = OpConstant %uint 0 %data_ptr = OpTypePointer RayPayloadKHR %int %data = OpVariable %data_ptr RayPayloadKHR %main = OpFunction %void None %func %label = OpLabel OpExecuteCallableKHR %uint_0 %data OpReturn OpFunctionEnd )"; CompileSuccessfully(body.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("Callable Data must have storage class CallableDataKHR " "or IncomingCallableDataKHR")); } TEST_F(ValidateRayTracing, ExecuteCallableSbtIndex) { const std::string body = R"( OpCapability RayTracingKHR OpExtension "SPV_KHR_ray_tracing" OpMemoryModel Logical GLSL450 OpEntryPoint CallableKHR %main "main" OpName %main "main" %void = OpTypeVoid %func = OpTypeFunction %void %int = OpTypeInt 32 1 %uint = OpTypeInt 32 0 %uint_0 = OpConstant %uint 0 %int_1 = OpConstant %int 1 %data_ptr = OpTypePointer CallableDataKHR %int %data = OpVariable %data_ptr CallableDataKHR %main = OpFunction %void None %func %label = OpLabel OpExecuteCallableKHR %int_1 %data OpReturn OpFunctionEnd )"; CompileSuccessfully(body.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("SBT Index must be a 32-bit unsigned int scalar")); } std::string GenerateRayTraceCode( const std::string& body, const std::string execution_model = "RayGenerationKHR") { std::ostringstream ss; ss << R"( OpCapability RayTracingKHR OpCapability Float64 OpExtension "SPV_KHR_ray_tracing" OpMemoryModel Logical GLSL450 OpEntryPoint )" << execution_model << R"( %main "main" OpDecorate %top_level_as DescriptorSet 0 OpDecorate %top_level_as Binding 0 %void = OpTypeVoid %func = OpTypeFunction %void %type_as = OpTypeAccelerationStructureKHR %as_uc_ptr = OpTypePointer UniformConstant %type_as %top_level_as = OpVariable %as_uc_ptr UniformConstant %uint = OpTypeInt 32 0 %uint_1 = OpConstant %uint 1 %float = OpTypeFloat 32 %float64 = OpTypeFloat 64 %f32vec3 = OpTypeVector %float 3 %f32vec4 = OpTypeVector %float 4 %float_0 = OpConstant %float 0 %float64_0 = OpConstant %float64 0 %v3composite = OpConstantComposite %f32vec3 %float_0 %float_0 %float_0 %v4composite = OpConstantComposite %f32vec4 %float_0 %float_0 %float_0 %float_0 %int = OpTypeInt 32 1 %int_1 = OpConstant %int 1 %payload_ptr = OpTypePointer RayPayloadKHR %int %payload = OpVariable %payload_ptr RayPayloadKHR %callable_ptr = OpTypePointer CallableDataKHR %int %callable = OpVariable %callable_ptr CallableDataKHR %ptr_uint = OpTypePointer Private %uint %var_uint = OpVariable %ptr_uint Private %ptr_float = OpTypePointer Private %float %var_float = OpVariable %ptr_float Private %ptr_f32vec3 = OpTypePointer Private %f32vec3 %var_f32vec3 = OpVariable %ptr_f32vec3 Private %main = OpFunction %void None %func %label = OpLabel )"; ss << body; ss << R"( OpReturn OpFunctionEnd)"; return ss.str(); } TEST_F(ValidateRayTracing, TraceRaySuccess) { const std::string body = R"( %as = OpLoad %type_as %top_level_as OpTraceRayKHR %as %uint_1 %uint_1 %uint_1 %uint_1 %uint_1 %v3composite %float_0 %v3composite %float_0 %payload %_uint = OpLoad %uint %var_uint %_float = OpLoad %float %var_float %_f32vec3 = OpLoad %f32vec3 %var_f32vec3 OpTraceRayKHR %as %_uint %_uint %_uint %_uint %_uint %_f32vec3 %_float %_f32vec3 %_float %payload )"; CompileSuccessfully(GenerateRayTraceCode(body).c_str()); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } TEST_F(ValidateRayTracing, TraceRayExecutionModel) { const std::string body = R"( %as = OpLoad %type_as %top_level_as OpTraceRayKHR %as %uint_1 %uint_1 %uint_1 %uint_1 %uint_1 %v3composite %float_0 %v3composite %float_0 %payload )"; CompileSuccessfully(GenerateRayTraceCode(body, "CallableKHR").c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("OpTraceRayKHR requires RayGenerationKHR, " "ClosestHitKHR and MissKHR execution models")); } TEST_F(ValidateRayTracing, TraceRayAccelerationStructure) { const std::string body = R"( %_uint = OpLoad %uint %var_uint OpTraceRayKHR %_uint %uint_1 %uint_1 %uint_1 %uint_1 %uint_1 %v3composite %float_0 %v3composite %float_0 %payload )"; CompileSuccessfully(GenerateRayTraceCode(body).c_str()); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("Expected Acceleration Structure to be of type " "OpTypeAccelerationStructureKHR")); } TEST_F(ValidateRayTracing, TraceRayRayFlags) { const std::string body = R"( %as = OpLoad %type_as %top_level_as OpTraceRayKHR %as %float_0 %uint_1 %uint_1 %uint_1 %uint_1 %v3composite %float_0 %v3composite %float_0 %payload )"; CompileSuccessfully(GenerateRayTraceCode(body).c_str()); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("Ray Flags must be a 32-bit int scalar")); } TEST_F(ValidateRayTracing, TraceRayCullMask) { const std::string body = R"( %as = OpLoad %type_as %top_level_as OpTraceRayKHR %as %uint_1 %float_0 %uint_1 %uint_1 %uint_1 %v3composite %float_0 %v3composite %float_0 %payload )"; CompileSuccessfully(GenerateRayTraceCode(body).c_str()); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("Cull Mask must be a 32-bit int scalar")); } TEST_F(ValidateRayTracing, TraceRaySbtOffest) { const std::string body = R"( %as = OpLoad %type_as %top_level_as OpTraceRayKHR %as %uint_1 %uint_1 %float_0 %uint_1 %uint_1 %v3composite %float_0 %v3composite %float_0 %payload )"; CompileSuccessfully(GenerateRayTraceCode(body).c_str()); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("SBT Offset must be a 32-bit int scalar")); } TEST_F(ValidateRayTracing, TraceRaySbtStride) { const std::string body = R"( %as = OpLoad %type_as %top_level_as OpTraceRayKHR %as %uint_1 %uint_1 %uint_1 %float_0 %uint_1 %v3composite %float_0 %v3composite %float_0 %payload )"; CompileSuccessfully(GenerateRayTraceCode(body).c_str()); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("SBT Stride must be a 32-bit int scalar")); } TEST_F(ValidateRayTracing, TraceRayMissIndex) { const std::string body = R"( %as = OpLoad %type_as %top_level_as OpTraceRayKHR %as %uint_1 %uint_1 %uint_1 %uint_1 %float_0 %v3composite %float_0 %v3composite %float_0 %payload )"; CompileSuccessfully(GenerateRayTraceCode(body).c_str()); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("Miss Index must be a 32-bit int scalar")); } TEST_F(ValidateRayTracing, TraceRayRayOrigin) { const std::string body = R"( %as = OpLoad %type_as %top_level_as OpTraceRayKHR %as %uint_1 %uint_1 %uint_1 %uint_1 %uint_1 %float_0 %float_0 %v3composite %float_0 %payload )"; CompileSuccessfully(GenerateRayTraceCode(body).c_str()); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), HasSubstr("Ray Origin must be a 32-bit float 3-component vector")); } TEST_F(ValidateRayTracing, TraceRayRayTMin) { const std::string body = R"( %as = OpLoad %type_as %top_level_as OpTraceRayKHR %as %uint_1 %uint_1 %uint_1 %uint_1 %uint_1 %v3composite %uint_1 %v3composite %float_0 %payload )"; CompileSuccessfully(GenerateRayTraceCode(body).c_str()); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("Ray TMin must be a 32-bit float scalar")); } TEST_F(ValidateRayTracing, TraceRayRayDirection) { const std::string body = R"( %as = OpLoad %type_as %top_level_as OpTraceRayKHR %as %uint_1 %uint_1 %uint_1 %uint_1 %uint_1 %v3composite %float_0 %v4composite %float_0 %payload )"; CompileSuccessfully(GenerateRayTraceCode(body).c_str()); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), HasSubstr("Ray Direction must be a 32-bit float 3-component vector")); } TEST_F(ValidateRayTracing, TraceRayRayTMax) { const std::string body = R"( %as = OpLoad %type_as %top_level_as OpTraceRayKHR %as %uint_1 %uint_1 %uint_1 %uint_1 %uint_1 %v3composite %float_0 %v3composite %float64_0 %payload )"; CompileSuccessfully(GenerateRayTraceCode(body).c_str()); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("Ray TMax must be a 32-bit float scalar")); } TEST_F(ValidateRayTracing, TraceRayPayload) { const std::string body = R"( %as = OpLoad %type_as %top_level_as OpTraceRayKHR %as %uint_1 %uint_1 %uint_1 %uint_1 %uint_1 %v3composite %float_0 %v3composite %float_0 %callable )"; CompileSuccessfully(GenerateRayTraceCode(body).c_str()); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("Payload must have storage class RayPayloadKHR or " "IncomingRayPayloadKHR")); } } // namespace } // namespace val } // namespace spvtools