| /*------------------------------------------------------------------------ |
| * Vulkan Conformance Tests |
| * ------------------------ |
| * |
| * Copyright (c) 2020 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. |
| * |
| *//*! |
| * \file |
| * \brief Ray Tracing Misc tests |
| *//*--------------------------------------------------------------------*/ |
| |
| #include "vktRayTracingMiscTests.hpp" |
| |
| #include "vkDefs.hpp" |
| |
| #include "vktTestCase.hpp" |
| #include "vkCmdUtil.hpp" |
| #include "vkObjUtil.hpp" |
| #include "vkBuilderUtil.hpp" |
| #include "vkBarrierUtil.hpp" |
| #include "vkBufferWithMemory.hpp" |
| #include "vkImageWithMemory.hpp" |
| #include "vkTypeUtil.hpp" |
| |
| #include "vkRayTracingUtil.hpp" |
| |
| #include "deRandom.hpp" |
| #include <algorithm> |
| #include <memory> |
| |
| namespace vkt |
| { |
| namespace RayTracing |
| { |
| namespace |
| { |
| using namespace vk; |
| using namespace std; |
| |
| enum class BaseType |
| { |
| F32, |
| F64, |
| I8, |
| I16, |
| I32, |
| I64, |
| U8, |
| U16, |
| U32, |
| U64, |
| |
| UNKNOWN |
| }; |
| |
| enum class GeometryType |
| { |
| FIRST = 0, |
| |
| AABB = FIRST, |
| TRIANGLES, |
| |
| COUNT, |
| |
| AABB_AND_TRIANGLES, //< Only compatible with ONE_TL_MANY_BLS_MANY_GEOMETRIES_WITH_VARYING_PRIM_TYPES AS layout. |
| }; |
| |
| enum class MatrixMajorOrder |
| { |
| COLUMN_MAJOR, |
| ROW_MAJOR, |
| |
| UNKNOWN |
| }; |
| |
| enum class ShaderGroups |
| { |
| FIRST_GROUP = 0, |
| RAYGEN_GROUP = FIRST_GROUP, |
| MISS_GROUP, |
| HIT_GROUP, |
| |
| FIRST_CALLABLE_GROUP, |
| }; |
| |
| enum class TestType |
| { |
| AABBS_AND_TRIS_IN_ONE_TL, |
| AS_STRESS_TEST, |
| CALLABLE_SHADER_STRESS_DYNAMIC_TEST, |
| CALLABLE_SHADER_STRESS_TEST, |
| CULL_MASK, |
| MAX_RAY_HIT_ATTRIBUTE_SIZE, |
| MAX_RT_INVOCATIONS_SUPPORTED, |
| CULL_MASK_EXTRA_BITS, |
| NO_DUPLICATE_ANY_HIT, |
| REPORT_INTERSECTION_RESULT, |
| RAY_PAYLOAD_IN, |
| RECURSIVE_TRACES_1, |
| RECURSIVE_TRACES_2, |
| RECURSIVE_TRACES_3, |
| RECURSIVE_TRACES_4, |
| RECURSIVE_TRACES_5, |
| RECURSIVE_TRACES_6, |
| RECURSIVE_TRACES_7, |
| RECURSIVE_TRACES_8, |
| RECURSIVE_TRACES_9, |
| RECURSIVE_TRACES_10, |
| RECURSIVE_TRACES_11, |
| RECURSIVE_TRACES_12, |
| RECURSIVE_TRACES_13, |
| RECURSIVE_TRACES_14, |
| RECURSIVE_TRACES_15, |
| RECURSIVE_TRACES_16, |
| RECURSIVE_TRACES_17, |
| RECURSIVE_TRACES_18, |
| RECURSIVE_TRACES_19, |
| RECURSIVE_TRACES_20, |
| RECURSIVE_TRACES_21, |
| RECURSIVE_TRACES_22, |
| RECURSIVE_TRACES_23, |
| RECURSIVE_TRACES_24, |
| RECURSIVE_TRACES_25, |
| RECURSIVE_TRACES_26, |
| RECURSIVE_TRACES_27, |
| RECURSIVE_TRACES_28, |
| RECURSIVE_TRACES_29, |
| SHADER_RECORD_BLOCK_EXPLICIT_SCALAR_OFFSET_1, |
| SHADER_RECORD_BLOCK_EXPLICIT_SCALAR_OFFSET_2, |
| SHADER_RECORD_BLOCK_EXPLICIT_SCALAR_OFFSET_3, |
| SHADER_RECORD_BLOCK_EXPLICIT_SCALAR_OFFSET_4, |
| SHADER_RECORD_BLOCK_EXPLICIT_SCALAR_OFFSET_5, |
| SHADER_RECORD_BLOCK_EXPLICIT_SCALAR_OFFSET_6, |
| SHADER_RECORD_BLOCK_EXPLICIT_STD430_OFFSET_1, |
| SHADER_RECORD_BLOCK_EXPLICIT_STD430_OFFSET_2, |
| SHADER_RECORD_BLOCK_EXPLICIT_STD430_OFFSET_3, |
| SHADER_RECORD_BLOCK_EXPLICIT_STD430_OFFSET_4, |
| SHADER_RECORD_BLOCK_EXPLICIT_STD430_OFFSET_5, |
| SHADER_RECORD_BLOCK_EXPLICIT_STD430_OFFSET_6, |
| SHADER_RECORD_BLOCK_SCALAR_1, |
| SHADER_RECORD_BLOCK_SCALAR_2, |
| SHADER_RECORD_BLOCK_SCALAR_3, |
| SHADER_RECORD_BLOCK_SCALAR_4, |
| SHADER_RECORD_BLOCK_SCALAR_5, |
| SHADER_RECORD_BLOCK_SCALAR_6, |
| SHADER_RECORD_BLOCK_STD430_1, |
| SHADER_RECORD_BLOCK_STD430_2, |
| SHADER_RECORD_BLOCK_STD430_3, |
| SHADER_RECORD_BLOCK_STD430_4, |
| SHADER_RECORD_BLOCK_STD430_5, |
| SHADER_RECORD_BLOCK_STD430_6, |
| |
| COUNT |
| }; |
| |
| enum class VariableType |
| { |
| FIRST, |
| |
| FLOAT = FIRST, |
| VEC2, |
| VEC3, |
| VEC4, |
| |
| MAT2, |
| MAT2X2, |
| MAT2X3, |
| MAT2X4, |
| MAT3, |
| MAT3X2, |
| MAT3X3, |
| MAT3X4, |
| MAT4, |
| MAT4X2, |
| MAT4X3, |
| MAT4X4, |
| |
| INT, |
| IVEC2, |
| IVEC3, |
| IVEC4, |
| |
| INT8, |
| I8VEC2, |
| I8VEC3, |
| I8VEC4, |
| |
| INT16, |
| I16VEC2, |
| I16VEC3, |
| I16VEC4, |
| |
| INT64, |
| I64VEC2, |
| I64VEC3, |
| I64VEC4, |
| |
| UINT, |
| UVEC2, |
| UVEC3, |
| UVEC4, |
| |
| UINT16, |
| U16VEC2, |
| U16VEC3, |
| U16VEC4, |
| |
| UINT64, |
| U64VEC2, |
| U64VEC3, |
| U64VEC4, |
| |
| UINT8, |
| U8VEC2, |
| U8VEC3, |
| U8VEC4, |
| |
| DOUBLE, |
| DVEC2, |
| DVEC3, |
| DVEC4, |
| |
| DMAT2, |
| DMAT2X2, |
| DMAT2X3, |
| DMAT2X4, |
| DMAT3, |
| DMAT3X2, |
| DMAT3X3, |
| DMAT3X4, |
| DMAT4, |
| DMAT4X2, |
| DMAT4X3, |
| DMAT4X4, |
| |
| UNKNOWN, |
| COUNT = UNKNOWN, |
| }; |
| |
| enum class AccelerationStructureLayout |
| { |
| FIRST = 0, |
| |
| ONE_TL_ONE_BL_ONE_GEOMETRY = FIRST, |
| ONE_TL_ONE_BL_MANY_GEOMETRIES, |
| ONE_TL_MANY_BLS_ONE_GEOMETRY, |
| ONE_TL_MANY_BLS_MANY_GEOMETRIES, |
| |
| COUNT, |
| |
| ONE_TL_MANY_BLS_MANY_GEOMETRIES_WITH_VARYING_PRIM_TYPES |
| }; |
| |
| static const VkFlags ALL_RAY_TRACING_STAGES = VK_SHADER_STAGE_RAYGEN_BIT_KHR |
| | VK_SHADER_STAGE_ANY_HIT_BIT_KHR |
| | VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR |
| | VK_SHADER_STAGE_MISS_BIT_KHR |
| | VK_SHADER_STAGE_INTERSECTION_BIT_KHR |
| | VK_SHADER_STAGE_CALLABLE_BIT_KHR; |
| |
| struct CaseDef |
| { |
| TestType type; |
| GeometryType geometryType; |
| AccelerationStructureLayout asLayout; |
| |
| CaseDef() |
| : type (TestType::COUNT), |
| geometryType (GeometryType::COUNT), |
| asLayout (AccelerationStructureLayout::COUNT) |
| { |
| /* Stub */ |
| } |
| |
| CaseDef(const TestType& inType) |
| : type (inType), |
| geometryType (GeometryType::COUNT), |
| asLayout (AccelerationStructureLayout::COUNT) |
| { |
| /* Stub */ |
| } |
| |
| CaseDef(const TestType& inType, |
| const GeometryType& inGeometryType, |
| const AccelerationStructureLayout& inAsLayout) |
| : type (inType), |
| geometryType (inGeometryType), |
| asLayout (inAsLayout) |
| { |
| /* Stub */ |
| } |
| }; |
| |
| /* Helper global functions */ |
| static const char* getSuffixForASLayout(const AccelerationStructureLayout& layout) |
| { |
| const char* result = "?!"; |
| |
| switch (layout) |
| { |
| case AccelerationStructureLayout::ONE_TL_ONE_BL_ONE_GEOMETRY: result = "1TL1BL1G"; break; |
| case AccelerationStructureLayout::ONE_TL_ONE_BL_MANY_GEOMETRIES: result = "1TL1BLnG"; break; |
| case AccelerationStructureLayout::ONE_TL_MANY_BLS_ONE_GEOMETRY: result = "1TLnBL1G"; break; |
| case AccelerationStructureLayout::ONE_TL_MANY_BLS_MANY_GEOMETRIES: result = "1TLnBLnG"; break; |
| |
| default: |
| { |
| deAssertFail("This should never happen", __FILE__, __LINE__); |
| } |
| } |
| |
| return result; |
| } |
| |
| static const char* getSuffixForGeometryType(const GeometryType& type) |
| { |
| const char* result = "?!"; |
| |
| switch (type) |
| { |
| case GeometryType::AABB: result = "AABB"; break; |
| case GeometryType::TRIANGLES: result = "tri"; break; |
| |
| default: |
| { |
| deAssertFail("This should never happen", __FILE__, __LINE__); |
| } |
| } |
| |
| return result; |
| } |
| |
| /* Instances and primitives in acceleration structures can have additional information assigned. |
| * |
| * By overriding functions of interest in this class, tests can further customize ASes generated by AS providers. |
| */ |
| class ASPropertyProvider |
| { |
| public: |
| virtual ~ASPropertyProvider() |
| { |
| /* Stub */ |
| } |
| |
| virtual deUint8 getCullMask(const deUint32& nBL, const deUint32& nInstance) const |
| { |
| DE_UNREF(nBL); |
| DE_UNREF(nInstance); |
| |
| return 0xFF; |
| } |
| |
| virtual deUint32 getInstanceCustomIndex(const deUint32& nBL, const deUint32& nInstance) const |
| { |
| DE_UNREF(nBL); |
| DE_UNREF(nInstance); |
| return 0; |
| } |
| }; |
| |
| class IGridASFeedback |
| { |
| public: |
| virtual ~IGridASFeedback() |
| { |
| /* Stub */ |
| } |
| |
| virtual void onCullMaskAssignedToCell (const tcu::UVec3& cellLocation, const deUint8& cullMaskAssigned) = 0; |
| virtual void onInstanceCustomIndexAssignedToCell(const tcu::UVec3& cellLocation, const deUint32& customIndexAssigned) = 0; |
| }; |
| |
| |
| /* Acceleration structure data providers. |
| * |
| * These are expected to be reused across different test cases. |
| **/ |
| class ASProviderBase |
| { |
| public: |
| virtual ~ASProviderBase() |
| { |
| /* Stub */ |
| } |
| |
| virtual std::unique_ptr<TopLevelAccelerationStructure> createTLAS( Context& context, |
| const AccelerationStructureLayout& asLayout, |
| VkCommandBuffer cmdBuffer, |
| const VkGeometryFlagsKHR& bottomLevelGeometryFlags, |
| const ASPropertyProvider* optAsPropertyProviderPtr = nullptr, |
| IGridASFeedback* optASFeedbackPtr = nullptr) const = 0; |
| virtual deUint32 getNPrimitives() const = 0; |
| |
| }; |
| |
| /* A 3D grid built of primitives. Size and distribution of the geometry can be configured both at creation time and at a later time. */ |
| class GridASProvider : public ASProviderBase |
| { |
| public: |
| GridASProvider( const tcu::Vec3& gridStartXYZ, |
| const tcu::Vec3& gridCellSizeXYZ, |
| const tcu::UVec3& gridSizeXYZ, |
| const tcu::Vec3& gridInterCellDeltaXYZ, |
| const GeometryType& geometryType) |
| :m_geometryType (geometryType), |
| m_gridCellSizeXYZ (gridCellSizeXYZ), |
| m_gridInterCellDeltaXYZ(gridInterCellDeltaXYZ), |
| m_gridSizeXYZ (gridSizeXYZ), |
| m_gridStartXYZ (gridStartXYZ) |
| { |
| fillVertexVec(); |
| } |
| |
| std::unique_ptr<TopLevelAccelerationStructure> createTLAS( Context& context, |
| const AccelerationStructureLayout& asLayout, |
| VkCommandBuffer cmdBuffer, |
| const VkGeometryFlagsKHR& bottomLevelGeometryFlags, |
| const ASPropertyProvider* optASPropertyProviderPtr, |
| IGridASFeedback* optASFeedbackPtr) const final |
| { |
| Allocator& allocator = context.getDefaultAllocator (); |
| const DeviceInterface& deviceInterface = context.getDeviceInterface (); |
| const VkDevice deviceVk = context.getDevice (); |
| const auto nCells = m_gridSizeXYZ.x() * m_gridSizeXYZ.y() * m_gridSizeXYZ.z(); |
| std::unique_ptr<TopLevelAccelerationStructure> resultPtr; |
| de::MovePtr<TopLevelAccelerationStructure> tlPtr = makeTopLevelAccelerationStructure (); |
| |
| DE_ASSERT(((asLayout == AccelerationStructureLayout::ONE_TL_MANY_BLS_MANY_GEOMETRIES_WITH_VARYING_PRIM_TYPES) && (m_geometryType == GeometryType::AABB_AND_TRIANGLES)) || |
| ((asLayout != AccelerationStructureLayout::ONE_TL_MANY_BLS_MANY_GEOMETRIES_WITH_VARYING_PRIM_TYPES) && (m_geometryType != GeometryType::AABB_AND_TRIANGLES)) ); |
| |
| switch (asLayout) |
| { |
| case AccelerationStructureLayout::ONE_TL_ONE_BL_ONE_GEOMETRY: |
| { |
| DE_ASSERT( (m_geometryType == GeometryType::AABB) || (m_geometryType == GeometryType::TRIANGLES) ); |
| |
| const auto& vertexVec = (m_geometryType == GeometryType::AABB) ? m_aabbVertexVec |
| : m_triVertexVec; |
| const auto cullMask = (optASPropertyProviderPtr != nullptr) ? optASPropertyProviderPtr->getCullMask(0, 0) |
| : static_cast<deUint8>(0xFF); |
| const auto instanceCustomIndex = (optASPropertyProviderPtr != nullptr) ? optASPropertyProviderPtr->getInstanceCustomIndex(0, 0) |
| : 0; |
| |
| tlPtr->setInstanceCount(1); |
| |
| { |
| de::MovePtr<BottomLevelAccelerationStructure> blPtr = makeBottomLevelAccelerationStructure(); |
| |
| blPtr->setGeometryCount (1u); |
| blPtr->addGeometry (vertexVec, |
| (m_geometryType == GeometryType::TRIANGLES), |
| bottomLevelGeometryFlags); |
| |
| blPtr->createAndBuild( deviceInterface, |
| deviceVk, |
| cmdBuffer, |
| allocator); |
| |
| tlPtr->addInstance( de::SharedPtr<BottomLevelAccelerationStructure>(blPtr.release() ), |
| identityMatrix3x4, |
| instanceCustomIndex, |
| cullMask); |
| } |
| |
| if (optASFeedbackPtr != nullptr) |
| { |
| for (auto nCell = 0u; |
| nCell < nCells; |
| nCell++) |
| { |
| const auto cellX = (((nCell) % m_gridSizeXYZ.x() )); |
| const auto cellY = (((nCell / m_gridSizeXYZ.x() ) % m_gridSizeXYZ.y() )); |
| const auto cellZ = (((nCell / m_gridSizeXYZ.x() ) / m_gridSizeXYZ.y() ) % m_gridSizeXYZ.z() ); |
| |
| optASFeedbackPtr->onCullMaskAssignedToCell ( tcu::UVec3(cellX, cellY, cellZ), |
| cullMask); |
| optASFeedbackPtr->onInstanceCustomIndexAssignedToCell ( tcu::UVec3(cellX, cellY, cellZ), |
| instanceCustomIndex); |
| } |
| } |
| |
| break; |
| } |
| |
| case AccelerationStructureLayout::ONE_TL_ONE_BL_MANY_GEOMETRIES: |
| { |
| DE_ASSERT( (m_geometryType == GeometryType::AABB) || (m_geometryType == GeometryType::TRIANGLES) ); |
| |
| const auto& vertexVec = (m_geometryType == GeometryType::AABB) ? m_aabbVertexVec |
| : m_triVertexVec; |
| const auto nVerticesPerPrimitive = (m_geometryType == GeometryType::AABB) ? 2u |
| : 12u /* tris */ * 3 /* verts */; |
| const auto cullMask = (optASPropertyProviderPtr != nullptr) ? optASPropertyProviderPtr->getCullMask(0, 0) |
| : static_cast<deUint8>(0xFF); |
| const auto instanceCustomIndex = (optASPropertyProviderPtr != nullptr) ? optASPropertyProviderPtr->getInstanceCustomIndex(0, 0) |
| : 0; |
| |
| DE_ASSERT( (vertexVec.size() % nVerticesPerPrimitive) == 0); |
| |
| tlPtr->setInstanceCount(1); |
| |
| { |
| de::MovePtr<BottomLevelAccelerationStructure> blPtr = makeBottomLevelAccelerationStructure(); |
| const auto nGeometries = vertexVec.size() / nVerticesPerPrimitive; |
| |
| blPtr->setGeometryCount (nGeometries); |
| |
| for (deUint32 nGeometry = 0; nGeometry < nGeometries; ++nGeometry) |
| { |
| std::vector<tcu::Vec3> currentGeometry(nVerticesPerPrimitive); |
| |
| for (deUint32 nVertex = 0; nVertex < nVerticesPerPrimitive; ++nVertex) |
| { |
| currentGeometry.at(nVertex) = vertexVec.at(nGeometry * nVerticesPerPrimitive + nVertex); |
| } |
| |
| blPtr->addGeometry (currentGeometry, |
| (m_geometryType == GeometryType::TRIANGLES), |
| bottomLevelGeometryFlags); |
| } |
| |
| blPtr->createAndBuild( deviceInterface, |
| deviceVk, |
| cmdBuffer, |
| allocator); |
| |
| tlPtr->addInstance( de::SharedPtr<BottomLevelAccelerationStructure>(blPtr.release() ), |
| identityMatrix3x4, |
| instanceCustomIndex, |
| cullMask); |
| } |
| |
| if (optASFeedbackPtr != nullptr) |
| { |
| for (auto nCell = 0u; |
| nCell < nCells; |
| nCell++) |
| { |
| const auto cellX = (((nCell) % m_gridSizeXYZ.x() )); |
| const auto cellY = (((nCell / m_gridSizeXYZ.x() ) % m_gridSizeXYZ.y() )); |
| const auto cellZ = (((nCell / m_gridSizeXYZ.x() ) / m_gridSizeXYZ.y() ) % m_gridSizeXYZ.z() ); |
| |
| optASFeedbackPtr->onCullMaskAssignedToCell ( tcu::UVec3(cellX, cellY, cellZ), |
| cullMask); |
| optASFeedbackPtr->onInstanceCustomIndexAssignedToCell ( tcu::UVec3(cellX, cellY, cellZ), |
| instanceCustomIndex); |
| } |
| } |
| |
| break; |
| } |
| |
| case AccelerationStructureLayout::ONE_TL_MANY_BLS_ONE_GEOMETRY: |
| { |
| DE_ASSERT( (m_geometryType == GeometryType::AABB) || (m_geometryType == GeometryType::TRIANGLES) ); |
| |
| const auto& vertexVec = (m_geometryType == GeometryType::AABB) ? m_aabbVertexVec |
| : m_triVertexVec; |
| const auto nVerticesPerPrimitive = (m_geometryType == GeometryType::AABB) ? 2u |
| : 12u /* tris */ * 3 /* verts */; |
| const auto nInstances = vertexVec.size() / nVerticesPerPrimitive; |
| |
| DE_ASSERT( (vertexVec.size() % nVerticesPerPrimitive) == 0); |
| |
| tlPtr->setInstanceCount(nInstances); |
| |
| for (deUint32 nInstance = 0; nInstance < nInstances; nInstance++) |
| { |
| de::MovePtr<BottomLevelAccelerationStructure> blPtr = makeBottomLevelAccelerationStructure(); |
| const auto cullMask = (optASPropertyProviderPtr != nullptr) ? optASPropertyProviderPtr->getCullMask(0, nInstance) |
| : static_cast<deUint8>(0xFF); |
| std::vector<tcu::Vec3> currentInstanceVertexVec; |
| const auto instanceCustomIndex = (optASPropertyProviderPtr != nullptr) ? optASPropertyProviderPtr->getInstanceCustomIndex(0, nInstance) |
| : 0; |
| |
| for (deUint32 nVertex = 0; nVertex < nVerticesPerPrimitive; ++nVertex) |
| { |
| currentInstanceVertexVec.push_back(vertexVec.at(nInstance * nVerticesPerPrimitive + nVertex) ); |
| } |
| |
| blPtr->setGeometryCount (1u); |
| blPtr->addGeometry (currentInstanceVertexVec, |
| (m_geometryType == GeometryType::TRIANGLES), |
| bottomLevelGeometryFlags); |
| |
| blPtr->createAndBuild( deviceInterface, |
| deviceVk, |
| cmdBuffer, |
| allocator); |
| |
| tlPtr->addInstance( de::SharedPtr<BottomLevelAccelerationStructure>(blPtr.release() ), |
| identityMatrix3x4, |
| instanceCustomIndex, |
| cullMask); |
| |
| |
| if (optASFeedbackPtr != nullptr) |
| { |
| const auto cellX = (((nInstance) % m_gridSizeXYZ.x() )); |
| const auto cellY = (((nInstance / m_gridSizeXYZ.x() ) % m_gridSizeXYZ.y() )); |
| const auto cellZ = (((nInstance / m_gridSizeXYZ.x() ) / m_gridSizeXYZ.y() ) % m_gridSizeXYZ.z() ); |
| |
| optASFeedbackPtr->onCullMaskAssignedToCell( tcu::UVec3(cellX, cellY, cellZ), |
| cullMask); |
| optASFeedbackPtr->onInstanceCustomIndexAssignedToCell( tcu::UVec3(cellX, cellY, cellZ), |
| instanceCustomIndex); |
| } |
| } |
| |
| break; |
| } |
| |
| case AccelerationStructureLayout::ONE_TL_MANY_BLS_MANY_GEOMETRIES: |
| { |
| DE_ASSERT( (m_geometryType == GeometryType::AABB) || (m_geometryType == GeometryType::TRIANGLES) ); |
| |
| const auto& vertexVec = (m_geometryType == GeometryType::AABB) ? m_aabbVertexVec |
| : m_triVertexVec; |
| const auto nVerticesPerPrimitive = (m_geometryType == GeometryType::AABB) ? 2u |
| : 12u /* tris */ * 3 /* verts */; |
| const auto nPrimitivesDefined = static_cast<deUint32>(vertexVec.size() / nVerticesPerPrimitive); |
| const auto nPrimitivesPerBLAS = 4; |
| const auto nBottomLevelASes = nPrimitivesDefined / nPrimitivesPerBLAS; |
| |
| DE_ASSERT( (vertexVec.size() % nVerticesPerPrimitive) == 0); |
| DE_ASSERT( (nPrimitivesDefined % nPrimitivesPerBLAS) == 0); |
| |
| tlPtr->setInstanceCount(nBottomLevelASes); |
| |
| for (deUint32 nBottomLevelAS = 0; nBottomLevelAS < nBottomLevelASes; nBottomLevelAS++) |
| { |
| de::MovePtr<BottomLevelAccelerationStructure> blPtr = makeBottomLevelAccelerationStructure(); |
| const auto cullMask = (optASPropertyProviderPtr != nullptr) ? optASPropertyProviderPtr->getCullMask(nBottomLevelAS, 0) |
| : static_cast<deUint8>(0xFF); |
| const auto instanceCustomIndex = (optASPropertyProviderPtr != nullptr) ? optASPropertyProviderPtr->getInstanceCustomIndex(nBottomLevelAS, 0) |
| : 0; |
| |
| blPtr->setGeometryCount(nPrimitivesPerBLAS); |
| |
| for (deUint32 nGeometry = 0; nGeometry < nPrimitivesPerBLAS; nGeometry++) |
| { |
| std::vector<tcu::Vec3> currentVertexVec; |
| |
| for (deUint32 nVertex = 0; nVertex < nVerticesPerPrimitive; ++nVertex) |
| { |
| currentVertexVec.push_back(vertexVec.at((nBottomLevelAS * nPrimitivesPerBLAS + nGeometry) * nVerticesPerPrimitive + nVertex) ); |
| } |
| |
| blPtr->addGeometry( currentVertexVec, |
| (m_geometryType == GeometryType::TRIANGLES), |
| bottomLevelGeometryFlags); |
| } |
| |
| blPtr->createAndBuild( deviceInterface, |
| deviceVk, |
| cmdBuffer, |
| allocator); |
| tlPtr->addInstance( de::SharedPtr<BottomLevelAccelerationStructure>(blPtr.release() ), |
| identityMatrix3x4, |
| instanceCustomIndex, |
| cullMask); |
| |
| if (optASFeedbackPtr != nullptr) |
| { |
| for (deUint32 cellIndex = nPrimitivesPerBLAS * nBottomLevelAS; cellIndex < nPrimitivesPerBLAS * (nBottomLevelAS + 1); cellIndex++) |
| { |
| const auto cellX = (((cellIndex) % m_gridSizeXYZ.x() )); |
| const auto cellY = (((cellIndex / m_gridSizeXYZ.x() ) % m_gridSizeXYZ.y() )); |
| const auto cellZ = (((cellIndex / m_gridSizeXYZ.x() ) / m_gridSizeXYZ.y() ) % m_gridSizeXYZ.z() ); |
| |
| optASFeedbackPtr->onCullMaskAssignedToCell ( tcu::UVec3(cellX, cellY, cellZ), |
| cullMask); |
| optASFeedbackPtr->onInstanceCustomIndexAssignedToCell ( tcu::UVec3(cellX, cellY, cellZ), |
| instanceCustomIndex); |
| } |
| } |
| } |
| |
| break; |
| } |
| |
| case AccelerationStructureLayout::ONE_TL_MANY_BLS_MANY_GEOMETRIES_WITH_VARYING_PRIM_TYPES: |
| { |
| DE_ASSERT(m_geometryType == GeometryType::AABB_AND_TRIANGLES); |
| |
| const auto nCellsDefined = m_gridSizeXYZ[0] * m_gridSizeXYZ[1] * m_gridSizeXYZ[2]; |
| const auto nPrimitivesPerBLAS = 1; |
| const auto nBottomLevelASes = nCellsDefined / nPrimitivesPerBLAS; |
| |
| DE_ASSERT( (nCellsDefined % nPrimitivesPerBLAS) == 0); |
| |
| tlPtr->setInstanceCount(nBottomLevelASes); |
| |
| for (deUint32 nBottomLevelAS = 0; nBottomLevelAS < nBottomLevelASes; nBottomLevelAS++) |
| { |
| de::MovePtr<BottomLevelAccelerationStructure> blPtr = makeBottomLevelAccelerationStructure(); |
| const auto cullMask = (optASPropertyProviderPtr != nullptr) ? optASPropertyProviderPtr->getCullMask(nBottomLevelAS, 0) |
| : static_cast<deUint8>(0xFF); |
| const auto instanceCustomIndex = (optASPropertyProviderPtr != nullptr) ? optASPropertyProviderPtr->getInstanceCustomIndex(nBottomLevelAS, 0) |
| : 0; |
| const bool usesAABB = (nBottomLevelAS % 2) == 0; |
| const auto& vertexVec = (usesAABB) ? m_aabbVertexVec |
| : m_triVertexVec; |
| const auto nVerticesPerPrimitive = (usesAABB) ? 2u |
| : 12u /* tris */ * 3 /* verts */; |
| |
| // For this case, AABBs use the first shader group and triangles use the second shader group in the table. |
| const auto instanceSBTOffset = (usesAABB ? 0u : 1u); |
| |
| blPtr->setGeometryCount(nPrimitivesPerBLAS); |
| |
| for (deUint32 nGeometry = 0; nGeometry < nPrimitivesPerBLAS; nGeometry++) |
| { |
| DE_ASSERT( (vertexVec.size() % nVerticesPerPrimitive) == 0); |
| |
| std::vector<tcu::Vec3> currentVertexVec; |
| |
| for (deUint32 nVertex = 0; nVertex < nVerticesPerPrimitive; ++nVertex) |
| { |
| currentVertexVec.push_back(vertexVec.at((nBottomLevelAS * nPrimitivesPerBLAS + nGeometry) * nVerticesPerPrimitive + nVertex) ); |
| } |
| |
| blPtr->addGeometry( currentVertexVec, |
| !usesAABB, |
| bottomLevelGeometryFlags); |
| } |
| |
| blPtr->createAndBuild ( deviceInterface, |
| deviceVk, |
| cmdBuffer, |
| allocator); |
| |
| tlPtr->addInstance( de::SharedPtr<BottomLevelAccelerationStructure>(blPtr.release() ), |
| identityMatrix3x4, |
| instanceCustomIndex, |
| cullMask, |
| instanceSBTOffset); |
| |
| if (optASFeedbackPtr != nullptr) |
| { |
| for (deUint32 cellIndex = nPrimitivesPerBLAS * nBottomLevelAS; cellIndex < nPrimitivesPerBLAS * (nBottomLevelAS + 1); cellIndex++) |
| { |
| const auto cellX = (((cellIndex) % m_gridSizeXYZ.x() )); |
| const auto cellY = (((cellIndex / m_gridSizeXYZ.x() ) % m_gridSizeXYZ.y() )); |
| const auto cellZ = (((cellIndex / m_gridSizeXYZ.x() ) / m_gridSizeXYZ.y() ) % m_gridSizeXYZ.z() ); |
| |
| optASFeedbackPtr->onCullMaskAssignedToCell ( tcu::UVec3(cellX, cellY, cellZ), |
| cullMask); |
| optASFeedbackPtr->onInstanceCustomIndexAssignedToCell ( tcu::UVec3(cellX, cellY, cellZ), |
| instanceCustomIndex); |
| } |
| } |
| } |
| |
| break; |
| } |
| |
| default: |
| { |
| deAssertFail("This should never happen", __FILE__, __LINE__); |
| } |
| } |
| |
| tlPtr->createAndBuild( deviceInterface, |
| deviceVk, |
| cmdBuffer, |
| allocator); |
| |
| resultPtr = decltype(resultPtr)(tlPtr.release() ); |
| return resultPtr; |
| } |
| |
| deUint32 getNPrimitives() const final |
| { |
| return m_gridSizeXYZ[0] * m_gridSizeXYZ[1] * m_gridSizeXYZ[2]; |
| } |
| |
| void setProperties( const tcu::Vec3& gridStartXYZ, |
| const tcu::Vec3& gridCellSizeXYZ, |
| const tcu::UVec3& gridSizeXYZ, |
| const tcu::Vec3& gridInterCellDeltaXYZ, |
| const GeometryType& geometryType) |
| { |
| m_gridStartXYZ = gridStartXYZ; |
| m_gridCellSizeXYZ = gridCellSizeXYZ; |
| m_gridSizeXYZ = gridSizeXYZ; |
| m_gridInterCellDeltaXYZ = gridInterCellDeltaXYZ; |
| m_geometryType = geometryType; |
| |
| fillVertexVec(); |
| } |
| |
| private: |
| void fillVertexVec() |
| { |
| const auto nCellsNeeded = m_gridSizeXYZ.x() * m_gridSizeXYZ.y() * m_gridSizeXYZ.z(); |
| |
| m_aabbVertexVec.clear(); |
| m_triVertexVec.clear (); |
| |
| for (auto nCell = 0u; |
| nCell < nCellsNeeded; |
| nCell++) |
| { |
| const auto cellX = (((nCell) % m_gridSizeXYZ.x() )); |
| const auto cellY = (((nCell / m_gridSizeXYZ.x() ) % m_gridSizeXYZ.y() )); |
| const auto cellZ = (((nCell / m_gridSizeXYZ.x() ) / m_gridSizeXYZ.y() ) % m_gridSizeXYZ.z() ); |
| |
| const auto cellX1Y1Z1 = tcu::Vec3( m_gridStartXYZ.x() + static_cast<float>(cellX) * m_gridInterCellDeltaXYZ.x(), |
| m_gridStartXYZ.y() + static_cast<float>(cellY) * m_gridInterCellDeltaXYZ.y(), |
| m_gridStartXYZ.z() + static_cast<float>(cellZ) * m_gridInterCellDeltaXYZ.z() ); |
| const auto cellX2Y2Z2 = tcu::Vec3( m_gridStartXYZ.x() + static_cast<float>(cellX) * m_gridInterCellDeltaXYZ.x() + m_gridCellSizeXYZ.x(), |
| m_gridStartXYZ.y() + static_cast<float>(cellY) * m_gridInterCellDeltaXYZ.y() + m_gridCellSizeXYZ.y(), |
| m_gridStartXYZ.z() + static_cast<float>(cellZ) * m_gridInterCellDeltaXYZ.z() + m_gridCellSizeXYZ.z() ); |
| |
| if (m_geometryType == GeometryType::AABB || |
| m_geometryType == GeometryType::AABB_AND_TRIANGLES) |
| { |
| /* Cell = AABB of the cell */ |
| m_aabbVertexVec.push_back(cellX1Y1Z1); |
| m_aabbVertexVec.push_back(cellX2Y2Z2); |
| } |
| |
| if (m_geometryType == GeometryType::AABB_AND_TRIANGLES || |
| m_geometryType == GeometryType::TRIANGLES) |
| { |
| /* Cell == Six triangles forming a cube |
| * |
| * Lower-case characters: vertices with Z == Z2 |
| * Upper-case characters: vertices with Z == Z1 |
| |
| |
| g h |
| |
| |
| C D |
| |
| |
| |
| e f |
| |
| A B |
| |
| |
| */ |
| const auto A = tcu::Vec3( cellX1Y1Z1.x(), |
| cellX1Y1Z1.y(), |
| cellX1Y1Z1.z() ); |
| const auto B = tcu::Vec3( cellX2Y2Z2.x(), |
| cellX1Y1Z1.y(), |
| cellX1Y1Z1.z() ); |
| const auto C = tcu::Vec3( cellX1Y1Z1.x(), |
| cellX2Y2Z2.y(), |
| cellX1Y1Z1.z() ); |
| const auto D = tcu::Vec3( cellX2Y2Z2.x(), |
| cellX2Y2Z2.y(), |
| cellX1Y1Z1.z() ); |
| const auto E = tcu::Vec3( cellX1Y1Z1.x(), |
| cellX1Y1Z1.y(), |
| cellX2Y2Z2.z() ); |
| const auto F = tcu::Vec3( cellX2Y2Z2.x(), |
| cellX1Y1Z1.y(), |
| cellX2Y2Z2.z() ); |
| const auto G = tcu::Vec3( cellX1Y1Z1.x(), |
| cellX2Y2Z2.y(), |
| cellX2Y2Z2.z() ); |
| const auto H = tcu::Vec3( cellX2Y2Z2.x(), |
| cellX2Y2Z2.y(), |
| cellX2Y2Z2.z() ); |
| |
| // Z = Z1 face |
| m_triVertexVec.push_back(A); |
| m_triVertexVec.push_back(C); |
| m_triVertexVec.push_back(D); |
| |
| m_triVertexVec.push_back(D); |
| m_triVertexVec.push_back(B); |
| m_triVertexVec.push_back(A); |
| |
| // Z = Z2 face |
| m_triVertexVec.push_back(E); |
| m_triVertexVec.push_back(H); |
| m_triVertexVec.push_back(G); |
| |
| m_triVertexVec.push_back(H); |
| m_triVertexVec.push_back(E); |
| m_triVertexVec.push_back(F); |
| |
| // X = X0 face |
| m_triVertexVec.push_back(A); |
| m_triVertexVec.push_back(G); |
| m_triVertexVec.push_back(C); |
| |
| m_triVertexVec.push_back(G); |
| m_triVertexVec.push_back(A); |
| m_triVertexVec.push_back(E); |
| |
| // X = X1 face |
| m_triVertexVec.push_back(B); |
| m_triVertexVec.push_back(D); |
| m_triVertexVec.push_back(H); |
| |
| m_triVertexVec.push_back(H); |
| m_triVertexVec.push_back(F); |
| m_triVertexVec.push_back(B); |
| |
| // Y = Y0 face |
| m_triVertexVec.push_back(C); |
| m_triVertexVec.push_back(H); |
| m_triVertexVec.push_back(D); |
| |
| m_triVertexVec.push_back(H); |
| m_triVertexVec.push_back(C); |
| m_triVertexVec.push_back(G); |
| |
| // Y = y1 face |
| m_triVertexVec.push_back(A); |
| m_triVertexVec.push_back(B); |
| m_triVertexVec.push_back(E); |
| |
| m_triVertexVec.push_back(B); |
| m_triVertexVec.push_back(F); |
| m_triVertexVec.push_back(E); |
| } |
| } |
| } |
| |
| std::vector<tcu::Vec3> m_aabbVertexVec; |
| std::vector<tcu::Vec3> m_triVertexVec; |
| |
| GeometryType m_geometryType; |
| tcu::Vec3 m_gridCellSizeXYZ; |
| tcu::Vec3 m_gridInterCellDeltaXYZ; |
| tcu::UVec3 m_gridSizeXYZ; |
| tcu::Vec3 m_gridStartXYZ; |
| }; |
| |
| |
| /* Test logic providers ==> */ |
| class TestBase |
| { |
| public: |
| virtual ~TestBase() |
| { |
| /* Stub */ |
| } |
| |
| virtual tcu::UVec3 getDispatchSize () const = 0; |
| virtual deUint32 getResultBufferSize () const = 0; |
| virtual std::vector<TopLevelAccelerationStructure*> getTLASPtrVecToBind () const = 0; |
| virtual void resetTLAS () = 0; |
| virtual void initAS ( vkt::Context& context, |
| RayTracingProperties* rtPropertiesPtr, |
| VkCommandBuffer commandBuffer) = 0; |
| virtual void initPrograms ( SourceCollections& programCollection) const = 0; |
| virtual bool verifyResultBuffer ( const void* inBufferPtr) const = 0; |
| |
| virtual std::vector<std::string> getAHitShaderCollectionShaderNames() const |
| { |
| return {"ahit"}; |
| } |
| |
| virtual deUint32 getASBindingArraySize() const |
| { |
| return 1u; |
| } |
| |
| virtual std::vector<std::string> getCallableShaderCollectionNames() const |
| { |
| return std::vector<std::string>{}; |
| } |
| |
| virtual std::vector<std::string> getCHitShaderCollectionShaderNames() const |
| { |
| return {"chit"}; |
| } |
| |
| virtual deUint32 getDynamicStackSize() const |
| { |
| DE_ASSERT(false); |
| |
| return 0; |
| } |
| |
| virtual std::vector<std::string> getIntersectionShaderCollectionShaderNames() const |
| { |
| return {"intersection"}; |
| } |
| |
| virtual deUint32 getMaxRecursionDepthUsed() const |
| { |
| return 1; |
| } |
| |
| virtual std::vector<std::string> getMissShaderCollectionShaderNames() const |
| { |
| return {"miss"}; |
| } |
| |
| virtual deUint32 getNTraceRayInvocationsNeeded() const |
| { |
| return 1; |
| } |
| |
| virtual Move<VkPipelineLayout> getPipelineLayout(const vk::DeviceInterface& deviceInterface, |
| VkDevice deviceVk, |
| VkDescriptorSetLayout descriptorSetLayout) |
| { |
| return makePipelineLayout( deviceInterface, |
| deviceVk, |
| descriptorSetLayout); |
| } |
| |
| virtual const void* getShaderRecordData(const ShaderGroups& /* shaderGroup */) const |
| { |
| return nullptr; |
| } |
| |
| virtual deUint32 getShaderRecordSize(const ShaderGroups& /* shaderGroup */) const |
| { |
| return 0; |
| } |
| |
| virtual VkSpecializationInfo* getSpecializationInfoPtr(const VkShaderStageFlagBits& /* shaderStage */) |
| { |
| return nullptr; |
| } |
| |
| virtual bool init( vkt::Context& /* context */, |
| RayTracingProperties* /* rtPropsPtr */) |
| { |
| return true; |
| } |
| |
| virtual void onBeforeCmdTraceRays( const deUint32& /* nDispatch */, |
| vkt::Context& /* context */, |
| VkCommandBuffer /* commandBuffer */, |
| VkPipelineLayout /* pipelineLayout */) |
| { |
| /* Stub */ |
| } |
| |
| virtual void onShaderStackSizeDiscovered( const VkDeviceSize& /* raygenShaderStackSize */, |
| const VkDeviceSize& /* ahitShaderStackSize */, |
| const VkDeviceSize& /* chitShaderStackSize */, |
| const VkDeviceSize& /* missShaderStackSize */, |
| const VkDeviceSize& /* callableShaderStackSize */, |
| const VkDeviceSize& /* isectShaderStackSize */) |
| { |
| /* Stub */ |
| } |
| |
| virtual bool usesDynamicStackSize() const |
| { |
| return false; |
| } |
| }; |
| |
| class AABBTriTLTest : public TestBase, |
| public ASPropertyProvider |
| { |
| public: |
| AABBTriTLTest( const GeometryType& geometryType, |
| const AccelerationStructureLayout& asStructureLayout) |
| : m_asStructureLayout (asStructureLayout), |
| m_geometryType (geometryType), |
| m_gridSize (tcu::UVec3(720, 1, 1) ), |
| m_lastCustomInstanceIndexUsed (0) |
| { |
| } |
| |
| ~AABBTriTLTest() |
| { |
| /* Stub */ |
| } |
| |
| virtual std::vector<std::string> getAHitShaderCollectionShaderNames() const |
| { |
| return {"ahit", "ahit"}; |
| } |
| |
| std::vector<std::string> getCHitShaderCollectionShaderNames() const final |
| { |
| return {}; |
| } |
| |
| deUint32 getInstanceCustomIndex(const deUint32& nBL, const deUint32& nInstance) const final |
| { |
| DE_UNREF(nBL); |
| DE_UNREF(nInstance); |
| |
| return ++m_lastCustomInstanceIndexUsed; |
| } |
| |
| tcu::UVec3 getDispatchSize() const final |
| { |
| return tcu::UVec3(m_gridSize[0], m_gridSize[1], m_gridSize[2]); |
| } |
| |
| deUint32 getResultBufferSize() const final |
| { |
| return static_cast<deUint32>((2 /* nHits, nMisses */ + m_gridSize[0] * m_gridSize[1] * m_gridSize[2] * 1 /* hit instance custom indices */) * sizeof(deUint32) ); |
| } |
| |
| std::vector<TopLevelAccelerationStructure*> getTLASPtrVecToBind() const final |
| { |
| DE_ASSERT(m_tlPtr != nullptr); |
| |
| return {m_tlPtr.get() }; |
| } |
| |
| void resetTLAS() final |
| { |
| m_tlPtr.reset(); |
| } |
| |
| void initAS(vkt::Context& context, |
| RayTracingProperties* /* rtPropertiesPtr */, |
| VkCommandBuffer commandBuffer) final |
| { |
| /* Each AS holds a single unit AABB / cube built of tris. |
| * |
| * Geometry in the zeroth acceleration structure starts at the origin. Subsequent ASes |
| * hold geometry that is positioned so that geometry formed by the union of all ASes never |
| * intersects. |
| * |
| * Each raygen shader invocation uses a unique origin+target pair for the traced ray, and |
| * only one AS is expected to hold geometry that the ray can find intersection for. |
| * The AS index is stored in the result buffer, which is later verified by the CPU. |
| * |
| * Due to the fact AccelerationStructureEXT array indexing must be dynamically uniform and |
| * it is not guaranteed we can determine workgroup size on VK 1.1-conformant platforms, |
| * we can only trace rays against the same AS in a single ray trace dispatch. |
| */ |
| std::unique_ptr<GridASProvider> asProviderPtr( |
| new GridASProvider( tcu::Vec3 (0, 0, 0), /* gridStartXYZ */ |
| tcu::Vec3 (1, 1, 1), /* gridCellSizeXYZ */ |
| m_gridSize, |
| tcu::Vec3 (3, 0, 0), /* gridInterCellDeltaXYZ */ |
| m_geometryType) |
| ); |
| |
| m_tlPtr = asProviderPtr->createTLAS( context, |
| m_asStructureLayout, |
| commandBuffer, |
| VK_GEOMETRY_NO_DUPLICATE_ANY_HIT_INVOCATION_BIT_KHR, |
| this, /* optASPropertyProviderPtr */ |
| nullptr); /* optASFeedbackPtr */ |
| } |
| |
| void initPrograms(SourceCollections& programCollection) const final |
| { |
| const vk::ShaderBuildOptions buildOptions( programCollection.usedVulkanVersion, |
| vk::SPIRV_VERSION_1_4, |
| 0u, /* flags */ |
| true); /* allowSpirv14 */ |
| |
| const char* hitPropsDefinition = |
| "struct HitProps\n" |
| "{\n" |
| " uint instanceCustomIndex;\n" |
| "};\n"; |
| |
| { |
| std::stringstream css; |
| |
| css << |
| "#version 460 core\n" |
| "\n" |
| "#extension GL_EXT_ray_tracing : require\n" |
| "\n" |
| "hitAttributeEXT vec3 dummyAttribute;\n" |
| "\n" |
| + de::toString(hitPropsDefinition) + |
| "\n" |
| "layout(location = 0) rayPayloadInEXT uint dummy;\n" |
| "layout(set = 0, binding = 0, std430) buffer result\n" |
| "{\n" |
| " uint nHitsRegistered;\n" |
| " uint nMissesRegistered;\n" |
| " HitProps hits[];\n" |
| "};\n" |
| "\n" |
| "void main()\n" |
| "{\n" |
| " uint nHit = atomicAdd(nHitsRegistered, 1);\n" |
| "\n" |
| " hits[nHit].instanceCustomIndex = gl_InstanceCustomIndexEXT;\n" |
| "}\n"; |
| |
| programCollection.glslSources.add("ahit") << glu::AnyHitSource(css.str() ) << buildOptions; |
| } |
| |
| { |
| std::stringstream css; |
| |
| css << |
| "#version 460 core\n" |
| "\n" |
| "#extension GL_EXT_ray_tracing : require\n" |
| "\n" |
| "hitAttributeEXT vec3 hitAttribute;\n" |
| "\n" |
| "void main()\n" |
| "{\n" |
| " reportIntersectionEXT(0.95f, 0);\n" |
| "}\n"; |
| |
| programCollection.glslSources.add("intersection") << glu::IntersectionSource(css.str() ) << buildOptions; |
| } |
| |
| { |
| std::stringstream css; |
| |
| css << |
| "#version 460 core\n" |
| "\n" |
| "#extension GL_EXT_ray_tracing : require\n" |
| "\n" |
| + de::toString(hitPropsDefinition) + |
| "\n" |
| "layout(set = 0, binding = 0, std430) buffer result\n" |
| "{\n" |
| " uint nHitsRegistered;\n" |
| " uint nMissesRegistered;\n" |
| " HitProps hits[];\n" |
| "};\n" |
| "\n" |
| "layout(location = 0) rayPayloadInEXT uint rayIndex;\n" |
| "\n" |
| "void main()\n" |
| "{\n" |
| " atomicAdd(nMissesRegistered, 1);\n" |
| "}\n"; |
| |
| programCollection.glslSources.add("miss") << glu::MissSource(css.str() ) << buildOptions; |
| } |
| |
| { |
| std::stringstream css; |
| |
| css << |
| "#version 460 core\n" |
| "\n" |
| "#extension GL_EXT_ray_tracing : require\n" |
| "\n" |
| "layout(location = 0) rayPayloadEXT uint dummy;\n" |
| "layout(set = 0, binding = 1) uniform accelerationStructureEXT accelerationStructure;\n" |
| "\n" |
| "void main()\n" |
| "{\n" |
| " uint nInvocation = gl_LaunchIDEXT.z * gl_LaunchSizeEXT.x * gl_LaunchSizeEXT.y + gl_LaunchIDEXT.y * gl_LaunchSizeEXT.x + gl_LaunchIDEXT.x;\n" |
| " uint rayFlags = 0;\n" |
| " float tmin = 0.001;\n" |
| " float tmax = 9.0;\n" |
| "\n" |
| " uint cullMask = 0xFF;\n" |
| " vec3 cellStartXYZ = vec3(nInvocation * 3.0, 0.0, 0.0);\n" |
| " vec3 cellEndXYZ = cellStartXYZ + vec3(1.0);\n" |
| " vec3 target = mix(cellStartXYZ, cellEndXYZ, vec3(0.5) );\n" |
| " vec3 origin = target - vec3(0, 2, 0);\n" |
| " vec3 direct = normalize(target - origin);\n" |
| "\n" |
| " traceRayEXT(accelerationStructure, rayFlags, cullMask, 0, 0, 0, origin, tmin, direct, tmax, 0);\n" |
| "}\n"; |
| |
| programCollection.glslSources.add("rgen") << glu::RaygenSource(css.str() ) << buildOptions; |
| } |
| } |
| |
| bool verifyResultBuffer (const void* resultDataPtr) const final |
| { |
| const deUint32* resultU32Ptr = reinterpret_cast<const deUint32*>(resultDataPtr); |
| bool result = false; |
| |
| typedef struct |
| { |
| deUint32 instanceCustomIndex; |
| } HitProperties; |
| |
| std::map<deUint32, deUint32> customInstanceIndexToHitCountMap; |
| const auto nHitsReported = *resultU32Ptr; |
| const auto nMissesReported = *(resultU32Ptr + 1); |
| |
| if (nHitsReported != m_gridSize[0] * m_gridSize[1] * m_gridSize[2]) |
| { |
| goto end; |
| } |
| |
| if (nMissesReported != 0) |
| { |
| goto end; |
| } |
| |
| for (deUint32 nHit = 0; nHit < nHitsReported; ++nHit) |
| { |
| const HitProperties* hitPropsPtr = reinterpret_cast<const HitProperties*>(resultU32Ptr + 2 /* preamble ints */) + nHit; |
| |
| customInstanceIndexToHitCountMap[hitPropsPtr->instanceCustomIndex]++; |
| |
| if (customInstanceIndexToHitCountMap[hitPropsPtr->instanceCustomIndex] > 1) |
| { |
| goto end; |
| } |
| } |
| |
| for (deUint32 nInstance = 0; nInstance < nHitsReported; ++nInstance) |
| { |
| if (customInstanceIndexToHitCountMap.find(1 + nInstance) == customInstanceIndexToHitCountMap.end() ) |
| { |
| goto end; |
| } |
| } |
| |
| result = true; |
| end: |
| return result; |
| } |
| |
| private: |
| const AccelerationStructureLayout m_asStructureLayout; |
| const GeometryType m_geometryType; |
| |
| const tcu::UVec3 m_gridSize; |
| mutable deUint32 m_lastCustomInstanceIndexUsed; |
| std::unique_ptr<TopLevelAccelerationStructure> m_tlPtr; |
| }; |
| |
| class ASStressTest : public TestBase, |
| public ASPropertyProvider |
| { |
| public: |
| ASStressTest( const GeometryType& geometryType, |
| const AccelerationStructureLayout& asStructureLayout) |
| : m_asStructureLayout (asStructureLayout), |
| m_geometryType (geometryType), |
| m_lastCustomInstanceIndexUsed (0), |
| m_nASesToUse (0), |
| m_nMaxASToUse (16u) |
| { |
| } |
| |
| ~ASStressTest() |
| { |
| /* Stub */ |
| } |
| |
| deUint32 getASBindingArraySize() const final |
| { |
| DE_ASSERT(m_nASesToUse != 0); |
| |
| return m_nASesToUse; |
| } |
| |
| std::vector<std::string> getCHitShaderCollectionShaderNames() const final |
| { |
| return {}; |
| } |
| |
| deUint32 getInstanceCustomIndex(const deUint32& nBL, const deUint32& nInstance) const final |
| { |
| DE_UNREF(nBL); |
| DE_UNREF(nInstance); |
| |
| return ++m_lastCustomInstanceIndexUsed; |
| } |
| |
| tcu::UVec3 getDispatchSize() const final |
| { |
| return tcu::UVec3(1, 1, 1); |
| } |
| |
| deUint32 getNTraceRayInvocationsNeeded() const final |
| { |
| return m_nMaxASToUse; |
| } |
| |
| deUint32 getResultBufferSize() const final |
| { |
| return static_cast<deUint32>((2 /* nHits, nMisses */ + 2 * m_nMaxASToUse /* hit instance custom indices + AS index */) * sizeof(deUint32) ); |
| } |
| |
| std::vector<TopLevelAccelerationStructure*> getTLASPtrVecToBind() const final |
| { |
| std::vector<TopLevelAccelerationStructure*> resultVec; |
| |
| DE_ASSERT(m_tlPtrVec.size() != 0); |
| |
| for (auto& currentTLPtr : m_tlPtrVec) |
| { |
| resultVec.push_back(currentTLPtr.get() ); |
| } |
| |
| return resultVec; |
| } |
| |
| void resetTLAS() final |
| { |
| for (auto& currentTLPtr : m_tlPtrVec) |
| { |
| currentTLPtr.reset(); |
| } |
| } |
| |
| bool init( vkt::Context& /* context */, |
| RayTracingProperties* rtPropertiesPtr) final |
| { |
| /* NOTE: We clamp the number below to a sensible value, in case the implementation has no restrictions on the number of |
| * ASes accessible to shaders. |
| */ |
| m_nASesToUse = std::min(rtPropertiesPtr->getMaxDescriptorSetAccelerationStructures(), |
| m_nMaxASToUse); |
| |
| return true; |
| } |
| |
| void initAS(vkt::Context& context, |
| RayTracingProperties* /* rtPropertiesPtr */, |
| VkCommandBuffer commandBuffer) final |
| { |
| /* Each AS holds a single unit AABB / cube built of tris. |
| * |
| * Geometry in the zeroth acceleration structure starts at the origin. Subsequent ASes |
| * hold geometry that is positioned so that geometry formed by the union of all ASes never |
| * intersects. |
| * |
| * Each raygen shader invocation uses a unique origin+target pair for the traced ray, and |
| * only one AS is expected to hold geometry that the ray can find intersection for. |
| * The AS index is stored in the result buffer, which is later verified by the CPU. |
| * |
| * Due to the fact AccelerationStructureEXT array indexing must be dynamically uniform and |
| * it is not guaranteed we can determine workgroup size on VK 1.1-conformant platforms, |
| * we can only trace rays against the same AS in a single ray trace dispatch. |
| */ |
| std::unique_ptr<GridASProvider> asProviderPtr( |
| new GridASProvider( tcu::Vec3 (0, 0, 0), /* gridStartXYZ */ |
| tcu::Vec3 (1, 1, 1), /* gridCellSizeXYZ */ |
| tcu::UVec3(1, 1, 1), /* gridSizeXYZ */ |
| tcu::Vec3 (0, 0, 0), /* gridInterCellDeltaXYZ */ |
| m_geometryType) |
| ); |
| |
| for (deUint32 nAS = 0; nAS < m_nASesToUse; ++nAS) |
| { |
| const auto origin = tcu::Vec3(3.0f * static_cast<float>(nAS), 0.0f, 0.0f); |
| |
| asProviderPtr->setProperties( |
| origin, |
| tcu::Vec3(1, 1, 1), /* gridCellSizeXYZ */ |
| tcu::UVec3(1, 1, 1), /* gridSizeXYZ */ |
| tcu::Vec3(0, 0, 0), /* gridInterCellDeltaXYZ */ |
| m_geometryType |
| ); |
| |
| auto tlPtr = asProviderPtr->createTLAS( context, |
| m_asStructureLayout, |
| commandBuffer, |
| VK_GEOMETRY_NO_DUPLICATE_ANY_HIT_INVOCATION_BIT_KHR, |
| this, /* optASPropertyProviderPtr */ |
| nullptr); /* optASFeedbackPtr */ |
| |
| m_tlPtrVec.push_back(std::move(tlPtr) ); |
| } |
| } |
| |
| void initPrograms(SourceCollections& programCollection) const final |
| { |
| const vk::ShaderBuildOptions buildOptions( programCollection.usedVulkanVersion, |
| vk::SPIRV_VERSION_1_4, |
| 0u, /* flags */ |
| true); /* allowSpirv14 */ |
| |
| const char* hitPropsDefinition = |
| "struct HitProps\n" |
| "{\n" |
| " uint instanceCustomIndex;\n" |
| " uint nAS;\n" |
| "};\n"; |
| |
| { |
| std::stringstream css; |
| |
| css << |
| "#version 460 core\n" |
| "\n" |
| "#extension GL_EXT_ray_tracing : require\n" |
| "\n" |
| "hitAttributeEXT vec3 dummyAttribute;\n" |
| "\n" |
| + de::toString(hitPropsDefinition) + |
| "\n" |
| "layout(location = 0) rayPayloadInEXT uint nAS;\n" |
| "layout(set = 0, binding = 0, std430) buffer result\n" |
| "{\n" |
| " uint nHitsRegistered;\n" |
| " uint nMissesRegistered;\n" |
| " HitProps hits[];\n" |
| "};\n" |
| "\n" |
| "void main()\n" |
| "{\n" |
| " uint nHit = atomicAdd(nHitsRegistered, 1);\n" |
| "\n" |
| " hits[nHit].instanceCustomIndex = gl_InstanceCustomIndexEXT;\n" |
| " hits[nHit].nAS = nAS;\n" |
| "}\n"; |
| |
| programCollection.glslSources.add("ahit") << glu::AnyHitSource(css.str() ) << buildOptions; |
| } |
| |
| { |
| std::stringstream css; |
| |
| css << |
| "#version 460 core\n" |
| "\n" |
| "#extension GL_EXT_ray_tracing : require\n" |
| "\n" |
| "hitAttributeEXT vec3 hitAttribute;\n" |
| "\n" |
| "void main()\n" |
| "{\n" |
| " reportIntersectionEXT(0.95f, 0);\n" |
| "}\n"; |
| |
| programCollection.glslSources.add("intersection") << glu::IntersectionSource(css.str() ) << buildOptions; |
| } |
| |
| { |
| std::stringstream css; |
| |
| css << |
| "#version 460 core\n" |
| "\n" |
| "#extension GL_EXT_ray_tracing : require\n" |
| "\n" |
| + de::toString(hitPropsDefinition) + |
| "\n" |
| "layout(set = 0, binding = 0, std430) buffer result\n" |
| "{\n" |
| " uint nHitsRegistered;\n" |
| " uint nMissesRegistered;\n" |
| " HitProps hits[];\n" |
| "};\n" |
| "\n" |
| "layout(location = 0) rayPayloadInEXT uint rayIndex;\n" |
| "\n" |
| "void main()\n" |
| "{\n" |
| " atomicAdd(nMissesRegistered, 1);\n" |
| "}\n"; |
| |
| programCollection.glslSources.add("miss") << glu::MissSource(css.str() ) << buildOptions; |
| } |
| |
| { |
| std::stringstream css; |
| |
| css << |
| "#version 460 core\n" |
| "\n" |
| "#extension GL_EXT_ray_tracing : require\n" |
| "\n" |
| "layout(push_constant) uniform pcUB\n" |
| "{\n" |
| " uint nAS;\n" |
| "} ub;\n" |
| "\n" |
| "layout(location = 0) rayPayloadEXT uint payload;\n" |
| "layout(set = 0, binding = 1) uniform accelerationStructureEXT accelerationStructures[" + de::toString(m_nMaxASToUse) + "];\n" |
| "\n" |
| "void main()\n" |
| "{\n" |
| " uint nInvocation = gl_LaunchIDEXT.z * gl_LaunchSizeEXT.x * gl_LaunchSizeEXT.y + gl_LaunchIDEXT.y * gl_LaunchSizeEXT.x + gl_LaunchIDEXT.x;\n" |
| " uint rayFlags = 0;\n" |
| " float tmin = 0.001;\n" |
| " float tmax = 9.0;\n" |
| "\n" |
| " uint cullMask = 0xFF;\n" |
| " vec3 cellStartXYZ = vec3(ub.nAS * 3.0, 0.0, 0.0);\n" |
| " vec3 cellEndXYZ = cellStartXYZ + vec3(1.0);\n" |
| " vec3 target = mix(cellStartXYZ, cellEndXYZ, vec3(0.5) );\n" |
| " vec3 origin = target - vec3(0, 2, 0);\n" |
| " vec3 direct = normalize(target - origin);\n" |
| "\n" |
| " payload = ub.nAS;\n" |
| "\n" |
| " traceRayEXT(accelerationStructures[ub.nAS], rayFlags, cullMask, 0, 0, 0, origin, tmin, direct, tmax, 0);\n" |
| "}\n"; |
| |
| programCollection.glslSources.add("rgen") << glu::RaygenSource(css.str() ) << buildOptions; |
| } |
| } |
| |
| Move<VkPipelineLayout> getPipelineLayout( const vk::DeviceInterface& deviceInterface, |
| VkDevice deviceVk, |
| VkDescriptorSetLayout descriptorSetLayout) final |
| { |
| VkPushConstantRange pushConstantRange; |
| |
| pushConstantRange.offset = 0; |
| pushConstantRange.size = sizeof(deUint32); |
| pushConstantRange.stageFlags = VK_SHADER_STAGE_RAYGEN_BIT_KHR; |
| |
| return makePipelineLayout( deviceInterface, |
| deviceVk, |
| 1, /* setLayoutCount */ |
| &descriptorSetLayout, |
| 1, /* pushRangeCount */ |
| &pushConstantRange); |
| } |
| |
| void onBeforeCmdTraceRays( const deUint32& nDispatch, |
| vkt::Context& context, |
| VkCommandBuffer commandBuffer, |
| VkPipelineLayout pipelineLayout) final |
| { |
| /* No need for a sync point in-between trace ray commands - all writes are atomic */ |
| VkMemoryBarrier memBarrier; |
| |
| memBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; |
| memBarrier.pNext = nullptr; |
| memBarrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;; |
| memBarrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER; |
| |
| context.getDeviceInterface().cmdPipelineBarrier(commandBuffer, |
| VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR, /* srcStageMask */ |
| VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR, /* dstStageMask */ |
| 0, /* dependencyFlags */ |
| 1, /* memoryBarrierCount */ |
| &memBarrier, |
| 0, /* bufferMemoryBarrierCount */ |
| nullptr, /* pBufferMemoryBarriers */ |
| 0, /* imageMemoryBarrierCount */ |
| nullptr); /* pImageMemoryBarriers */ |
| |
| context.getDeviceInterface().cmdPushConstants( commandBuffer, |
| pipelineLayout, |
| VK_SHADER_STAGE_RAYGEN_BIT_KHR, |
| 0, /* offset */ |
| sizeof(deUint32), |
| &nDispatch); |
| } |
| |
| bool verifyResultBuffer (const void* resultDataPtr) const final |
| { |
| const deUint32* resultU32Ptr = reinterpret_cast<const deUint32*>(resultDataPtr); |
| bool result = false; |
| |
| typedef struct |
| { |
| deUint32 instanceCustomIndex; |
| deUint32 nAS; |
| } HitProperties; |
| |
| const auto nHitsReported = *resultU32Ptr; |
| const auto nMissesReported = *(resultU32Ptr + 1); |
| |
| if (nHitsReported != m_nMaxASToUse) |
| { |
| goto end; |
| } |
| |
| if (nMissesReported != 0) |
| { |
| goto end; |
| } |
| |
| for (deUint32 nHit = 0; nHit < nHitsReported; ++nHit) |
| { |
| const HitProperties* hitPropsPtr = reinterpret_cast<const HitProperties*>(resultU32Ptr + 2 /* preamble ints */) + nHit; |
| |
| if (hitPropsPtr->instanceCustomIndex != (nHit + 1) ) |
| { |
| goto end; |
| } |
| |
| if (hitPropsPtr->nAS != nHit) |
| { |
| goto end; |
| } |
| } |
| |
| result = true; |
| end: |
| return result; |
| } |
| |
| private: |
| const AccelerationStructureLayout m_asStructureLayout; |
| const GeometryType m_geometryType; |
| |
| mutable deUint32 m_lastCustomInstanceIndexUsed; |
| deUint32 m_nASesToUse; |
| std::vector<std::unique_ptr<TopLevelAccelerationStructure> > m_tlPtrVec; |
| |
| const deUint32 m_nMaxASToUse; |
| }; |
| |
| class CallableShaderStressTest: public TestBase |
| { |
| public: |
| CallableShaderStressTest( const GeometryType& geometryType, |
| const AccelerationStructureLayout& asStructureLayout, |
| const bool& useDynamicStackSize) |
| : m_asStructureLayout (asStructureLayout), |
| m_geometryType (geometryType), |
| m_gridSizeXYZ (tcu::UVec3 (128, 1, 1) ), |
| m_nMaxCallableLevels ( (useDynamicStackSize) ? 8 |
| : 2 /* as per spec */), |
| m_maxPipelineRayRecursionDepth (0), |
| m_useDynamicStackSize (useDynamicStackSize), |
| m_ahitShaderStackSize (0), |
| m_callableShaderStackSize (0), |
| m_chitShaderStackSize (0), |
| m_isectShaderStackSize (0), |
| m_missShaderStackSize (0), |
| m_raygenShaderStackSize (0) |
| { |
| } |
| |
| ~CallableShaderStressTest() |
| { |
| /* Stub */ |
| } |
| |
| std::vector<std::string> getCallableShaderCollectionNames() const final |
| { |
| std::vector<std::string> resultVec(m_nMaxCallableLevels); |
| |
| for (deUint32 nLevel = 0; nLevel < m_nMaxCallableLevels; nLevel++) |
| { |
| resultVec.at(nLevel) = "call" + de::toString(nLevel); |
| } |
| |
| return resultVec; |
| } |
| |
| tcu::UVec3 getDispatchSize() const final |
| { |
| DE_ASSERT(m_gridSizeXYZ[0] != 0); |
| DE_ASSERT(m_gridSizeXYZ[1] != 0); |
| DE_ASSERT(m_gridSizeXYZ[2] != 0); |
| |
| return tcu::UVec3(m_gridSizeXYZ[0], m_gridSizeXYZ[1], m_gridSizeXYZ[2]); |
| } |
| |
| deUint32 getDynamicStackSize() const final |
| { |
| deUint32 result = 0; |
| const auto maxStackSpaceNeededForZerothTrace = static_cast<deUint32>(de::max(de::max(m_chitShaderStackSize, m_missShaderStackSize), m_isectShaderStackSize + m_ahitShaderStackSize) ); |
| const auto maxStackSpaceNeededForNonZerothTraces = static_cast<deUint32>(de::max(m_chitShaderStackSize, m_missShaderStackSize) ); |
| |
| DE_ASSERT(m_useDynamicStackSize); |
| |
| result = static_cast<deUint32>(m_raygenShaderStackSize) + |
| de::min(1u, m_maxPipelineRayRecursionDepth) * maxStackSpaceNeededForZerothTrace + |
| de::max(0u, m_maxPipelineRayRecursionDepth - 1) * maxStackSpaceNeededForNonZerothTraces + |
| m_nMaxCallableLevels * static_cast<deUint32>(m_callableShaderStackSize); |
| |
| DE_ASSERT(result != 0); |
| return result; |
| } |
| |
| deUint32 getResultBufferSize() const final |
| { |
| const auto nRaysTraced = m_gridSizeXYZ[0] * m_gridSizeXYZ[1] * m_gridSizeXYZ[2]; |
| const auto nClosestHitShaderInvocationsExpected = nRaysTraced / 2; |
| const auto nMissShaderInvocationsExpected = nRaysTraced / 2; |
| const auto resultItemSize = sizeof(deUint32) * 3 /* shaderStage, nOriginRay, nLevel */ + sizeof(float) * m_nMaxCallableLevels; |
| |
| DE_ASSERT((nRaysTraced % 2) == 0); |
| DE_ASSERT(m_nMaxCallableLevels != 0); |
| DE_ASSERT(m_gridSizeXYZ[0] != 0); |
| DE_ASSERT(m_gridSizeXYZ[1] != 0); |
| DE_ASSERT(m_gridSizeXYZ[2] != 0); |
| |
| return static_cast<deUint32>(sizeof(deUint32) /* nItemsStored */ + (resultItemSize * m_nMaxCallableLevels) * (nRaysTraced + nMissShaderInvocationsExpected + nClosestHitShaderInvocationsExpected) ); |
| } |
| |
| std::vector<TopLevelAccelerationStructure*> getTLASPtrVecToBind() const final |
| { |
| DE_ASSERT(m_tlPtr != nullptr); |
| |
| return {m_tlPtr.get() }; |
| } |
| |
| bool init( vkt::Context& /* context */, |
| RayTracingProperties* rtPropertiesPtr) final |
| { |
| m_maxPipelineRayRecursionDepth = rtPropertiesPtr->getMaxRecursionDepth(); |
| |
| return true; |
| } |
| |
| void initAS(vkt::Context& context, |
| RayTracingProperties* /* rtPropertiesPtr */, |
| VkCommandBuffer commandBuffer) final |
| { |
| std::unique_ptr<GridASProvider> asProviderPtr( |
| new GridASProvider( tcu::Vec3 (0, 0, 0), /* gridStartXYZ */ |
| tcu::Vec3 (1, 1, 1), /* gridCellSizeXYZ */ |
| m_gridSizeXYZ, |
| tcu::Vec3 (6, 0, 0), /* gridInterCellDeltaXYZ */ |
| m_geometryType) |
| ); |
| |
| m_tlPtr = asProviderPtr->createTLAS( context, |
| m_asStructureLayout, |
| commandBuffer, |
| 0, /* bottomLevelGeometryFlags */ |
| nullptr, /* optASPropertyProviderPtr */ |
| nullptr); /* optASFeedbackPtr */ |
| } |
| |
| void initPrograms(SourceCollections& programCollection) const final |
| { |
| const vk::ShaderBuildOptions buildOptions( programCollection.usedVulkanVersion, |
| vk::SPIRV_VERSION_1_4, |
| 0u, /* flags */ |
| true); /* allowSpirv14 */ |
| |
| std::vector<std::string> callableDataDefinitions (m_nMaxCallableLevels); |
| std::vector<std::string> callableDataInDefinitions (m_nMaxCallableLevels); |
| |
| for (deUint32 nCallableDataLevel = 0; nCallableDataLevel < m_nMaxCallableLevels; ++nCallableDataLevel) |
| { |
| const auto locationsPerCallableData = (3 /* uints */ + (nCallableDataLevel + 1) /* dataChunks */); |
| const auto callableDataLocation = locationsPerCallableData * nCallableDataLevel; |
| |
| callableDataDefinitions.at(nCallableDataLevel) = |
| "layout (location = " + de::toString(callableDataLocation) + ") callableDataEXT struct\n" |
| "{\n" |
| " uint shaderStage;\n" |
| " uint nOriginRay;\n" |
| " uint nLevel;\n" |
| " float dataChunk[" + de::toString(nCallableDataLevel + 1) + "];\n" |
| "} callableData" + de::toString(nCallableDataLevel) + ";\n"; |
| |
| callableDataInDefinitions.at(nCallableDataLevel) = |
| "layout(location = " + de::toString(callableDataLocation) + ") callableDataInEXT struct\n" |
| "{\n" |
| " uint shaderStage;\n" |
| " uint nOriginRay;\n" |
| " uint nLevel;\n" |
| " float dataChunk[" + de::toString(nCallableDataLevel + 1) + "];\n" |
| "} inData;\n"; |
| |
| m_callableDataLevelToCallableDataLocation[nCallableDataLevel] = callableDataLocation; |
| } |
| |
| const auto resultBufferDefinition = |
| "struct ResultData\n" |
| "{\n" |
| " uint shaderStage;\n" |
| " uint nOriginRay;\n" |
| " uint nLevel;\n" |
| " float dataChunk[" + de::toString(m_nMaxCallableLevels) + "];\n" |
| "};\n" |
| "\n" |
| "layout(set = 0, binding = 0, std430) buffer result\n" |
| "{\n" |
| " uint nInvocationsRegistered;\n" |
| " ResultData resultData[];\n" |
| "};\n"; |
| |
| { |
| std::stringstream css; |
| |
| /* NOTE: executeCallable() is unavailable in ahit stage */ |
| css << |
| "#version 460 core\n" |
| "\n" |
| "#extension GL_EXT_ray_tracing : require\n" |
| "\n" |
| "layout(location = 128) rayPayloadInEXT uint dummy;\n" |
| "\n" |
| "void main()\n" |
| "{\n" |
| "}\n"; |
| |
| programCollection.glslSources.add("ahit") << glu::AnyHitSource(css.str() ) << buildOptions; |
| } |
| |
| { |
| std::stringstream css; |
| |
| css << |
| "#version 460 core\n" |
| "\n" |
| "#extension GL_EXT_ray_tracing : require\n" |
| "\n" |
| "layout(location = 128) rayPayloadInEXT uint rayIndex;\n" |
| "\n" + |
| de::toString(callableDataDefinitions.at(0) ) + |
| de::toString(resultBufferDefinition) + |
| "void main()\n" |
| "{\n" |
| " uint nInvocation = gl_LaunchIDEXT.z * gl_LaunchSizeEXT.x * gl_LaunchSizeEXT.y + gl_LaunchIDEXT.y * gl_LaunchSizeEXT.x + gl_LaunchIDEXT.x;\n" |
| "\n" |
| " callableData0.shaderStage = 3;\n" |
| " callableData0.nOriginRay = nInvocation;\n" |
| " callableData0.nLevel = 0;\n" |
| " callableData0.dataChunk[0] = float(nInvocation);\n" |
| "\n" |
| " executeCallableEXT(0 /* sbtRecordIndex */, " + de::toString(m_callableDataLevelToCallableDataLocation.at(0) ) + ");\n" |
| "}\n"; |
| |
| programCollection.glslSources.add("chit") << glu::ClosestHitSource(css.str() ) << buildOptions; |
| } |
| |
| { |
| std::stringstream css; |
| |
| /* NOTE: executeCallable() is unavailable in isect stage */ |
| css << |
| "#version 460 core\n" |
| "\n" |
| "#extension GL_EXT_ray_tracing : require\n" |
| "\n" |
| "void main()\n" |
| "{\n" |
| " reportIntersectionEXT(0.95f, 0);\n" |
| "}\n"; |
| |
| programCollection.glslSources.add("intersection") << glu::IntersectionSource(css.str() ) << buildOptions; |
| } |
| |
| { |
| std::stringstream css; |
| |
| css << |
| "#version 460 core\n" |
| "\n" |
| "#extension GL_EXT_ray_tracing : require\n" |
| "\n" + |
| de::toString(callableDataDefinitions.at(0) ) + |
| de::toString(resultBufferDefinition) + |
| "\n" |
| "void main()\n" |
| "{\n" |
| " uint nInvocation = gl_LaunchIDEXT.z * gl_LaunchSizeEXT.x * gl_LaunchSizeEXT.y + gl_LaunchIDEXT.y * gl_LaunchSizeEXT.x + gl_LaunchIDEXT.x;\n" |
| "\n" |
| " callableData0.shaderStage = 2;\n" |
| " callableData0.nOriginRay = nInvocation;\n" |
| " callableData0.nLevel = 0;\n" |
| " callableData0.dataChunk[0] = float(nInvocation);\n" |
| "\n" |
| " executeCallableEXT(0 /* sbtRecordIndex */, " + de::toString(m_callableDataLevelToCallableDataLocation.at(0) ) + ");\n" |
| "}\n"; |
| |
| programCollection.glslSources.add("miss") << glu::MissSource(css.str() ) << buildOptions; |
| } |
| |
| { |
| std::stringstream css; |
| |
| css << |
| "#version 460 core\n" |
| "\n" |
| "#extension GL_EXT_ray_tracing : require\n" |
| "\n" |
| + de::toString(callableDataDefinitions.at(0) ) + |
| "layout(location = 128) rayPayloadEXT uint dummy;\n" |
| "layout(set = 0, binding = 1) uniform accelerationStructureEXT accelerationStructure;\n" |
| "\n" |
| "void main()\n" |
| "{\n" |
| " uint nInvocation = gl_LaunchIDEXT.z * gl_LaunchSizeEXT.x * gl_LaunchSizeEXT.y + gl_LaunchIDEXT.y * gl_LaunchSizeEXT.x + gl_LaunchIDEXT.x;\n" |
| " uint rayFlags = 0;\n" |
| " float tmin = 0.001;\n" |
| " float tmax = 9.0;\n" |
| "\n" |
| " uint cullMask = 0xFF;\n" |
| " vec3 cellStartXYZ = vec3(nInvocation * 3.0, 0.0, 0.0);\n" |
| " vec3 cellEndXYZ = cellStartXYZ + vec3(1.0);\n" |
| " vec3 target = mix(cellStartXYZ, cellEndXYZ, vec3(0.5) );\n" |
| " vec3 origin = target - vec3(0, 2, 0);\n" |
| " vec3 direct = normalize(target - origin);\n" |
| "\n" |
| " callableData0.shaderStage = 0;\n" |
| " callableData0.nOriginRay = nInvocation;\n" |
| " callableData0.nLevel = 0;\n" |
| " callableData0.dataChunk[0] = float(nInvocation);\n" |
| "\n" |
| " executeCallableEXT(0 /* sbtRecordIndex */, " + de::toString(m_callableDataLevelToCallableDataLocation.at(0) ) + ");\n" |
| "\n" |
| " traceRayEXT(accelerationStructure, rayFlags, cullMask, 0, 0, 0, origin, tmin, direct, tmax, 128);\n" |
| "}\n"; |
| |
| programCollection.glslSources.add("rgen") << glu::RaygenSource(css.str() ) << buildOptions; |
| } |
| |
| for (deUint32 nCallableShader = 0; nCallableShader < m_nMaxCallableLevels; ++nCallableShader) |
| { |
| const bool canInvokeExecutable = (nCallableShader != (m_nMaxCallableLevels - 1) ); |
| std::stringstream css; |
| |
| css << |
| "#version 460 core\n" |
| "\n" |
| "#extension GL_EXT_ray_tracing : require\n" |
| "\n" |
| + de::toString(resultBufferDefinition); |
| |
| if ((nCallableShader + 1) != m_nMaxCallableLevels) |
| { |
| css << de::toString(callableDataDefinitions.at(nCallableShader + 1) ); |
| } |
| |
| css << |
| callableDataInDefinitions[nCallableShader] + |
| "\n" |
| "void main()\n" |
| "{\n" |
| " uint nInvocation = atomicAdd(nInvocationsRegistered, 1);\n" |
| "\n" |
| " resultData[nInvocation].shaderStage = inData.shaderStage;\n" |
| " resultData[nInvocation].nOriginRay = inData.nOriginRay;\n" |
| " resultData[nInvocation].nLevel = inData.nLevel;\n"; |
| |
| for (deUint32 nLevel = 0; nLevel < nCallableShader + 1; ++nLevel) |
| { |
| css << |
| " resultData[nInvocation].dataChunk[" + de::toString(nLevel) + "] = inData.dataChunk[" + de::toString(nLevel) + "];\n"; |
| } |
| |
| if (canInvokeExecutable) |
| { |
| css << |
| "\n" |
| " callableData" + de::toString(nCallableShader + 1) + ".shaderStage = 1;\n" |
| " callableData" + de::toString(nCallableShader + 1) + ".nOriginRay = inData.nOriginRay;\n" |
| " callableData" + de::toString(nCallableShader + 1) + ".nLevel = " + de::toString(nCallableShader) + ";\n" |
| "\n"; |
| |
| for (deUint32 nLevel = 0; nLevel <= nCallableShader + 1; ++nLevel) |
| { |
| css << |
| " callableData" + de::toString(nCallableShader + 1) + ".dataChunk[" + de::toString(nLevel) + "] = float(inData.nOriginRay + " + de::toString(nLevel) + ");\n"; |
| } |
| |
| css << |
| "\n" |
| " executeCallableEXT(" + de::toString(nCallableShader + 1) + ", " + de::toString(m_callableDataLevelToCallableDataLocation[nCallableShader + 1]) + ");\n"; |
| } |
| |
| css << |
| "\n" |
| "};\n"; |
| |
| programCollection.glslSources.add("call" + de::toString(nCallableShader) ) << glu::CallableSource(css.str() ) << buildOptions; |
| } |
| } |
| |
| void onShaderStackSizeDiscovered( const VkDeviceSize& raygenShaderStackSize, |
| const VkDeviceSize& ahitShaderStackSize, |
| const VkDeviceSize& chitShaderStackSize, |
| const VkDeviceSize& missShaderStackSize, |
| const VkDeviceSize& callableShaderStackSize, |
| const VkDeviceSize& isectShaderStackSize) final |
| { |
| m_ahitShaderStackSize = ahitShaderStackSize; |
| m_callableShaderStackSize = callableShaderStackSize; |
| m_chitShaderStackSize = chitShaderStackSize; |
| m_isectShaderStackSize = isectShaderStackSize; |
| m_missShaderStackSize = missShaderStackSize; |
| m_raygenShaderStackSize = raygenShaderStackSize; |
| } |
| |
| void resetTLAS() final |
| { |
| m_tlPtr.reset(); |
| } |
| |
| bool usesDynamicStackSize() const final |
| { |
| return m_useDynamicStackSize; |
| } |
| |
| bool verifyResultBuffer (const void* resultDataPtr) const final |
| { |
| const deUint32* resultU32Ptr = reinterpret_cast<const deUint32*>(resultDataPtr); |
| bool result = false; |
| const auto nItemsStored = *resultU32Ptr; |
| |
| /* Convert raw binary data into a human-readable vector representation */ |
| struct ResultItem |
| { |
| VkShaderStageFlagBits shaderStage; |
| deUint32 nLevel; |
| std::vector<float> dataChunk; |
| |
| ResultItem() |
| : shaderStage (VK_SHADER_STAGE_ALL), |
| nLevel (0) |
| { |
| /* Stub */ |
| } |
| }; |
| |
| std::map<deUint32, std::vector<ResultItem> > nRayToResultItemVecMap; |
| |
| for (deUint32 nItem = 0; nItem < nItemsStored; ++nItem) |
| { |
| const deUint32* itemDataPtr = resultU32Ptr + 1 /* nItemsStored */ + nItem * (3 /* preamble ints */ + m_nMaxCallableLevels /* received data */); |
| ResultItem item; |
| const auto& nOriginRay = *(itemDataPtr + 1); |
| |
| item.dataChunk.resize(m_nMaxCallableLevels); |
| |
| switch (*itemDataPtr) |
| { |
| case 0: item.shaderStage = VK_SHADER_STAGE_RAYGEN_BIT_KHR; break; |
| case 1: item.shaderStage = VK_SHADER_STAGE_CALLABLE_BIT_KHR; break; |
| case 2: item.shaderStage = VK_SHADER_STAGE_MISS_BIT_KHR; break; |
| case 3: item.shaderStage = VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR; break; |
| |
| default: |
| { |
| deAssertFail("This should never happen", __FILE__, __LINE__); |
| } |
| } |
| |
| item.nLevel = *(itemDataPtr + 2); |
| |
| memcpy( item.dataChunk.data(), |
| itemDataPtr + 3, |
| m_nMaxCallableLevels * sizeof(float) ); |
| |
| nRayToResultItemVecMap[nOriginRay].push_back(item); |
| } |
| |
| for (deUint32 nRay = 0; nRay < m_gridSizeXYZ[0] * m_gridSizeXYZ[1] * m_gridSizeXYZ[2]; ++nRay) |
| { |
| /* 1. Make sure each ray generated the anticipated number of stores */ |
| const bool closestHitShaderInvoked = (nRay % 2) == 0; |
| const bool missShaderInvoked = (nRay % 2) != 0; |
| const deUint32 nShaderStagesInvokingCallables = 1 + /* raygen */ |
| ((closestHitShaderInvoked) ? 1 : 0) + |
| ((missShaderInvoked) ? 1 : 0); |
| auto rayIterator = nRayToResultItemVecMap.find(nRay); |
| |
| if (rayIterator == nRayToResultItemVecMap.end()) |
| { |
| goto end; |
| } |
| |
| if (rayIterator->second.size() != nShaderStagesInvokingCallables * m_nMaxCallableLevels) |
| { |
| goto end; |
| } |
| |
| /* 2. Make sure each shader stage generated the anticipated number of result items */ |
| { |
| deUint32 nCallableShaderStageItemsFound = 0; |
| deUint32 nClosestHitShaderStageItemsFound = 0; |
| deUint32 nMissShaderStageItemsFound = 0; |
| deUint32 nRaygenShaderStageItemsFound = 0; |
| |
| for (const auto& currentItem : rayIterator->second) |
| { |
| if (currentItem.shaderStage == VK_SHADER_STAGE_RAYGEN_BIT_KHR) |
| { |
| nRaygenShaderStageItemsFound++; |
| } |
| else |
| if (currentItem.shaderStage == VK_SHADER_STAGE_CALLABLE_BIT_KHR) |
| { |
| nCallableShaderStageItemsFound ++; |
| } |
| else |
| if (currentItem.shaderStage == VK_SHADER_STAGE_MISS_BIT_KHR) |
| { |
| nMissShaderStageItemsFound ++; |
| } |
| else |
| if (currentItem.shaderStage == VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR) |
| { |
| nClosestHitShaderStageItemsFound ++; |
| } |
| else |
| { |
| DE_ASSERT(false); |
| } |
| } |
| |
| if (nRaygenShaderStageItemsFound != 1) |
| { |
| goto end; |
| } |
| |
| /* Even rays hit geometry. Odd ones don't */ |
| if (!missShaderInvoked) |
| { |
| if (nClosestHitShaderStageItemsFound == 0) |
| { |
| goto end; |
| } |
| |
| if (nMissShaderStageItemsFound != 0) |
| { |
| goto end; |
| } |
| } |
| else |
| { |
| if (nClosestHitShaderStageItemsFound != 0) |
| { |
| goto end; |
| } |
| |
| if (nMissShaderStageItemsFound != 1) |
| { |
| goto end; |
| } |
| } |
| |
| if (nCallableShaderStageItemsFound != nShaderStagesInvokingCallables * (m_nMaxCallableLevels - 1) ) |
| { |
| goto end; |
| } |
| } |
| |
| /* 3. Verify data chunk's correctness */ |
| { |
| for (const auto& currentItem : rayIterator->second) |
| { |
| const auto nValidItemsRequired = (currentItem.shaderStage == VK_SHADER_STAGE_RAYGEN_BIT_KHR) ? 1 |
| : (currentItem.shaderStage == VK_SHADER_STAGE_MISS_BIT_KHR) ? 1 |
| : (currentItem.shaderStage == VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR) ? 1 |
| : (currentItem.nLevel + 1); |
| |
| for (deUint32 nItem = 0; nItem < nValidItemsRequired; ++nItem) |
| { |
| if (fabsf(currentItem.dataChunk.at(nItem) - static_cast<float>(nRay + nItem)) > 1e-3f) |
| { |
| goto end; |
| } |
| } |
| } |
| } |
| |
| /* 4. Verify all shader levels have been reported for relevant shader stages */ |
| { |
| std::map<VkShaderStageFlagBits, std::vector<deUint32> > shaderStageToLevelVecReportedMap; |
| |
| for (const auto& currentItem : rayIterator->second) |
| { |
| shaderStageToLevelVecReportedMap[currentItem.shaderStage].push_back(currentItem.nLevel); |
| } |
| |
| if (shaderStageToLevelVecReportedMap.at(VK_SHADER_STAGE_RAYGEN_BIT_KHR).size() != 1 || |
| shaderStageToLevelVecReportedMap.at(VK_SHADER_STAGE_RAYGEN_BIT_KHR).at (0) != 0) |
| { |
| goto end; |
| } |
| |
| if (closestHitShaderInvoked) |
| { |
| if (shaderStageToLevelVecReportedMap.at(VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR).size() != 1 || |
| shaderStageToLevelVecReportedMap.at(VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR).at (0) != 0) |
| { |
| goto end; |
| } |
| } |
| else |
| { |
| if (shaderStageToLevelVecReportedMap.find(VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR) != shaderStageToLevelVecReportedMap.end() ) |
| { |
| goto end; |
| } |
| } |
| |
| if (missShaderInvoked) |
| { |
| if (shaderStageToLevelVecReportedMap.at(VK_SHADER_STAGE_MISS_BIT_KHR).size() != 1 || |
| shaderStageToLevelVecReportedMap.at(VK_SHADER_STAGE_MISS_BIT_KHR).at (0) != 0) |
| { |
| goto end; |
| } |
| } |
| else |
| { |
| if (shaderStageToLevelVecReportedMap.find(VK_SHADER_STAGE_MISS_BIT_KHR) != shaderStageToLevelVecReportedMap.end() ) |
| { |
| goto end; |
| } |
| } |
| |
| if (shaderStageToLevelVecReportedMap.at(VK_SHADER_STAGE_CALLABLE_BIT_KHR).size() != nShaderStagesInvokingCallables * (m_nMaxCallableLevels - 1)) |
| { |
| goto end; |
| } |
| |
| for (deUint32 nLevel = 0; nLevel < m_nMaxCallableLevels - 1; ++nLevel) |
| { |
| const auto& vec = shaderStageToLevelVecReportedMap.at(VK_SHADER_STAGE_CALLABLE_BIT_KHR); |
| auto vecIterator = std::find(vec.begin (), |
| vec.end (), |
| nLevel); |
| |
| if (vecIterator == vec.end()) |
| { |
| goto end; |
| } |
| } |
| } |
| } |
| |
| result = true; |
| end: |
| return result; |
| } |
| |
| private: |
| |
| const AccelerationStructureLayout m_asStructureLayout; |
| const GeometryType m_geometryType; |
| |
| const tcu::UVec3 m_gridSizeXYZ; |
| const deUint32 m_nMaxCallableLevels; |
| deUint32 m_maxPipelineRayRecursionDepth; |
| const bool m_useDynamicStackSize; |
| std::unique_ptr<TopLevelAccelerationStructure> m_tlPtr; |
| |
| VkDeviceSize m_ahitShaderStackSize; |
| VkDeviceSize m_callableShaderStackSize; |
| VkDeviceSize m_chitShaderStackSize; |
| VkDeviceSize m_isectShaderStackSize; |
| VkDeviceSize m_missShaderStackSize; |
| VkDeviceSize m_raygenShaderStackSize; |
| |
| mutable std::map<deUint32, deUint32> m_callableDataLevelToCallableDataLocation; |
| }; |
| |
| class CullMaskTest : public TestBase, |
| public ASPropertyProvider |
| { |
| public: |
| CullMaskTest( const AccelerationStructureLayout& asLayout, |
| const GeometryType& geometryType, |
| const bool& useExtraCullMaskBits) |
| :m_asLayout (asLayout), |
| m_geometryType (geometryType), |
| m_nMaxHitsToRegister (256), |
| m_nRaysPerInvocation (4), |
| m_useExtraCullMaskBits (useExtraCullMaskBits), |
| m_lastCustomInstanceIndexUsed (0), |
| m_nCullMasksUsed (1) |
| { |
| /* Stub */ |
| } |
| |
| ~CullMaskTest() |
| { |
| /* Stub */ |
| } |
| |
| std::vector<std::string> getCHitShaderCollectionShaderNames() const final |
| { |
| return {}; |
| } |
| |
| deUint8 getCullMask(const deUint32& nBL, const deUint32& nInstance) const final |
| { |
| DE_UNREF(nBL); |
| DE_UNREF(nInstance); |
| |
| deUint8 result = (m_nCullMasksUsed++) & 0xFF; |
| |
| DE_ASSERT(result != 0); |
| return result; |
| } |
| |
| deUint32 getInstanceCustomIndex(const deUint32& nBL, const deUint32& nInstance) const final |
| { |
| DE_UNREF(nBL); |
| DE_UNREF(nInstance); |
| |
| /* NOTE: The formula below generates a sequence of unique large values. */ |
| deUint32 result = (m_lastCustomInstanceIndexUsed * 7 + 153325) & ((1 << 24) - 1); |
| |
| if (m_instanceCustomIndexVec.size() <= nInstance) |
| { |
| m_instanceCustomIndexVec.resize(nInstance + 1); |
| } |
| |
| m_instanceCustomIndexVec [nInstance] = result; |
| m_lastCustomInstanceIndexUsed = result; |
| |
| return result; |
| } |
| |
| tcu::UVec3 getDispatchSize() const final |
| { |
| //< 3*5*17 == 255, which coincidentally is the maximum cull mask value the spec permits. |
| //< |
| //< This global WG size is excessively large if m_nRaysPerInvocation > 1 but the raygen shader has |
| //< a guard condition check that drops extraneous invocations. |
| return tcu::UVec3(3, 5, 17); |
| } |
| |
| deUint32 getResultBufferSize() const final |
| { |
| return static_cast<deUint32>((1 + m_nMaxHitsToRegister * 2) * sizeof(deUint32) ); |
| } |
| |
| std::vector<TopLevelAccelerationStructure*> getTLASPtrVecToBind() const final |
| { |
| return {m_tlPtr.get() }; |
| } |
| |
| void resetTLAS() final |
| { |
| m_tlPtr.reset(); |
| } |
| |
| void initAS(vkt::Context& context, |
| RayTracingProperties* /* rtPropertiesPtr */, |
| VkCommandBuffer commandBuffer) final |
| { |
| m_asProviderPtr.reset( |
| new GridASProvider( tcu::Vec3 (0, 0, 0), /* gridStartXYZ */ |
| tcu::Vec3 (1, 1, 1), /* gridCellSizeXYZ */ |
| tcu::UVec3 (3, 5, 17), /* gridSizeXYZ */ |
| tcu::Vec3 (2.0f, 2.0f, 2.0f), /* gridInterCellDeltaXYZ */ |
| m_geometryType) |
| ); |
| |
| m_tlPtr = m_asProviderPtr->createTLAS( context, |
| m_asLayout, |
| commandBuffer, |
| VK_GEOMETRY_NO_DUPLICATE_ANY_HIT_INVOCATION_BIT_KHR, |
| this, /* optASPropertyProviderPtr */ |
| nullptr); /* optASFeedbackPtr */ |
| } |
| |
| void initPrograms(SourceCollections& programCollection) const final |
| { |
| const vk::ShaderBuildOptions buildOptions( programCollection.usedVulkanVersion, |
| vk::SPIRV_VERSION_1_4, |
| 0u, /* flags */ |
| true); /* allowSpirv14 */ |
| |
| const char* hitPropsDefinition = |
| "struct HitProps\n" |
| "{\n" |
| " uint rayIndex;\n" |
| " uint instanceCustomIndex;\n" |
| "};\n"; |
| |
| { |
| std::stringstream css; |
| |
| css << |
| "#version 460 core\n" |
| "\n" |
| "#extension GL_EXT_ray_tracing : require\n" |
| "\n" |
| "hitAttributeEXT vec3 dummyAttribute;\n" |
| "\n" |
| + de::toString(hitPropsDefinition) + |
| "\n" |
| "layout(location = 0) rayPayloadInEXT uint nRay;\n" |
| "layout(set = 0, binding = 0, std430) buffer result\n" |
| "{\n" |
| " uint nHitsRegistered;\n" |
| " uint nMissesRegistered;\n" |
| " HitProps hits[];\n" |
| "};\n" |
| "\n" |
| "void main()\n" |
| "{\n" |
| " uint nHit = atomicAdd(nHitsRegistered, 1);\n" |
| "\n" |
| " if (nHit < " + de::toString(m_nMaxHitsToRegister) + ")\n" |
| " {\n" |
| " hits[nHit].rayIndex = nRay;\n" |
| " hits[nHit].instanceCustomIndex = gl_InstanceCustomIndexEXT;\n" |
| " }\n" |
| "}\n"; |
| |
| programCollection.glslSources.add("ahit") << glu::AnyHitSource(css.str() ) << buildOptions; |
| } |
| |
| { |
| std::stringstream css; |
| |
| css << |
| "#version 460 core\n" |
| "\n" |
| "#extension GL_EXT_ray_tracing : require\n" |
| "\n" |
| "hitAttributeEXT vec3 hitAttribute;\n" |
| "\n" |
| "void main()\n" |
| "{\n" |
| " reportIntersectionEXT(0.95f, 0);\n" |
| "}\n"; |
| |
| programCollection.glslSources.add("intersection") << glu::IntersectionSource(css.str() ) << buildOptions; |
| } |
| |
| { |
| std::stringstream css; |
| |
| css << |
| "#version 460 core\n" |
| "\n" |
| "#extension GL_EXT_ray_tracing : require\n" |
| "\n" |
| + de::toString(hitPropsDefinition) + |
| "\n" |
| "layout(set = 0, binding = 0, std430) buffer result\n" |
| "{\n" |
| " uint nHitsRegistered;\n" |
| " uint nMissesRegistered;\n" |
| " HitProps hits[];\n" |
| "};\n" |
| "\n" |
| "layout(location = 0) rayPayloadInEXT uint rayIndex;\n" |
| "\n" |
| "void main()\n" |
| "{\n" |
| " atomicAdd(nMissesRegistered, 1);\n" |
| "}\n"; |
| |
| programCollection.glslSources.add("miss") << glu::MissSource(css.str() ) << buildOptions; |
| } |
| |
| { |
| std::stringstream css; |
| |
| css << |
| "#version 460 core\n" |
| "\n" |
| "#extension GL_EXT_ray_tracing : require\n" |
| "\n" |
| "layout(location = 0) rayPayloadEXT uint rayIndex;\n" |
| "layout(set = 0, binding = 1) uniform accelerationStructureEXT topLevelAS;\n" |
| "\n" |
| "void main()\n" |
| "{\n" |
| " const uint nRaysPerInvocation = " + de::toString(m_nRaysPerInvocation) + ";\n" |
| "\n" |
| " uint nInvocation = gl_LaunchIDEXT.z * gl_LaunchSizeEXT.x * gl_LaunchSizeEXT.y + gl_LaunchIDEXT.y * gl_LaunchSizeEXT.x + gl_LaunchIDEXT.x;\n" |
| " uint rayFlags = 0;\n" |
| " float tmin = 0.001;\n" |
| " float tmax = 4.0;\n" |
| "\n" |
| " if (nInvocation >= 256 / nRaysPerInvocation)\n" |
| " {\n" |
| " return;\n" |
| " }\n" |
| "\n" |
| " for (uint nRay = 0; nRay < nRaysPerInvocation; ++nRay)\n" |
| " {\n" |
| " uint cullMask = 1 + nInvocation * nRaysPerInvocation + nRay;\n"; |
| |
| if (m_useExtraCullMaskBits) |
| { |
| css << "cullMask |= 0x00FFFFFF;\n"; |
| } |
| |
| css << |
| " uint nCell = nInvocation * nRaysPerInvocation + nRay;\n" |
| " uvec3 cellXYZ = uvec3(nCell % gl_LaunchSizeEXT.x, (nCell / gl_LaunchSizeEXT.x) % gl_LaunchSizeEXT.y, (nCell / gl_LaunchSizeEXT.x / gl_LaunchSizeEXT.y) % gl_LaunchSizeEXT.z);\n" |
| " vec3 cellStartXYZ = vec3(cellXYZ) * vec3(2.0);\n" |
| " vec3 cellEndXYZ = cellStartXYZ + vec3(1.0);\n" |
| " vec3 target = mix(cellStartXYZ, cellEndXYZ, vec3(0.5) );\n" |
| " vec3 origin = target - vec3(1, 1, 1);\n" |
| " vec3 direct = normalize(target - origin);\n" |
| "\n" |
| " if (nCell < 255)\n" |
| " {\n" |
| " rayIndex = nCell;" |
| "\n" |
| " traceRayEXT(topLevelAS, rayFlags, cullMask, 0, 0, 0, origin, tmin, direct, tmax, 0);\n" |
| " }\n" |
| " }\n" |
| "}\n"; |
| |
| programCollection.glslSources.add("rgen") << glu::RaygenSource(css.str() ) << buildOptions; |
| } |
| } |
| |
| bool verifyResultBuffer (const void* resultDataPtr) const final |
| { |
| const deUint32* resultU32Ptr = reinterpret_cast<const deUint32*>(resultDataPtr); |
| const auto nHitsReported = *resultU32Ptr; |
| const auto nMissesReported = *(resultU32Ptr + 1); |
| bool result = true; |
| |
| // For each traced ray: |
| // |
| // 1. Exactly one ahit invocation per ray should be reported. |
| // 2. All hits reported for a ray R should point to a primitive with a valid custom instance index |
| // 3. The reported custom instance indices must be valid. |
| std::map<deUint32, std::vector<deUint32> > customInstanceIndexToRayIndexVecMap; |
| std::map<deUint32, std::vector<deUint32> > rayIndexToCustomInstanceIndexVecMap; |
| |
| typedef struct |
| { |
| deUint32 rayIndex; |
| deUint32 customInstanceHit; |
| } HitProperties; |
| |
| if (nHitsReported != 0xFF) |
| { |
| result = false; |
| |
| goto end; |
| } |
| |
| if (nMissesReported != 0) |
| { |
| result = false; |
| |
| goto end; |
| } |
| |
| for (deUint32 nHit = 0; nHit < nHitsReported; ++nHit) |
| { |
| const HitProperties* hitPropsPtr = reinterpret_cast<const HitProperties*>(resultU32Ptr + 2 /* preamble ints */ + nHit * 2 /* ints per HitProperties item */); |
| |
| customInstanceIndexToRayIndexVecMap[hitPropsPtr->customInstanceHit].push_back(hitPropsPtr->rayIndex); |
| rayIndexToCustomInstanceIndexVecMap[hitPropsPtr->rayIndex].push_back (hitPropsPtr->customInstanceHit); |
| } |
| |
| if (static_cast<deUint32>(customInstanceIndexToRayIndexVecMap.size()) != nHitsReported) |
| { |
| /* Invalid number of unique custom instance indices reported. */ |
| result = false; |
| |
| goto end; |
| } |
| |
| if (static_cast<deUint32>(rayIndexToCustomInstanceIndexVecMap.size()) != nHitsReported) |
| { |
| /* Invalid ray indices reported by ahit invocations */ |
| result = false; |
| |
| goto end; |
| } |
| |
| for (const auto& currentItem : customInstanceIndexToRayIndexVecMap) |
| { |
| if (currentItem.second.size() != 1) |
| { |
| /* More than one ray associated with the same custom instance index */ |
| result = false; |
| |
| goto end; |
| } |
| |
| if (currentItem.second.at(0) > 255) |
| { |
| /* Invalid ray index associated with the instance index */ |
| result = false; |
| |
| goto end; |
| } |
| |
| if (std::find( m_instanceCustomIndexVec.begin (), |
| m_instanceCustomIndexVec.end (), |
| currentItem.first) == m_instanceCustomIndexVec.end() ) |
| { |
| /* Invalid custom instance index reported for the ray */ |
| result = false; |
| |
| goto end; |
| } |
| } |
| |
| end: |
| return result; |
| } |
| |
| private: |
| |
| const AccelerationStructureLayout m_asLayout; |
| const GeometryType m_geometryType; |
| const deUint32 m_nMaxHitsToRegister; |
| const deUint32 m_nRaysPerInvocation; |
| const bool m_useExtraCullMaskBits; |
| |
| mutable std::vector<deUint32> m_instanceCustomIndexVec; |
| mutable deUint32 m_lastCustomInstanceIndexUsed; |
| mutable deUint32 m_nCullMasksUsed; |
| |
| std::unique_ptr<GridASProvider> m_asProviderPtr; |
| std::unique_ptr<TopLevelAccelerationStructure> m_tlPtr; |
| }; |
| |
| |
| |
| class MAXRayHitAttributeSizeTest: public TestBase |
| { |
| public: |
| MAXRayHitAttributeSizeTest( const GeometryType& geometryType, |
| const AccelerationStructureLayout& asStructureLayout) |
| : m_asStructureLayout (asStructureLayout), |
| m_geometryType (geometryType), |
| m_gridSizeXYZ (tcu::UVec3 (512, 1, 1) ), |
| m_nRayAttributeU32s (0) |
| { |
| } |
| |
| ~MAXRayHitAttributeSizeTest() |
| { |
| /* Stub */ |
| } |
| |
| tcu::UVec3 getDispatchSize() const final |
| { |
| DE_ASSERT(m_gridSizeXYZ[0] != 0); |
| DE_ASSERT(m_gridSizeXYZ[1] != 0); |
| DE_ASSERT(m_gridSizeXYZ[2] != 0); |
| |
| return tcu::UVec3(m_gridSizeXYZ[0], m_gridSizeXYZ[1], m_gridSizeXYZ[2]); |
| } |
| |
| deUint32 getResultBufferSize() const final |
| { |
| DE_ASSERT(m_gridSizeXYZ[0] != 0); |
| DE_ASSERT(m_gridSizeXYZ[1] != 0); |
| DE_ASSERT(m_gridSizeXYZ[2] != 0); |
| |
| return static_cast<deUint32>((3 /* nAHits, nCHits, nMisses */ + m_gridSizeXYZ[0] * m_gridSizeXYZ[1] * m_gridSizeXYZ[2] * m_nRayAttributeU32s * 2 /* stages where result data is stored */) * sizeof(deUint32) ); |
| } |
| |
| VkSpecializationInfo* getSpecializationInfoPtr(const VkShaderStageFlagBits& shaderStage) final |
| { |
| VkSpecializationInfo* resultPtr = nullptr; |
| |
| if (shaderStage == VK_SHADER_STAGE_INTERSECTION_BIT_KHR || |
| shaderStage == VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR || |
| shaderStage == VK_SHADER_STAGE_ANY_HIT_BIT_KHR) |
| { |
| resultPtr = &m_specializationInfo; |
| } |
| |
| return resultPtr; |
| } |
| |
| std::vector<TopLevelAccelerationStructure*> getTLASPtrVecToBind() const final |
| { |
| DE_ASSERT(m_tlPtr != nullptr); |
| |
| return {m_tlPtr.get() }; |
| } |
| |
| void resetTLAS() final |
| { |
| m_tlPtr.reset(); |
| } |
| |
| bool init( vkt::Context& /* context */, |
| RayTracingProperties* rtPropertiesPtr) final |
| { |
| const auto maxRayHitAttributeSize = rtPropertiesPtr->getMaxRayHitAttributeSize(); |
| |
| // TODO: If U8s are supported, we could cover the remaining space with these.. |
| m_nRayAttributeU32s = maxRayHitAttributeSize / static_cast<deUint32>(sizeof(deUint32) ); |
| DE_ASSERT(m_nRayAttributeU32s != 0); |
| |
| m_specializationInfoMapEntry.constantID = 1; |
| m_specializationInfoMapEntry.offset = 0; |
| m_specializationInfoMapEntry.size = sizeof(deUint32); |
| |
| m_specializationInfo.dataSize = sizeof(deUint32); |
| m_specializationInfo.mapEntryCount = 1; |
| m_specializationInfo.pData = reinterpret_cast<const void*>(&m_nRayAttributeU32s); |
| m_specializationInfo.pMapEntries = &m_specializationInfoMapEntry; |
| |
| return true; |
| } |
| |
| void initAS(vkt::Context& context, |
| RayTracingProperties* /* rtPropertiesPtr */, |
| VkCommandBuffer commandBuffer) final |
| { |
| std::unique_ptr<GridASProvider> asProviderPtr( |
| new GridASProvider( tcu::Vec3 (0, 0, 0), /* gridStartXYZ */ |
| tcu::Vec3 (1, 1, 1), /* gridCellSizeXYZ */ |
| m_gridSizeXYZ, |
| tcu::Vec3 (6, 0, 0), /* gridInterCellDeltaXYZ */ |
| m_geometryType) |
| ); |
| |
| m_tlPtr = asProviderPtr->createTLAS( context, |
| m_asStructureLayout, |
| commandBuffer, |
| 0, /* bottomLevelGeometryFlags */ |
| nullptr, /* optASPropertyProviderPtr */ |
| nullptr); /* optASFeedbackPtr */ |
| } |
| |
| void initPrograms(SourceCollections& programCollection) const final |
| { |
| const vk::ShaderBuildOptions buildOptions( programCollection.usedVulkanVersion, |
| vk::SPIRV_VERSION_1_4, |
| 0u, /* flags */ |
| true); /* allowSpirv14 */ |
| |
| const char* constantDefinitions = |
| "layout(constant_id = 1) const uint N_UINTS_IN_HIT_ATTRIBUTE = 1;\n"; |
| |
| const char* hitAttributeDefinition = |
| "\n" |
| "hitAttributeEXT block\n" |
| "{\n" |
| " uint values[N_UINTS_IN_HIT_ATTRIBUTE];\n" |
| "};\n" |
| "\n"; |
| |
| const char* resultBufferDefinition = |
| "layout(set = 0, binding = 0, std430) buffer result\n" |
| "{\n" |
| " uint nAHitsRegistered;\n" |
| " uint nCHitsRegistered;\n" |
| " uint nMissesRegistered;\n" |
| " uint retrievedValues[N_UINTS_IN_HIT_ATTRIBUTE];\n" |
| "};\n"; |
| |
| { |
| std::stringstream css; |
| |
| css << |
| "#version 460 core\n" |
| "\n" |
| "#extension GL_EXT_ray_tracing : require\n" |
| "\n" |
| + de::toString(constantDefinitions) |
| + de::toString(hitAttributeDefinition) + |
| "\n" |
| "layout(location = 0) rayPayloadInEXT uint dummy;\n" |
| + de::toString(resultBufferDefinition) + |
| "\n" |
| "void main()\n" |
| "{\n" |
| " atomicAdd(nAHitsRegistered, 1);\n" |
| "\n" |
| " uint nInvocation = gl_LaunchIDEXT.z * gl_LaunchSizeEXT.x * gl_LaunchSizeEXT.y + gl_LaunchIDEXT.y * gl_LaunchSizeEXT.x + gl_LaunchIDEXT.x;\n" |
| "\n" |
| " for (uint nUint = 0; nUint < N_UINTS_IN_HIT_ATTRIBUTE; ++nUint)\n" |
| " {\n" |
| " retrievedValues[(2 * nInvocation + 1) * N_UINTS_IN_HIT_ATTRIBUTE + nUint] = values[nUint];\n" |
| " }\n" |
| "}\n"; |
| |
| programCollection.glslSources.add("ahit") << glu::AnyHitSource(css.str() ) << buildOptions; |
| } |
| |
| { |
| std::stringstream css; |
| |
| css << |
| "#version 460 core\n" |
| "\n" |
| "#extension GL_EXT_ray_tracing : require\n" |
| "\n" |
| + de::toString(constantDefinitions) |
| + de::toString(hitAttributeDefinition) + |
| de::toString(resultBufferDefinition) + |
| "\n" |
| "layout(location = 0) rayPayloadInEXT uint rayIndex;\n" |
| "\n" |
| "void main()\n" |
| "{\n" |
| " atomicAdd(nCHitsRegistered, 1);\n" |
| "\n" |
| " uint nInvocation = gl_LaunchIDEXT.z * gl_LaunchSizeEXT.x * gl_LaunchSizeEXT.y + gl_LaunchIDEXT.y * gl_LaunchSizeEXT.x + gl_LaunchIDEXT.x;\n" |
| "\n" |
| " for (uint nUint = 0; nUint < N_UINTS_IN_HIT_ATTRIBUTE; ++nUint)\n" |
| " {\n" |
| " retrievedValues[(2 * nInvocation + 0) * N_UINTS_IN_HIT_ATTRIBUTE + nUint] = values[nUint];\n" |
| " }\n" |
| "}\n"; |
| |
| programCollection.glslSources.add("chit") << glu::ClosestHitSource(css.str() ) << buildOptions; |
| } |
| |
| { |
| std::stringstream css; |
| |
| css << |
| "#version 460 core\n" |
| "\n" |
| "#extension GL_EXT_ray_tracing : require\n" |
| "\n" |
| + de::toString(constantDefinitions) |
| + de::toString(hitAttributeDefinition) + |
| de::toString(resultBufferDefinition) + |
| "\n" |
| "void main()\n" |
| "{\n" |
| " uint nInvocation = gl_LaunchIDEXT.z * gl_LaunchSizeEXT.x * gl_LaunchSizeEXT.y + gl_LaunchIDEXT.y * gl_LaunchSizeEXT.x + gl_LaunchIDEXT.x;\n" |
| "\n" |
| " for (uint nUint = 0; nUint < N_UINTS_IN_HIT_ATTRIBUTE; ++nUint)\n" |
| " {\n" |
| " values[nUint] = 1 + nInvocation + nUint;\n" |
| " }\n" |
| "\n" |
| " reportIntersectionEXT(0.95f, 0);\n" |
| "}\n"; |
| |
| programCollection.glslSources.add("intersection") << glu::IntersectionSource(css.str() ) << buildOptions; |
| } |
| |
| { |
| std::stringstream css; |
| |
| css << |
| "#version 460 core\n" |
| "\n" |
| "#extension GL_EXT_ray_tracing : require\n" |
| "\n" |
| + de::toString(constantDefinitions) |
| + de::toString(resultBufferDefinition) + |
| "\n" |
| "void main()\n" |
| "{\n" |
| " atomicAdd(nMissesRegistered, 1);\n" |
| "}\n"; |
| |
| programCollection.glslSources.add("miss") << glu::MissSource(css.str() ) << buildOptions; |
| } |
| |
| { |
| std::stringstream css; |
| |
| css << |
| "#version 460 core\n" |
| "\n" |
| "#extension GL_EXT_ray_tracing : require\n" |
| "\n" |
| "layout(location = 0) rayPayloadEXT uint dummy;\n" |
| "layout(set = 0, binding = 1) uniform accelerationStructureEXT accelerationStructure;\n" |
| "\n" |
| "void main()\n" |
| "{\n" |
| " uint nInvocation = gl_LaunchIDEXT.z * gl_LaunchSizeEXT.x * gl_LaunchSizeEXT.y + gl_LaunchIDEXT.y * gl_LaunchSizeEXT.x + gl_LaunchIDEXT.x;\n" |
| " uint rayFlags = 0;\n" |
| " float tmin = 0.001;\n" |
| " float tmax = 9.0;\n" |
| "\n" |
| " uint cullMask = 0xFF;\n" |
| " vec3 cellStartXYZ = vec3(nInvocation * 3.0, 0.0, 0.0);\n" |
| " vec3 cellEndXYZ = cellStartXYZ + vec3(1.0);\n" |
| " vec3 target = mix(cellStartXYZ, cellEndXYZ, vec3(0.5) );\n" |
| " vec3 origin = target - vec3(0, 2, 0);\n" |
| " vec3 direct = normalize(target - origin);\n" |
| "\n" |
| " traceRayEXT(accelerationStructure, rayFlags, cullMask, 0, 0, 0, origin, tmin, direct, tmax, 0);\n" |
| "}\n"; |
| |
| programCollection.glslSources.add("rgen") << glu::RaygenSource(css.str() ) << buildOptions; |
| } |
| } |
| |
| bool verifyResultBuffer (const void* resultDataPtr) const final |
| { |
| const deUint32* resultU32Ptr = reinterpret_cast<const deUint32*>(resultDataPtr); |
| bool result = false; |
| |
| |
| const auto nAHitsReported = *resultU32Ptr; |
| const auto nCHitsRegistered = *(resultU32Ptr + 1); |
| const auto nMissesRegistered = *(resultU32Ptr + 2); |
| |
| if (nAHitsReported != m_gridSizeXYZ[0] * m_gridSizeXYZ[1] * m_gridSizeXYZ[2] / 2) |
| { |
| goto end; |
| } |
| |
| if (nCHitsRegistered != nAHitsReported) |
| { |
| goto end; |
| } |
| |
| if (nMissesRegistered != nAHitsReported) |
| { |
| goto end; |
| } |
| |
| for (deUint32 nHit = 0; nHit < nAHitsReported; ++nHit) |
| { |
| const deUint32* ahitValues = resultU32Ptr + 3 /* preamble ints */ + (2 * nHit + 0) * m_nRayAttributeU32s; |
| const deUint32* chitValues = resultU32Ptr + 3 /* preamble ints */ + (2 * nHit + 1) * m_nRayAttributeU32s; |
| const bool missExpected = (nHit % 2) != 0; |
| |
| for (deUint32 nValue = 0; nValue < m_nRayAttributeU32s; ++nValue) |
| { |
| if (!missExpected) |
| { |
| if (ahitValues[nValue] != 1 + nHit + nValue) |
| { |
| goto end; |
| } |
| |
| if (chitValues[nValue] != 1 + nHit + nValue) |
| { |
| goto end; |
| } |
| } |
| else |
| { |
| if (ahitValues[nValue] != 0) |
| { |
| goto end; |
| } |
| |
| if (chitValues[nValue] != 0) |
| { |
| goto end; |
| } |
| } |
| } |
| } |
| |
| result = true; |
| end: |
| return result; |
| } |
| |
| private: |
| |
| const AccelerationStructureLayout m_asStructureLayout; |
| const GeometryType m_geometryType; |
| |
| const tcu::UVec3 m_gridSizeXYZ; |
| deUint32 m_nRayAttributeU32s; |
| std::unique_ptr<TopLevelAccelerationStructure> m_tlPtr; |
| |
| VkSpecializationInfo m_specializationInfo; |
| VkSpecializationMapEntry m_specializationInfoMapEntry; |
| }; |
| |
| class MAXRTInvocationsSupportedTest : public TestBase, |
| public ASPropertyProvider, |
| public IGridASFeedback |
| { |
| public: |
| MAXRTInvocationsSupportedTest( const GeometryType& geometryType, |
| const AccelerationStructureLayout& asStructureLayout) |
| : m_asStructureLayout (asStructureLayout), |
| m_geometryType (geometryType), |
| m_lastCustomInstanceIndexUsed (0), |
| m_nMaxCells (8 * 8 * 8) |
| { |
| } |
| |
| ~MAXRTInvocationsSupportedTest() |
| { |
| /* Stub */ |
| } |
| |
| std::vector<std::string> getCHitShaderCollectionShaderNames() const final |
| { |
| return {}; |
| } |
| |
| deUint32 getInstanceCustomIndex(const deUint32& nBL, const deUint32& nInstance) const final |
| { |
| DE_UNREF(nBL); |
| DE_UNREF(nInstance); |
| |
| return ++m_lastCustomInstanceIndexUsed; |
| } |
| |
| tcu::UVec3 getDispatchSize() const final |
| { |
| DE_ASSERT(m_gridSizeXYZ[0] != 0); |
| DE_ASSERT(m_gridSizeXYZ[1] != 0); |
| DE_ASSERT(m_gridSizeXYZ[2] != 0); |
| |
| return tcu::UVec3(m_gridSizeXYZ[0], m_gridSizeXYZ[1], m_gridSizeXYZ[2]); |
| } |
| |
| deUint32 getResultBufferSize() const final |
| { |
| DE_ASSERT(m_gridSizeXYZ[0] != 0); |
| DE_ASSERT(m_gridSizeXYZ[1] != 0); |
| DE_ASSERT(m_gridSizeXYZ[2] != 0); |
| |
| return static_cast<deUint32>((2 /* nHits, nMisses */ + m_gridSizeXYZ[0] * m_gridSizeXYZ[1] * m_gridSizeXYZ[2] * 1 /* hit instance custom index */) * sizeof(deUint32) ); |
| } |
| |
| std::vector<TopLevelAccelerationStructure*> getTLASPtrVecToBind() const final |
| { |
| DE_ASSERT(m_tlPtr != nullptr); |
| |
| return {m_tlPtr.get() }; |
| } |
| |
| bool init( vkt::Context& context, |
| RayTracingProperties* rtPropertiesPtr) final |
| { |
| /* NOTE: In order to avoid running into a situation where the test attempts to create a buffer of size larger than permitted by Vulkan, |
| * we limit the maximum number of testable invocations to 2^29. */ |
| const auto maxComputeWorkGroupCount = context.getDeviceProperties().limits.maxComputeWorkGroupCount; |
| const auto maxComputeWorkGroupSize = context.getDeviceProperties().limits.maxComputeWorkGroupSize; |
| const deUint64 maxGlobalRTWorkGroupSize[3] = { static_cast<deUint64>(maxComputeWorkGroupCount[0]) * static_cast<deUint64>(maxComputeWorkGroupSize[0]), |
| static_cast<deUint64>(maxComputeWorkGroupCount[1]) * static_cast<deUint64>(maxComputeWorkGroupSize[1]), |
| static_cast<deUint64>(maxComputeWorkGroupCount[2]) * static_cast<deUint64>(maxComputeWorkGroupSize[2]) }; |
| const auto maxRayDispatchInvocationCount = de::min( static_cast<deUint64>(rtPropertiesPtr->getMaxRayDispatchInvocationCount() ), |
| static_cast<deUint64>(1ULL << 29) ); |
| |
| m_gridSizeXYZ[0] = de::max( 1u, |
| static_cast<deUint32>((maxRayDispatchInvocationCount) % maxGlobalRTWorkGroupSize[0]) |