blob: b43ea252af602947a52bdd566fef108af7682149 [file] [log] [blame]
/*-------------------------------------------------------------------------
* Vulkan Conformance Tests
* ------------------------
*
* Copyright (c) 2021 The Khronos Group Inc.
* Copyright (c) 2021 Valve Corporation.
*
* 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 Direction Tests
*//*--------------------------------------------------------------------*/
#include "vktRayTracingDirectionTests.hpp"
#include "vktTestCase.hpp"
#include "vkObjUtil.hpp"
#include "vkCmdUtil.hpp"
#include "vkTypeUtil.hpp"
#include "vkBuilderUtil.hpp"
#include "vkRayTracingUtil.hpp"
#include "vkBufferWithMemory.hpp"
#include "vkBarrierUtil.hpp"
#include "tcuVector.hpp"
#include "tcuMatrix.hpp"
#include "deUniquePtr.hpp"
#include "deRandom.hpp"
#include "deStringUtil.hpp"
#include "deDefs.hpp"
#include <vector>
#include <cmath>
#include <sstream>
#include <utility>
#include <algorithm>
#include <limits>
namespace vkt
{
namespace RayTracing
{
namespace
{
using namespace vk;
using GeometryData = std::vector<tcu::Vec3>;
// Should rays be shot from inside the geometry or not?
enum class RayOriginType
{
OUTSIDE = 0, // Works with AABBs and triangles.
INSIDE, // Works with AABBs only.
};
// When rays are shot from the outside, they are expected to cross the geometry.
// When shot from the inside, they can end inside, at the edge or outside the geometry.
enum class RayEndType
{
CROSS = 0, // For RayOriginType::OUTSIDE.
ZERO, // For RayOriginType::INSIDE.
INSIDE, // For RayOriginType::INSIDE.
EDGE, // For RayOriginType::INSIDE.
OUTSIDE, // For RayOriginType::INSIDE.
};
struct SpaceObjects
{
tcu::Vec3 origin;
tcu::Vec3 direction;
GeometryData geometry;
SpaceObjects (RayOriginType rayOriginType, VkGeometryTypeKHR geometryType)
: origin (0.0f, 0.0f, 1.0f) // Origin of the ray at (0, 0, 1).
, direction (0.0f, 0.0f, 1.0f) // Shooting towards (0, 0, 1).
, geometry ()
{
DE_ASSERT(geometryType == VK_GEOMETRY_TYPE_TRIANGLES_KHR || geometryType == VK_GEOMETRY_TYPE_AABBS_KHR);
DE_ASSERT(rayOriginType == RayOriginType::OUTSIDE || geometryType == VK_GEOMETRY_TYPE_AABBS_KHR);
if (geometryType == VK_GEOMETRY_TYPE_TRIANGLES_KHR)
{
// Triangle around (0, 0, 5).
geometry.reserve(3u);
geometry.push_back(tcu::Vec3( 0.0f, 0.5f, 5.0f));
geometry.push_back(tcu::Vec3(-0.5f, -0.5f, 5.0f));
geometry.push_back(tcu::Vec3( 0.5f, -0.5f, 5.0f));
}
else
{
// AABB around (0, 0, 5) or with its back side at that distance when shot from the inside.
geometry.reserve(2u);
geometry.push_back(tcu::Vec3(-0.5f, -0.5f, ((rayOriginType == RayOriginType::INSIDE) ? 0.0f : 5.0f)));
geometry.push_back(tcu::Vec3( 0.5f, 0.5f, 5.0f));
}
}
static float getDefaultDistance (void)
{
// Consistent with the Z coordinates of the origin, direction and points in constructors.
return 4.0f;
}
// Calculates distance to geometry edge given the direction scaling factor.
static float getDistanceToEdge (float directionScale)
{
return getDefaultDistance() / directionScale;
}
};
// Default test tolerance for distance values.
constexpr float kDefaultTolerance = 0.001f;
// Calculates appropriate values for Tmin/Tmax given the distance to the geometry edge.
std::pair<float, float> calcTminTmax (RayOriginType rayOriginType, RayEndType rayEndType, float distanceToEdge)
{
std::pair<float, float> result;
if (rayOriginType == RayOriginType::OUTSIDE)
{
DE_ASSERT(rayEndType == RayEndType::CROSS);
const auto margin = kDefaultTolerance / 2.0f;
result = std::make_pair(de::max(distanceToEdge - margin, 0.0f), distanceToEdge + margin);
}
else
{
result.first = 0.0f;
switch (rayEndType)
{
case RayEndType::ZERO: result.second = 0.0f; break;
case RayEndType::INSIDE: result.second = distanceToEdge / 2.0f; break;
case RayEndType::EDGE: result.second = distanceToEdge; break;
case RayEndType::OUTSIDE: result.second = distanceToEdge + 1.0f; break;
default: DE_ASSERT(false); break;
}
}
return result;
}
// Get matrix to scale a point with the given scale factor.
tcu::Mat3 getScaleMatrix (float scaleFactor)
{
const float scaleDirectionMatrixData[] =
{
scaleFactor, 0.f, 0.f,
0.f, scaleFactor, 0.f,
0.f, 0.f, scaleFactor,
};
return tcu::Mat3(scaleDirectionMatrixData);
}
// Get a matrix to rotate a point around the X and Y axis by the given angles in radians.
tcu::Mat3 getRotationMatrix (float rotationX, float rotationY)
{
const float cosA = std::cos(rotationX);
const float sinA = std::sin(rotationX);
const float cosB = std::cos(rotationY);
const float sinB = std::sin(rotationY);
const float rotationMatrixDataX[] =
{
1.0f, 0.0f, 0.0f,
0.0f, cosA,-sinA,
0.0f, sinA, cosA,
};
const tcu::Mat3 rotationMatrixX (rotationMatrixDataX);
const float rotationMatrixDataY[] =
{
cosB, 0.0f,-sinB,
0.0f, 1.0f, 0.0f,
sinB, 0.0f, cosB,
};
const tcu::Mat3 rotationMatrixY (rotationMatrixDataY);
return rotationMatrixX * rotationMatrixY;
}
// Converts transformation matrix to the expected KHR format.
VkTransformMatrixKHR toTransformMatrixKHR (const tcu::Mat3& mat3)
{
VkTransformMatrixKHR result;
deMemset(result.matrix, 0, sizeof(result.matrix));
for (int y = 0; y < 3; ++y)
for (int x = 0; x < 3; ++x)
result.matrix[x][y] = mat3[x][y];
return result;
}
struct TestParams
{
SpaceObjects spaceObjects;
float directionScale;
float rotationX;
float rotationY;
VkShaderStageFlagBits testStage;
VkGeometryTypeKHR geometryType;
bool useArraysOfPointers;
bool updateMatrixAfterBuild;
RayOriginType rayOriginType;
RayEndType rayEndtype;
VkShaderStageFlags usedStages (void) const
{
VkShaderStageFlags flags = (VK_SHADER_STAGE_RAYGEN_BIT_KHR | VK_SHADER_STAGE_MISS_BIT_KHR | testStage);
if (geometryType == VK_GEOMETRY_TYPE_AABBS_KHR)
flags |= VK_SHADER_STAGE_INTERSECTION_BIT_KHR;
return flags;
}
// True if we are testing the intersection shader.
bool isecMain (void) const
{
return (testStage == VK_SHADER_STAGE_INTERSECTION_BIT_KHR);
}
// True if the intersection shader is needed as an auxiliar shader.
bool isecAux (void) const
{
return (!isecMain() && geometryType == VK_GEOMETRY_TYPE_AABBS_KHR);
}
// True if the intersection shader is used in some capacity.
bool isecUsed (void) const
{
return (isecMain() || isecAux());
}
};
class DirectionTestCase : public vkt::TestCase
{
public:
DirectionTestCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TestParams& params);
virtual ~DirectionTestCase (void) {}
virtual void checkSupport (Context& context) const;
virtual void initPrograms (vk::SourceCollections& programCollection) const;
virtual TestInstance* createInstance (Context& context) const;
protected:
TestParams m_params;
};
class DirectionTestInstance : public vkt::TestInstance
{
public:
DirectionTestInstance (Context& context, const TestParams& params);
virtual ~DirectionTestInstance (void) {}
virtual tcu::TestStatus iterate (void);
protected:
TestParams m_params;
};
DirectionTestCase::DirectionTestCase(tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TestParams& params)
: vkt::TestCase (testCtx, name, description)
, m_params (params)
{}
void DirectionTestCase::checkSupport (Context& context) const
{
context.requireDeviceFunctionality("VK_KHR_acceleration_structure");
context.requireDeviceFunctionality("VK_KHR_ray_tracing_pipeline");
}
// Push constants. They need to match the shaders.
// Note: origin and direction will be used as a Vec3. Declaring them as Vec4 eases matching alignments.
struct PushConstants
{
tcu::Vec4 origin;
tcu::Vec4 direction;
float tmix;
float tmax;
};
tcu::Vec4 toVec4 (const tcu::Vec3& vec3)
{
return tcu::Vec4(vec3.x(), vec3.y(), vec3.z(), 0.0f);
}
void DirectionTestCase::initPrograms (vk::SourceCollections& programCollection) const
{
const vk::ShaderBuildOptions buildOptions (programCollection.usedVulkanVersion, vk::SPIRV_VERSION_1_4, 0u, true);
std::ostringstream rgen;
rgen
<< "#version 460 core\n"
<< "#extension GL_EXT_ray_tracing : require\n"
<< "layout(location=0) rayPayloadEXT vec3 hitValue;\n"
<< "layout(set=0, binding=0) uniform accelerationStructureEXT topLevelAS;\n"
// Needs to match the PushConstants struct above.
<< "layout(push_constant, std430) uniform PushConstants {\n"
<< " vec4 origin;\n"
<< " vec4 direction;\n"
<< " float tmin;\n"
<< " float tmax;\n"
<< "} pc;\n"
<< "\n"
<< "void main()\n"
<< "{\n"
<< " const uint cullMask = 0xFF;\n"
<< " traceRayEXT(topLevelAS, gl_RayFlagsNoneEXT, cullMask, 0, 0, 0, pc.origin.xyz, pc.tmin, pc.direction.xyz, pc.tmax, 0);\n"
<< "}\n"
;
programCollection.glslSources.add("rgen") << glu::RaygenSource(updateRayTracingGLSL(rgen.str())) << buildOptions;
const bool isecTest = m_params.isecMain();
const std::string bufferDecl = "layout(set=0, binding=1, std430) buffer OutBuffer { float val; } outBuffer;\n";
std::ostringstream isec;
isec
<< "#version 460 core\n"
<< "#extension GL_EXT_ray_tracing : require\n"
<< "hitAttributeEXT vec3 hitAttribute;\n"
<< (isecTest ? bufferDecl : "")
<< "void main()\n"
<< "{\n"
<< " hitAttribute = vec3(0.0f, 0.0f, 0.0f);\n"
<< (isecTest ? " outBuffer.val = gl_RayTminEXT;\n" : "")
<< " reportIntersectionEXT(gl_RayTminEXT, 0);\n"
<< "}\n"
;
std::ostringstream hits;
hits
<< "#version 460 core\n"
<< "#extension GL_EXT_ray_tracing : require\n"
<< "layout(location=0) rayPayloadInEXT vec3 hitValue;\n"
<< "hitAttributeEXT vec3 attribs;\n"
<< bufferDecl
<< "\n"
<< "void main()\n"
<< "{\n"
<< " outBuffer.val = gl_HitTEXT;\n"
<< "}\n"
;
switch (m_params.testStage)
{
case VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR:
programCollection.glslSources.add("hits") << glu::ClosestHitSource(updateRayTracingGLSL(hits.str())) << buildOptions;
break;
case VK_SHADER_STAGE_ANY_HIT_BIT_KHR:
programCollection.glslSources.add("hits") << glu::AnyHitSource(updateRayTracingGLSL(hits.str())) << buildOptions;
break;
case VK_SHADER_STAGE_INTERSECTION_BIT_KHR:
programCollection.glslSources.add("isec") << glu::IntersectionSource(updateRayTracingGLSL(isec.str())) << buildOptions;
break;
default:
DE_ASSERT(false);
break;
}
// Also add the intersection shader if needed for AABBs.
if (m_params.isecAux())
programCollection.glslSources.add("isec") << glu::IntersectionSource(updateRayTracingGLSL(isec.str())) << buildOptions;
std::ostringstream miss;
miss
<< "#version 460 core\n"
<< "#extension GL_EXT_ray_tracing : require\n"
<< "layout(location = 0) rayPayloadInEXT vec3 hitValue;\n"
<< bufferDecl
<< "\n"
<< "void main()\n"
<< "{\n"
<< " outBuffer.val = -10000.0f;\n"
<< "}\n";
;
programCollection.glslSources.add("miss") << glu::MissSource(updateRayTracingGLSL(miss.str())) << buildOptions;
}
TestInstance* DirectionTestCase::createInstance (Context& context) const
{
return new DirectionTestInstance(context, m_params);
}
DirectionTestInstance::DirectionTestInstance (Context& context, const TestParams& params)
: vkt::TestInstance (context)
, m_params (params)
{}
tcu::TestStatus DirectionTestInstance::iterate (void)
{
const auto& vki = m_context.getInstanceInterface();
const auto physDev = m_context.getPhysicalDevice();
const auto& vkd = m_context.getDeviceInterface();
const auto device = m_context.getDevice();
auto& alloc = m_context.getDefaultAllocator();
const auto qIndex = m_context.getUniversalQueueFamilyIndex();
const auto queue = m_context.getUniversalQueue();
const auto stages = m_params.usedStages();
const auto pcSize = static_cast<deUint32>(sizeof(PushConstants));
const auto scaleMatrix = getScaleMatrix(m_params.directionScale);
const auto rotationMatrix = getRotationMatrix(m_params.rotationX, m_params.rotationY);
const auto transformMatrix = toTransformMatrixKHR(rotationMatrix);
// Command pool and buffer.
const auto cmdPool = makeCommandPool(vkd, device, qIndex);
const auto cmdBufferPtr = allocateCommandBuffer(vkd, device, cmdPool.get(), VK_COMMAND_BUFFER_LEVEL_PRIMARY);
const auto cmdBuffer = cmdBufferPtr.get();
beginCommandBuffer(vkd, cmdBuffer);
// Build acceleration structures.
auto topLevelAS = makeTopLevelAccelerationStructure();
auto bottomLevelAS = makeBottomLevelAccelerationStructure();
const bool isTriangles = (m_params.geometryType == VK_GEOMETRY_TYPE_TRIANGLES_KHR);
const VkGeometryInstanceFlagsKHR instanceFlags = (isTriangles ? VK_GEOMETRY_INSTANCE_TRIANGLE_FACING_CULL_DISABLE_BIT_KHR : 0);
bottomLevelAS->addGeometry(m_params.spaceObjects.geometry, isTriangles, VK_GEOMETRY_NO_DUPLICATE_ANY_HIT_INVOCATION_BIT_KHR);
bottomLevelAS->createAndBuild(vkd, device, cmdBuffer, alloc);
de::SharedPtr<BottomLevelAccelerationStructure> blasSharedPtr (bottomLevelAS.release());
topLevelAS->setUseArrayOfPointers(m_params.useArraysOfPointers);
topLevelAS->setUsePPGeometries(m_params.useArraysOfPointers);
topLevelAS->setInstanceCount(1);
{
const auto& initialMatrix = (m_params.updateMatrixAfterBuild ? identityMatrix3x4 : transformMatrix);
topLevelAS->addInstance(blasSharedPtr, initialMatrix, 0, 0xFFu, 0u, instanceFlags);
}
topLevelAS->createAndBuild(vkd, device, cmdBuffer, alloc);
if (m_params.updateMatrixAfterBuild)
topLevelAS->updateInstanceMatrix(vkd, device, 0u, transformMatrix);
// Create output buffer.
const auto bufferSize = static_cast<VkDeviceSize>(sizeof(float));
const auto bufferCreateInfo = makeBufferCreateInfo(bufferSize, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT);
BufferWithMemory buffer (vkd, device, alloc, bufferCreateInfo, MemoryRequirement::HostVisible);
auto& bufferAlloc = buffer.getAllocation();
// Fill output buffer with an initial value.
deMemset(bufferAlloc.getHostPtr(), 0, sizeof(float));
flushAlloc(vkd, device, bufferAlloc);
// Descriptor set layout and pipeline layout.
DescriptorSetLayoutBuilder setLayoutBuilder;
setLayoutBuilder.addSingleBinding(VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, stages);
setLayoutBuilder.addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, stages);
const auto setLayout = setLayoutBuilder.build(vkd, device);
const VkPushConstantRange pcRange =
{
stages, // VkShaderStageFlags stageFlags;
0u, // deUint32 offset;
pcSize, // deUint32 size;
};
const VkPipelineLayoutCreateInfo pipelineLayoutInfo =
{
VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, // VkStructureType sType;
nullptr, // const void* pNext;
0u, // VkPipelineLayoutCreateFlags flags;
1u, // deUint32 setLayoutCount;
&setLayout.get(), // const VkDescriptorSetLayout* pSetLayouts;
1u, // deUint32 pushConstantRangeCount;
&pcRange, // const VkPushConstantRange* pPushConstantRanges;
};
const auto pipelineLayout = createPipelineLayout(vkd, device, &pipelineLayoutInfo);
// Descriptor pool and set.
DescriptorPoolBuilder poolBuilder;
poolBuilder.addType(VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR);
poolBuilder.addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1u);
const auto descriptorPool = poolBuilder.build(vkd, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u);
const auto descriptorSet = makeDescriptorSet(vkd, device, descriptorPool.get(), setLayout.get());
// Update descriptor set.
{
const VkWriteDescriptorSetAccelerationStructureKHR accelDescInfo =
{
VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_ACCELERATION_STRUCTURE_KHR,
nullptr,
1u,
topLevelAS.get()->getPtr(),
};
const auto bufferDescInfo = makeDescriptorBufferInfo(buffer.get(), 0ull, VK_WHOLE_SIZE);
DescriptorSetUpdateBuilder updateBuilder;
updateBuilder.writeSingle(descriptorSet.get(), DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, &accelDescInfo);
updateBuilder.writeSingle(descriptorSet.get(), DescriptorSetUpdateBuilder::Location::binding(1u), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &bufferDescInfo);
updateBuilder.update(vkd, device);
}
// Shader modules.
Move<VkShaderModule> rgenModule;
Move<VkShaderModule> missModule;
Move<VkShaderModule> hitsModule;
Move<VkShaderModule> isecModule;
rgenModule = createShaderModule(vkd, device, m_context.getBinaryCollection().get("rgen"), 0);
missModule = createShaderModule(vkd, device, m_context.getBinaryCollection().get("miss"), 0);
if (!m_params.isecMain())
hitsModule = createShaderModule(vkd, device, m_context.getBinaryCollection().get("hits"), 0);
if (m_params.isecUsed())
isecModule = createShaderModule(vkd, device, m_context.getBinaryCollection().get("isec"), 0);
// Get some ray tracing properties.
deUint32 shaderGroupHandleSize = 0u;
deUint32 shaderGroupBaseAlignment = 1u;
{
const auto rayTracingPropertiesKHR = makeRayTracingProperties(vki, physDev);
shaderGroupHandleSize = rayTracingPropertiesKHR->getShaderGroupHandleSize();
shaderGroupBaseAlignment = rayTracingPropertiesKHR->getShaderGroupBaseAlignment();
}
// Create raytracing pipeline and shader binding tables.
Move<VkPipeline> pipeline;
de::MovePtr<BufferWithMemory> raygenSBT;
de::MovePtr<BufferWithMemory> missSBT;
de::MovePtr<BufferWithMemory> hitSBT;
de::MovePtr<BufferWithMemory> callableSBT;
VkStridedDeviceAddressRegionKHR raygenSBTRegion = makeStridedDeviceAddressRegionKHR(DE_NULL, 0, 0);
VkStridedDeviceAddressRegionKHR missSBTRegion = makeStridedDeviceAddressRegionKHR(DE_NULL, 0, 0);
VkStridedDeviceAddressRegionKHR hitSBTRegion = makeStridedDeviceAddressRegionKHR(DE_NULL, 0, 0);
VkStridedDeviceAddressRegionKHR callableSBTRegion = makeStridedDeviceAddressRegionKHR(DE_NULL, 0, 0);
{
const auto hitModuleCount = (m_params.isecAux() ? 2 : 1);
const auto rayTracingPipeline = de::newMovePtr<RayTracingPipeline>();
rayTracingPipeline->addShader(VK_SHADER_STAGE_RAYGEN_BIT_KHR, rgenModule, 0);
rayTracingPipeline->addShader(VK_SHADER_STAGE_MISS_BIT_KHR, missModule, 1);
if (!m_params.isecMain())
rayTracingPipeline->addShader(m_params.testStage, hitsModule, 2);
if (m_params.isecUsed())
rayTracingPipeline->addShader(VK_SHADER_STAGE_INTERSECTION_BIT_KHR, isecModule, 2);
pipeline = rayTracingPipeline->createPipeline(vkd, device, pipelineLayout.get());
raygenSBT = rayTracingPipeline->createShaderBindingTable(vkd, device, pipeline.get(), alloc, shaderGroupHandleSize, shaderGroupBaseAlignment, 0, 1);
raygenSBTRegion = makeStridedDeviceAddressRegionKHR(getBufferDeviceAddress(vkd, device, raygenSBT->get(), 0), shaderGroupHandleSize, shaderGroupHandleSize);
missSBT = rayTracingPipeline->createShaderBindingTable(vkd, device, pipeline.get(), alloc, shaderGroupHandleSize, shaderGroupBaseAlignment, 1, 1);
missSBTRegion = makeStridedDeviceAddressRegionKHR(getBufferDeviceAddress(vkd, device, missSBT->get(), 0), shaderGroupHandleSize, shaderGroupHandleSize);
hitSBT = rayTracingPipeline->createShaderBindingTable(vkd, device, pipeline.get(), alloc, shaderGroupHandleSize, shaderGroupBaseAlignment, 2, 1);
hitSBTRegion = makeStridedDeviceAddressRegionKHR(getBufferDeviceAddress(vkd, device, hitSBT->get(), 0), shaderGroupHandleSize, shaderGroupHandleSize * hitModuleCount);
}
// Push constants.
const auto rotatedOrigin = m_params.spaceObjects.origin * rotationMatrix;
const auto finalDirection = m_params.spaceObjects.direction * scaleMatrix * rotationMatrix;
const auto distanceToEdge = SpaceObjects::getDistanceToEdge(m_params.directionScale);
const auto tMinMax = calcTminTmax(m_params.rayOriginType, m_params.rayEndtype, distanceToEdge);
const PushConstants pcData =
{
toVec4(rotatedOrigin), // tcu::Vec4 origin;
toVec4(finalDirection), // tcu::Vec4 direction;
tMinMax.first, // float tmix;
tMinMax.second, // float tmax;
};
// Trace rays.
vkd.cmdBindPipeline(cmdBuffer, VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, pipeline.get());
vkd.cmdBindDescriptorSets(cmdBuffer, VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, pipelineLayout.get(), 0u, 1u, &descriptorSet.get(), 0u, nullptr);
vkd.cmdPushConstants(cmdBuffer, pipelineLayout.get(), stages, 0u, pcSize, &pcData);
vkd.cmdTraceRaysKHR(cmdBuffer, &raygenSBTRegion, &missSBTRegion, &hitSBTRegion, &callableSBTRegion, 1u, 1u, 1u);
// Barrier for the output buffer.
const auto bufferBarrier = makeMemoryBarrier(VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT);
vkd.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR, VK_PIPELINE_STAGE_HOST_BIT, 0u, 1u, &bufferBarrier, 0u, nullptr, 0u, nullptr);
endCommandBuffer(vkd, cmdBuffer);
submitCommandsAndWait(vkd, device, queue, cmdBuffer);
// Read value back from the buffer.
float bufferValue = 0.0f;
invalidateAlloc(vkd, device, bufferAlloc);
deMemcpy(&bufferValue, bufferAlloc.getHostPtr(), sizeof(bufferValue));
if (m_params.rayEndtype == RayEndType::CROSS)
{
// Shooting from the ouside.
if (de::abs(bufferValue - distanceToEdge) > kDefaultTolerance)
{
std::ostringstream msg;
msg << "Result distance (" << bufferValue << ") differs from expected distance (" << distanceToEdge << ", tolerance " << kDefaultTolerance << ")";
TCU_FAIL(msg.str());
}
}
else
{
// Rays are shot from inside AABBs, rayTMin should be zero and the reported hit distance.
if (bufferValue != 0.0f)
{
std::ostringstream msg;
msg << "Result distance nonzero (" << bufferValue << ")";
TCU_FAIL(msg.str());
}
}
return tcu::TestStatus::pass("Pass");
}
using GroupPtr = de::MovePtr<tcu::TestCaseGroup>;
// Generate a list of scaling factors suitable for the tests.
std::vector<float> generateScalingFactors (de::Random& rnd)
{
const float kMinScalingFactor = 0.5f;
const float kMaxScalingFactor = 10.0f;
const int kNumRandomScalingFactors = 5;
// Scaling factors: 1.0 and some randomly-generated ones.
std::vector<float> scalingFactors;
scalingFactors.reserve(kNumRandomScalingFactors + 1);
scalingFactors.push_back(1.0f);
for (int i = 0; i < kNumRandomScalingFactors; ++i)
scalingFactors.push_back(rnd.getFloat() * (kMaxScalingFactor - kMinScalingFactor) + kMinScalingFactor);
return scalingFactors;
}
// Generate a list of rotation angles suitable for the tests.
std::vector<std::pair<float, float>> generateRotationAngles (de::Random& rnd)
{
const float kPi2 = DE_PI * 2.0f;
const int kNumRandomRotations = 4;
// Rotations: 0.0 on both axis and some randomly-generated ones.
std::vector<std::pair<float, float>> rotationAngles;
rotationAngles.reserve(kNumRandomRotations + 1);
rotationAngles.push_back(std::make_pair(0.0f, 0.0f));
for (int i = 0; i < kNumRandomRotations; ++i)
rotationAngles.push_back(std::make_pair(rnd.getFloat() * kPi2, rnd.getFloat() * kPi2));
return rotationAngles;
}
} // anonymous
tcu::TestCaseGroup* createDirectionLengthTests(tcu::TestContext& testCtx)
{
GroupPtr directionGroup (new tcu::TestCaseGroup(testCtx, "direction_length", "Test direction vector length when tracing rays"));
struct
{
VkShaderStageFlagBits hitStage;
const char* name;
} stages[] =
{
{ VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR, "chit" },
{ VK_SHADER_STAGE_ANY_HIT_BIT_KHR, "ahit" },
{ VK_SHADER_STAGE_INTERSECTION_BIT_KHR, "isec" },
};
struct
{
VkGeometryTypeKHR geometryType;
const char* name;
} geometryTypes[] =
{
{ VK_GEOMETRY_TYPE_TRIANGLES_KHR, "triangles" },
{ VK_GEOMETRY_TYPE_AABBS_KHR, "aabbs" },
};
de::Random rnd(1613648516u);
deUint32 caseCounter = 0u;
// Scaling factors and rotation angles.
const auto scalingFactors = generateScalingFactors(rnd);
const auto rotationAngles = generateRotationAngles(rnd);
for (int stageIdx = 0; stageIdx < DE_LENGTH_OF_ARRAY(stages); ++stageIdx)
{
const auto& stageData = stages[stageIdx];
GroupPtr stageGroup (new tcu::TestCaseGroup(testCtx, stageData.name, ""));
for (int geometryTypeIdx = 0; geometryTypeIdx < DE_LENGTH_OF_ARRAY(geometryTypes); ++geometryTypeIdx)
{
const auto& gType = geometryTypes[geometryTypeIdx];
// We cannot test triangles with the ray intersection stage.
if (gType.geometryType == VK_GEOMETRY_TYPE_TRIANGLES_KHR && stageData.hitStage == VK_SHADER_STAGE_INTERSECTION_BIT_KHR)
continue;
GroupPtr geomGroup (new tcu::TestCaseGroup(testCtx, gType.name, ""));
for (size_t scalingIdx = 0; scalingIdx < scalingFactors.size(); ++scalingIdx)
{
const auto scale = scalingFactors[scalingIdx];
const auto scaleName = "scaling_factor_" + de::toString(scalingIdx);
GroupPtr factorGroup (new tcu::TestCaseGroup(testCtx, scaleName.c_str(), ""));
for (size_t rotationIdx = 0; rotationIdx < rotationAngles.size(); ++rotationIdx)
{
const auto angles = rotationAngles[rotationIdx];
const auto angleName = "rotation_" + de::toString(rotationIdx);
const auto geometryType = gType.geometryType;
const auto rayOrigType = RayOriginType::OUTSIDE;
const auto rayEndType = RayEndType::CROSS;
SpaceObjects spaceObjects(rayOrigType, geometryType);
TestParams params =
{
spaceObjects, // SpaceObjects spaceObjects;
scale, // float directionScale;
angles.first, // float rotationX;
angles.second, // float rotationY;
stageData.hitStage, // VkShaderStageFlagBits hitStage;
geometryType, // VkGeometryTypeKHR geometryType;
// Use arrays of pointers when building the TLAS in every other test.
(caseCounter % 2u == 0u), // bool useArraysOfPointers;
// Sometimes, update matrix after building the lop level AS and before submitting the command buffer.
(caseCounter % 3u == 0u), // bool updateMatrixAfterBuild;
rayOrigType, // RayOriginType rayOriginType;
rayEndType, // RayEndType rayEndType;
};
++caseCounter;
factorGroup->addChild(new DirectionTestCase(testCtx, angleName, "", params));
}
geomGroup->addChild(factorGroup.release());
}
stageGroup->addChild(geomGroup.release());
}
directionGroup->addChild(stageGroup.release());
}
return directionGroup.release();
}
tcu::TestCaseGroup* createInsideAABBsTests(tcu::TestContext& testCtx)
{
GroupPtr insideAABBsGroup (new tcu::TestCaseGroup(testCtx, "inside_aabbs", "Test shooting rays that start inside AABBs"));
struct
{
VkShaderStageFlagBits hitStage;
const char* name;
} stages[] =
{
{ VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR, "chit" },
{ VK_SHADER_STAGE_ANY_HIT_BIT_KHR, "ahit" },
{ VK_SHADER_STAGE_INTERSECTION_BIT_KHR, "isec" },
};
struct
{
RayEndType rayEndType;
const char* name;
} rayEndCases[] =
{
{ RayEndType::ZERO, "tmax_zero" },
{ RayEndType::INSIDE, "inside" },
{ RayEndType::EDGE, "edge" },
{ RayEndType::OUTSIDE, "outside" },
};
de::Random rnd(1621936010u);
// Scaling factors and rotation angles.
const auto scalingFactors = generateScalingFactors(rnd);
const auto rotationAngles = generateRotationAngles(rnd);
for (int stageIdx = 0; stageIdx < DE_LENGTH_OF_ARRAY(stages); ++stageIdx)
{
const auto& stageData = stages[stageIdx];
GroupPtr stageGroup (new tcu::TestCaseGroup(testCtx, stageData.name, ""));
for (int rayEndCaseIdx = 0; rayEndCaseIdx < DE_LENGTH_OF_ARRAY(rayEndCases); ++rayEndCaseIdx)
{
const auto& rayEndCase = rayEndCases[rayEndCaseIdx];
const std::string rayEndName = std::string("ray_end_") + rayEndCase.name;
GroupPtr rayEndGroup (new tcu::TestCaseGroup(testCtx, rayEndName.c_str(), ""));
for (size_t scalingIdx = 0; scalingIdx < scalingFactors.size(); ++scalingIdx)
{
const auto scale = scalingFactors[scalingIdx];
const auto scaleName = "scaling_factor_" + de::toString(scalingIdx);
GroupPtr factorGroup (new tcu::TestCaseGroup(testCtx, scaleName.c_str(), ""));
for (size_t rotationIdx = 0; rotationIdx < rotationAngles.size(); ++rotationIdx)
{
const auto angles = rotationAngles[rotationIdx];
const auto angleName = "rotation_" + de::toString(rotationIdx);
const auto geometryType = VK_GEOMETRY_TYPE_AABBS_KHR;
const auto rayOrigType = RayOriginType::INSIDE;
SpaceObjects spaceObjects(rayOrigType, geometryType);
TestParams params =
{
spaceObjects, // SpaceObjects spaceObjects;
scale, // float directionScale;
angles.first, // float rotationX;
angles.second, // float rotationY;
stageData.hitStage, // VkShaderStageFlagBits hitStage;
geometryType, // VkGeometryTypeKHR geometryType;
false, // bool useArraysOfPointers;
false, // bool updateMatrixAfterBuild;
rayOrigType, // RayOriginType rayOriginType;
rayEndCase.rayEndType, // RayEndType rayEndType;
};
factorGroup->addChild(new DirectionTestCase(testCtx, angleName, "", params));
}
rayEndGroup->addChild(factorGroup.release());
}
stageGroup->addChild(rayEndGroup.release());
}
insideAABBsGroup->addChild(stageGroup.release());
}
return insideAABBsGroup.release();
}
} // RayTracing
} // vkt