blob: 7a29a7f07e5158fc5cb0af2e6733912a8cec346c [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 Pipeline Flags tests
*//*--------------------------------------------------------------------*/
#include "vktRayTracingPipelineFlagsTests.hpp"
#include "vkDefs.hpp"
#include "vktTestCase.hpp"
#include "vktTestGroupUtil.hpp"
#include "vktCustomInstancesDevices.hpp"
#include "vkCmdUtil.hpp"
#include "vkObjUtil.hpp"
#include "vkBuilderUtil.hpp"
#include "vkBarrierUtil.hpp"
#include "vkBufferWithMemory.hpp"
#include "vkImageWithMemory.hpp"
#include "vkTypeUtil.hpp"
#include "vkImageUtil.hpp"
#include "vkRayTracingUtil.hpp"
#include "tcuCommandLine.hpp"
#include "tcuTextureUtil.hpp"
#include "tcuStringTemplate.hpp"
#include <algorithm>
#include <array>
#include <cmath>
#include <functional>
#include <iterator>
#include <memory>
#include <set>
#include <tuple>
#ifdef INTERNAL_DEBUG
#include <iostream>
#endif
#define 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)
namespace vkt
{
namespace RayTracing
{
namespace
{
using namespace vk;
using namespace vkt;
#define ALIGN_STD430(type_) alignas(sizeof(type_)) type_
#define INRANGE(x_, a_, b_) (((x_) >= (a_) && (x_) <= (b_)) || ((x_) >= (b_) && (x_) <= (a_)))
enum class GeometryTypes : deUint32
{
None = 0x0,
Triangle = 0x1,
Box = 0x2,
TriangleAndBox = Triangle | Box
};
struct TestParams
{
deUint32 width;
deUint32 height;
VkBool32 onHhost;
VkPipelineCreateFlags flags;
bool useLibs;
deUint32 instCount;
GeometryTypes geomTypes;
deUint32 geomCount;
deUint32 stbRecStride;
deUint32 stbRecOffset;
float accuracy;
#define ENABLED(val_, mask_) (((val_)&(mask_))==(mask_))
bool miss() const { return ENABLED(flags, VK_PIPELINE_CREATE_RAY_TRACING_NO_NULL_MISS_SHADERS_BIT_KHR); }
bool ahit() const { return ENABLED(flags, VK_PIPELINE_CREATE_RAY_TRACING_NO_NULL_ANY_HIT_SHADERS_BIT_KHR); }
bool chit() const { return ENABLED(flags, VK_PIPELINE_CREATE_RAY_TRACING_NO_NULL_CLOSEST_HIT_SHADERS_BIT_KHR); }
bool isect() const { return ENABLED(flags, VK_PIPELINE_CREATE_RAY_TRACING_NO_NULL_INTERSECTION_SHADERS_BIT_KHR); }
};
tcu::Vec3 rotateCcwZ (const tcu::Vec3& p, const tcu::Vec3& center, const float& radians)
{
const float s = std::sin(radians);
const float c = std::cos(radians);
const auto t = p - center;
return tcu::Vec3(c * t.x() - s * t.y(), s * t.x() + c * t.y(), t.z()) + center;
}
bool pointInRect2D(const tcu::Vec3& p, const tcu::Vec3& p0, const tcu::Vec3& p1)
{
return INRANGE(p.x(), p0.x(), p1.x()) && INRANGE(p.y(), p0.y(), p1.y());
}
deUint32 computeEffectiveShaderGroupCount(const TestParams& p)
{
DE_ASSERT(p.instCount && p.geomCount);
return (p.geomCount * p.stbRecStride + p.stbRecOffset + 1);
}
class RayTracingTestPipeline;
class PipelineFlagsCase : public TestCase
{
public:
PipelineFlagsCase (tcu::TestContext& testCtx,
const std::string& name,
const TestParams& testParams);
virtual ~PipelineFlagsCase (void) = default;
virtual void initPrograms (SourceCollections& programCollection) const override;
virtual TestInstance* createInstance (Context& context) const override;
virtual void checkSupport (Context& context) const override;
const deUint32& shaderGroupHandleSize;
const deUint32& shaderGroupBaseAlignment;
private:
const TestParams m_params;
const tcu::IVec4 m_rgenPayload;
const deInt32 m_defMissRetGreenComp;
const deInt32 m_defTriRetGreenComp;
const deInt32 m_defBoxRetGreenComp;
static deInt32 calcDefBoxRetGreenComp (const TestParams& params,
deInt32 defTriRetGreenComp);
static deUint32 m_shaderGroupHandleSize;
static deUint32 m_shaderGroupBaseAlignment;
};
deInt32 PipelineFlagsCase::calcDefBoxRetGreenComp (const TestParams& params, deInt32 defTriRetGreenComp)
{
const deUint32 nameCount = params.stbRecStride ? (params.geomCount * params.instCount) : params.instCount;
const deUint32 triangleCount = (params.geomTypes == GeometryTypes::Triangle || params.geomTypes == GeometryTypes::TriangleAndBox) ? nameCount : 0u;
return defTriRetGreenComp + std::max(triangleCount, 32u);
}
deUint32 PipelineFlagsCase::m_shaderGroupHandleSize;
deUint32 PipelineFlagsCase::m_shaderGroupBaseAlignment;
class PipelineFlagsInstance : public TestInstance
{
using TopLevelASPtr = de::SharedPtr<TopLevelAccelerationStructure>;
using BottomLevelASPtr = de::SharedPtr<BottomLevelAccelerationStructure>;
using BottomLevelASPtrs = std::vector<BottomLevelASPtr>;
using TriGeometry = std::array<tcu::Vec3, 3>;
using BoxGeometry = std::array<tcu::Vec3, 2>;
friend class RayTracingTestPipeline;
public:
PipelineFlagsInstance (Context& context,
const TestParams& params,
const deUint32& shaderGroupHandleSize_,
const deUint32& shaderGroupBaseAlignment_,
const tcu::IVec4& rgenPayload_,
deInt32 defMissRetGreenComp_,
deInt32 defTriRetGreenComp_,
deInt32 defBoxRetGreenComp_);
virtual ~PipelineFlagsInstance (void) = default;
struct ShaderRecordEXT
{
ALIGN_STD430(GeometryTypes) geomType;
ALIGN_STD430(deUint32) geomIndex;
ALIGN_STD430(tcu::IVec4) retValue;
ShaderRecordEXT ();
ShaderRecordEXT (GeometryTypes type, deUint32 index, const tcu::IVec4& ret);
};
virtual tcu::TestStatus iterate (void) override;
const tcu::IVec4 rgenPayload;
const deInt32 defMissRetGreenComp;
const deInt32 defTriRetGreenComp;
const deInt32 defBoxRetGreenComp;
const deUint32 shaderGroupHandleSize;
const deUint32 shaderGroupBaseAlignment;
private:
struct HitGroup;
using ShaderRecordEntry = std::tuple<VkShaderStageFlags, HitGroup, ShaderRecordEXT, bool /* initalized */>;
VkImageCreateInfo makeImageCreateInfo () const;
std::vector<TriGeometry> prepareTriGeometries (const float zCoord) const;
std::vector<BoxGeometry> prepareBoxGeometries (const float zFront,
const float zBack) const;
std::vector<ShaderRecordEntry> prepareShaderBindingTable (void) const;
BottomLevelASPtrs createBottomLevelAccelerationStructs (VkCommandBuffer cmdBuffer) const;
TopLevelASPtr createTopLevelAccelerationStruct (VkCommandBuffer cmdBuffer,
const BottomLevelASPtrs& blasPtrs) const;
bool verifyResult (const BufferWithMemory* resultBuffer) const;
#ifdef INTERNAL_DEBUG
void printImage (const tcu::IVec4* image) const;
#endif
template<class rayPayloadEXT, class shaderRecordEXT>
struct Shader
{
virtual bool ignoreIntersection (const rayPayloadEXT&, const shaderRecordEXT&) const { return false; }
virtual rayPayloadEXT invoke (const rayPayloadEXT&, const shaderRecordEXT&) const = 0;
};
struct ShaderBase : Shader<tcu::IVec4, ShaderRecordEXT>
{
typedef tcu::IVec4 rayPayloadEXT;
typedef ShaderRecordEXT shaderRecordEXT;
static const rayPayloadEXT dummyPayload;
virtual rayPayloadEXT invoke (const rayPayloadEXT&, const shaderRecordEXT&) const override {
return rayPayloadEXT();
}
};
struct ClosestHitShader : ShaderBase
{
virtual rayPayloadEXT invoke (const rayPayloadEXT& hitAttr, const shaderRecordEXT& rec) const override {
return (rec.geomType == GeometryTypes::Triangle) ? rec.retValue : hitAttr;
}
};
struct AnyHitShader : ShaderBase
{
virtual bool ignoreIntersection(const rayPayloadEXT&, const shaderRecordEXT& rec) const override {
return (rec.geomIndex % 2 == 1);
}
};
struct IntersectionShader : ShaderBase
{
virtual rayPayloadEXT invoke(const rayPayloadEXT&, const shaderRecordEXT& rec) const override {
return (rec.retValue + tcu::IVec4(0, 2, 3, 4));
}
};
struct MissShader : ShaderBase
{
virtual rayPayloadEXT invoke(const rayPayloadEXT&, const shaderRecordEXT& rec) const override {
return rec.retValue; }
};
struct HitGroup
{
de::SharedPtr<AnyHitShader> ahit;
de::SharedPtr<ClosestHitShader> chit;
de::SharedPtr<IntersectionShader> isect;
};
tcu::IVec2 rayToImage (const tcu::Vec2& rayCoords) const;
tcu::Vec2 imageToRay (const tcu::IVec2& imageCoords) const;
deUint32 computeSamePixelCount (const std::vector<tcu::IVec4>& image,
const tcu::Vec2 pixelCoords,
const tcu::IVec4& requiredColor,
const tcu::IVec4& floodColor,
const std::function<bool(const tcu::Vec3&)>& pointInGeometry,
std::vector<std::pair<tcu::IVec4, tcu::IVec2>> &auxBuffer) const;
void travelRay (std::vector<tcu::IVec4>& outImage,
const deUint32 glLaunchIdExtX,
const deUint32 glLaunchIdExtY,
const std::vector<ShaderRecordEntry>& shaderBindingTable,
const MissShader& missShader,
const std::vector<TriGeometry>& triangleGeometries,
const std::vector<BoxGeometry>& boxGeometries) const;
const TestParams m_params;
const VkFormat m_format;
};
PipelineFlagsInstance::ShaderBase::rayPayloadEXT const PipelineFlagsInstance::ShaderBase::dummyPayload{};
PipelineFlagsInstance::ShaderRecordEXT::ShaderRecordEXT ()
: geomType (GeometryTypes::None)
, geomIndex (~0u)
, retValue ()
{
}
PipelineFlagsInstance::ShaderRecordEXT::ShaderRecordEXT (GeometryTypes type, deUint32 index, const tcu::IVec4& ret)
: geomType(type)
, geomIndex(index)
, retValue(ret)
{
}
class RayTracingTestPipeline : protected RayTracingPipeline
{
public:
RayTracingTestPipeline (Context& context, const PipelineFlagsInstance& testInstance, const TestParams& params)
: m_context (context)
, m_vkd (context.getDeviceInterface())
, m_device (context.getDevice())
, m_allocator (context.getDefaultAllocator())
, m_testInstance (testInstance)
, m_params (params)
{
m_rgenModule = createShaderModule(m_vkd, m_device, m_context.getBinaryCollection().get("rgen"), 0);
// miss shader is loaded into each test regardless m_params.miss() is set
m_missModule = createShaderModule(m_vkd, m_device, m_context.getBinaryCollection().get("miss"), 0);
// cloest hit shader is loaded into each test regardless m_params.chit() is set
m_chitModule = createShaderModule(m_vkd, m_device, m_context.getBinaryCollection().get("chit"), 0);
if (m_params.ahit())
m_ahitModule = createShaderModule(m_vkd, m_device, m_context.getBinaryCollection().get("ahit"), 0);
if (m_params.isect() || (m_params.geomTypes == GeometryTypes::Box) || (m_params.geomTypes == GeometryTypes::TriangleAndBox))
m_isectModule = createShaderModule(m_vkd, m_device, m_context.getBinaryCollection().get("isect"), 0);
setCreateFlags(m_params.flags);
setMaxPayloadSize(sizeof(PipelineFlagsInstance::ShaderRecordEXT::retValue));
setMaxAttributeSize(sizeof(PipelineFlagsInstance::ShaderRecordEXT::retValue));
}
template<class ShaderRecord> struct SBT
{
static const deUint32 recordSize = static_cast<deUint32>(sizeof(ShaderRecord));
const DeviceInterface& m_vkd;
const VkDevice m_dev;
const VkPipeline m_pipeline;
const deUint32 m_groupCount;
const deUint32 m_handleSize;
const deUint32 m_alignment;
de::MovePtr<BufferWithMemory> m_buffer;
deUint8* m_content;
SBT (const DeviceInterface& vkd, VkDevice dev, Allocator& allocator, VkPipeline pipeline,
deUint32 groupCount, deUint32 shaderGroupHandleSize, deUint32 shaderGroupBaseAlignment)
: m_vkd(vkd), m_dev(dev), m_pipeline(pipeline)
, m_groupCount(groupCount), m_handleSize(shaderGroupHandleSize)
, m_alignment(deAlign32(shaderGroupHandleSize + recordSize, shaderGroupBaseAlignment))
, m_buffer(), m_content()
{
const deUint32 size = groupCount * m_alignment;
const VkBufferUsageFlags flags = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_SHADER_BINDING_TABLE_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT;
VkBufferCreateInfo info = makeBufferCreateInfo(size, flags);
const MemoryRequirement memReq = MemoryRequirement::HostVisible | MemoryRequirement::Coherent | MemoryRequirement::DeviceAddress;
m_buffer = de::MovePtr<BufferWithMemory>(new BufferWithMemory(m_vkd, m_dev, allocator, info, memReq));
m_content = (deUint8*)m_buffer->getAllocation().getHostPtr();
}
void updateAt (deUint32 index, const deUint8* handle, const ShaderRecord& rec)
{
DE_ASSERT(index < m_groupCount);
deUint8* groupPos = m_content + index * m_alignment;
deMemcpy(groupPos, handle, m_handleSize);
deMemcpy(groupPos + m_handleSize, &rec, recordSize);
}
void flush ()
{
Allocation& alloc = m_buffer->getAllocation();
flushMappedMemoryRange(m_vkd, m_dev, alloc.getMemory(), alloc.getOffset(), VK_WHOLE_SIZE);
}
deUint32 getAlignment () const { return m_alignment; }
de::MovePtr<BufferWithMemory> get () { return m_buffer; }
};
struct PLDeleter
{
const RayTracingTestPipeline* pipeline;
std::function<void(RayTracingPipeline*)> whenDestroying;
PLDeleter (RayTracingTestPipeline* pl, std::function<void(RayTracingPipeline*)> doWhenDestroying)
: pipeline(pl), whenDestroying(doWhenDestroying) {}
void operator()(RayTracingPipeline* pl)
{
if (pipeline != pl && pipeline->m_params.useLibs)
{
if (whenDestroying) whenDestroying(pl);
delete pl;
}
}
};
auto createLibraryPipeline (std::function<void(RayTracingPipeline*)> doWhenDestroying)
-> de::UniquePtr<RayTracingPipeline, PLDeleter>
{
RayTracingPipeline* pl = this;
if (m_params.useLibs) {
pl = new RayTracingPipeline;
pl->setCreateFlags(m_params.flags | VK_PIPELINE_CREATE_LIBRARY_BIT_KHR);
pl->setMaxPayloadSize(sizeof(PipelineFlagsInstance::ShaderRecordEXT::retValue));
pl->setMaxAttributeSize(sizeof(PipelineFlagsInstance::ShaderRecordEXT::retValue));
}
return de::UniquePtr<RayTracingPipeline, PLDeleter>(pl, PLDeleter(this, doWhenDestroying));
}
Move<VkPipeline> createPipeline (const VkPipelineLayout pipelineLayout)
{
deUint32 groupIndex = 0;
const bool checkIsect = (m_params.geomTypes == GeometryTypes::Box) || (m_params.geomTypes == GeometryTypes::TriangleAndBox);
auto appendPipelineLibrary = [this, &pipelineLayout](RayTracingPipeline* pl) -> void
{
m_libraries.emplace_back(makeVkSharedPtr(pl->createPipeline(m_vkd, m_device, pipelineLayout)));
};
DE_ASSERT( (VkShaderModule(0) != *m_rgenModule));
DE_ASSERT( (VkShaderModule(0) != *m_missModule));
DE_ASSERT(m_params.ahit() == (VkShaderModule(0) != *m_ahitModule));
DE_ASSERT( (VkShaderModule(0) != *m_chitModule));
DE_ASSERT(checkIsect == (VkShaderModule(0) != *m_isectModule));
// rgen in the main pipeline only
addShader(VK_SHADER_STAGE_RAYGEN_BIT_KHR, *m_rgenModule, groupIndex++);
createLibraryPipeline(appendPipelineLibrary)->addShader(VK_SHADER_STAGE_MISS_BIT_KHR, *m_missModule, (m_params.useLibs ? 0 : groupIndex++));
{
const deUint32 hitGroupIndex = m_params.useLibs ? 0 : groupIndex;
auto pipeline = createLibraryPipeline(appendPipelineLibrary);
if (m_params.ahit()) pipeline->addShader(VK_SHADER_STAGE_ANY_HIT_BIT_KHR, *m_ahitModule, hitGroupIndex);
pipeline->addShader(VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR, *m_chitModule, hitGroupIndex);
if (checkIsect) pipeline->addShader(VK_SHADER_STAGE_INTERSECTION_BIT_KHR, *m_isectModule, hitGroupIndex);
}
for (const auto& sg : m_shadersGroupCreateInfos)
{
static_cast<void>(sg);
DE_ASSERT(sg.type != VK_RAY_TRACING_SHADER_GROUP_TYPE_MAX_ENUM_KHR);
}
return RayTracingPipeline::createPipeline(m_vkd, m_device, pipelineLayout, m_libraries);
}
std::pair<de::SharedPtr<BufferWithMemory>, VkStridedDeviceAddressRegionKHR> createRaygenShaderBindingTable (VkPipeline pipeline)
{
de::MovePtr<BufferWithMemory> sbt = createShaderBindingTable(m_vkd, m_device, pipeline, m_allocator,
m_testInstance.shaderGroupHandleSize,
m_testInstance.shaderGroupBaseAlignment, 0, 1);
VkStridedDeviceAddressRegionKHR rgn = makeStridedDeviceAddressRegionKHR(getBufferDeviceAddress(m_vkd, m_device, **sbt, 0),
m_testInstance.shaderGroupHandleSize,
m_testInstance.shaderGroupHandleSize);
return { de::SharedPtr<BufferWithMemory>(sbt.release()), rgn };
}
std::pair<de::SharedPtr<BufferWithMemory>, VkStridedDeviceAddressRegionKHR> createMissShaderBindingTable (VkPipeline pipeline)
{
const auto entries = m_testInstance.prepareShaderBindingTable();
const void* shaderRecPtr = static_cast<PipelineFlagsInstance::ShaderRecordEXT const*>(&std::get<2>(entries[1]));
const deUint32 shaderRecSize = static_cast<deUint32>(sizeof(PipelineFlagsInstance::ShaderRecordEXT));
const deUint32 alignment = deAlign32(m_testInstance.shaderGroupHandleSize + shaderRecSize, m_testInstance.shaderGroupBaseAlignment);
const deUint32 sbtOffset = 0;
de::MovePtr<BufferWithMemory> sbt = createShaderBindingTable(m_vkd, m_device, pipeline, m_allocator,
m_testInstance.shaderGroupHandleSize,
m_testInstance.shaderGroupBaseAlignment,
1, 1,
VkBufferCreateFlags(0u),
VkBufferUsageFlags(0u),
MemoryRequirement::Any,
VkDeviceAddress(0),
sbtOffset,
shaderRecSize,
&shaderRecPtr);
VkStridedDeviceAddressRegionKHR rgn = makeStridedDeviceAddressRegionKHR(getBufferDeviceAddress(m_vkd, m_device, **sbt, 0),
alignment,
m_testInstance.shaderGroupHandleSize);
return { de::SharedPtr<BufferWithMemory>(sbt.release()), rgn };
}
std::pair<de::SharedPtr<BufferWithMemory>, VkStridedDeviceAddressRegionKHR> createHitShaderBindingTable (VkPipeline pipeline)
{
de::MovePtr<BufferWithMemory> buf;
VkStridedDeviceAddressRegionKHR rgn;
std::vector<deUint8> handles (m_testInstance.shaderGroupHandleSize);
auto records = m_testInstance.prepareShaderBindingTable();
const deUint32 hitGroupCount = deUint32(records.size() - 2);
SBT<PipelineFlagsInstance::ShaderRecordEXT> sbt(m_vkd, m_device, m_allocator, pipeline, hitGroupCount,
m_testInstance.shaderGroupHandleSize, m_testInstance.shaderGroupBaseAlignment);
VK_CHECK(m_vkd.getRayTracingShaderGroupHandlesKHR(m_device, pipeline, 2, 1, handles.size(), handles.data()));
for (deUint32 i = 0; i < hitGroupCount; ++i)
{
// copy the SBT record if it was initialized in prepareShaderBindingTable()
if (std::get<3>(records[i + 2]))
{
const PipelineFlagsInstance::ShaderRecordEXT& rec = std::get<2>(records[i + 2]);
sbt.updateAt(i, handles.data(), rec);
}
}
sbt.flush();
buf = sbt.get();
rgn = makeStridedDeviceAddressRegionKHR(getBufferDeviceAddress(m_vkd, m_device, **buf, 0),
sbt.getAlignment(), m_testInstance.shaderGroupHandleSize);
return { de::SharedPtr<BufferWithMemory>(buf.release()), rgn };
}
private:
Context& m_context;
const DeviceInterface& m_vkd;
const VkDevice m_device;
Allocator& m_allocator;
const PipelineFlagsInstance& m_testInstance;
const TestParams m_params;
Move<VkShaderModule> m_rgenModule;
Move<VkShaderModule> m_chitModule;
Move<VkShaderModule> m_ahitModule;
Move<VkShaderModule> m_isectModule;
Move<VkShaderModule> m_missModule;
Move<VkShaderModule> m_gapModule;
std::vector<de::SharedPtr<Move<VkPipeline>>> m_libraries;
};
template<class T, class P = T(*)[1], class R = decltype(std::begin(*std::declval<P>()))>
auto makeStdBeginEnd(T* p, deUint32 n) -> std::pair<R, R>
{
auto tmp = std::begin(*P(p));
auto begin = tmp;
std::advance(tmp, n);
return { begin, tmp };
}
PipelineFlagsCase::PipelineFlagsCase (tcu::TestContext& testCtx, const std::string& name, const TestParams& params)
: TestCase (testCtx, name, std::string())
, shaderGroupHandleSize (m_shaderGroupHandleSize)
, shaderGroupBaseAlignment (m_shaderGroupBaseAlignment)
, m_params (params)
, m_rgenPayload (0, ':', 0, 0)
, m_defMissRetGreenComp ('-')
, m_defTriRetGreenComp ('A')
, m_defBoxRetGreenComp (calcDefBoxRetGreenComp(params, m_defTriRetGreenComp))
{
}
void PipelineFlagsCase::checkSupport (Context& context) const
{
if ((VK_PIPELINE_CREATE_RAY_TRACING_NO_NULL_INTERSECTION_SHADERS_BIT_KHR & m_params.flags)
&& (GeometryTypes::Triangle == m_params.geomTypes))
{
TCU_THROW(InternalError, "Illegal params combination: VK_PIPELINE_CREATE_RAY_TRACING_NO_NULL_INTERSECTION_SHADERS_BIT_KHR and Triangles");
}
if (!context.isDeviceFunctionalitySupported("VK_KHR_ray_tracing_pipeline"))
TCU_THROW(NotSupportedError, "VK_KHR_ray_tracing_pipeline not supported");
// VK_KHR_acceleration_structure is required by VK_KHR_ray_tracing_pipeline.
if (!context.isDeviceFunctionalitySupported("VK_KHR_acceleration_structure"))
TCU_FAIL("VK_KHR_acceleration_structure not supported but VK_KHR_ray_tracing_pipeline supported");
// The same for VK_KHR_buffer_device_address.
if (!context.isDeviceFunctionalitySupported("VK_KHR_buffer_device_address"))
TCU_FAIL("VK_KHR_buffer_device_address not supported but VK_KHR_acceleration_structure supported");
if (m_params.useLibs && !context.isDeviceFunctionalitySupported("VK_KHR_pipeline_library"))
TCU_FAIL("VK_KHR_pipeline_library not supported but VK_KHR_ray_tracing_pipeline supported");
const VkPhysicalDeviceRayTracingPipelineFeaturesKHR& rayTracingPipelineFeaturesKHR = context.getRayTracingPipelineFeatures();
if (rayTracingPipelineFeaturesKHR.rayTracingPipeline == DE_FALSE)
TCU_THROW(NotSupportedError, "Requires VkPhysicalDeviceRayTracingPipelineFeaturesKHR.rayTracingPipeline");
const VkPhysicalDeviceAccelerationStructureFeaturesKHR& accelerationStructureFeaturesKHR = context.getAccelerationStructureFeatures();
if (accelerationStructureFeaturesKHR.accelerationStructure == DE_FALSE)
TCU_THROW(TestError, "VK_KHR_ray_tracing_pipeline requires VkPhysicalDeviceAccelerationStructureFeaturesKHR.accelerationStructure");
if (m_params.onHhost && accelerationStructureFeaturesKHR.accelerationStructureHostCommands == DE_FALSE)
TCU_THROW(NotSupportedError, "Requires VkPhysicalDeviceAccelerationStructureFeaturesKHR.accelerationStructureHostCommands");
checkAccelerationStructureVertexBufferFormat(context.getInstanceInterface(), context.getPhysicalDevice(), VK_FORMAT_R32G32B32_SFLOAT);
auto rayTracingProperties = makeRayTracingProperties(context.getInstanceInterface(), context.getPhysicalDevice());
m_shaderGroupHandleSize = rayTracingProperties->getShaderGroupHandleSize();
m_shaderGroupBaseAlignment = rayTracingProperties->getShaderGroupBaseAlignment();
}
void PipelineFlagsCase::initPrograms (SourceCollections& programCollection) const
{
const vk::ShaderBuildOptions buildOptions (programCollection.usedVulkanVersion, vk::SPIRV_VERSION_1_4, 0u, true);
const char endl = '\n';
const deUint32 missIdx = 0;
const std::string payloadInDecl =
"layout(location = 0) rayPayloadInEXT ivec4 payload;";
const std::string recordDecl =
"layout(shaderRecordEXT, std430) buffer Rec {\n"
" uint geomType;\n"
" uint geomIndex;\n"
" ivec4 retValue;\n"
"} record;";
{
std::stringstream str;
str << "#version 460 core" << endl
<< "#extension GL_EXT_ray_tracing : require" << endl
<< "layout(location = 0) rayPayloadEXT ivec4 payload;" << endl
<< "layout(r32i, set = 0, binding = 0) uniform iimage2D result;" << endl
<< "layout(set = 0, binding = 1) uniform accelerationStructureEXT topLevelAS;" << endl
<< "void main()" << endl
<< "{" << endl
<< " float rx = (float(gl_LaunchIDEXT.x * 2) / float(gl_LaunchSizeEXT.x)) - 1.0;" << endl
<< " float ry = (float(gl_LaunchIDEXT.y) + 0.5) / float(gl_LaunchSizeEXT.y);" << endl
<< " payload = ivec4" << m_rgenPayload << ";" << endl
<< " uint rayFlags = gl_RayFlagsNoneEXT;" << endl
<< " uint cullMask = 0xFFu;" << endl
<< " uint stbRecOffset = " << m_params.stbRecOffset << "u;" << endl
<< " uint stbRecStride = " << m_params.stbRecStride << "u;" << endl
<< " uint missIdx = " << missIdx << "u;" << endl
<< " vec3 orig = vec3(rx, ry, 1.0);" << endl
<< " float tmin = 0.0;" << endl
<< " vec3 dir = vec3(0.0, 0.0, -1.0);" << endl
<< " float tmax = 1000.0;" << endl
<< " traceRayEXT(topLevelAS, rayFlags, cullMask, stbRecOffset, stbRecStride, missIdx, orig, tmin, dir, tmax, 0);" << endl
<< " imageStore(result, ivec2(gl_LaunchIDEXT.xy), payload);" << endl
<< "}";
programCollection.glslSources.add("rgen") << glu::RaygenSource(str.str()) << buildOptions;
}
// miss shader is created in each test regardless the m_params.miss() is set
{
std::stringstream str;
str << "#version 460 core" << endl
<< "#extension GL_EXT_ray_tracing : require" << endl
<< payloadInDecl << endl
<< recordDecl << endl
<< "void main()" << endl
<< "{" << endl
<< " payload = record.retValue;" << endl
<< "}";
programCollection.glslSources.add("miss") << glu::MissSource(str.str()) << buildOptions;
}
// closest hit shader is created in each test regardless the m_params.chit() is set
{
std::stringstream str;
str << "#version 460 core" << endl
<< "#extension GL_EXT_ray_tracing : require" << endl
<< "hitAttributeEXT ivec4 hitAttribute;" << endl
<< payloadInDecl << endl
<< recordDecl << endl
<< "void main()" << endl
<< "{" << endl
<< " if (record.geomType == " << deUint32(GeometryTypes::Triangle) << ")" << endl
<< " payload = record.retValue;" << endl
<< " else payload = hitAttribute;" << endl
<< "}";
programCollection.glslSources.add("chit") << glu::ClosestHitSource(str.str()) << buildOptions;
}
if (m_params.ahit())
{
std::stringstream str;
str << "#version 460 core" << endl
<< "#extension GL_EXT_ray_tracing : require" << endl
<< recordDecl << endl
<< "void main()" << endl
<< "{" << endl
<< " if (record.geomIndex % 2 == 1)" << endl
<< " ignoreIntersectionEXT;" << endl
<< "}";
programCollection.glslSources.add("ahit") << glu::AnyHitSource(str.str()) << buildOptions;
}
if (m_params.isect() || (m_params.geomTypes == GeometryTypes::Box) || (m_params.geomTypes == GeometryTypes::TriangleAndBox))
{
std::stringstream str;
str << "#version 460 core" << endl
<< "#extension GL_EXT_ray_tracing : require" << endl
<< "hitAttributeEXT ivec4 hitAttribute;" << endl
<< recordDecl << endl
<< "void main()" << endl
<< "{" << endl
<< " hitAttribute = ivec4(record.retValue.x + 0" << endl
<< " ,record.retValue.y + 2" << endl
<< " ,record.retValue.z + 3" << endl
<< " ,record.retValue.w + 4);" << endl
<< " reportIntersectionEXT(0.0, 0);" << endl
<< "}";
programCollection.glslSources.add("isect") << glu::IntersectionSource(str.str()) << buildOptions;
}
}
TestInstance* PipelineFlagsCase::createInstance (Context& context) const
{
return new PipelineFlagsInstance(context, m_params, shaderGroupHandleSize, shaderGroupBaseAlignment,
m_rgenPayload, m_defMissRetGreenComp, m_defTriRetGreenComp, m_defBoxRetGreenComp);
}
PipelineFlagsInstance::PipelineFlagsInstance (Context& context,
const TestParams& params,
const deUint32& shaderGroupHandleSize_,
const deUint32& shaderGroupBaseAlignment_,
const tcu::IVec4& rgenPayload_,
deInt32 defMissRetGreenComp_,
deInt32 defTriRetGreenComp_,
deInt32 defBoxRetGreenComp_)
: TestInstance (context)
, rgenPayload (rgenPayload_)
, defMissRetGreenComp (defMissRetGreenComp_)
, defTriRetGreenComp (defTriRetGreenComp_)
, defBoxRetGreenComp (defBoxRetGreenComp_)
, shaderGroupHandleSize (shaderGroupHandleSize_)
, shaderGroupBaseAlignment (shaderGroupBaseAlignment_)
, m_params (params)
, m_format (VK_FORMAT_R32G32B32A32_SINT)
{
}
VkImageCreateInfo PipelineFlagsInstance::makeImageCreateInfo () const
{
const deUint32 familyIndex = m_context.getUniversalQueueFamilyIndex();
const VkImageCreateInfo imageCreateInfo =
{
VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
(VkImageCreateFlags)0u, // VkImageCreateFlags flags;
VK_IMAGE_TYPE_2D, // VkImageType imageType;
m_format, // VkFormat format;
makeExtent3D(m_params.width, m_params.height, 1u), // VkExtent3D extent;
1u, // deUint32 mipLevels;
1u, // deUint32 arrayLayers;
VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples;
VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling;
VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT, // VkImageUsageFlags usage;
VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
1u, // deUint32 queueFamilyIndexCount;
&familyIndex, // const deUint32* pQueueFamilyIndices;
VK_IMAGE_LAYOUT_UNDEFINED // VkImageLayout initialLayout;
};
return imageCreateInfo;
}
std::vector<PipelineFlagsInstance::TriGeometry>
PipelineFlagsInstance::prepareTriGeometries (const float zCoord) const
{
const tcu::Vec3 center(-0.5f, 0.5f, zCoord);
const tcu::Vec3 start(0.0f, 0.5f, zCoord);
const deUint32 maxTriangles = m_params.instCount * m_params.geomCount;
const deUint32 trianglesCount = deMaxu32(maxTriangles, 3);
const float angle = (4.0f * std::acos(0.0f)) / float(trianglesCount);
tcu::Vec3 point(start);
std::vector<TriGeometry> geometries(maxTriangles);
for (deUint32 inst = 0, idx = 0; inst < m_params.instCount; ++inst)
{
for (deUint32 geom = 0; geom < m_params.geomCount; ++geom, ++idx)
{
TriGeometry& geometry = geometries[idx];
geometry[0] = center;
geometry[1] = point;
geometry[2] = (maxTriangles >= 3 && trianglesCount - idx == 1u) ? start : rotateCcwZ(point, center, angle);
point = geometry[2];
}
}
return geometries;
}
std::vector<PipelineFlagsInstance::BoxGeometry>
PipelineFlagsInstance::prepareBoxGeometries (const float zFront, const float zBack) const
{
const deUint32 maxBoxes = m_params.instCount * m_params.geomCount;
std::vector<BoxGeometry> boxes (maxBoxes);
deUint32 boxesPerDim = 0u;
float boxWidth = 0.0f;
float boxHeight = 0.0f;
// find nearest square ceil number
do
{
++boxesPerDim;
boxWidth = 1.0f / float(boxesPerDim);
boxHeight = 1.0f / float(boxesPerDim);
} while (boxesPerDim * boxesPerDim < maxBoxes);
for (deUint32 boxY = 0, boxIdx = 0; boxY < boxesPerDim && boxIdx < maxBoxes; ++boxY)
{
for (deUint32 boxX = 0; boxX < boxesPerDim && boxIdx < maxBoxes; ++boxX, ++boxIdx)
{
const float x = float(boxX) * boxWidth;
const float y = float(boxY) * boxHeight;
BoxGeometry box = { { tcu::Vec3(x, y, zFront), tcu::Vec3((x + boxWidth), (y + boxHeight), zBack) } };
boxes[boxIdx].swap(box);
}
}
return boxes;
}
PipelineFlagsInstance::BottomLevelASPtrs
PipelineFlagsInstance::createBottomLevelAccelerationStructs (VkCommandBuffer cmdBuffer) const
{
const DeviceInterface& vkd = m_context.getDeviceInterface();
const VkDevice device = m_context.getDevice();
Allocator& allocator = m_context.getDefaultAllocator();
const VkGeometryFlagsKHR geomFlags = m_params.ahit() ? VK_GEOMETRY_NO_DUPLICATE_ANY_HIT_INVOCATION_BIT_KHR : VK_GEOMETRY_OPAQUE_BIT_KHR;
BottomLevelASPtrs result;
if (!m_params.isect() && ((m_params.geomTypes == GeometryTypes::Triangle) || (m_params.geomTypes == GeometryTypes::TriangleAndBox)))
{
const auto geometries = prepareTriGeometries(0.0f);
for (deUint32 inst = 0, idx = 0; inst < m_params.instCount; ++inst)
{
auto blas = makeBottomLevelAccelerationStructure();
blas->setBuildType(m_params.onHhost ? VK_ACCELERATION_STRUCTURE_BUILD_TYPE_HOST_KHR : VK_ACCELERATION_STRUCTURE_BUILD_TYPE_DEVICE_KHR);
for (deUint32 geom = 0; geom < m_params.geomCount; ++geom, ++idx)
{
const TriGeometry& triangle = geometries[idx];
blas->addGeometry(std::vector<tcu::Vec3>(triangle.begin(), triangle.end()), true, geomFlags);
}
blas->createAndBuild(vkd, device, cmdBuffer, allocator);
result.emplace_back(de::SharedPtr<BottomLevelAccelerationStructure>(blas.release()));
}
}
if (m_params.isect() || (m_params.geomTypes == GeometryTypes::Box) || (m_params.geomTypes == GeometryTypes::TriangleAndBox))
{
const auto geometries = prepareBoxGeometries(0.0f, 0.0f);
for (deUint32 inst = 0, idx = 0; inst < m_params.instCount; ++inst)
{
auto blas = makeBottomLevelAccelerationStructure();
blas->setBuildType(m_params.onHhost ? VK_ACCELERATION_STRUCTURE_BUILD_TYPE_HOST_KHR : VK_ACCELERATION_STRUCTURE_BUILD_TYPE_DEVICE_KHR);
for (deUint32 geom = 0; geom < m_params.geomCount; ++geom, ++idx)
{
const BoxGeometry& box = geometries[idx];
blas->addGeometry(std::vector<tcu::Vec3>(box.begin(), box.end()), false, geomFlags);
}
blas->createAndBuild(vkd, device, cmdBuffer, allocator);
result.emplace_back(de::SharedPtr<BottomLevelAccelerationStructure>(blas.release()));
}
}
return result;
}
PipelineFlagsInstance::TopLevelASPtr
PipelineFlagsInstance::createTopLevelAccelerationStruct (VkCommandBuffer cmdBuffer, const BottomLevelASPtrs& blasPtrs) const
{
const DeviceInterface& vkd = m_context.getDeviceInterface();
const VkDevice device = m_context.getDevice();
Allocator& allocator = m_context.getDefaultAllocator();
const deUint32 groupsAndGapsPerInstance = computeEffectiveShaderGroupCount(m_params);
auto tlas = makeTopLevelAccelerationStructure();
tlas->setBuildType(m_params.onHhost ? VK_ACCELERATION_STRUCTURE_BUILD_TYPE_HOST_KHR : VK_ACCELERATION_STRUCTURE_BUILD_TYPE_DEVICE_KHR);
tlas->setInstanceCount(blasPtrs.size());
for (auto begin = blasPtrs.begin(), end = blasPtrs.end(), i = begin; i != end; ++i)
{
const deUint32 instanceShaderBindingTableRecordOffset = static_cast<deUint32>(std::distance(begin, i) * groupsAndGapsPerInstance);
tlas->addInstance(*i, identityMatrix3x4, 0, 0xFF, instanceShaderBindingTableRecordOffset);
}
tlas->createAndBuild(vkd, device, cmdBuffer, allocator);
return TopLevelASPtr(tlas.release());
}
std::vector<PipelineFlagsInstance::ShaderRecordEntry> PipelineFlagsInstance::prepareShaderBindingTable() const
{
const bool includeTriangles = (!m_params.isect() && ((m_params.geomTypes == GeometryTypes::Triangle) || (m_params.geomTypes == GeometryTypes::TriangleAndBox)));
const bool includeBoxes = (m_params.isect() || (m_params.geomTypes == GeometryTypes::Box) || (m_params.geomTypes == GeometryTypes::TriangleAndBox));
const deUint32 groupsAndGapsPerInstance = computeEffectiveShaderGroupCount(m_params);
const deUint32 commonGroupCount = 2; // general groups for rgen and miss
const deUint32 triangleGroupCount = includeTriangles ? (groupsAndGapsPerInstance * m_params.instCount) : 0;
const deUint32 proceduralGroupCount = includeBoxes ? (groupsAndGapsPerInstance * m_params.instCount) : 0;
const deUint32 totalGroupCount = commonGroupCount + triangleGroupCount + proceduralGroupCount;
std::vector<ShaderRecordEntry> shaderRecords(totalGroupCount);
shaderRecords[0] = std::tuple<VkShaderStageFlags, HitGroup, ShaderRecordEXT, bool>( VK_SHADER_STAGE_RAYGEN_BIT_KHR, {}, {}, true );
shaderRecords[1] = std::tuple<VkShaderStageFlags, HitGroup, ShaderRecordEXT, bool>( VK_SHADER_STAGE_MISS_BIT_KHR, {}, { GeometryTypes::Box, (~0u), tcu::IVec4(0, defMissRetGreenComp, 0, 0) }, true );
de::SharedPtr<AnyHitShader> ahit(new AnyHitShader);
de::SharedPtr<ClosestHitShader> chit(new ClosestHitShader);
de::SharedPtr<IntersectionShader> isect(new IntersectionShader);
if (includeTriangles)
{
std::set<deUint32> usedIndexes;
deInt32 greenComp = defTriRetGreenComp;
const deUint32 recordsToSkip = commonGroupCount;
for (deUint32 instance = 0; instance < m_params.instCount; ++instance)
{
const deUint32 instanceShaderBindingTableRecordOffset = recordsToSkip + instance * groupsAndGapsPerInstance;
for (deUint32 geometryIndex = 0; geometryIndex < m_params.geomCount; ++geometryIndex)
{
const deUint32 shaderGroupIndex = instanceShaderBindingTableRecordOffset + geometryIndex * m_params.stbRecStride + m_params.stbRecOffset;
if (usedIndexes.find(shaderGroupIndex) == usedIndexes.end())
{
HitGroup hitGroup;
VkShaderStageFlags flags = 0;
if (m_params.ahit()) {
flags |= VK_SHADER_STAGE_ANY_HIT_BIT_KHR;
hitGroup.ahit = ahit;
}
flags |= VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR;
hitGroup.chit = chit;
shaderRecords[shaderGroupIndex] = std::tuple<VkShaderStageFlags, HitGroup, ShaderRecordEXT, bool>( flags, hitGroup, { GeometryTypes::Triangle, geometryIndex, tcu::IVec4(0, greenComp++, 0, 0) }, true );
usedIndexes.insert(shaderGroupIndex);
}
}
}
}
if (includeBoxes)
{
std::set<deUint32> usedIndexes;
deInt32 greenComp = defBoxRetGreenComp;
const deUint32 recordsToSkip = triangleGroupCount + commonGroupCount;
for (deUint32 instance = 0; instance < m_params.instCount; ++instance)
{
const deUint32 instanceShaderBindingTableRecordOffset = recordsToSkip + instance * groupsAndGapsPerInstance;
for (deUint32 geometryIndex = 0; geometryIndex < m_params.geomCount; ++geometryIndex)
{
const deUint32 shaderGroupIndex = instanceShaderBindingTableRecordOffset + geometryIndex * m_params.stbRecStride + m_params.stbRecOffset;
if (usedIndexes.find(shaderGroupIndex) == usedIndexes.end())
{
HitGroup hitGroup;
VkShaderStageFlags flags = 0;
if (m_params.ahit()) {
flags |= VK_SHADER_STAGE_ANY_HIT_BIT_KHR;
hitGroup.ahit = ahit;
}
flags |= VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR;
hitGroup.chit = chit;
{ //In the case of AABB isect must be provided, otherwise we will process AABB with TRIANGLES_HIT_GROUP
flags |= VK_SHADER_STAGE_INTERSECTION_BIT_KHR;
hitGroup.isect = isect;
}
shaderRecords[shaderGroupIndex] = std::tuple<VkShaderStageFlags, HitGroup, ShaderRecordEXT, bool>( flags, hitGroup, { GeometryTypes::Box, geometryIndex, tcu::IVec4(0, greenComp++, 0, 0) }, true );
usedIndexes.insert(shaderGroupIndex);
}
}
}
}
return shaderRecords;
}
tcu::IVec2 PipelineFlagsInstance::rayToImage(const tcu::Vec2& rayCoords) const
{
return tcu::IVec2(
deInt32(((rayCoords.x() + 1.0f) * float(m_params.width)) / 2.0f),
deInt32((rayCoords.y() * float(m_params.height)) - 0.5f)
);
}
tcu::Vec2 PipelineFlagsInstance::imageToRay(const tcu::IVec2& imageCoords) const
{
const float rx = (float(imageCoords.x() * 2) / float(m_params.width)) - 1.0f;
const float ry = (float(imageCoords.y()) + 0.5f) / float(m_params.height);
return tcu::Vec2(rx, ry);
}
deUint32 PipelineFlagsInstance::computeSamePixelCount (const std::vector<tcu::IVec4>& image,
const tcu::Vec2 pixelCoords,
const tcu::IVec4& requiredColor,
const tcu::IVec4& floodColor,
const std::function<bool(const tcu::Vec3&)>& pointInGeometry,
std::vector<std::pair<tcu::IVec4, tcu::IVec2>>& auxBuffer) const
{
if (!pointInGeometry(tcu::Vec3(pixelCoords.x(), pixelCoords.y(), 0.0f))) return 0;
auxBuffer.resize(image.size() * 4);
std::transform(image.begin(), image.end(), auxBuffer.begin(), [](const tcu::IVec4& c) -> std::pair<tcu::IVec4, tcu::IVec2> { return { c, tcu::IVec2() }; });
tcu::Vec2 rayCoord;
tcu::IVec2 imageCoords = rayToImage(pixelCoords);
deUint32 pixelIndex = imageCoords.y() * m_params.width + imageCoords.x();
tcu::IVec4 imageColor = image[pixelIndex];
if (requiredColor != imageColor) return 0;
deInt32 stackIndex = 0;
deUint32 sameCount = 1;
auxBuffer[stackIndex].second = imageCoords;
while (stackIndex >= 0)
{
imageCoords = auxBuffer[stackIndex].second; --stackIndex;
if (imageCoords.x() < 0 || imageCoords.x() >= deInt32(m_params.width)
|| imageCoords.y() < 0 || imageCoords.y() >= deInt32(m_params.height)) continue;
rayCoord = imageToRay(imageCoords);
if (!pointInGeometry(tcu::Vec3(rayCoord.x(), rayCoord.y(), 0.0f))) continue;
pixelIndex = imageCoords.y() * m_params.width + imageCoords.x();
imageColor = auxBuffer[pixelIndex].first;
if (requiredColor != imageColor) continue;
auxBuffer[pixelIndex].first = floodColor;
sameCount += 1;
auxBuffer[++stackIndex].second = tcu::IVec2(imageCoords.x() - 1, imageCoords.y());
auxBuffer[++stackIndex].second = tcu::IVec2(imageCoords.x() + 1, imageCoords.y());
auxBuffer[++stackIndex].second = tcu::IVec2(imageCoords.x(), imageCoords.y() - 1);
auxBuffer[++stackIndex].second = tcu::IVec2(imageCoords.x(), imageCoords.y() + 1);
}
return sameCount;
}
void PipelineFlagsInstance::travelRay (std::vector<tcu::IVec4>& outImage,
const deUint32 glLaunchIdExtX,
const deUint32 glLaunchIdExtY,
const std::vector<ShaderRecordEntry>& shaderBindingTable,
const MissShader& missShader,
const std::vector<TriGeometry>& triangleGeometries,
const std::vector<BoxGeometry>& boxGeometries) const
{
const tcu::Vec2 rayCoords = imageToRay(tcu::IVec2(glLaunchIdExtX, glLaunchIdExtY));
const bool includeTriangles = (!m_params.isect() && ((m_params.geomTypes == GeometryTypes::Triangle) || (m_params.geomTypes == GeometryTypes::TriangleAndBox)));
const bool includeBoxes = (m_params.isect() || (m_params.geomTypes == GeometryTypes::Box) || (m_params.geomTypes == GeometryTypes::TriangleAndBox));
const deUint32 commonGroupCount = 2; // general groups for rgen and miss
const deUint32 groupsAndGapsPerInstance = computeEffectiveShaderGroupCount(m_params);
const deUint32 triangleGroupCount = includeTriangles ? (groupsAndGapsPerInstance * m_params.instCount) : 0;
bool hitHappened (false);
deUint32 shaderGroupIndex (~0u);
tcu::IVec4 payload (rgenPayload);
const tcu::Vec3 origin (rayCoords.x(), rayCoords.y(), 1.0f);
if (includeTriangles)
{
const deUint32 recordsToSkip = commonGroupCount;
for (deUint32 instance = 0; !hitHappened && (instance < m_params.instCount); ++instance)
{
const deUint32 instanceShaderBindingTableRecordOffset = recordsToSkip + instance * groupsAndGapsPerInstance;
for (deUint32 geometryIndex = 0; !hitHappened && (geometryIndex < m_params.geomCount); ++geometryIndex)
{
const TriGeometry& geometry = triangleGeometries[instance * m_params.geomCount + geometryIndex];
shaderGroupIndex = instanceShaderBindingTableRecordOffset + geometryIndex * m_params.stbRecStride + m_params.stbRecOffset;
if (pointInTriangle2D(origin, geometry[0], geometry[1], geometry[2]))
{
hitHappened = true;
}
}
}
}
if (includeBoxes)
{
const deUint32 recordsToSkip = triangleGroupCount + commonGroupCount;
for (deUint32 instance = 0; !hitHappened && (instance < m_params.instCount); ++instance)
{
const deUint32 instanceShaderBindingTableRecordOffset = recordsToSkip + instance * groupsAndGapsPerInstance;
for (deUint32 geometryIndex = 0; !hitHappened && (geometryIndex < m_params.geomCount); ++geometryIndex)
{
const BoxGeometry& geometry = boxGeometries[instance * m_params.geomCount + geometryIndex];
shaderGroupIndex = instanceShaderBindingTableRecordOffset + geometryIndex * m_params.stbRecStride + m_params.stbRecOffset;
if (pointInRect2D(origin, geometry[0], geometry[1]))
{
hitHappened = true;
}
}
}
}
if (hitHappened)
{
const ShaderRecordEXT& shaderRecord = std::get<2>(shaderBindingTable[shaderGroupIndex]);
const HitGroup& hitGroup = std::get<1>(shaderBindingTable[shaderGroupIndex]);
const VkShaderStageFlags flags = std::get<0>(shaderBindingTable[shaderGroupIndex]);
auto hitAttribute = rgenPayload;
bool ignoreIsect = false;
// check if the SBT entry was was initialized
DE_ASSERT(std::get<3>(shaderBindingTable[shaderGroupIndex]));
if (flags & VK_SHADER_STAGE_INTERSECTION_BIT_KHR)
{
hitAttribute = hitGroup.isect->invoke(IntersectionShader::dummyPayload, shaderRecord);
}
if (flags & VK_SHADER_STAGE_ANY_HIT_BIT_KHR)
{
ignoreIsect = hitGroup.ahit->ignoreIntersection(AnyHitShader::dummyPayload, shaderRecord);
}
if (ignoreIsect)
{
payload = missShader.invoke(MissShader::dummyPayload, std::get<2>(shaderBindingTable[1]));
}
else if (flags & VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR)
{
payload = hitGroup.chit->invoke(hitAttribute, shaderRecord);
}
}
else
{
payload = missShader.invoke(MissShader::dummyPayload, std::get<2>(shaderBindingTable[1]));
}
outImage[glLaunchIdExtY * m_params.width + glLaunchIdExtX] = payload;
}
#ifdef INTERNAL_DEBUG
void PipelineFlagsInstance::printImage(const tcu::IVec4* image) const
{
for (deUint32 y = 0; y < m_params.height; ++y)
{
for (deUint32 x = 0; x < m_params.width; ++x)
std::cout << static_cast<char>(image[(m_params.height - y - 1) * m_params.width + x].y());
std::cout << std::endl;
}
}
#endif
bool PipelineFlagsInstance::verifyResult (const BufferWithMemory* resultBuffer) const
{
const auto triangleGeometries = prepareTriGeometries(0.0f);
const auto boxGeometries = prepareBoxGeometries(0.0f, 0.0f);
const bool includeTriangles = (!m_params.isect() && ((m_params.geomTypes == GeometryTypes::Triangle) || (m_params.geomTypes == GeometryTypes::TriangleAndBox)));
const bool includeBoxes = (m_params.isect() || (m_params.geomTypes == GeometryTypes::Box) || (m_params.geomTypes == GeometryTypes::TriangleAndBox));
const tcu::IVec4* resultImageData = (tcu::IVec4*)(resultBuffer->getAllocation().getHostPtr());
auto resultImageBounds = makeStdBeginEnd(resultImageData, (m_params.width * m_params.height));
const std::vector<tcu::IVec4> resultImage (resultImageBounds.first, resultImageBounds.second);
std::vector<tcu::IVec4> referenceImage (m_params.width * m_params.height);
const std::vector<ShaderRecordEntry> shaderBindingTable = prepareShaderBindingTable();
MissShader missShader{};
// perform offline ray-tracing
for (deUint32 glLaunchIdExtY = 0; glLaunchIdExtY < m_params.height; ++glLaunchIdExtY)
{
for (deUint32 glLaunchIdExtX = 0; glLaunchIdExtX < m_params.width; ++glLaunchIdExtX)
{
travelRay(referenceImage, glLaunchIdExtX, glLaunchIdExtY,
shaderBindingTable, missShader,
triangleGeometries, boxGeometries);
}
}
#ifdef INTERNAL_DEBUG
std::cout << "===== RES =====" << std::endl;
printImage(resultImageData);
std::cout << std::endl;
std::cout << "===== REF =====" << std::endl;
printImage(referenceImage.data());
std::cout << std::endl;
#endif
const tcu::IVec4 floodColor(0, '*', 0, 0);
std::vector<std::pair<tcu::IVec4, tcu::IVec2>> auxBuffer(referenceImage.size() * 4);
if (includeTriangles)
{
TriGeometry tri;
std::function<bool(const tcu::Vec3&)> pointInGeometry = std::bind(pointInTriangle2D, std::placeholders::_1, std::ref(tri[0]), std::ref(tri[1]), std::ref(tri[2]));
for (deUint32 instance = 0; instance < m_params.instCount; ++instance)
{
for (deUint32 geometryIndex = 0; geometryIndex < m_params.geomCount; ++geometryIndex)
{
if (!(m_params.ahit() && (geometryIndex % 2 == 1)))
{
tri = triangleGeometries[instance * m_params.geomCount + geometryIndex];
const tcu::Vec2 center((tri[0].x() + tri[1].x() + tri[2].x()) / 3.0f, (tri[0].y() + tri[1].y() + tri[2].y()) / 3.0f);
const tcu::IVec2 refImageCoords = rayToImage(center);
const tcu::IVec4 requiredColor = referenceImage[refImageCoords.y() * m_params.width + refImageCoords.x()];
deUint32 resultPixelCount = computeSamePixelCount(resultImage, center, requiredColor, floodColor, pointInGeometry, auxBuffer);
deUint32 referencePixelCount = computeSamePixelCount(referenceImage, center, requiredColor, floodColor, pointInGeometry, auxBuffer);
if (!resultPixelCount || !referencePixelCount) return false;
if (resultPixelCount > referencePixelCount) std::swap(resultPixelCount, referencePixelCount);
const float similarity = float(resultPixelCount) / float(referencePixelCount);
if (similarity < m_params.accuracy) return false;
}
}
}
}
if (includeBoxes)
{
BoxGeometry box;
std::function<bool(const tcu::Vec3&)> pointInGeometry = std::bind(pointInRect2D, std::placeholders::_1, std::ref(box[0]), std::ref(box[1]));
for (deUint32 instance = 0; instance < m_params.instCount; ++instance)
{
for (deUint32 geometryIndex = 0; geometryIndex < m_params.geomCount; ++geometryIndex)
{
if (!(m_params.ahit() && (geometryIndex % 2 == 1)))
{
box = boxGeometries[instance * m_params.geomCount + geometryIndex];
const tcu::Vec2 center((box[0].x() + box[1].x()) / 2.0f, (box[0].y() + box[1].y()) / 2.0f);
const tcu::IVec2 refImageCoords = rayToImage(center);
const tcu::IVec4 requiredColor = referenceImage[refImageCoords.y() * m_params.width + refImageCoords.x()];
deUint32 resultPixelCount = computeSamePixelCount(resultImage, center, requiredColor, floodColor, pointInGeometry, auxBuffer);
deUint32 referencePixelCount = computeSamePixelCount(referenceImage, center, requiredColor, floodColor, pointInGeometry, auxBuffer);
if (!resultPixelCount || !referencePixelCount) return false;
if (resultPixelCount > referencePixelCount) std::swap(resultPixelCount, referencePixelCount);
const float similarity = float(resultPixelCount) / float(referencePixelCount);
if (similarity < m_params.accuracy) return false;
}
}
}
}
return true;
}
tcu::TestStatus PipelineFlagsInstance::iterate(void)
{
const DeviceInterface& vkd = m_context.getDeviceInterface();
const VkDevice device = m_context.getDevice();
const deUint32 familyIndex = m_context.getUniversalQueueFamilyIndex();
const VkQueue queue = m_context.getUniversalQueue();
Allocator& allocator = m_context.getDefaultAllocator();
const VkImageCreateInfo imageCreateInfo = makeImageCreateInfo();
const VkImageSubresourceRange imageSubresourceRange = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0, 1u);
const de::MovePtr<ImageWithMemory> image = de::MovePtr<ImageWithMemory>(new ImageWithMemory(vkd, device, allocator, imageCreateInfo, MemoryRequirement::Any));
const Move<VkImageView> imageView = makeImageView(vkd, device, **image, VK_IMAGE_VIEW_TYPE_2D, m_format, imageSubresourceRange);
const VkDescriptorImageInfo descriptorImageInfo = makeDescriptorImageInfo(DE_NULL, *imageView, VK_IMAGE_LAYOUT_GENERAL);
const deUint32 resultBufferSize = (m_params.width * m_params.height * mapVkFormat(m_format).getPixelSize());
const VkBufferCreateInfo resultBufferCreateInfo = makeBufferCreateInfo(resultBufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT);
const VkImageSubresourceLayers resultBufferImageSubresourceLayers = makeImageSubresourceLayers(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, 1u);
const VkBufferImageCopy resultBufferImageRegion = makeBufferImageCopy(makeExtent3D(m_params.width, m_params.height, 1u), resultBufferImageSubresourceLayers);
de::MovePtr<BufferWithMemory> resultBuffer = de::MovePtr<BufferWithMemory>(new BufferWithMemory(vkd, device, allocator, resultBufferCreateInfo, MemoryRequirement::HostVisible));
const Move<VkDescriptorSetLayout> descriptorSetLayout = DescriptorSetLayoutBuilder()
.addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, ALL_RAY_TRACING_STAGES)
.addSingleBinding(VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, ALL_RAY_TRACING_STAGES)
.build(vkd, device);
const Move<VkDescriptorPool> descriptorPool = DescriptorPoolBuilder()
.addType(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE)
.addType(VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR)
.build(vkd, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u);
const Move<VkDescriptorSet> descriptorSet = makeDescriptorSet(vkd, device, *descriptorPool, *descriptorSetLayout);
de::MovePtr<RayTracingTestPipeline> rayTracingPipeline = de::newMovePtr<RayTracingTestPipeline>(std::ref(m_context), std::cref(*this), m_params);
const Move<VkPipelineLayout> pipelineLayout = makePipelineLayout(vkd, device, *descriptorSetLayout);
Move<VkPipeline> pipeline = rayTracingPipeline->createPipeline(*pipelineLayout);
de::SharedPtr<BufferWithMemory> raygenShaderBindingTable;
VkStridedDeviceAddressRegionKHR raygenShaderBindingTableRegion;
std::tie(raygenShaderBindingTable, raygenShaderBindingTableRegion) = rayTracingPipeline->createRaygenShaderBindingTable(*pipeline);
de::SharedPtr<BufferWithMemory> missShaderBindingTable;
VkStridedDeviceAddressRegionKHR missShaderBindingTableRegion;
std::tie(missShaderBindingTable, missShaderBindingTableRegion) = rayTracingPipeline->createMissShaderBindingTable(*pipeline);
de::SharedPtr<BufferWithMemory> hitShaderBindingTable;
VkStridedDeviceAddressRegionKHR hitShaderBindingTableRegion;
std::tie(hitShaderBindingTable, hitShaderBindingTableRegion) = rayTracingPipeline->createHitShaderBindingTable(*pipeline);
const VkStridedDeviceAddressRegionKHR callableShaderBindingTableRegion = makeStridedDeviceAddressRegionKHR(DE_NULL, 0, 0);
const Move<VkCommandPool> cmdPool = createCommandPool(vkd, device, 0, familyIndex);
const Move<VkCommandBuffer> cmdBuffer = allocateCommandBuffer(vkd, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY);
beginCommandBuffer(vkd, *cmdBuffer);
BottomLevelASPtrs blasPtrs = createBottomLevelAccelerationStructs(*cmdBuffer);
TopLevelASPtr tlasPtr = createTopLevelAccelerationStruct(*cmdBuffer, blasPtrs);
VkWriteDescriptorSetAccelerationStructureKHR accelerationStructureWriteDescriptorSet =
{
VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_ACCELERATION_STRUCTURE_KHR, // VkStructureType sType;
DE_NULL, // const void* pNext;
1u, // deUint32 accelerationStructureCount;
tlasPtr->getPtr() // const VkAccelerationStructureKHR* pAccelerationStructures;
};
DescriptorSetUpdateBuilder()
.writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, &descriptorImageInfo)
.writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(1u), VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, &accelerationStructureWriteDescriptorSet)
.update(vkd, device);
vkd.cmdBindDescriptorSets(*cmdBuffer, VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, *pipelineLayout, 0, 1, &descriptorSet.get(), 0, DE_NULL);
vkd.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, *pipeline);
cmdTraceRays(vkd,
*cmdBuffer,
&raygenShaderBindingTableRegion, // rgen
&missShaderBindingTableRegion, // miss
&hitShaderBindingTableRegion, // hit
&callableShaderBindingTableRegion, // call
m_params.width, m_params.height, 1);
const VkMemoryBarrier postTraceMemoryBarrier = makeMemoryBarrier(VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT);
const VkMemoryBarrier postCopyMemoryBarrier = makeMemoryBarrier(VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT);
cmdPipelineMemoryBarrier(vkd, *cmdBuffer, VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR, VK_PIPELINE_STAGE_TRANSFER_BIT, &postTraceMemoryBarrier);
vkd.cmdCopyImageToBuffer(*cmdBuffer, **image, VK_IMAGE_LAYOUT_GENERAL, **resultBuffer, 1u, &resultBufferImageRegion);
cmdPipelineMemoryBarrier(vkd, *cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, &postCopyMemoryBarrier);
endCommandBuffer(vkd, *cmdBuffer);
submitCommandsAndWait(vkd, device, queue, *cmdBuffer);
invalidateMappedMemoryRange(vkd, device, resultBuffer->getAllocation().getMemory(), resultBuffer->getAllocation().getOffset(), resultBufferSize);
return verifyResult(resultBuffer.get()) ? tcu::TestStatus::pass("") : tcu::TestStatus::fail("");
}
class NoNullShadersFlagGenerator
{
public:
using FlagsSet = std::set<VkPipelineCreateFlags>;
struct BitAndName {
VkPipelineCreateFlagBits bit;
const char* name;
};
static const BitAndName bits[4];
static std::string name (VkPipelineCreateFlags flags) {
int count = 0;
std::stringstream ss;
for (auto begin = std::begin(bits), end = std::end(bits), i = begin; i != end; ++i) {
if (flags & i->bit) {
if (count++) ss << "_or_";
ss << i->name;
}
}
return count ? ss.str() : "none";
}
static VkPipelineCreateFlags mask (const FlagsSet& flags) {
VkPipelineCreateFlags result{};
for (auto f : flags) result |= f;
return result;
}
NoNullShadersFlagGenerator() : m_next(0) {
FlagsSet fs;
for (const auto& b : bits) fs.insert(b.bit);
combine(m_combs, fs);
}
void reset() { m_next = 0; }
bool next(VkPipelineCreateFlags& flags) {
if (m_next < m_combs.size()) {
flags = mask(m_combs[m_next++]);
return true;
}
return false;
}
void combine(std::vector<FlagsSet>& result, const FlagsSet& v) {
if (v.empty() || result.end() != std::find(result.begin(), result.end(), v)) return;
result.push_back(v);
for (deUint32 i = 0; i < v.size(); ++i) {
FlagsSet w(v);
w.erase(std::next(w.begin(), i));
combine(result, w);
}
}
private:
size_t m_next;
std::vector<FlagsSet> m_combs;
};
const NoNullShadersFlagGenerator::BitAndName NoNullShadersFlagGenerator::bits[] = {
{ VK_PIPELINE_CREATE_RAY_TRACING_NO_NULL_ANY_HIT_SHADERS_BIT_KHR, "any" },
{ VK_PIPELINE_CREATE_RAY_TRACING_NO_NULL_CLOSEST_HIT_SHADERS_BIT_KHR, "chit" },
{ VK_PIPELINE_CREATE_RAY_TRACING_NO_NULL_INTERSECTION_SHADERS_BIT_KHR, "isect" },
{ VK_PIPELINE_CREATE_RAY_TRACING_NO_NULL_MISS_SHADERS_BIT_KHR, "miss" },
};
} // unnamed
tcu::TestCaseGroup* createPipelineFlagsTests (tcu::TestContext& testCtx)
{
const deUint32 strides[] = { 3, 5 };
const deUint32 offsets[] = { 7 };
struct {
bool type;
const char* name;
} const processors[] = { { false, "gpu" }, { true, "cpu" } };
struct {
bool type;
const char* name;
} const libs[]{ { true, "use_libs" }, { false, "no_libs" } };
struct {
GeometryTypes type;
const char* name;
} const geometries[] = { { GeometryTypes::Triangle, "triangles" }, { GeometryTypes::Box, "boxes" }, { GeometryTypes::TriangleAndBox, "tri_and_box" } };
NoNullShadersFlagGenerator flagsGenerator;
TestParams p;
#ifdef INTERNAL_DEBUG
p.width = 30;
p.height = 8;
p.accuracy = 0.80f;
#else
p.width = 256;
p.height = 256;
p.accuracy = 0.95f;
#endif
p.onHhost = false;
p.useLibs = false;
p.flags = 0;
p.geomTypes = GeometryTypes::None;
p.instCount = 3;
p.geomCount = 2;
p.stbRecStride = 0;
p.stbRecOffset = 0;
auto group = new tcu::TestCaseGroup(testCtx, "pipeline_no_null_shaders_flag", "Pipeline NO_NULL_*_SHADER flags tests");
for (auto& processor : processors)
{
auto processorGroup = new tcu::TestCaseGroup(testCtx, processor.name, "");
for (auto& geometry : geometries)
{
auto geometryGroup = new tcu::TestCaseGroup(testCtx, geometry.name, "");
for (auto& stride : strides)
{
auto strideGroup = new tcu::TestCaseGroup(testCtx, ("stride_" + std::to_string(stride)).c_str(), "");
for (auto& offset : offsets)
{
auto offsetGroup = new tcu::TestCaseGroup(testCtx, ("offset_" + std::to_string(offset)).c_str(), "");
for (auto& lib : libs)
{
auto libGroup = new tcu::TestCaseGroup(testCtx, lib.name, "");
VkPipelineCreateFlags flags;
flagsGenerator.reset();
while (flagsGenerator.next(flags))
{
if ((VK_PIPELINE_CREATE_RAY_TRACING_NO_NULL_INTERSECTION_SHADERS_BIT_KHR & flags)
&& (GeometryTypes::Triangle == geometry.type)) continue;
p.onHhost = processor.type;
p.geomTypes = geometry.type;
p.stbRecStride = stride;
p.stbRecOffset = offset;
p.flags = flags;
p.useLibs = lib.type;
libGroup->addChild(new PipelineFlagsCase(testCtx, flagsGenerator.name(flags), p));
}
offsetGroup->addChild(libGroup);
}
strideGroup->addChild(offsetGroup);
}
geometryGroup->addChild(strideGroup);
}
processorGroup->addChild(geometryGroup);
}
group->addChild(processorGroup);
}
return group;
}
} // RayTracing
} // vkt