blob: caab0362cf6ac9c455dfaf16c450a5280d9f72cb [file] [log] [blame]
/*------------------------------------------------------------------------
* 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])