blob: 3ab2874c59c3f93d027725695a1ad52929dbde9f [file] [log] [blame]
/*------------------------------------------------------------------------
* Vulkan Conformance Tests
* ------------------------
*
* Copyright (c) 2020 The Khronos Group Inc.
* Copyright (c) 2020 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 barrier tests
*//*--------------------------------------------------------------------*/
#include "vktRayTracingBarrierTests.hpp"
#include "vktTestCase.hpp"
#include "vkDefs.hpp"
#include "vkQueryUtil.hpp"
#include "vkRayTracingUtil.hpp"
#include "vkObjUtil.hpp"
#include "vkBufferWithMemory.hpp"
#include "vkTypeUtil.hpp"
#include "vkBarrierUtil.hpp"
#include "vkImageWithMemory.hpp"
#include "vkBuilderUtil.hpp"
#include "vkCmdUtil.hpp"
#include "deUniquePtr.hpp"
#include <string>
#include <sstream>
#include <memory>
#include <numeric>
#include <vector>
#include <algorithm>
namespace vkt
{
namespace RayTracing
{
namespace
{
using namespace vk;
constexpr deUint32 kBufferElements = 1024u;
constexpr deUint32 kBufferSize = kBufferElements * static_cast<deUint32>(sizeof(tcu::UVec4)); // std140
constexpr deUint32 kBufferSize430 = kBufferElements * static_cast<deUint32>(sizeof(deUint32)); // std430
constexpr deUint32 kValuesOffset = 2048u;
constexpr deUint32 kImageDim = 32u; // So that kImageDim*kImageDim == kBufferElements.
constexpr VkFormat kImageFormat = VK_FORMAT_R32_UINT; // So that each pixel has the same size as a deUint32.
const auto kImageExtent = makeExtent3D(kImageDim, kImageDim, 1u);
const std::vector<tcu::Vec4> kFullScreenQuad =
{
tcu::Vec4(-1.0f, 1.0f, 0.0f, 1.0f),
tcu::Vec4( 1.0f, 1.0f, 0.0f, 1.0f),
tcu::Vec4( 1.0f, -1.0f, 0.0f, 1.0f),
tcu::Vec4(-1.0f, 1.0f, 0.0f, 1.0f),
tcu::Vec4( 1.0f, -1.0f, 0.0f, 1.0f),
tcu::Vec4(-1.0f, -1.0f, 0.0f, 1.0f),
};
enum class Stage
{
HOST = 0,
TRANSFER,
RAYGEN,
INTERSECT,
ANY_HIT,
CLOSEST_HIT,
MISS,
CALLABLE,
COMPUTE,
FRAGMENT,
};
VkImageLayout getOptimalReadLayout (Stage stage)
{
VkImageLayout layout = VK_IMAGE_LAYOUT_UNDEFINED;
switch (stage)
{
case Stage::HOST:
break; // Images will not be read directly from the host.
case Stage::TRANSFER:
layout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
break;
case Stage::RAYGEN:
case Stage::INTERSECT:
case Stage::ANY_HIT:
case Stage::CLOSEST_HIT:
case Stage::MISS:
case Stage::CALLABLE:
case Stage::COMPUTE:
case Stage::FRAGMENT:
layout = VK_IMAGE_LAYOUT_GENERAL;
break;
default:
DE_ASSERT(false);
break;
}
return layout;
}
VkPipelineStageFlagBits getPipelineStage (Stage stage)
{
VkPipelineStageFlagBits bits = VK_PIPELINE_STAGE_FLAG_BITS_MAX_ENUM;
switch (stage)
{
case Stage::HOST:
bits = VK_PIPELINE_STAGE_HOST_BIT;
break;
case Stage::TRANSFER:
bits = VK_PIPELINE_STAGE_TRANSFER_BIT;
break;
case Stage::RAYGEN:
case Stage::INTERSECT:
case Stage::ANY_HIT:
case Stage::CLOSEST_HIT:
case Stage::MISS:
case Stage::CALLABLE:
bits = VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR;
break;
case Stage::COMPUTE:
bits = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
break;
case Stage::FRAGMENT:
bits = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
break;
default:
DE_ASSERT(false);
break;
}
return bits;
}
VkAccessFlagBits getWriterAccessFlag (Stage stage)
{
VkAccessFlagBits bits = VK_ACCESS_FLAG_BITS_MAX_ENUM;
switch (stage)
{
case Stage::HOST:
bits = VK_ACCESS_HOST_WRITE_BIT;
break;
case Stage::TRANSFER:
bits = VK_ACCESS_TRANSFER_WRITE_BIT;
break;
case Stage::RAYGEN:
case Stage::INTERSECT:
case Stage::ANY_HIT:
case Stage::CLOSEST_HIT:
case Stage::MISS:
case Stage::CALLABLE:
case Stage::COMPUTE:
case Stage::FRAGMENT:
bits = VK_ACCESS_SHADER_WRITE_BIT;
break;
default:
DE_ASSERT(false);
break;
}
return bits;
}
VkAccessFlagBits getReaderAccessFlag (Stage stage, VkDescriptorType resourceType)
{
VkAccessFlagBits bits = VK_ACCESS_FLAG_BITS_MAX_ENUM;
switch (stage)
{
case Stage::HOST:
bits = VK_ACCESS_HOST_READ_BIT;
break;
case Stage::TRANSFER:
bits = VK_ACCESS_TRANSFER_READ_BIT;
break;
case Stage::RAYGEN:
case Stage::INTERSECT:
case Stage::ANY_HIT:
case Stage::CLOSEST_HIT:
case Stage::MISS:
case Stage::CALLABLE:
case Stage::COMPUTE:
case Stage::FRAGMENT:
bits = ((resourceType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER) ? VK_ACCESS_UNIFORM_READ_BIT : VK_ACCESS_SHADER_READ_BIT);
break;
default:
DE_ASSERT(false);
break;
}
return bits;
}
// Translate a stage to the corresponding single stage flag.
VkShaderStageFlagBits getShaderStageFlagBits (Stage stage)
{
VkShaderStageFlagBits bits = VK_SHADER_STAGE_FLAG_BITS_MAX_ENUM;
switch (stage)
{
case Stage::RAYGEN: bits = VK_SHADER_STAGE_RAYGEN_BIT_KHR; break;
case Stage::INTERSECT: bits = VK_SHADER_STAGE_INTERSECTION_BIT_KHR; break;
case Stage::ANY_HIT: bits = VK_SHADER_STAGE_ANY_HIT_BIT_KHR; break;
case Stage::CLOSEST_HIT: bits = VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR; break;
case Stage::MISS: bits = VK_SHADER_STAGE_MISS_BIT_KHR; break;
case Stage::CALLABLE: bits = VK_SHADER_STAGE_CALLABLE_BIT_KHR; break;
case Stage::COMPUTE: bits = VK_SHADER_STAGE_COMPUTE_BIT; break;
case Stage::FRAGMENT: bits = VK_SHADER_STAGE_FRAGMENT_BIT; break;
default: DE_ASSERT(false); break;
}
return bits;
}
// Gets shader stage flags that will be used when choosing a given stage.
VkShaderStageFlags getStageFlags (Stage stage)
{
VkShaderStageFlags flags = 0u;
switch (stage)
{
case Stage::HOST: break;
case Stage::TRANSFER: break;
case Stage::RAYGEN: flags |= (VK_SHADER_STAGE_RAYGEN_BIT_KHR); break;
case Stage::INTERSECT: flags |= (VK_SHADER_STAGE_RAYGEN_BIT_KHR | VK_SHADER_STAGE_INTERSECTION_BIT_KHR); break;
case Stage::ANY_HIT: flags |= (VK_SHADER_STAGE_RAYGEN_BIT_KHR | VK_SHADER_STAGE_ANY_HIT_BIT_KHR); break;
case Stage::CLOSEST_HIT: flags |= (VK_SHADER_STAGE_RAYGEN_BIT_KHR | VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR); break;
case Stage::MISS: flags |= (VK_SHADER_STAGE_RAYGEN_BIT_KHR | VK_SHADER_STAGE_MISS_BIT_KHR); break;
case Stage::CALLABLE: flags |= (VK_SHADER_STAGE_RAYGEN_BIT_KHR | VK_SHADER_STAGE_CALLABLE_BIT_KHR); break;
case Stage::COMPUTE: flags |= (VK_SHADER_STAGE_COMPUTE_BIT); break;
case Stage::FRAGMENT: flags |= (VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT); break;
default: DE_ASSERT(false); break;
}
return flags;
}
bool isRayTracingStage (Stage stage)
{
bool isRT = false;
switch (stage)
{
case Stage::HOST:
case Stage::TRANSFER:
case Stage::COMPUTE:
case Stage::FRAGMENT:
break;
case Stage::RAYGEN:
case Stage::INTERSECT:
case Stage::ANY_HIT:
case Stage::CLOSEST_HIT:
case Stage::MISS:
case Stage::CALLABLE:
isRT = true;
break;
default:
DE_ASSERT(false);
break;
}
return isRT;
}
enum class BarrierType
{
GENERAL = 0,
SPECIFIC = 1,
};
struct TestParams
{
VkDescriptorType resourceType;
Stage writerStage;
Stage readerStage;
BarrierType barrierType;
TestParams (VkDescriptorType resourceType_, Stage writerStage_, Stage readerStage_, BarrierType barrierType_)
: resourceType (resourceType_)
, writerStage (writerStage_)
, readerStage (readerStage_)
, barrierType (barrierType_)
{
DE_ASSERT(resourceType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER ||
resourceType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER ||
resourceType == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE);
}
};
bool resourceNeedsHostVisibleMemory (const TestParams& params)
{
return (params.writerStage == Stage::HOST || params.readerStage == Stage::HOST);
}
bool needsAccelerationStructure (Stage stage)
{
bool needed;
switch (stage)
{
case Stage::INTERSECT:
case Stage::ANY_HIT:
case Stage::CLOSEST_HIT:
case Stage::MISS:
case Stage::CALLABLE:
needed = true;
break;
default:
needed = false;
break;
}
return needed;
}
// The general idea is having a resource like a buffer or image that is generated from a given pipeline stage (including host,
// transfer and all ray shader stages) and read from another stage, using a barrier to synchronize access to the resource. Read
// values are copied to an output host-visible buffer for verification.
class BarrierTestCase : public vkt::TestCase
{
public:
BarrierTestCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TestParams& testParams);
virtual ~BarrierTestCase (void) {}
virtual void initPrograms (SourceCollections& programCollection) const;
virtual TestInstance* createInstance (Context& context) const;
virtual void checkSupport (Context& context) const;
private:
TestParams m_params;
};
class BarrierTestInstance : public vkt::TestInstance
{
public:
BarrierTestInstance (Context& context, const TestParams& testParams);
virtual ~BarrierTestInstance (void) {}
virtual tcu::TestStatus iterate (void);
private:
TestParams m_params;
};
BarrierTestCase::BarrierTestCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TestParams& testParams)
: vkt::TestCase (testCtx, name, description)
, m_params (testParams)
{
}
void BarrierTestCase::initPrograms (SourceCollections& programCollection) const
{
const auto& wstage = m_params.writerStage;
const auto& rstage = m_params.readerStage;
const bool readNeedAS = needsAccelerationStructure(rstage);
const deUint32 readerVerifierBinding = (readNeedAS ? 2u : 1u); // 0 is the barrier resource, 1 may be the AS.
const ShaderBuildOptions buildOptions (programCollection.usedVulkanVersion, SPIRV_VERSION_1_4, 0u, true);
const std::string valStatement = " const uint val = id1d + " + de::toString(kValuesOffset) + ";\n";
const std::string readerSaveStatement = " verificationBuffer.data[id1d] = val;\n";
// Common for all ray tracing shaders.
std::stringstream rayTracingIdsStream;
rayTracingIdsStream
<< " const uint id1d = gl_LaunchIDEXT.y * " << kImageDim << " + gl_LaunchIDEXT.x;\n"
<< " const ivec2 id2d = ivec2(gl_LaunchIDEXT.xy);\n"
;
const std::string rayTracingIds = rayTracingIdsStream.str();
// Common for all compute shaders.
std::stringstream computeIdsStream;
computeIdsStream
<< " const uint id1d = gl_GlobalInvocationID.y * " << kImageDim << " + gl_GlobalInvocationID.x;\n"
<< " const ivec2 id2d = ivec2(gl_GlobalInvocationID.xy);\n"
;
const std::string computeIds = computeIdsStream.str();
// Common for all fragment shaders.
std::stringstream fragIdsStream;
fragIdsStream
<< " const uint id1d = uint(gl_FragCoord.y) * " << kImageDim << " + uint(gl_FragCoord.x);\n"
<< " const ivec2 id2d = ivec2(gl_FragCoord.xy);\n"
;
const std::string fragIds = fragIdsStream.str();
// Statements to declare the resource in the writer and reader sides, as well as writing to and reading from it.
std::stringstream writerResourceDecl;
std::stringstream readerResourceDecl;
std::stringstream writeStatement;
std::stringstream readStatement;
switch (m_params.resourceType)
{
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
writerResourceDecl << "layout(set = 0, binding = 0, std140) uniform ubodef { uint data[" << kBufferElements << "]; } ubo;\n";
readerResourceDecl << "layout(set = 0, binding = 0, std140) uniform ubodef { uint data[" << kBufferElements << "]; } ubo;\n";
// No writes can happen from shaders in this case.
readStatement << " const uint val = ubo.data[id1d];\n";
break;
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
writerResourceDecl << "layout(set = 0, binding = 0, std140) buffer ssbodef { uint data[" << kBufferElements << "]; } ssbo;\n";
readerResourceDecl << "layout(set = 0, binding = 0, std140) buffer ssbodef { uint data[" << kBufferElements << "]; } ssbo;\n";
writeStatement << " ssbo.data[id1d] = val;\n";
readStatement << " const uint val = ssbo.data[id1d];\n";
break;
case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
writerResourceDecl << "layout(r32ui, set = 0, binding = 0) uniform uimage2D simage;\n";
readerResourceDecl << "layout(r32ui, set = 0, binding = 0) uniform uimage2D simage;\n";
writeStatement << " imageStore(simage, id2d, uvec4(val, 0, 0, 0));\n";
readStatement << " const uint val = imageLoad(simage, id2d).x;\n";
break;
default:
DE_ASSERT(false);
break;
}
// This extra buffer will be used to copy values from the resource as obtained by the reader and will later be verified on the host.
std::stringstream readerVerifierDeclStream;
readerVerifierDeclStream << "layout(set = 0, binding = " << readerVerifierBinding << ") buffer vssbodef { uint data[" << kBufferElements << "]; } verificationBuffer;\n";
const std::string readerVerifierDecl = readerVerifierDeclStream.str();
// These are always used together in writer shaders.
const std::string writerCalcAndWrite = valStatement + writeStatement.str();
// Add shaders that will be used to write to the resource.
if (wstage == Stage::HOST || wstage == Stage::TRANSFER)
; // Nothing to do here.
else if (wstage == Stage::RAYGEN)
{
std::stringstream rgen;
rgen
<< "#version 460 core\n"
<< "#extension GL_EXT_ray_tracing : require\n"
<< writerResourceDecl.str()
<< "void main()\n"
<< "{\n"
<< rayTracingIds
<< writerCalcAndWrite
<< "}\n"
;
programCollection.glslSources.add("writer_rgen") << glu::RaygenSource(updateRayTracingGLSL(rgen.str())) << buildOptions;
}
else if (wstage == Stage::INTERSECT)
{
programCollection.glslSources.add("writer_aux_rgen") << glu::RaygenSource(updateRayTracingGLSL(getCommonRayGenerationShader())) << buildOptions;
std::stringstream isect;
isect
<< "#version 460 core\n"
<< "#extension GL_EXT_ray_tracing : require\n"
<< "hitAttributeEXT vec3 hitAttribute;\n"
<< writerResourceDecl.str()
<< "void main()\n"
<< "{\n"
<< rayTracingIds
<< writerCalcAndWrite
<< " hitAttribute = vec3(0.0f, 0.0f, 0.0f);\n"
<< " reportIntersectionEXT(1.0f, 0);\n"
<< "}\n";
;
programCollection.glslSources.add("writer_isect") << glu::IntersectionSource(updateRayTracingGLSL(isect.str())) << buildOptions;
}
else if (wstage == Stage::ANY_HIT)
{
programCollection.glslSources.add("writer_aux_rgen") << glu::RaygenSource(updateRayTracingGLSL(getCommonRayGenerationShader())) << buildOptions;
std::stringstream ahit;
ahit
<< "#version 460 core\n"
<< "#extension GL_EXT_ray_tracing : require\n"
<< "layout(location = 0) rayPayloadInEXT vec3 hitValue;\n"
<< "hitAttributeEXT vec3 attribs;\n"
<< writerResourceDecl.str()
<< "void main()\n"
<< "{\n"
<< rayTracingIds
<< writerCalcAndWrite
<< "}\n";
;
programCollection.glslSources.add("writer_ahit") << glu::AnyHitSource(updateRayTracingGLSL(ahit.str())) << buildOptions;
}
else if (wstage == Stage::CLOSEST_HIT)
{
programCollection.glslSources.add("writer_aux_rgen") << glu::RaygenSource(updateRayTracingGLSL(getCommonRayGenerationShader())) << buildOptions;
std::stringstream chit;
chit
<< "#version 460 core\n"
<< "#extension GL_EXT_ray_tracing : require\n"
<< "layout(location = 0) rayPayloadInEXT vec3 hitValue;\n"
<< "hitAttributeEXT vec3 attribs;\n"
<< writerResourceDecl.str()
<< "void main()\n"
<< "{\n"
<< rayTracingIds
<< writerCalcAndWrite
<< "}\n";
;
programCollection.glslSources.add("writer_chit") << glu::ClosestHitSource(updateRayTracingGLSL(chit.str())) << buildOptions;
}
else if (wstage == Stage::MISS)
{
programCollection.glslSources.add("writer_aux_rgen") << glu::RaygenSource(updateRayTracingGLSL(getCommonRayGenerationShader())) << buildOptions;
std::stringstream miss;
miss
<< "#version 460 core\n"
<< "#extension GL_EXT_ray_tracing : require\n"
<< "layout(location = 0) rayPayloadInEXT dummyPayload { vec4 dummy; };\n"
<< writerResourceDecl.str()
<< "void main()\n"
<< "{\n"
<< rayTracingIds
<< writerCalcAndWrite
<< "}\n";
;
programCollection.glslSources.add("writer_miss") << glu::MissSource(updateRayTracingGLSL(miss.str())) << buildOptions;
}
else if (wstage == Stage::CALLABLE)
{
{
std::stringstream rgen;
rgen
<< "#version 460 core\n"
<< "#extension GL_EXT_ray_tracing : require\n"
<< "layout(location = 0) callableDataEXT float dummy;"
<< "layout(set = 0, binding = 1) uniform accelerationStructureEXT topLevelAS;\n"
<< "\n"
<< "void main()\n"
<< "{\n"
<< " executeCallableEXT(0, 0);\n"
<< "}\n"
;
programCollection.glslSources.add("writer_aux_rgen") << glu::RaygenSource(updateRayTracingGLSL(rgen.str())) << buildOptions;
}
std::stringstream callable;
callable
<< "#version 460 core\n"
<< "#extension GL_EXT_ray_tracing : require\n"
<< "layout(location = 0) callableDataInEXT float dummy;\n"
<< writerResourceDecl.str()
<< "void main()\n"
<< "{\n"
<< rayTracingIds
<< writerCalcAndWrite
<< "}\n";
;
programCollection.glslSources.add("writer_callable") << glu::CallableSource(updateRayTracingGLSL(callable.str())) << buildOptions;
}
else if (wstage == Stage::COMPUTE)
{
std::stringstream compute;
compute
<< "#version 460 core\n"
<< writerResourceDecl.str()
<< "void main()\n"
<< "{\n"
<< computeIds
<< writerCalcAndWrite
<< "}\n"
;
programCollection.glslSources.add("writer_comp") << glu::ComputeSource(compute.str());
}
else if (wstage == Stage::FRAGMENT)
{
{
std::stringstream vert;
vert
<< "#version 460 core\n"
<< "layout(location = 0) in highp vec4 position;\n"
<< "void main()\n"
<< "{\n"
<< " gl_Position = position;\n"
<< "}\n"
;
programCollection.glslSources.add("writer_aux_vert") << glu::VertexSource(vert.str());
}
std::stringstream frag;
frag
<< "#version 460 core\n"
<< writerResourceDecl.str()
<< "void main()\n"
<< "{\n"
<< fragIds
<< writerCalcAndWrite
<< "}\n"
;
programCollection.glslSources.add("writer_frag") << glu::FragmentSource(frag.str());
}
else
{
DE_ASSERT(false);
}
// These are always used together by reader shaders.
const std::string readerAllDecls = readerResourceDecl.str() + readerVerifierDecl;
const std::string readerReadAndSave = readStatement.str() + readerSaveStatement;
// Add shaders that will be used to read from the resource.
if (rstage == Stage::HOST || rstage == Stage::TRANSFER)
; // Nothing to do here.
else if (rstage == Stage::RAYGEN)
{
std::stringstream rgen;
rgen
<< "#version 460 core\n"
<< "#extension GL_EXT_ray_tracing : require\n"
<< readerAllDecls
<< "void main()\n"
<< "{\n"
<< rayTracingIds
<< readerReadAndSave
<< "}\n"
;
programCollection.glslSources.add("reader_rgen") << glu::RaygenSource(updateRayTracingGLSL(rgen.str())) << buildOptions;
}
else if (rstage == Stage::INTERSECT)
{
programCollection.glslSources.add("reader_aux_rgen") << glu::RaygenSource(updateRayTracingGLSL(getCommonRayGenerationShader())) << buildOptions;
std::stringstream isect;
isect
<< "#version 460 core\n"
<< "#extension GL_EXT_ray_tracing : require\n"
<< "hitAttributeEXT vec3 hitAttribute;\n"
<< readerAllDecls
<< "void main()\n"
<< "{\n"
<< rayTracingIds
<< readerReadAndSave
<< " hitAttribute = vec3(0.0f, 0.0f, 0.0f);\n"
<< " reportIntersectionEXT(1.0f, 0);\n"
<< "}\n";
;
programCollection.glslSources.add("reader_isect") << glu::IntersectionSource(updateRayTracingGLSL(isect.str())) << buildOptions;
}
else if (rstage == Stage::ANY_HIT)
{
programCollection.glslSources.add("reader_aux_rgen") << glu::RaygenSource(updateRayTracingGLSL(getCommonRayGenerationShader())) << buildOptions;
std::stringstream ahit;
ahit
<< "#version 460 core\n"
<< "#extension GL_EXT_ray_tracing : require\n"
<< "layout(location = 0) rayPayloadInEXT vec3 hitValue;\n"
<< "hitAttributeEXT vec3 attribs;\n"
<< readerAllDecls
<< "void main()\n"
<< "{\n"
<< rayTracingIds
<< readerReadAndSave
<< "}\n";
;
programCollection.glslSources.add("reader_ahit") << glu::AnyHitSource(updateRayTracingGLSL(ahit.str())) << buildOptions;
}
else if (rstage == Stage::CLOSEST_HIT)
{
programCollection.glslSources.add("reader_aux_rgen") << glu::RaygenSource(updateRayTracingGLSL(getCommonRayGenerationShader())) << buildOptions;
std::stringstream chit;
chit
<< "#version 460 core\n"
<< "#extension GL_EXT_ray_tracing : require\n"
<< "layout(location = 0) rayPayloadInEXT vec3 hitValue;\n"
<< "hitAttributeEXT vec3 attribs;\n"
<< readerAllDecls
<< "void main()\n"
<< "{\n"
<< rayTracingIds
<< readerReadAndSave
<< "}\n";
;
programCollection.glslSources.add("reader_chit") << glu::ClosestHitSource(updateRayTracingGLSL(chit.str())) << buildOptions;
}
else if (rstage == Stage::MISS)
{
programCollection.glslSources.add("reader_aux_rgen") << glu::RaygenSource(updateRayTracingGLSL(getCommonRayGenerationShader())) << buildOptions;
std::stringstream miss;
miss
<< "#version 460 core\n"
<< "#extension GL_EXT_ray_tracing : require\n"
<< "layout(location = 0) rayPayloadInEXT dummyPayload { vec4 dummy; };\n"
<< readerAllDecls
<< "void main()\n"
<< "{\n"
<< rayTracingIds
<< readerReadAndSave
<< "}\n";
;
programCollection.glslSources.add("reader_miss") << glu::MissSource(updateRayTracingGLSL(miss.str())) << buildOptions;
}
else if (rstage == Stage::CALLABLE)
{
{
std::stringstream rgen;
rgen
<< "#version 460 core\n"
<< "#extension GL_EXT_ray_tracing : require\n"
<< "layout(location = 0) callableDataEXT float dummy;"
<< "layout(set = 0, binding = 1) uniform accelerationStructureEXT topLevelAS;\n"
<< "\n"
<< "void main()\n"
<< "{\n"
<< " executeCallableEXT(0, 0);\n"
<< "}\n"
;
programCollection.glslSources.add("reader_aux_rgen") << glu::RaygenSource(updateRayTracingGLSL(rgen.str())) << buildOptions;
}
std::stringstream callable;
callable
<< "#version 460 core\n"
<< "#extension GL_EXT_ray_tracing : require\n"
<< "layout(location = 0) callableDataInEXT float dummy;\n"
<< readerAllDecls
<< "void main()\n"
<< "{\n"
<< rayTracingIds
<< readerReadAndSave
<< "}\n";
;
programCollection.glslSources.add("reader_callable") << glu::CallableSource(updateRayTracingGLSL(callable.str())) << buildOptions;
}
else if (rstage == Stage::COMPUTE)
{
std::stringstream compute;
compute
<< "#version 460 core\n"
<< readerAllDecls
<< "void main()\n"
<< "{\n"
<< computeIds
<< readerReadAndSave
<< "}\n"
;
programCollection.glslSources.add("reader_comp") << glu::ComputeSource(compute.str());
}
else if (rstage == Stage::FRAGMENT)
{
{
std::stringstream vert;
vert
<< "#version 460 core\n"
<< "layout(location = 0) in highp vec4 position;\n"
<< "void main()\n"
<< "{\n"
<< " gl_Position = position;\n"
<< "}\n"
;
programCollection.glslSources.add("reader_aux_vert") << glu::VertexSource(vert.str());
}
std::stringstream frag;
frag
<< "#version 460 core\n"
<< readerAllDecls
<< "void main()\n"
<< "{\n"
<< fragIds
<< readerReadAndSave
<< "}\n"
;
programCollection.glslSources.add("reader_frag") << glu::FragmentSource(frag.str());
}
else
{
DE_ASSERT(false);
}
}
TestInstance* BarrierTestCase::createInstance (Context& context) const
{
return new BarrierTestInstance(context, m_params);
}
void BarrierTestCase::checkSupport (Context& context) const
{
if (m_params.writerStage == Stage::FRAGMENT)
{
const auto& features = context.getDeviceFeatures();
if (!features.fragmentStoresAndAtomics)
TCU_THROW(NotSupportedError, "Fragment shader does not support stores");
}
if (isRayTracingStage(m_params.readerStage) || isRayTracingStage(m_params.writerStage))
{
context.requireDeviceFunctionality("VK_KHR_acceleration_structure");
context.requireDeviceFunctionality("VK_KHR_ray_tracing_pipeline");
const auto& rtFeatures = context.getRayTracingPipelineFeatures();
if (!rtFeatures.rayTracingPipeline)
TCU_THROW(NotSupportedError, "Ray Tracing pipelines not supported");
const auto& asFeatures = context.getAccelerationStructureFeatures();
if (!asFeatures.accelerationStructure)
TCU_FAIL("VK_KHR_acceleration_structure supported without accelerationStructure support");
}
}
BarrierTestInstance::BarrierTestInstance (Context& context, const TestParams& testParams)
: vkt::TestInstance (context)
, m_params (testParams)
{
}
// Creates a buffer with kBufferElements elements of type deUint32 and std140 padding.
std::unique_ptr<BufferWithMemory> makeStd140Buffer (const DeviceInterface& vkd, VkDevice device, Allocator& alloc, VkBufferUsageFlags flags, MemoryRequirement memReq)
{
std::unique_ptr<BufferWithMemory> buffer;
const auto bufferCreateInfo = makeBufferCreateInfo(static_cast<VkDeviceSize>(kBufferSize), flags);
buffer.reset(new BufferWithMemory(vkd, device, alloc, bufferCreateInfo, memReq));
return buffer;
}
// Fill buffer with data using std140 padding rules.
void fillStd140Buffer (const DeviceInterface& vkd, VkDevice device, const BufferWithMemory& buffer)
{
// Buffer host ptr.
auto& bufferAlloc = buffer.getAllocation();
auto* bufferPtr = bufferAlloc.getHostPtr();
// Fill buffer with data. This uses the same strategy as the writer shaders.
std::vector<tcu::UVec4> bufferData(kBufferElements, tcu::UVec4(kValuesOffset, 0u, 0u, 0u));
for (size_t i = 0; i < bufferData.size(); ++i)
bufferData[i].x() += static_cast<deUint32>(i);
deMemcpy(bufferPtr, bufferData.data(), static_cast<size_t>(kBufferSize));
flushAlloc(vkd, device, bufferAlloc);
}
// Fill buffer with data using std430 padding rules (compact integers).
void fillStd430Buffer (const DeviceInterface& vkd, VkDevice device, const BufferWithMemory& buffer)
{
// Buffer host ptr.
auto& bufferAlloc = buffer.getAllocation();
auto* bufferPtr = bufferAlloc.getHostPtr();
// Fill buffer with data. This uses the same strategy as the writer shaders.
std::vector<deUint32> bufferData(kBufferElements);
std::iota(begin(bufferData), end(bufferData), kValuesOffset);
deMemcpy(bufferPtr, bufferData.data(), static_cast<size_t>(kBufferSize430));
flushAlloc(vkd, device, bufferAlloc);
}
// Creates a host-visible std430 buffer with kBufferElements elements of type deUint32. If requested, fill buffer with values
// starting at kValuesOffset.
std::unique_ptr<BufferWithMemory> makeStd430BufferImpl (const DeviceInterface& vkd, VkDevice device, Allocator& alloc, VkBufferUsageFlags flags, bool fill)
{
std::unique_ptr<BufferWithMemory> buffer;
const auto bufferCreateInfo = makeBufferCreateInfo(static_cast<VkDeviceSize>(kBufferSize430), flags);
buffer.reset(new BufferWithMemory(vkd, device, alloc, bufferCreateInfo, MemoryRequirement::HostVisible));
if (fill)
fillStd430Buffer(vkd, device, *buffer);
return buffer;
}
std::unique_ptr<BufferWithMemory> makeStd430Buffer (const DeviceInterface& vkd, VkDevice device, Allocator& alloc, VkBufferUsageFlags flags)
{
return makeStd430BufferImpl(vkd, device, alloc, flags, false);
}
std::unique_ptr<BufferWithMemory> makeStd430BufferFilled (const DeviceInterface& vkd, VkDevice device, Allocator& alloc, VkBufferUsageFlags flags)
{
return makeStd430BufferImpl(vkd, device, alloc, flags, true);
}
// Helper struct to group data related to the writer or reader stages.
// Not every member will be used at the same time.
struct StageData
{
Move<VkDescriptorSetLayout> descriptorSetLayout;
Move<VkPipelineLayout> pipelineLayout;
Move<VkDescriptorPool> descriptorPool;
Move<VkDescriptorSet> descriptorSet;
Move<VkPipeline> pipeline;
Move<VkRenderPass> renderPass;
Move<VkFramebuffer> framebuffer;
std::unique_ptr<BufferWithMemory> vertexBuffer;
de::MovePtr<BottomLevelAccelerationStructure> bottomLevelAccelerationStructure;
de::MovePtr<TopLevelAccelerationStructure> topLevelAccelerationStructure;
de::MovePtr<BufferWithMemory> raygenShaderBindingTable;
de::MovePtr<BufferWithMemory> missShaderBindingTable;
de::MovePtr<BufferWithMemory> hitShaderBindingTable;
de::MovePtr<BufferWithMemory> callableShaderBindingTable;
VkStridedDeviceAddressRegionKHR raygenShaderBindingTableRegion;
VkStridedDeviceAddressRegionKHR missShaderBindingTableRegion;
VkStridedDeviceAddressRegionKHR hitShaderBindingTableRegion;
VkStridedDeviceAddressRegionKHR callableShaderBindingTableRegion;
StageData ()
: descriptorSetLayout ()
, pipelineLayout ()
, descriptorPool ()
, descriptorSet ()
, pipeline ()
, renderPass ()
, framebuffer ()
, vertexBuffer ()
, bottomLevelAccelerationStructure ()
, topLevelAccelerationStructure ()
, raygenShaderBindingTable ()
, missShaderBindingTable ()
, hitShaderBindingTable ()
, callableShaderBindingTable ()
, raygenShaderBindingTableRegion (makeStridedDeviceAddressRegionKHR(DE_NULL, 0, 0))
, missShaderBindingTableRegion (makeStridedDeviceAddressRegionKHR(DE_NULL, 0, 0))
, hitShaderBindingTableRegion (makeStridedDeviceAddressRegionKHR(DE_NULL, 0, 0))
, callableShaderBindingTableRegion (makeStridedDeviceAddressRegionKHR(DE_NULL, 0, 0))
{
}
// Make sure we don't mistakenly pass one of these by value.
StageData (const StageData&) = delete;
StageData (StageData&&) = delete;
};
// Auxiliar function to update the descriptor set for the writer or reader stages.
void updateDescriptorSet (const DeviceInterface& vkd, VkDevice device, VkCommandBuffer cmdBuffer, Allocator& alloc, VkDescriptorType resourceType, Stage stage, StageData& stageData, BufferWithMemory* resourceBuffer, VkImageView resourceImgView, VkImageLayout layout, bool asNeeded, BufferWithMemory* verificationBuffer)
{
DescriptorSetUpdateBuilder updateBuilder;
VkWriteDescriptorSetAccelerationStructureKHR writeASInfo;
if (resourceType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER || resourceType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER)
{
const auto descriptorBufferInfo = makeDescriptorBufferInfo(resourceBuffer->get(), 0ull, VK_WHOLE_SIZE);
updateBuilder.writeSingle(stageData.descriptorSet.get(), DescriptorSetUpdateBuilder::Location::binding(0u), resourceType, &descriptorBufferInfo);
}
else if (resourceType == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE)
{
const auto descriptorImageInfo = makeDescriptorImageInfo(DE_NULL, resourceImgView, layout);
updateBuilder.writeSingle(stageData.descriptorSet.get(), DescriptorSetUpdateBuilder::Location::binding(0u), resourceType, &descriptorImageInfo);
}
else
{
DE_ASSERT(false);
}
// Create top and bottom level acceleration structures if needed.
if (asNeeded)
{
stageData.bottomLevelAccelerationStructure = makeBottomLevelAccelerationStructure();
stageData.bottomLevelAccelerationStructure->setDefaultGeometryData(getShaderStageFlagBits(stage));
stageData.bottomLevelAccelerationStructure->createAndBuild(vkd, device, cmdBuffer, alloc);
stageData.topLevelAccelerationStructure = makeTopLevelAccelerationStructure();
stageData.topLevelAccelerationStructure->setInstanceCount(1);
stageData.topLevelAccelerationStructure->addInstance(de::SharedPtr<BottomLevelAccelerationStructure>(stageData.bottomLevelAccelerationStructure.release()));
stageData.topLevelAccelerationStructure->createAndBuild(vkd, device, cmdBuffer, alloc);
writeASInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_ACCELERATION_STRUCTURE_KHR;
writeASInfo.pNext = nullptr;
writeASInfo.accelerationStructureCount = 1u;
writeASInfo.pAccelerationStructures = stageData.topLevelAccelerationStructure.get()->getPtr();
updateBuilder.writeSingle(stageData.descriptorSet.get(), DescriptorSetUpdateBuilder::Location::binding(1u), VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, &writeASInfo);
}
if (verificationBuffer)
{
const deUint32 bindingNumber = (asNeeded ? 2u : 1u);
const auto descriptorBufferInfo = makeDescriptorBufferInfo(verificationBuffer->get(), 0ull, VK_WHOLE_SIZE);
updateBuilder.writeSingle(stageData.descriptorSet.get(), DescriptorSetUpdateBuilder::Location::binding(bindingNumber), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &descriptorBufferInfo);
}
updateBuilder.update(vkd, device);
}
// Auxiliar function to create the writer or reader compute pipeline
void createComputePipeline (const DeviceInterface& vkd, VkDevice device, Context& context, const char* shaderName, StageData& stageData)
{
const auto shaderModule = createShaderModule(vkd, device, context.getBinaryCollection().get(shaderName), 0u);
const VkPipelineShaderStageCreateInfo stageInfo =
{
VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, // VkStructureType sType;
nullptr, // const void* pNext;
0u, // VkPipelineShaderStageCreateFlags flags;
VK_SHADER_STAGE_COMPUTE_BIT, // VkShaderStageFlagBits stage;
shaderModule.get(), // VkShaderModule module;
"main", // const char* pName;
nullptr, // const VkSpecializationInfo* pSpecializationInfo;
};
const VkComputePipelineCreateInfo createInfo =
{
VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, // VkStructureType sType;
nullptr, // const void* pNext;
0u, // VkPipelineCreateFlags flags;
stageInfo, // VkPipelineShaderStageCreateInfo stage;
stageData.pipelineLayout.get(), // VkPipelineLayout layout;
DE_NULL, // VkPipeline basePipelineHandle;
0, // deInt32 basePipelineIndex;
};
// Compute pipeline.
stageData.pipeline = createComputePipeline(vkd, device, DE_NULL, &createInfo);
}
// Auxiliar function to record commands using the compute pipeline.
void useComputePipeline (const DeviceInterface& vkd, VkCommandBuffer cmdBuffer, StageData& stageData)
{
vkd.cmdBindPipeline(cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, stageData.pipeline.get());
vkd.cmdBindDescriptorSets(cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, stageData.pipelineLayout.get(), 0u, 1u, &stageData.descriptorSet.get(), 0u, nullptr);
vkd.cmdDispatch(cmdBuffer, kImageDim, kImageDim, 1u);
}
// Auxiliar function to create graphics pipeline objects for writer or reader stages.
void createGraphicsPipelineObjects (const DeviceInterface& vkd, VkDevice device, Allocator& alloc, Context& context, const char* vertShaderName, const char* fragShaderName, StageData& stageData)
{
const auto vertShader = createShaderModule(vkd, device, context.getBinaryCollection().get(vertShaderName), 0u);
const auto fragShader = createShaderModule(vkd, device, context.getBinaryCollection().get(fragShaderName), 0u);
// Render pass.
const auto subpassDescription = makeSubpassDescription(0u, VK_PIPELINE_BIND_POINT_GRAPHICS, 0u, nullptr, 0u, nullptr, nullptr, nullptr, 0u, nullptr);
const VkRenderPassCreateInfo renderPassInfo =
{
VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, // VkStructureType sType;
nullptr, // const void* pNext;
0u, // VkRenderPassCreateFlags flags;
0u, // deUint32 attachmentCount;
nullptr, // const VkAttachmentDescription* pAttachments;
1u, // deUint32 subpassCount;
&subpassDescription, // const VkSubpassDescription* pSubpasses;
0u, // deUint32 dependencyCount;
nullptr, // const VkSubpassDependency* pDependencies;
};
stageData.renderPass = createRenderPass(vkd, device, &renderPassInfo);
// Viewport.
const auto viewport = makeViewport(kImageExtent);
const std::vector<VkViewport> viewports(1u, viewport);
// Scissor.
const auto scissor = makeRect2D(kImageExtent);
const std::vector<VkRect2D> scissors(1u, scissor);
// Pipeline.
stageData.pipeline = makeGraphicsPipeline(vkd, device, stageData.pipelineLayout.get(), vertShader.get(), DE_NULL, DE_NULL, DE_NULL, fragShader.get(), stageData.renderPass.get(), viewports, scissors);
// Framebuffer.
stageData.framebuffer = makeFramebuffer(vkd, device, stageData.renderPass.get(), 0u, nullptr, kImageDim, kImageDim);
// Vertex buffer with full-screen quad.
const auto vertexBufferSize = static_cast<VkDeviceSize>(kFullScreenQuad.size() * sizeof(decltype(kFullScreenQuad)::value_type));
const auto vertexBufferInfo = makeBufferCreateInfo(vertexBufferSize, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT);
stageData.vertexBuffer.reset(new BufferWithMemory(vkd, device, alloc, vertexBufferInfo, MemoryRequirement::HostVisible));
const auto& vertexBufferAlloc = stageData.vertexBuffer->getAllocation();
deMemcpy(vertexBufferAlloc.getHostPtr(), kFullScreenQuad.data(), static_cast<size_t>(vertexBufferSize));
flushAlloc(vkd, device, vertexBufferAlloc);
}
// Auxiliar function to record commands using the graphics pipeline.
void useGraphicsPipeline (const DeviceInterface& vkd, VkCommandBuffer cmdBuffer, StageData& stageData)
{
const VkDeviceSize vertexBufferOffset = 0ull;
const auto scissor = makeRect2D(kImageExtent);
beginRenderPass(vkd, cmdBuffer, stageData.renderPass.get(), stageData.framebuffer.get(), scissor);
vkd.cmdBindPipeline(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, stageData.pipeline.get());
vkd.cmdBindDescriptorSets(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, stageData.pipelineLayout.get(), 0u, 1u, &stageData.descriptorSet.get(), 0u, nullptr);
vkd.cmdBindVertexBuffers(cmdBuffer, 0u, 1u, &stageData.vertexBuffer->get(), &vertexBufferOffset);
vkd.cmdDraw(cmdBuffer, static_cast<deUint32>(kFullScreenQuad.size()), 1u, 0u, 0u);
endRenderPass(vkd, cmdBuffer);
}
// Auxiliar function to create ray tracing pipelines for the writer or reader stages.
void createRayTracingPipelineData (const DeviceInterface& vkd, VkDevice device, Allocator& alloc, Context& context,
Stage stage, StageData& stageData, deUint32 shaderGroupHandleSize, deUint32 shaderGroupBaseAlignment,
const char* rgenAuxName, const char* rgenName, const char* isectName, const char* ahitName, const char* chitName, const char* missName, const char* callableName)
{
// Ray tracing stage
DE_ASSERT(isRayTracingStage(stage));
if (stage == Stage::RAYGEN)
{
const auto rayTracingPipeline = de::newMovePtr<RayTracingPipeline>();
rayTracingPipeline->addShader(VK_SHADER_STAGE_RAYGEN_BIT_KHR, createShaderModule(vkd, device, context.getBinaryCollection().get(rgenName), 0), 0);
stageData.pipeline = rayTracingPipeline->createPipeline(vkd, device, stageData.pipelineLayout.get());
stageData.raygenShaderBindingTable = rayTracingPipeline->createShaderBindingTable(vkd, device, stageData.pipeline.get(), alloc, shaderGroupHandleSize, shaderGroupBaseAlignment, 0, 1);
stageData.raygenShaderBindingTableRegion = makeStridedDeviceAddressRegionKHR(getBufferDeviceAddress(vkd, device, stageData.raygenShaderBindingTable->get(), 0), shaderGroupHandleSize, shaderGroupHandleSize);
}
else if (stage == Stage::INTERSECT)
{
const auto rayTracingPipeline = de::newMovePtr<RayTracingPipeline>();
rayTracingPipeline->addShader(VK_SHADER_STAGE_RAYGEN_BIT_KHR, createShaderModule(vkd, device, context.getBinaryCollection().get(rgenAuxName), 0), 0);
rayTracingPipeline->addShader(VK_SHADER_STAGE_INTERSECTION_BIT_KHR, createShaderModule(vkd, device, context.getBinaryCollection().get(isectName), 0), 1);
stageData.pipeline = rayTracingPipeline->createPipeline(vkd, device, stageData.pipelineLayout.get());
stageData.raygenShaderBindingTable = rayTracingPipeline->createShaderBindingTable(vkd, device, stageData.pipeline.get(), alloc, shaderGroupHandleSize, shaderGroupBaseAlignment, 0, 1);
stageData.raygenShaderBindingTableRegion = makeStridedDeviceAddressRegionKHR(getBufferDeviceAddress(vkd, device, stageData.raygenShaderBindingTable->get(), 0), shaderGroupHandleSize, shaderGroupHandleSize);
stageData.hitShaderBindingTable = rayTracingPipeline->createShaderBindingTable(vkd, device, stageData.pipeline.get(), alloc, shaderGroupHandleSize, shaderGroupBaseAlignment, 1, 1);
stageData.hitShaderBindingTableRegion = makeStridedDeviceAddressRegionKHR(getBufferDeviceAddress(vkd, device, stageData.hitShaderBindingTable->get(), 0), shaderGroupHandleSize, shaderGroupHandleSize);
}
else if (stage == Stage::ANY_HIT)
{
const auto rayTracingPipeline = de::newMovePtr<RayTracingPipeline>();
rayTracingPipeline->addShader(VK_SHADER_STAGE_RAYGEN_BIT_KHR, createShaderModule(vkd, device, context.getBinaryCollection().get(rgenAuxName), 0), 0);
rayTracingPipeline->addShader(VK_SHADER_STAGE_ANY_HIT_BIT_KHR, createShaderModule(vkd, device, context.getBinaryCollection().get(ahitName), 0), 1);
stageData.pipeline = rayTracingPipeline->createPipeline(vkd, device, stageData.pipelineLayout.get());
stageData.raygenShaderBindingTable = rayTracingPipeline->createShaderBindingTable(vkd, device, stageData.pipeline.get(), alloc, shaderGroupHandleSize, shaderGroupBaseAlignment, 0, 1);
stageData.raygenShaderBindingTableRegion = makeStridedDeviceAddressRegionKHR(getBufferDeviceAddress(vkd, device, stageData.raygenShaderBindingTable->get(), 0), shaderGroupHandleSize, shaderGroupHandleSize);
stageData.hitShaderBindingTable = rayTracingPipeline->createShaderBindingTable(vkd, device, stageData.pipeline.get(), alloc, shaderGroupHandleSize, shaderGroupBaseAlignment, 1, 1);
stageData.hitShaderBindingTableRegion = makeStridedDeviceAddressRegionKHR(getBufferDeviceAddress(vkd, device, stageData.hitShaderBindingTable->get(), 0), shaderGroupHandleSize, shaderGroupHandleSize);
}
else if (stage == Stage::CLOSEST_HIT)
{
const auto rayTracingPipeline = de::newMovePtr<RayTracingPipeline>();
rayTracingPipeline->addShader(VK_SHADER_STAGE_RAYGEN_BIT_KHR, createShaderModule(vkd, device, context.getBinaryCollection().get(rgenAuxName), 0), 0);
rayTracingPipeline->addShader(VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR, createShaderModule(vkd, device, context.getBinaryCollection().get(chitName), 0), 1);
stageData.pipeline = rayTracingPipeline->createPipeline(vkd, device, stageData.pipelineLayout.get());
stageData.raygenShaderBindingTable = rayTracingPipeline->createShaderBindingTable(vkd, device, stageData.pipeline.get(), alloc, shaderGroupHandleSize, shaderGroupBaseAlignment, 0, 1);
stageData.raygenShaderBindingTableRegion = makeStridedDeviceAddressRegionKHR(getBufferDeviceAddress(vkd, device, stageData.raygenShaderBindingTable->get(), 0), shaderGroupHandleSize, shaderGroupHandleSize);
stageData.hitShaderBindingTable = rayTracingPipeline->createShaderBindingTable(vkd, device, stageData.pipeline.get(), alloc, shaderGroupHandleSize, shaderGroupBaseAlignment, 1, 1);
stageData.hitShaderBindingTableRegion = makeStridedDeviceAddressRegionKHR(getBufferDeviceAddress(vkd, device, stageData.hitShaderBindingTable->get(), 0), shaderGroupHandleSize, shaderGroupHandleSize);
}
else if (stage == Stage::MISS)
{
const auto rayTracingPipeline = de::newMovePtr<RayTracingPipeline>();
rayTracingPipeline->addShader(VK_SHADER_STAGE_RAYGEN_BIT_KHR, createShaderModule(vkd, device, context.getBinaryCollection().get(rgenAuxName), 0), 0);
rayTracingPipeline->addShader(VK_SHADER_STAGE_MISS_BIT_KHR, createShaderModule(vkd, device, context.getBinaryCollection().get(missName), 0), 1);
stageData.pipeline = rayTracingPipeline->createPipeline(vkd, device, stageData.pipelineLayout.get());
stageData.raygenShaderBindingTable = rayTracingPipeline->createShaderBindingTable(vkd, device, stageData.pipeline.get(), alloc, shaderGroupHandleSize, shaderGroupBaseAlignment, 0, 1);
stageData.raygenShaderBindingTableRegion = makeStridedDeviceAddressRegionKHR(getBufferDeviceAddress(vkd, device, stageData.raygenShaderBindingTable->get(), 0), shaderGroupHandleSize, shaderGroupHandleSize);
stageData.missShaderBindingTable = rayTracingPipeline->createShaderBindingTable(vkd, device, stageData.pipeline.get(), alloc, shaderGroupHandleSize, shaderGroupBaseAlignment, 1, 1);
stageData.missShaderBindingTableRegion = makeStridedDeviceAddressRegionKHR(getBufferDeviceAddress(vkd, device, stageData.missShaderBindingTable->get(), 0), shaderGroupHandleSize, shaderGroupHandleSize);
}
else if (stage == Stage::CALLABLE)
{
const auto rayTracingPipeline = de::newMovePtr<RayTracingPipeline>();
rayTracingPipeline->addShader(VK_SHADER_STAGE_RAYGEN_BIT_KHR, createShaderModule(vkd, device, context.getBinaryCollection().get(rgenAuxName), 0), 0);
rayTracingPipeline->addShader(VK_SHADER_STAGE_CALLABLE_BIT_KHR, createShaderModule(vkd, device, context.getBinaryCollection().get(callableName), 0), 1);
stageData.pipeline = rayTracingPipeline->createPipeline(vkd, device, stageData.pipelineLayout.get());
stageData.raygenShaderBindingTable = rayTracingPipeline->createShaderBindingTable(vkd, device, stageData.pipeline.get(), alloc, shaderGroupHandleSize, shaderGroupBaseAlignment, 0, 1);
stageData.raygenShaderBindingTableRegion = makeStridedDeviceAddressRegionKHR(getBufferDeviceAddress(vkd, device, stageData.raygenShaderBindingTable->get(), 0), shaderGroupHandleSize, shaderGroupHandleSize);
stageData.callableShaderBindingTable = rayTracingPipeline->createShaderBindingTable(vkd, device, stageData.pipeline.get(), alloc, shaderGroupHandleSize, shaderGroupBaseAlignment, 1, 1);
stageData.callableShaderBindingTableRegion = makeStridedDeviceAddressRegionKHR(getBufferDeviceAddress(vkd, device, stageData.callableShaderBindingTable->get(), 0), shaderGroupHandleSize, shaderGroupHandleSize);
}
else
{
DE_ASSERT(false);
}
}
// Auxiliar function to record commands using the ray tracing pipeline for the writer or reader stages.
void useRayTracingPipeline (const DeviceInterface& vkd, VkCommandBuffer cmdBuffer, StageData& stageData)
{
vkd.cmdBindPipeline(cmdBuffer, VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, stageData.pipeline.get());
vkd.cmdBindDescriptorSets(cmdBuffer, VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, stageData.pipelineLayout.get(), 0u, 1u, &stageData.descriptorSet.get(), 0u, nullptr);
vkd.cmdTraceRaysKHR(cmdBuffer, &stageData.raygenShaderBindingTableRegion, &stageData.missShaderBindingTableRegion, &stageData.hitShaderBindingTableRegion, &stageData.callableShaderBindingTableRegion, kImageDim, kImageDim, 1u);
}
tcu::TestStatus BarrierTestInstance::iterate (void)
{
const auto& vki = m_context.getInstanceInterface();
const auto physicalDevice = m_context.getPhysicalDevice();
const auto& vkd = m_context.getDeviceInterface();
const auto device = m_context.getDevice();
const auto queue = m_context.getUniversalQueue();
const auto familyIndex = m_context.getUniversalQueueFamilyIndex();
auto& alloc = m_context.getDefaultAllocator();
const auto imageSubresourceLayers = makeImageSubresourceLayers(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, 1u);
const bool rtInUse = (isRayTracingStage(m_params.readerStage) || isRayTracingStage(m_params.writerStage));
// Stage data for the writer and reader stages.
StageData writerStageData;
StageData readerStageData;
// Get some ray tracing properties.
deUint32 shaderGroupHandleSize = 0u;
deUint32 shaderGroupBaseAlignment = 1u;
if (rtInUse)
{
const auto rayTracingPropertiesKHR = makeRayTracingProperties(vki, physicalDevice);
shaderGroupHandleSize = rayTracingPropertiesKHR->getShaderGroupHandleSize();
shaderGroupBaseAlignment = rayTracingPropertiesKHR->getShaderGroupBaseAlignment();
}
// Shader stages involved.
const auto writerStages = getStageFlags(m_params.writerStage);
const auto readerStages = getStageFlags(m_params.readerStage);
const auto allStages = (writerStages | readerStages);
const bool writerNeedsAS = needsAccelerationStructure(m_params.writerStage);
const bool readerNeedsAS = needsAccelerationStructure(m_params.readerStage);
// Command buffer.
const auto cmdPool = makeCommandPool(vkd, device, familyIndex);
const auto cmdBufferPtr = allocateCommandBuffer(vkd, device, cmdPool.get(), VK_COMMAND_BUFFER_LEVEL_PRIMARY);
const auto cmdBuffer = cmdBufferPtr.get();
beginCommandBuffer(vkd, cmdBuffer);
std::unique_ptr<ImageWithMemory> resourceImg;
Move<VkImageView> resourceImgView;
VkImageLayout resourceImgLayout = VK_IMAGE_LAYOUT_UNDEFINED;
const auto resourceImgSubresourceRange = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u);
std::unique_ptr<BufferWithMemory> stagingBuffer;
std::unique_ptr<BufferWithMemory> resourceBuffer;
std::unique_ptr<BufferWithMemory> verificationBuffer;
const VkBufferUsageFlags stagingBufferFlags = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
// Create verification buffer for later use.
{
VkBufferUsageFlags verificationBufferFlags = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
if (m_params.readerStage == Stage::TRANSFER)
verificationBufferFlags |= VK_BUFFER_USAGE_TRANSFER_DST_BIT;
verificationBuffer = makeStd430Buffer(vkd, device, alloc, verificationBufferFlags);
}
// Create resource buffer or resource image.
if (m_params.resourceType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER || m_params.resourceType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER)
{
if (m_params.resourceType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER)
DE_ASSERT(m_params.writerStage == Stage::HOST || m_params.writerStage == Stage::TRANSFER);
VkBufferUsageFlags bufferFlags = ((m_params.resourceType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER)
? VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT
: VK_BUFFER_USAGE_STORAGE_BUFFER_BIT);
if (m_params.writerStage == Stage::TRANSFER)
bufferFlags |= VK_BUFFER_USAGE_TRANSFER_DST_BIT;
if (m_params.readerStage == Stage::TRANSFER)
bufferFlags |= VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
MemoryRequirement bufferMemReq = (resourceNeedsHostVisibleMemory(m_params) ? MemoryRequirement::HostVisible : MemoryRequirement::Any);
resourceBuffer = makeStd140Buffer(vkd, device, alloc, bufferFlags, bufferMemReq);
}
else if (m_params.resourceType == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE)
{
DE_ASSERT(m_params.writerStage != Stage::HOST);
VkImageUsageFlags imageUsage = VK_IMAGE_USAGE_STORAGE_BIT;
if (m_params.writerStage == Stage::TRANSFER)
imageUsage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
if (m_params.readerStage == Stage::TRANSFER)
imageUsage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
const VkImageCreateInfo resourceImageInfo =
{
VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType;
nullptr, // const void* pNext;
0u, // VkImageCreateFlags flags;
VK_IMAGE_TYPE_2D, // VkImageType imageType;
kImageFormat, // VkFormat format;
kImageExtent, // VkExtent3D extent;
1u, // deUint32 mipLevels;
1u, // deUint32 arrayLayers;
VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples;
VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling;
imageUsage, // VkImageUsageFlags usage;
VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
0u, // deUint32 queueFamilyIndexCount;
nullptr, // const deUint32* pQueueFamilyIndices;
VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout;
};
resourceImg.reset(new ImageWithMemory(vkd, device, alloc, resourceImageInfo, MemoryRequirement::Any));
resourceImgLayout = VK_IMAGE_LAYOUT_UNDEFINED;
// Image view.
resourceImgView = makeImageView(vkd, device, resourceImg->get(), VK_IMAGE_VIEW_TYPE_2D, kImageFormat, resourceImgSubresourceRange);
}
else
{
DE_ASSERT(false);
}
// Populate resource from the writer stage.
if (m_params.writerStage == Stage::HOST)
{
DE_ASSERT(m_params.resourceType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER || m_params.resourceType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
// Fill buffer data from the host.
fillStd140Buffer(vkd, device, *resourceBuffer);
}
else if (m_params.writerStage == Stage::TRANSFER)
{
// Similar to the previous one, but using a staging buffer.
if (m_params.resourceType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER || m_params.resourceType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER)
{
// Create and fill staging buffer.
stagingBuffer = makeStd140Buffer(vkd, device, alloc, stagingBufferFlags, MemoryRequirement::HostVisible);
fillStd140Buffer(vkd, device, *stagingBuffer);
// Fill resource buffer using a transfer operation.
const auto region = makeBufferCopy(0u, 0u, static_cast<VkDeviceSize>(kBufferSize));
const auto barrier = makeMemoryBarrier(VK_ACCESS_HOST_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT);
vkd.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u, 1u, &barrier, 0u, nullptr, 0u, nullptr);
vkd.cmdCopyBuffer(cmdBuffer, stagingBuffer->get(), resourceBuffer->get(), 1u, &region);
}
else if (m_params.resourceType == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE)
{
// Prepare staging buffer with packed pixels.
stagingBuffer = makeStd430BufferFilled(vkd, device, alloc, stagingBufferFlags);
// Barrier for the staging buffer.
const auto stagingBufferBarrier = makeMemoryBarrier(VK_ACCESS_HOST_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT);
vkd.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u, 1u, &stagingBufferBarrier, 0u, nullptr, 0u, nullptr);
// Transition image to the proper layout.
const auto expectedLayout = ((m_params.barrierType == BarrierType::SPECIFIC) ? VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL : VK_IMAGE_LAYOUT_GENERAL);
if (expectedLayout != resourceImgLayout)
{
const auto imgBarrier = makeImageMemoryBarrier(0u, VK_ACCESS_TRANSFER_WRITE_BIT, resourceImgLayout, expectedLayout, resourceImg->get(), resourceImgSubresourceRange);
vkd.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u, 0u, nullptr, 0u, nullptr, 1u, &imgBarrier);
resourceImgLayout = expectedLayout;
}
// Copy buffer to image.
const auto bufferImageCopy = makeBufferImageCopy(kImageExtent, imageSubresourceLayers);
vkd.cmdCopyBufferToImage(cmdBuffer, stagingBuffer->get(), resourceImg->get(), resourceImgLayout, 1u, &bufferImageCopy);
}
else
{
DE_ASSERT(false);
}
}
else
{
// Other cases use pipelines and a shader to fill the resource.
// Descriptor set layout.
DescriptorSetLayoutBuilder dslBuilder;
dslBuilder.addBinding(m_params.resourceType, 1u, allStages, nullptr); // The resource is used in the writer and reader stages.
if (writerNeedsAS)
dslBuilder.addBinding(VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, 1u, writerStages, nullptr);
writerStageData.descriptorSetLayout = dslBuilder.build(vkd, device);
// Pipeline layout.
writerStageData.pipelineLayout = makePipelineLayout(vkd, device, writerStageData.descriptorSetLayout.get());
// Descriptor pool and set.
DescriptorPoolBuilder poolBuilder;
poolBuilder.addType(m_params.resourceType);
if (writerNeedsAS)
poolBuilder.addType(VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR);
writerStageData.descriptorPool = poolBuilder.build(vkd, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u);
writerStageData.descriptorSet = makeDescriptorSet(vkd, device, writerStageData.descriptorPool.get(), writerStageData.descriptorSetLayout.get());
// Update descriptor set.
updateDescriptorSet(vkd, device, cmdBuffer, alloc, m_params.resourceType, m_params.writerStage, writerStageData, resourceBuffer.get(), resourceImgView.get(), VK_IMAGE_LAYOUT_GENERAL, writerNeedsAS, nullptr);
if (m_params.writerStage == Stage::COMPUTE)
{
createComputePipeline(vkd, device, m_context, "writer_comp", writerStageData);
if (m_params.resourceType == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE)
{
// Make sure the image is in the proper layout for shader writes.
const auto expectedLayout = VK_IMAGE_LAYOUT_GENERAL;
if (expectedLayout != resourceImgLayout)
{
const auto imgBarrier = makeImageMemoryBarrier(0u, VK_ACCESS_SHADER_WRITE_BIT, resourceImgLayout, expectedLayout, resourceImg->get(), resourceImgSubresourceRange);
vkd.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0u, 0u, nullptr, 0u, nullptr, 1u, &imgBarrier);
resourceImgLayout = expectedLayout;
}
}
// Generate the resource using the pipeline.
useComputePipeline(vkd, cmdBuffer, writerStageData);
}
else if (m_params.writerStage == Stage::FRAGMENT)
{
createGraphicsPipelineObjects(vkd, device, alloc, m_context, "writer_aux_vert", "writer_frag", writerStageData);
if (m_params.resourceType == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE)
{
// Make sure the image is in the proper layout for shader writes.
const auto expectedLayout = VK_IMAGE_LAYOUT_GENERAL;
if (expectedLayout != resourceImgLayout)
{
const auto imgBarrier = makeImageMemoryBarrier(0u, VK_ACCESS_SHADER_WRITE_BIT, resourceImgLayout, expectedLayout, resourceImg->get(), resourceImgSubresourceRange);
vkd.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0u, 0u, nullptr, 0u, nullptr, 1u, &imgBarrier);
resourceImgLayout = expectedLayout;
}
}
useGraphicsPipeline(vkd, cmdBuffer, writerStageData);
}
else
{
createRayTracingPipelineData(vkd, device, alloc, m_context, m_params.writerStage, writerStageData, shaderGroupHandleSize, shaderGroupBaseAlignment,
"writer_aux_rgen", "writer_rgen", "writer_isect", "writer_ahit", "writer_chit", "writer_miss", "writer_callable");
if (m_params.resourceType == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE)
{
// Make sure the image is in the proper layout for shader writes.
const auto expectedLayout = VK_IMAGE_LAYOUT_GENERAL;
if (expectedLayout != resourceImgLayout)
{
const auto imgBarrier = makeImageMemoryBarrier(0u, VK_ACCESS_SHADER_WRITE_BIT, resourceImgLayout, expectedLayout, resourceImg->get(), resourceImgSubresourceRange);
vkd.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR, 0u, 0u, nullptr, 0u, nullptr, 1u, &imgBarrier);
resourceImgLayout = expectedLayout;
}
}
useRayTracingPipeline(vkd, cmdBuffer, writerStageData);
}
}
// Main barrier to synchronize the writer stage to the reader stage.
const auto writerPipelineStage = getPipelineStage(m_params.writerStage);
const auto readerPipelineStage = getPipelineStage(m_params.readerStage);
const auto writerAccessFlag = getWriterAccessFlag(m_params.writerStage);
const auto readerAccessFlag = getReaderAccessFlag(m_params.readerStage, m_params.resourceType);
if (m_params.barrierType == BarrierType::GENERAL)
{
const auto memoryBarrier = makeMemoryBarrier(writerAccessFlag, readerAccessFlag);
vkd.cmdPipelineBarrier(cmdBuffer, writerPipelineStage, readerPipelineStage, 0u, 1u, &memoryBarrier, 0u, nullptr, 0u, nullptr);
// Note the image will remain in the general layout in this case.
if (m_params.resourceType == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE)
DE_ASSERT(resourceImgLayout == VK_IMAGE_LAYOUT_GENERAL);
}
else if (m_params.barrierType == BarrierType::SPECIFIC)
{
if (m_params.resourceType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER || m_params.resourceType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER)
{
const auto bufferBarrier = makeBufferMemoryBarrier(writerAccessFlag, readerAccessFlag, resourceBuffer->get(), 0ull, VK_WHOLE_SIZE);
vkd.cmdPipelineBarrier(cmdBuffer, writerPipelineStage, readerPipelineStage, 0u, 0u, nullptr, 1u, &bufferBarrier, 0u, nullptr);
}
else if (m_params.resourceType == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE)
{
// We'll switch the image layout from the current layout to the one the reader expects.
const auto newLayout = getOptimalReadLayout(m_params.readerStage);
const auto imageBarrier = makeImageMemoryBarrier(writerAccessFlag, readerAccessFlag, resourceImgLayout, newLayout, resourceImg->get(), resourceImgSubresourceRange);
vkd.cmdPipelineBarrier(cmdBuffer, writerPipelineStage, readerPipelineStage, 0u, 0u, nullptr, 0u, nullptr, 1u, &imageBarrier);
resourceImgLayout = newLayout;
}
else
{
DE_ASSERT(false);
}
}
else
{
DE_ASSERT(false);
}
// Read resource from the reader stage copying it to the verification buffer.
if (m_params.readerStage == Stage::HOST)
{
// This needs to wait until we have submitted the command buffer. See below.
}
else if (m_params.readerStage == Stage::TRANSFER)
{
if (m_params.resourceType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER || m_params.resourceType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER)
{
// This is a bit tricky because the resource buffer is in std140 format and the verification buffer is in std430 format.
std::vector<VkBufferCopy> regions;
regions.reserve(kBufferElements);
for (deUint32 i = 0; i < kBufferElements; ++i)
{
const VkBufferCopy region =
{
static_cast<VkDeviceSize>(i * sizeof(tcu::UVec4)), // VkDeviceSize srcOffset;
static_cast<VkDeviceSize>(i * sizeof(deUint32)), // VkDeviceSize dstOffset;
static_cast<VkDeviceSize>(sizeof(deUint32)), // VkDeviceSize size;
};
regions.push_back(region);
}
vkd.cmdCopyBuffer(cmdBuffer, resourceBuffer->get(), verificationBuffer->get(), static_cast<deUint32>(regions.size()), regions.data());
}
else if (m_params.resourceType == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE)
{
const auto bufferImageCopyRegion = makeBufferImageCopy(kImageExtent, imageSubresourceLayers);
vkd.cmdCopyImageToBuffer(cmdBuffer, resourceImg->get(), resourceImgLayout, verificationBuffer->get(), 1u, &bufferImageCopyRegion);
}
else
{
DE_ASSERT(false);
}
}
else
{
// All other stages use shaders to read the resource into the verification buffer.
// Descriptor set layout.
DescriptorSetLayoutBuilder dslBuilder;
dslBuilder.addBinding(m_params.resourceType, 1u, allStages, nullptr); // Resource accessed in writers and readers.
if (readerNeedsAS)
dslBuilder.addBinding(VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, 1u, readerStages, nullptr);
dslBuilder.addBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1u, readerStages, nullptr); // Verification buffer.
readerStageData.descriptorSetLayout = dslBuilder.build(vkd, device);
// Pipeline layout.
readerStageData.pipelineLayout = makePipelineLayout(vkd, device, readerStageData.descriptorSetLayout.get());
// Descriptor pool and set.
DescriptorPoolBuilder poolBuilder;
poolBuilder.addType(m_params.resourceType);
if (readerNeedsAS)
poolBuilder.addType(VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR);
poolBuilder.addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
readerStageData.descriptorPool = poolBuilder.build(vkd, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u);
readerStageData.descriptorSet = makeDescriptorSet(vkd, device, readerStageData.descriptorPool.get(), readerStageData.descriptorSetLayout.get());
// Update descriptor set.
updateDescriptorSet(vkd, device, cmdBuffer, alloc, m_params.resourceType, m_params.readerStage, readerStageData, resourceBuffer.get(), resourceImgView.get(), resourceImgLayout, readerNeedsAS, verificationBuffer.get());
if (m_params.readerStage == Stage::COMPUTE)
{
createComputePipeline(vkd, device, m_context, "reader_comp", readerStageData);
useComputePipeline(vkd, cmdBuffer, readerStageData);
}
else if (m_params.readerStage == Stage::FRAGMENT)
{
createGraphicsPipelineObjects(vkd, device, alloc, m_context, "reader_aux_vert", "reader_frag", readerStageData);
useGraphicsPipeline(vkd, cmdBuffer, readerStageData);
}
else
{
createRayTracingPipelineData(vkd, device, alloc, m_context, m_params.readerStage, readerStageData, shaderGroupHandleSize, shaderGroupBaseAlignment,
"reader_aux_rgen", "reader_rgen", "reader_isect", "reader_ahit", "reader_chit", "reader_miss", "reader_callable");
useRayTracingPipeline(vkd, cmdBuffer, readerStageData);
}
}
// Sync verification buffer.
{
const auto readerVerificationFlags = getWriterAccessFlag(m_params.readerStage);
const auto barrier = makeBufferMemoryBarrier(readerVerificationFlags, VK_ACCESS_HOST_READ_BIT, verificationBuffer->get(), 0ull, VK_WHOLE_SIZE);
vkd.cmdPipelineBarrier(cmdBuffer, readerPipelineStage, VK_PIPELINE_STAGE_HOST_BIT, 0u, 0u, nullptr, 1u, &barrier, 0u, nullptr);
}
// Submit all recorded commands.
endCommandBuffer(vkd, cmdBuffer);
submitCommandsAndWait(vkd, device, queue, cmdBuffer);
invalidateAlloc(vkd, device, verificationBuffer->getAllocation());
// If the reader stage is the host, we have to wait until the commands have been submitted and the work has been done.
if (m_params.readerStage == Stage::HOST)
{
DE_ASSERT(m_params.resourceType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER || m_params.resourceType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
auto& resourceBufferAlloc = resourceBuffer->getAllocation();
auto* resourceBufferPtr = resourceBufferAlloc.getHostPtr();
std::vector<tcu::UVec4> resourceData(kBufferElements);
invalidateAlloc(vkd, device, resourceBufferAlloc);
deMemcpy(resourceData.data(), resourceBufferPtr, static_cast<size_t>(kBufferElements) * sizeof(tcu::UVec4));
// Convert from std140 to std430 on the host.
std::vector<deUint32> verificationData(kBufferElements);
std::transform(begin(resourceData), end(resourceData), begin(verificationData),
[](const tcu::UVec4 &v) -> deUint32 { return v.x(); });
auto& verificationBufferAlloc = verificationBuffer->getAllocation();
auto* verificationBufferPtr = verificationBufferAlloc.getHostPtr();
deMemcpy(verificationBufferPtr, verificationData.data(), static_cast<size_t>(kBufferElements) * sizeof(deUint32));
flushAlloc(vkd, device, verificationBufferAlloc);
}
// Check verification buffer on the host.
{
auto& verificationAlloc = verificationBuffer->getAllocation();
auto* verificationPtr = verificationAlloc.getHostPtr();
std::vector<deUint32> verificationData(kBufferElements);
deMemcpy(verificationData.data(), verificationPtr, static_cast<size_t>(kBufferElements) * sizeof(deUint32));
for (size_t i = 0; i < verificationData.size(); ++i)
{
const auto& value = verificationData[i];
const auto expected = kValuesOffset + i;
if (value != expected)
{
std::ostringstream msg;
msg << "Unexpected value found at position " << i << ": found " << value << " and expected " << expected;
return tcu::TestStatus::fail(msg.str());
}
}
}
return tcu::TestStatus::pass("Pass");
}
} // anonymous.
tcu::TestCaseGroup* createBarrierTests(tcu::TestContext& testCtx)
{
de::MovePtr<tcu::TestCaseGroup> group(new tcu::TestCaseGroup(testCtx, "barrier", "Tests involving pipeline barriers and ray tracing"));
const struct
{
VkDescriptorType type;
const char* name;
} resourceTypes[] =
{
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, "ubo" },
{ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, "ssbo" },
{ VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, "simg" },
};
const struct
{
Stage stage;
const char* name;
} stageList[] =
{
{ Stage::HOST, "host" },
{ Stage::TRANSFER, "xfer" },
{ Stage::RAYGEN, "rgen" },
{ Stage::INTERSECT, "isec" },
{ Stage::ANY_HIT, "ahit" },
{ Stage::CLOSEST_HIT, "chit" },
{ Stage::MISS, "miss" },
{ Stage::CALLABLE, "call" },
{ Stage::COMPUTE, "comp" },
{ Stage::FRAGMENT, "frag" },
};
const struct
{
BarrierType barrierType;
const char* name;
} barrierTypes[] =
{
{ BarrierType::GENERAL, "memory_barrier" },
{ BarrierType::SPECIFIC, "specific_barrier" },
};
for (int resourceTypeIdx = 0; resourceTypeIdx < DE_LENGTH_OF_ARRAY(resourceTypes); ++resourceTypeIdx)
{
de::MovePtr<tcu::TestCaseGroup> resourceTypeGroup(new tcu::TestCaseGroup(testCtx, resourceTypes[resourceTypeIdx].name, ""));
for (int barrierTypeIdx = 0; barrierTypeIdx < DE_LENGTH_OF_ARRAY(barrierTypes); ++barrierTypeIdx)
{
de::MovePtr<tcu::TestCaseGroup> barrierTypeGroup(new tcu::TestCaseGroup(testCtx, barrierTypes[barrierTypeIdx].name, ""));
for (int writerStageIdx = 0; writerStageIdx < DE_LENGTH_OF_ARRAY(stageList); ++writerStageIdx)
for (int readerStageIdx = 0; readerStageIdx < DE_LENGTH_OF_ARRAY(stageList); ++readerStageIdx)
{
const auto resourceType = resourceTypes[resourceTypeIdx].type;
const auto barrierType = barrierTypes[barrierTypeIdx].barrierType;
const auto readerStage = stageList[readerStageIdx].stage;
const auto writerStage = stageList[writerStageIdx].stage;
// Skip tests that do not involve ray tracing.
if (!isRayTracingStage(readerStage) && !isRayTracingStage(writerStage))
continue;
// Skip tests which require host acess to images.
if (resourceType == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE && (writerStage == Stage::HOST || readerStage == Stage::HOST))
continue;
// Skip tests that would require writes from shaders to an UBO.
if (resourceType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER && writerStage != Stage::HOST && writerStage != Stage::TRANSFER)
continue;
const std::string testName = std::string("from_") + stageList[writerStageIdx].name + "_to_" + stageList[readerStageIdx].name;
barrierTypeGroup->addChild(new BarrierTestCase(testCtx, testName, "", TestParams(resourceType, writerStage, readerStage, barrierType)));
}
resourceTypeGroup->addChild(barrierTypeGroup.release());
}
group->addChild(resourceTypeGroup.release());
}
return group.release();
}
} // RayTracing
} // vkt