blob: a8828c43e7c7730926546e5278fbde08d57810d3 [file] [log] [blame]
/*------------------------------------------------------------------------
* Vulkan Conformance Tests
* ------------------------
*
* Copyright (c) 2016 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 vktPipelineMultisampleShaderBuiltInTests.cpp
* \brief Multisample Shader BuiltIn Tests
*//*--------------------------------------------------------------------*/
#include "vktPipelineMultisampleShaderBuiltInTests.hpp"
#include "vktPipelineMultisampleBaseResolveAndPerSampleFetch.hpp"
#include "vktPipelineMakeUtil.hpp"
#include "vkBuilderUtil.hpp"
#include "vkQueryUtil.hpp"
#include "vkObjUtil.hpp"
#include "vkImageWithMemory.hpp"
#include "vkBufferWithMemory.hpp"
#include "vkBarrierUtil.hpp"
#include "vkCmdUtil.hpp"
#include "vkTypeUtil.hpp"
#include "tcuVectorUtil.hpp"
#include "tcuTestLog.hpp"
#include <set>
using std::set;
namespace vkt
{
namespace pipeline
{
namespace multisample
{
using namespace vk;
struct VertexDataNdc
{
VertexDataNdc (const tcu::Vec4& posNdc) : positionNdc(posNdc) {}
tcu::Vec4 positionNdc;
};
MultisampleInstanceBase::VertexDataDesc getVertexDataDescriptonNdc (void)
{
MultisampleInstanceBase::VertexDataDesc vertexDataDesc;
vertexDataDesc.verticesCount = 4u;
vertexDataDesc.dataStride = sizeof(VertexDataNdc);
vertexDataDesc.dataSize = vertexDataDesc.verticesCount * vertexDataDesc.dataStride;
vertexDataDesc.primitiveTopology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
const VkVertexInputAttributeDescription vertexAttribPositionNdc =
{
0u, // deUint32 location;
0u, // deUint32 binding;
VK_FORMAT_R32G32B32A32_SFLOAT, // VkFormat format;
DE_OFFSET_OF(VertexDataNdc, positionNdc), // deUint32 offset;
};
vertexDataDesc.vertexAttribDescVec.push_back(vertexAttribPositionNdc);
return vertexDataDesc;
}
void uploadVertexDataNdc (const Allocation& vertexBufferAllocation, const MultisampleInstanceBase::VertexDataDesc& vertexDataDescripton)
{
std::vector<VertexDataNdc> vertices;
vertices.push_back(VertexDataNdc(tcu::Vec4(-1.0f, -1.0f, 0.0f, 1.0f)));
vertices.push_back(VertexDataNdc(tcu::Vec4( 1.0f, -1.0f, 0.0f, 1.0f)));
vertices.push_back(VertexDataNdc(tcu::Vec4(-1.0f, 1.0f, 0.0f, 1.0f)));
vertices.push_back(VertexDataNdc(tcu::Vec4( 1.0f, 1.0f, 0.0f, 1.0f)));
deMemcpy(vertexBufferAllocation.getHostPtr(), dataPointer(vertices), static_cast<std::size_t>(vertexDataDescripton.dataSize));
}
struct VertexDataNdcScreen
{
VertexDataNdcScreen (const tcu::Vec4& posNdc, const tcu::Vec2& posScreen) : positionNdc(posNdc), positionScreen(posScreen) {}
tcu::Vec4 positionNdc;
tcu::Vec2 positionScreen;
};
MultisampleInstanceBase::VertexDataDesc getVertexDataDescriptonNdcScreen (void)
{
MultisampleInstanceBase::VertexDataDesc vertexDataDesc;
vertexDataDesc.verticesCount = 4u;
vertexDataDesc.dataStride = sizeof(VertexDataNdcScreen);
vertexDataDesc.dataSize = vertexDataDesc.verticesCount * vertexDataDesc.dataStride;
vertexDataDesc.primitiveTopology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
const VkVertexInputAttributeDescription vertexAttribPositionNdc =
{
0u, // deUint32 location;
0u, // deUint32 binding;
VK_FORMAT_R32G32B32A32_SFLOAT, // VkFormat format;
DE_OFFSET_OF(VertexDataNdcScreen, positionNdc), // deUint32 offset;
};
vertexDataDesc.vertexAttribDescVec.push_back(vertexAttribPositionNdc);
const VkVertexInputAttributeDescription vertexAttribPositionScreen =
{
1u, // deUint32 location;
0u, // deUint32 binding;
VK_FORMAT_R32G32_SFLOAT, // VkFormat format;
DE_OFFSET_OF(VertexDataNdcScreen, positionScreen), // deUint32 offset;
};
vertexDataDesc.vertexAttribDescVec.push_back(vertexAttribPositionScreen);
return vertexDataDesc;
}
void uploadVertexDataNdcScreen (const Allocation& vertexBufferAllocation, const MultisampleInstanceBase::VertexDataDesc& vertexDataDescripton, const tcu::Vec2& screenSize)
{
std::vector<VertexDataNdcScreen> vertices;
vertices.push_back(VertexDataNdcScreen(tcu::Vec4(-1.0f, -1.0f, 0.0f, 1.0f), tcu::Vec2(0.0f, 0.0f)));
vertices.push_back(VertexDataNdcScreen(tcu::Vec4( 1.0f, -1.0f, 0.0f, 1.0f), tcu::Vec2(screenSize.x(), 0.0f)));
vertices.push_back(VertexDataNdcScreen(tcu::Vec4(-1.0f, 1.0f, 0.0f, 1.0f), tcu::Vec2(0.0f, screenSize.y())));
vertices.push_back(VertexDataNdcScreen(tcu::Vec4( 1.0f, 1.0f, 0.0f, 1.0f), tcu::Vec2(screenSize.x(), screenSize.y())));
deMemcpy(vertexBufferAllocation.getHostPtr(), dataPointer(vertices), static_cast<std::size_t>(vertexDataDescripton.dataSize));
}
bool checkForErrorMS (const vk::VkImageCreateInfo& imageMSInfo, const std::vector<tcu::ConstPixelBufferAccess>& dataPerSample, const deUint32 errorCompNdx)
{
const deUint32 numSamples = static_cast<deUint32>(imageMSInfo.samples);
for (deUint32 z = 0u; z < imageMSInfo.extent.depth; ++z)
for (deUint32 y = 0u; y < imageMSInfo.extent.height; ++y)
for (deUint32 x = 0u; x < imageMSInfo.extent.width; ++x)
{
for (deUint32 sampleNdx = 0u; sampleNdx < numSamples; ++sampleNdx)
{
const deUint32 errorComponent = dataPerSample[sampleNdx].getPixelUint(x, y, z)[errorCompNdx];
if (errorComponent > 0)
return true;
}
}
return false;
}
bool checkForErrorRS (const vk::VkImageCreateInfo& imageRSInfo, const tcu::ConstPixelBufferAccess& dataRS, const deUint32 errorCompNdx)
{
for (deUint32 z = 0u; z < imageRSInfo.extent.depth; ++z)
for (deUint32 y = 0u; y < imageRSInfo.extent.height; ++y)
for (deUint32 x = 0u; x < imageRSInfo.extent.width; ++x)
{
const deUint32 errorComponent = dataRS.getPixelUint(x, y, z)[errorCompNdx];
if (errorComponent > 0)
return true;
}
return false;
}
template <typename CaseClassName>
class MSCase : public MSCaseBaseResolveAndPerSampleFetch
{
public:
MSCase (tcu::TestContext& testCtx,
const std::string& name,
const ImageMSParams& imageMSParams)
: MSCaseBaseResolveAndPerSampleFetch(testCtx, name, imageMSParams) {}
virtual void checkSupport (Context&) const {}
void init (void);
void initPrograms (vk::SourceCollections& programCollection) const;
TestInstance* createInstance (Context& context) const;
static MultisampleCaseBase* createCase (tcu::TestContext& testCtx,
const std::string& name,
const ImageMSParams& imageMSParams);
};
template <typename CaseClassName>
MultisampleCaseBase* MSCase<CaseClassName>::createCase (tcu::TestContext& testCtx, const std::string& name, const ImageMSParams& imageMSParams)
{
return new MSCase<CaseClassName>(testCtx, name, imageMSParams);
}
template <typename InstanceClassName>
class MSInstance : public MSInstanceBaseResolveAndPerSampleFetch
{
public:
MSInstance (Context& context,
const ImageMSParams& imageMSParams)
: MSInstanceBaseResolveAndPerSampleFetch(context, imageMSParams) {}
VertexDataDesc getVertexDataDescripton (void) const;
void uploadVertexData (const Allocation& vertexBufferAllocation,
const VertexDataDesc& vertexDataDescripton) const;
tcu::TestStatus verifyImageData (const vk::VkImageCreateInfo& imageMSInfo,
const vk::VkImageCreateInfo& imageRSInfo,
const std::vector<tcu::ConstPixelBufferAccess>& dataPerSample,
const tcu::ConstPixelBufferAccess& dataRS) const;
virtual VkPipelineMultisampleStateCreateInfo getMSStateCreateInfo (const ImageMSParams& imageMSParams) const
{
return MSInstanceBaseResolveAndPerSampleFetch::getMSStateCreateInfo(imageMSParams);
}
};
class MSInstanceSampleID;
template<> MultisampleInstanceBase::VertexDataDesc MSInstance<MSInstanceSampleID>::getVertexDataDescripton (void) const
{
return getVertexDataDescriptonNdc();
}
template<> void MSInstance<MSInstanceSampleID>::uploadVertexData (const Allocation& vertexBufferAllocation, const VertexDataDesc& vertexDataDescripton) const
{
uploadVertexDataNdc(vertexBufferAllocation, vertexDataDescripton);
}
template<> tcu::TestStatus MSInstance<MSInstanceSampleID>::verifyImageData (const vk::VkImageCreateInfo& imageMSInfo,
const vk::VkImageCreateInfo& imageRSInfo,
const std::vector<tcu::ConstPixelBufferAccess>& dataPerSample,
const tcu::ConstPixelBufferAccess& dataRS) const
{
DE_UNREF(imageRSInfo);
DE_UNREF(dataRS);
const deUint32 numSamples = static_cast<deUint32>(imageMSInfo.samples);
for (deUint32 sampleNdx = 0u; sampleNdx < numSamples; ++sampleNdx)
{
for (deUint32 z = 0u; z < imageMSInfo.extent.depth; ++z)
for (deUint32 y = 0u; y < imageMSInfo.extent.height; ++y)
for (deUint32 x = 0u; x < imageMSInfo.extent.width; ++x)
{
const deUint32 sampleID = dataPerSample[sampleNdx].getPixelUint(x, y, z).x();
if (sampleID != sampleNdx)
return tcu::TestStatus::fail("gl_SampleID does not have correct value");
}
}
return tcu::TestStatus::pass("Passed");
}
class MSCaseSampleID;
template<> void MSCase<MSCaseSampleID>::checkSupport (Context& context) const
{
context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_SAMPLE_RATE_SHADING);
}
template<> void MSCase<MSCaseSampleID>::init (void)
{
m_testCtx.getLog()
<< tcu::TestLog::Message
<< "Writing gl_SampleID to the red channel of the texture and verifying texture values.\n"
<< "Expecting value N at sample index N of a multisample texture.\n"
<< tcu::TestLog::EndMessage;
MultisampleCaseBase::init();
}
template<> void MSCase<MSCaseSampleID>::initPrograms (vk::SourceCollections& programCollection) const
{
MSCaseBaseResolveAndPerSampleFetch::initPrograms(programCollection);
// Create vertex shader
std::ostringstream vs;
vs << "#version 440\n"
<< "layout(location = 0) in vec4 vs_in_position_ndc;\n"
<< "\n"
<< "out gl_PerVertex {\n"
<< " vec4 gl_Position;\n"
<< "};\n"
<< "void main (void)\n"
<< "{\n"
<< " gl_Position = vs_in_position_ndc;\n"
<< "}\n";
programCollection.glslSources.add("vertex_shader") << glu::VertexSource(vs.str());
// Create fragment shader
std::ostringstream fs;
fs << "#version 440\n"
<< "\n"
<< "layout(location = 0) out vec4 fs_out_color;\n"
<< "\n"
<< "void main (void)\n"
<< "{\n"
<< " fs_out_color = vec4(float(gl_SampleID) / float(255), 0.0, 0.0, 1.0);\n"
<< "}\n";
programCollection.glslSources.add("fragment_shader") << glu::FragmentSource(fs.str());
}
template<> TestInstance* MSCase<MSCaseSampleID>::createInstance (Context& context) const
{
return new MSInstance<MSInstanceSampleID>(context, m_imageMSParams);
}
class MSInstanceSamplePosDistribution;
template<> MultisampleInstanceBase::VertexDataDesc MSInstance<MSInstanceSamplePosDistribution>::getVertexDataDescripton (void) const
{
return getVertexDataDescriptonNdc();
}
template<> void MSInstance<MSInstanceSamplePosDistribution>::uploadVertexData (const Allocation& vertexBufferAllocation, const VertexDataDesc& vertexDataDescripton) const
{
uploadVertexDataNdc(vertexBufferAllocation, vertexDataDescripton);
}
template<> tcu::TestStatus MSInstance<MSInstanceSamplePosDistribution>::verifyImageData (const vk::VkImageCreateInfo& imageMSInfo,
const vk::VkImageCreateInfo& imageRSInfo,
const std::vector<tcu::ConstPixelBufferAccess>& dataPerSample,
const tcu::ConstPixelBufferAccess& dataRS) const
{
const deUint32 numSamples = static_cast<deUint32>(imageMSInfo.samples);
// approximate Bates distribution as normal
const float variance = (1.0f / (12.0f * (float)numSamples));
const float standardDeviation = deFloatSqrt(variance);
// 95% of means of sample positions are within 2 standard deviations if
// they were randomly assigned. Sample patterns are expected to be more
// uniform than a random pattern.
const float distanceThreshold = 2.0f * standardDeviation;
for (deUint32 z = 0u; z < imageRSInfo.extent.depth; ++z)
for (deUint32 y = 0u; y < imageRSInfo.extent.height; ++y)
for (deUint32 x = 0u; x < imageRSInfo.extent.width; ++x)
{
const deUint32 errorComponent = dataRS.getPixelUint(x, y, z).z();
if (errorComponent > 0)
return tcu::TestStatus::fail("gl_SamplePosition is not within interval [0,1]");
if (numSamples >= VK_SAMPLE_COUNT_4_BIT)
{
const tcu::Vec2 averageSamplePos = tcu::Vec2((float)dataRS.getPixelUint(x, y, z).x() / 255.0f, (float)dataRS.getPixelUint(x, y, z).y() / 255.0f);
const tcu::Vec2 distanceFromCenter = tcu::abs(averageSamplePos - tcu::Vec2(0.5f, 0.5f));
if (distanceFromCenter.x() > distanceThreshold || distanceFromCenter.y() > distanceThreshold)
return tcu::TestStatus::fail("Sample positions are not uniformly distributed within the pixel");
}
}
for (deUint32 z = 0u; z < imageMSInfo.extent.depth; ++z)
for (deUint32 y = 0u; y < imageMSInfo.extent.height; ++y)
for (deUint32 x = 0u; x < imageMSInfo.extent.width; ++x)
{
std::vector<tcu::Vec2> samplePositions(numSamples);
for (deUint32 sampleNdx = 0u; sampleNdx < numSamples; ++sampleNdx)
{
const deUint32 errorComponent = dataPerSample[sampleNdx].getPixelUint(x, y, z).z();
if (errorComponent > 0)
return tcu::TestStatus::fail("gl_SamplePosition is not within interval [0,1]");
samplePositions[sampleNdx] = tcu::Vec2( (float)dataPerSample[sampleNdx].getPixelUint(x, y, z).x() / 255.0f,
(float)dataPerSample[sampleNdx].getPixelUint(x, y, z).y() / 255.0f);
}
for (deUint32 sampleNdxA = 0u; sampleNdxA < numSamples; ++sampleNdxA)
for (deUint32 sampleNdxB = sampleNdxA + 1u; sampleNdxB < numSamples; ++sampleNdxB)
{
if (samplePositions[sampleNdxA] == samplePositions[sampleNdxB])
return tcu::TestStatus::fail("Two samples have the same position");
}
if (numSamples >= VK_SAMPLE_COUNT_4_BIT)
{
tcu::Vec2 averageSamplePos(0.0f, 0.0f);
for (deUint32 sampleNdx = 0u; sampleNdx < numSamples; ++sampleNdx)
{
averageSamplePos.x() += samplePositions[sampleNdx].x();
averageSamplePos.y() += samplePositions[sampleNdx].y();
}
averageSamplePos.x() /= (float)numSamples;
averageSamplePos.y() /= (float)numSamples;
const tcu::Vec2 distanceFromCenter = tcu::abs(averageSamplePos - tcu::Vec2(0.5f, 0.5f));
if (distanceFromCenter.x() > distanceThreshold || distanceFromCenter.y() > distanceThreshold)
return tcu::TestStatus::fail("Sample positions are not uniformly distributed within the pixel");
}
}
return tcu::TestStatus::pass("Passed");
}
class MSCaseSamplePosDistribution;
template<> void MSCase<MSCaseSamplePosDistribution>::checkSupport (Context& context) const
{
context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_SAMPLE_RATE_SHADING);
}
template<> void MSCase<MSCaseSamplePosDistribution>::init (void)
{
m_testCtx.getLog()
<< tcu::TestLog::Message
<< "Verifying gl_SamplePosition value with multisample targets:\n"
<< " a) Expect legal sample position.\n"
<< " b) Sample position is unique within the set of all sample positions of a pixel.\n"
<< " c) Sample position distribution is uniform or almost uniform.\n"
<< tcu::TestLog::EndMessage;
MultisampleCaseBase::init();
}
template<> void MSCase<MSCaseSamplePosDistribution>::initPrograms (vk::SourceCollections& programCollection) const
{
MSCaseBaseResolveAndPerSampleFetch::initPrograms(programCollection);
// Create vertex shader
std::ostringstream vs;
vs << "#version 440\n"
<< "layout(location = 0) in vec4 vs_in_position_ndc;\n"
<< "\n"
<< "out gl_PerVertex {\n"
<< " vec4 gl_Position;\n"
<< "};\n"
<< "void main (void)\n"
<< "{\n"
<< " gl_Position = vs_in_position_ndc;\n"
<< "}\n";
programCollection.glslSources.add("vertex_shader") << glu::VertexSource(vs.str());
// Create fragment shader
std::ostringstream fs;
fs << "#version 440\n"
<< "\n"
<< "layout(location = 0) out vec4 fs_out_color;\n"
<< "\n"
<< "void main (void)\n"
<< "{\n"
<< " if (gl_SamplePosition.x < 0.0 || gl_SamplePosition.x > 1.0 || gl_SamplePosition.y < 0.0 || gl_SamplePosition.y > 1.0)\n"
" fs_out_color = vec4(0.0, 0.0, 1.0, 1.0);\n"
" else\n"
" fs_out_color = vec4(gl_SamplePosition.x, gl_SamplePosition.y, 0.0, 1.0);\n"
"}\n";
programCollection.glslSources.add("fragment_shader") << glu::FragmentSource(fs.str());
}
template<> TestInstance* MSCase<MSCaseSamplePosDistribution>::createInstance (Context& context) const
{
return new MSInstance<MSInstanceSamplePosDistribution>(context, m_imageMSParams);
}
class MSInstanceSamplePosCorrectness;
template<> MultisampleInstanceBase::VertexDataDesc MSInstance<MSInstanceSamplePosCorrectness>::getVertexDataDescripton (void) const
{
return getVertexDataDescriptonNdcScreen();
}
template<> void MSInstance<MSInstanceSamplePosCorrectness>::uploadVertexData (const Allocation& vertexBufferAllocation, const VertexDataDesc& vertexDataDescripton) const
{
const tcu::UVec3 layerSize = getLayerSize(IMAGE_TYPE_2D, m_imageMSParams.imageSize);
uploadVertexDataNdcScreen(vertexBufferAllocation, vertexDataDescripton, tcu::Vec2(static_cast<float>(layerSize.x()), static_cast<float>(layerSize.y())));
}
template<> tcu::TestStatus MSInstance<MSInstanceSamplePosCorrectness>::verifyImageData (const vk::VkImageCreateInfo& imageMSInfo,
const vk::VkImageCreateInfo& imageRSInfo,
const std::vector<tcu::ConstPixelBufferAccess>& dataPerSample,
const tcu::ConstPixelBufferAccess& dataRS) const
{
if (checkForErrorMS(imageMSInfo, dataPerSample, 0))
return tcu::TestStatus::fail("Varying values are not sampled at gl_SamplePosition");
if (checkForErrorRS(imageRSInfo, dataRS, 0))
return tcu::TestStatus::fail("Varying values are not sampled at gl_SamplePosition");
return tcu::TestStatus::pass("Passed");
}
class MSCaseSamplePosCorrectness;
template<> void MSCase<MSCaseSamplePosCorrectness>::checkSupport (Context& context) const
{
context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_SAMPLE_RATE_SHADING);
}
template<> void MSCase<MSCaseSamplePosCorrectness>::init (void)
{
m_testCtx.getLog()
<< tcu::TestLog::Message
<< "Verifying gl_SamplePosition correctness:\n"
<< " 1) Varying values should be sampled at the sample position.\n"
<< " => fract(position_screen) == gl_SamplePosition\n"
<< tcu::TestLog::EndMessage;
MultisampleCaseBase::init();
}
template<> void MSCase<MSCaseSamplePosCorrectness>::initPrograms (vk::SourceCollections& programCollection) const
{
MSCaseBaseResolveAndPerSampleFetch::initPrograms(programCollection);
// Create vertex shaders
std::ostringstream vs;
vs << "#version 440\n"
<< "layout(location = 0) in vec4 vs_in_position_ndc;\n"
<< "layout(location = 1) in vec2 vs_in_position_screen;\n"
<< "\n"
<< "layout(location = 0) sample out vec2 vs_out_position_screen;\n"
<< "\n"
<< "out gl_PerVertex {\n"
<< " vec4 gl_Position;\n"
<< "};\n"
<< "void main (void)\n"
<< "{\n"
<< " gl_Position = vs_in_position_ndc;\n"
<< " vs_out_position_screen = vs_in_position_screen;\n"
<< "}\n";
programCollection.glslSources.add("vertex_shader") << glu::VertexSource(vs.str());
// Create fragment shader
std::ostringstream fs;
fs << "#version 440\n"
<< "layout(location = 0) sample in vec2 fs_in_position_screen;\n"
<< "\n"
<< "layout(location = 0) out vec4 fs_out_color;\n"
<< "\n"
<< "void main (void)\n"
<< "{\n"
<< " const float threshold = 0.15625; // 4 subpixel bits. Assume 3 accurate bits + 0.03125 for other errors\n"
<< " const ivec2 nearby_pixel = ivec2(floor(fs_in_position_screen));\n"
<< " bool ok = false;\n"
<< "\n"
<< " // sample at edge + inaccuaries may cause us to round to any neighboring pixel\n"
<< " // check all neighbors for any match\n"
<< " for (int dy = -1; dy <= 1; ++dy)\n"
<< " for (int dx = -1; dx <= 1; ++dx)\n"
<< " {\n"
<< " ivec2 current_pixel = nearby_pixel + ivec2(dx, dy);\n"
<< " vec2 position_inside_pixel = vec2(current_pixel) + gl_SamplePosition;\n"
<< " vec2 position_diff = abs(position_inside_pixel - fs_in_position_screen);\n"
<< "\n"
<< " if (all(lessThan(position_diff, vec2(threshold))))\n"
<< " ok = true;\n"
<< " }\n"
<< "\n"
<< " if (ok)\n"
<< " fs_out_color = vec4(0.0, 1.0, 0.0, 1.0);\n"
<< " else\n"
<< " fs_out_color = vec4(1.0, 0.0, 0.0, 1.0);\n"
<< "}\n";
programCollection.glslSources.add("fragment_shader") << glu::FragmentSource(fs.str());
}
template<> TestInstance* MSCase<MSCaseSamplePosCorrectness>::createInstance (Context& context) const
{
return new MSInstance<MSInstanceSamplePosCorrectness>(context, m_imageMSParams);
}
class MSInstanceSampleMaskPattern : public MSInstanceBaseResolveAndPerSampleFetch
{
public:
MSInstanceSampleMaskPattern (Context& context,
const ImageMSParams& imageMSParams);
VkPipelineMultisampleStateCreateInfo getMSStateCreateInfo (const ImageMSParams& imageMSParams) const;
const VkDescriptorSetLayout* createMSPassDescSetLayout (const ImageMSParams& imageMSParams);
const VkDescriptorSet* createMSPassDescSet (const ImageMSParams& imageMSParams,
const VkDescriptorSetLayout* descSetLayout);
VertexDataDesc getVertexDataDescripton (void) const;
void uploadVertexData (const Allocation& vertexBufferAllocation,
const VertexDataDesc& vertexDataDescripton) const;
tcu::TestStatus verifyImageData (const vk::VkImageCreateInfo& imageMSInfo,
const vk::VkImageCreateInfo& imageRSInfo,
const std::vector<tcu::ConstPixelBufferAccess>& dataPerSample,
const tcu::ConstPixelBufferAccess& dataRS) const;
protected:
VkSampleMask m_sampleMask;
Move<VkDescriptorSetLayout> m_descriptorSetLayout;
Move<VkDescriptorPool> m_descriptorPool;
Move<VkDescriptorSet> m_descriptorSet;
de::MovePtr<Buffer> m_buffer;
};
MSInstanceSampleMaskPattern::MSInstanceSampleMaskPattern (Context& context, const ImageMSParams& imageMSParams) : MSInstanceBaseResolveAndPerSampleFetch(context, imageMSParams)
{
m_sampleMask = 0xAAAAAAAAu & ((1u << imageMSParams.numSamples) - 1u);
}
VkPipelineMultisampleStateCreateInfo MSInstanceSampleMaskPattern::getMSStateCreateInfo (const ImageMSParams& imageMSParams) const
{
const VkPipelineMultisampleStateCreateInfo multisampleStateInfo =
{
VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
(VkPipelineMultisampleStateCreateFlags)0u, // VkPipelineMultisampleStateCreateFlags flags;
imageMSParams.numSamples, // VkSampleCountFlagBits rasterizationSamples;
VK_FALSE, // VkBool32 sampleShadingEnable;
1.0f, // float minSampleShading;
&m_sampleMask, // const VkSampleMask* pSampleMask;
VK_FALSE, // VkBool32 alphaToCoverageEnable;
VK_FALSE, // VkBool32 alphaToOneEnable;
};
return multisampleStateInfo;
}
const VkDescriptorSetLayout* MSInstanceSampleMaskPattern::createMSPassDescSetLayout (const ImageMSParams& imageMSParams)
{
DE_UNREF(imageMSParams);
const DeviceInterface& deviceInterface = m_context.getDeviceInterface();
const VkDevice device = m_context.getDevice();
// Create descriptor set layout
m_descriptorSetLayout = DescriptorSetLayoutBuilder()
.addSingleBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_FRAGMENT_BIT)
.build(deviceInterface, device);
return &m_descriptorSetLayout.get();
}
const VkDescriptorSet* MSInstanceSampleMaskPattern::createMSPassDescSet (const ImageMSParams& imageMSParams, const VkDescriptorSetLayout* descSetLayout)
{
DE_UNREF(imageMSParams);
const DeviceInterface& deviceInterface = m_context.getDeviceInterface();
const VkDevice device = m_context.getDevice();
Allocator& allocator = m_context.getDefaultAllocator();
// Create descriptor pool
m_descriptorPool = DescriptorPoolBuilder()
.addType(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1u)
.build(deviceInterface, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u);
// Create descriptor set
m_descriptorSet = makeDescriptorSet(deviceInterface, device, *m_descriptorPool, *descSetLayout);
const VkBufferCreateInfo bufferSampleMaskInfo = makeBufferCreateInfo(sizeof(VkSampleMask), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT);
m_buffer = de::MovePtr<Buffer>(new Buffer(deviceInterface, device, allocator, bufferSampleMaskInfo, MemoryRequirement::HostVisible));
deMemcpy(m_buffer->getAllocation().getHostPtr(), &m_sampleMask, sizeof(VkSampleMask));
flushAlloc(deviceInterface, device, m_buffer->getAllocation());
const VkDescriptorBufferInfo descBufferInfo = makeDescriptorBufferInfo(**m_buffer, 0u, sizeof(VkSampleMask));
DescriptorSetUpdateBuilder()
.writeSingle(*m_descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, &descBufferInfo)
.update(deviceInterface, device);
return &m_descriptorSet.get();
}
MultisampleInstanceBase::VertexDataDesc MSInstanceSampleMaskPattern::getVertexDataDescripton (void) const
{
return getVertexDataDescriptonNdc();
}
void MSInstanceSampleMaskPattern::uploadVertexData (const Allocation& vertexBufferAllocation, const VertexDataDesc& vertexDataDescripton) const
{
uploadVertexDataNdc(vertexBufferAllocation, vertexDataDescripton);
}
tcu::TestStatus MSInstanceSampleMaskPattern::verifyImageData (const vk::VkImageCreateInfo& imageMSInfo,
const vk::VkImageCreateInfo& imageRSInfo,
const std::vector<tcu::ConstPixelBufferAccess>& dataPerSample,
const tcu::ConstPixelBufferAccess& dataRS) const
{
DE_UNREF(imageRSInfo);
DE_UNREF(dataRS);
if (checkForErrorMS(imageMSInfo, dataPerSample, 0))
return tcu::TestStatus::fail("gl_SampleMaskIn bits have not been killed by pSampleMask state");
return tcu::TestStatus::pass("Passed");
}
class MSCaseSampleMaskPattern;
template<> void MSCase<MSCaseSampleMaskPattern>::init (void)
{
m_testCtx.getLog()
<< tcu::TestLog::Message
<< "Verifying gl_SampleMaskIn value with pSampleMask state. gl_SampleMaskIn does not contain any bits set that are have been killed by pSampleMask state. Expecting:\n"
<< "Expected result: gl_SampleMaskIn AND ~(pSampleMask) should be zero.\n"
<< tcu::TestLog::EndMessage;
MultisampleCaseBase::init();
}
template<> void MSCase<MSCaseSampleMaskPattern>::initPrograms (vk::SourceCollections& programCollection) const
{
MSCaseBaseResolveAndPerSampleFetch::initPrograms(programCollection);
// Create vertex shader
std::ostringstream vs;
vs << "#version 440\n"
<< "layout(location = 0) in vec4 vs_in_position_ndc;\n"
<< "\n"
<< "out gl_PerVertex {\n"
<< " vec4 gl_Position;\n"
<< "};\n"
<< "void main (void)\n"
<< "{\n"
<< " gl_Position = vs_in_position_ndc;\n"
<< "}\n";
programCollection.glslSources.add("vertex_shader") << glu::VertexSource(vs.str());
// Create fragment shader
std::ostringstream fs;
fs << "#version 440\n"
<< "\n"
<< "layout(location = 0) out vec4 fs_out_color;\n"
<< "\n"
<< "layout(set = 0, binding = 0, std140) uniform SampleMaskBlock\n"
<< "{\n"
<< " int sampleMaskPattern;\n"
<< "};"
<< "\n"
<< "void main (void)\n"
<< "{\n"
<< " if ((gl_SampleMaskIn[0] & ~sampleMaskPattern) != 0)\n"
<< " fs_out_color = vec4(1.0, 0.0, 0.0, 1.0);\n"
<< " else\n"
<< " fs_out_color = vec4(0.0, 1.0, 0.0, 1.0);\n"
<< "}\n";
programCollection.glslSources.add("fragment_shader") << glu::FragmentSource(fs.str());
}
template<> TestInstance* MSCase<MSCaseSampleMaskPattern>::createInstance (Context& context) const
{
return new MSInstanceSampleMaskPattern(context, m_imageMSParams);
}
class MSInstanceSampleMaskBitCount;
template<> MultisampleInstanceBase::VertexDataDesc MSInstance<MSInstanceSampleMaskBitCount>::getVertexDataDescripton (void) const
{
return getVertexDataDescriptonNdc();
}
template<> void MSInstance<MSInstanceSampleMaskBitCount>::uploadVertexData (const Allocation& vertexBufferAllocation, const VertexDataDesc& vertexDataDescripton) const
{
uploadVertexDataNdc(vertexBufferAllocation, vertexDataDescripton);
}
template<> tcu::TestStatus MSInstance<MSInstanceSampleMaskBitCount>::verifyImageData (const vk::VkImageCreateInfo& imageMSInfo,
const vk::VkImageCreateInfo& imageRSInfo,
const std::vector<tcu::ConstPixelBufferAccess>& dataPerSample,
const tcu::ConstPixelBufferAccess& dataRS) const
{
DE_UNREF(imageRSInfo);
DE_UNREF(dataRS);
if (checkForErrorMS(imageMSInfo, dataPerSample, 0))
return tcu::TestStatus::fail("gl_SampleMaskIn has more than one bit set for some shader invocations");
return tcu::TestStatus::pass("Passed");
}
class MSCaseSampleMaskBitCount;
template<> void MSCase<MSCaseSampleMaskBitCount>::checkSupport (Context& context) const
{
context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_SAMPLE_RATE_SHADING);
}
template<> void MSCase<MSCaseSampleMaskBitCount>::init (void)
{
m_testCtx.getLog()
<< tcu::TestLog::Message
<< "Verifying gl_SampleMaskIn.\n"
<< " Fragment shader will be invoked numSamples times.\n"
<< " => gl_SampleMaskIn should have only one bit set for each shader invocation.\n"
<< tcu::TestLog::EndMessage;
MultisampleCaseBase::init();
}
template<> void MSCase<MSCaseSampleMaskBitCount>::initPrograms (vk::SourceCollections& programCollection) const
{
MSCaseBaseResolveAndPerSampleFetch::initPrograms(programCollection);
// Create vertex shader
std::ostringstream vs;
vs << "#version 440\n"
<< "layout(location = 0) in vec4 vs_in_position_ndc;\n"
<< "\n"
<< "out gl_PerVertex {\n"
<< " vec4 gl_Position;\n"
<< "};\n"
<< "void main (void)\n"
<< "{\n"
<< " gl_Position = vs_in_position_ndc;\n"
<< "}\n";
programCollection.glslSources.add("vertex_shader") << glu::VertexSource(vs.str());
// Create fragment shader
std::ostringstream fs;
fs << "#version 440\n"
<< "\n"
<< "layout(location = 0) out vec4 fs_out_color;\n"
<< "\n"
<< "void main (void)\n"
<< "{\n"
<< " uint maskBitCount = 0u;\n"
<< "\n"
<< " for (int i = 0; i < 32; ++i)\n"
<< " if (((gl_SampleMaskIn[0] >> i) & 0x01) == 0x01)\n"
<< " ++maskBitCount;\n"
<< "\n"
<< " if (maskBitCount != 1u)\n"
<< " fs_out_color = vec4(1.0, 0.0, 0.0, 1.0);\n"
<< " else\n"
<< " fs_out_color = vec4(0.0, 1.0, 0.0, 1.0);\n"
<< "}\n";
programCollection.glslSources.add("fragment_shader") << glu::FragmentSource(fs.str());
}
template<> TestInstance* MSCase<MSCaseSampleMaskBitCount>::createInstance (Context& context) const
{
return new MSInstance<MSInstanceSampleMaskBitCount>(context, m_imageMSParams);
}
class MSInstanceSampleMaskCorrectBit;
template<> MultisampleInstanceBase::VertexDataDesc MSInstance<MSInstanceSampleMaskCorrectBit>::getVertexDataDescripton (void) const
{
return getVertexDataDescriptonNdc();
}
template<> void MSInstance<MSInstanceSampleMaskCorrectBit>::uploadVertexData (const Allocation& vertexBufferAllocation, const VertexDataDesc& vertexDataDescripton) const
{
uploadVertexDataNdc(vertexBufferAllocation, vertexDataDescripton);
}
template<> tcu::TestStatus MSInstance<MSInstanceSampleMaskCorrectBit>::verifyImageData (const vk::VkImageCreateInfo& imageMSInfo,
const vk::VkImageCreateInfo& imageRSInfo,
const std::vector<tcu::ConstPixelBufferAccess>& dataPerSample,
const tcu::ConstPixelBufferAccess& dataRS) const
{
DE_UNREF(imageRSInfo);
DE_UNREF(dataRS);
if (checkForErrorMS(imageMSInfo, dataPerSample, 0))
return tcu::TestStatus::fail("The bit corresponsing to current gl_SampleID is not set in gl_SampleMaskIn");
return tcu::TestStatus::pass("Passed");
}
class MSCaseSampleMaskCorrectBit;
template<> void MSCase<MSCaseSampleMaskCorrectBit>::checkSupport (Context& context) const
{
context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_SAMPLE_RATE_SHADING);
}
template<> void MSCase<MSCaseSampleMaskCorrectBit>::init (void)
{
m_testCtx.getLog()
<< tcu::TestLog::Message
<< "Verifying gl_SampleMaskIn.\n"
<< " Fragment shader will be invoked numSamples times.\n"
<< " => In each invocation gl_SampleMaskIn should have the bit set that corresponds to gl_SampleID.\n"
<< tcu::TestLog::EndMessage;
MultisampleCaseBase::init();
}
template<> void MSCase<MSCaseSampleMaskCorrectBit>::initPrograms (vk::SourceCollections& programCollection) const
{
MSCaseBaseResolveAndPerSampleFetch::initPrograms(programCollection);
// Create vertex shader
std::ostringstream vs;
vs << "#version 440\n"
<< "layout(location = 0) in vec4 vs_in_position_ndc;\n"
<< "\n"
<< "out gl_PerVertex {\n"
<< " vec4 gl_Position;\n"
<< "};\n"
<< "void main (void)\n"
<< "{\n"
<< " gl_Position = vs_in_position_ndc;\n"
<< "}\n";
programCollection.glslSources.add("vertex_shader") << glu::VertexSource(vs.str());
// Create fragment shader
std::ostringstream fs;
fs << "#version 440\n"
<< "\n"
<< "layout(location = 0) out vec4 fs_out_color;\n"
<< "\n"
<< "void main (void)\n"
<< "{\n"
<< " if (((gl_SampleMaskIn[0] >> gl_SampleID) & 0x01) == 0x01)\n"
<< " fs_out_color = vec4(0.0, 1.0, 0.0, 1.0);\n"
<< " else\n"
<< " fs_out_color = vec4(1.0, 0.0, 0.0, 1.0);\n"
<< "}\n";
programCollection.glslSources.add("fragment_shader") << glu::FragmentSource(fs.str());
}
template<> TestInstance* MSCase<MSCaseSampleMaskCorrectBit>::createInstance (Context& context) const
{
return new MSInstance<MSInstanceSampleMaskCorrectBit>(context, m_imageMSParams);
}
class MSInstanceSampleMaskWrite;
template<> MultisampleInstanceBase::VertexDataDesc MSInstance<MSInstanceSampleMaskWrite>::getVertexDataDescripton (void) const
{
return getVertexDataDescriptonNdc();
}
template<> void MSInstance<MSInstanceSampleMaskWrite>::uploadVertexData (const Allocation& vertexBufferAllocation, const VertexDataDesc& vertexDataDescripton) const
{
uploadVertexDataNdc(vertexBufferAllocation, vertexDataDescripton);
}
//! Creates VkPipelineMultisampleStateCreateInfo with sample shading disabled.
template<> VkPipelineMultisampleStateCreateInfo MSInstance<MSInstanceSampleMaskWrite>::getMSStateCreateInfo (const ImageMSParams& imageMSParams) const
{
const VkPipelineMultisampleStateCreateInfo multisampleStateInfo =
{
VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
(VkPipelineMultisampleStateCreateFlags)0u, // VkPipelineMultisampleStateCreateFlags flags;
imageMSParams.numSamples, // VkSampleCountFlagBits rasterizationSamples;
VK_FALSE, // VkBool32 sampleShadingEnable;
0.0f, // float minSampleShading;
DE_NULL, // const VkSampleMask* pSampleMask;
VK_FALSE, // VkBool32 alphaToCoverageEnable;
VK_FALSE, // VkBool32 alphaToOneEnable;
};
return multisampleStateInfo;
}
template<> tcu::TestStatus MSInstance<MSInstanceSampleMaskWrite>::verifyImageData (const vk::VkImageCreateInfo& imageMSInfo,
const vk::VkImageCreateInfo& imageRSInfo,
const std::vector<tcu::ConstPixelBufferAccess>& dataPerSample,
const tcu::ConstPixelBufferAccess& dataRS) const
{
const deUint32 numSamples = static_cast<deUint32>(imageMSInfo.samples);
for (deUint32 z = 0u; z < imageMSInfo.extent.depth; ++z)
for (deUint32 y = 0u; y < imageMSInfo.extent.height; ++y)
for (deUint32 x = 0u; x < imageMSInfo.extent.width; ++x)
{
for (deUint32 sampleNdx = 0u; sampleNdx < numSamples; ++sampleNdx)
{
const deUint32 firstComponent = dataPerSample[sampleNdx].getPixelUint(x, y, z)[0];
if (firstComponent != 0u && firstComponent != 255u)
return tcu::TestStatus::fail("Expected color to be zero or saturated on the first channel");
}
}
for (deUint32 z = 0u; z < imageRSInfo.extent.depth; ++z)
for (deUint32 y = 0u; y < imageRSInfo.extent.height; ++y)
for (deUint32 x = 0u; x < imageRSInfo.extent.width; ++x)
{
const float firstComponent = dataRS.getPixel(x, y, z)[0];
if (deFloatAbs(firstComponent - 0.5f) > 0.02f)
return tcu::TestStatus::fail("Expected resolve color to be half intensity on the first channel");
}
return tcu::TestStatus::pass("Passed");
}
class MSCaseSampleMaskWrite;
template<> void MSCase<MSCaseSampleMaskWrite>::init (void)
{
m_testCtx.getLog()
<< tcu::TestLog::Message
<< "Discarding half of the samples using gl_SampleMask."
<< "Expecting half intensity on multisample targets (numSamples > 1)\n"
<< tcu::TestLog::EndMessage;
MultisampleCaseBase::init();
}
template<> void MSCase<MSCaseSampleMaskWrite>::initPrograms (vk::SourceCollections& programCollection) const
{
MSCaseBaseResolveAndPerSampleFetch::initPrograms(programCollection);
// Create vertex shader
std::ostringstream vs;
vs << "#version 440\n"
<< "layout(location = 0) in vec4 vs_in_position_ndc;\n"
<< "\n"
<< "out gl_PerVertex {\n"
<< " vec4 gl_Position;\n"
<< "};\n"
<< "void main (void)\n"
<< "{\n"
<< " gl_Position = vs_in_position_ndc;\n"
<< "}\n";
programCollection.glslSources.add("vertex_shader") << glu::VertexSource(vs.str());
// Create fragment shader
std::ostringstream fs;
fs << "#version 440\n"
<< "\n"
<< "layout(location = 0) out vec4 fs_out_color;\n"
<< "\n"
<< "void main (void)\n"
<< "{\n"
<< " gl_SampleMask[0] = 0xAAAAAAAA;\n"
<< "\n"
<< " fs_out_color = vec4(1.0, 0.0, 0.0, 1.0);\n"
<< "}\n";
programCollection.glslSources.add("fragment_shader") << glu::FragmentSource(fs.str());
}
template<> TestInstance* MSCase<MSCaseSampleMaskWrite>::createInstance (Context& context) const
{
return new MSInstance<MSInstanceSampleMaskWrite>(context, m_imageMSParams);
}
const set<deUint32> kValidSquareSampleCounts =
{
vk::VK_SAMPLE_COUNT_1_BIT,
vk::VK_SAMPLE_COUNT_2_BIT,
vk::VK_SAMPLE_COUNT_4_BIT,
vk::VK_SAMPLE_COUNT_8_BIT,
vk::VK_SAMPLE_COUNT_16_BIT,
};
void assertSquareSampleCount (deUint32 sampleCount)
{
DE_ASSERT(kValidSquareSampleCounts.find(sampleCount) != kValidSquareSampleCounts.end());
DE_UNREF(sampleCount); // for release builds.
}
// When dealing with N samples, each coordinate (x, y) will be used to decide which samples will be written to, using N/2 bits for
// each of the X and Y values. Take into account this returns 0 for 1 sample.
deUint32 bitsPerCoord (deUint32 numSamples)
{
assertSquareSampleCount(numSamples);
return (numSamples / 2u);
}
// These tests will try to verify all write or mask bit combinations for the given sample count, and will verify one combination per
// image pixel. This means the following image sizes need to be used:
// - 2 samples: 2x2
// - 4 samples: 4x4
// - 8 samples: 16x16
// - 16 samples: 256x256
// In other words, images will be square with 2^(samples-1) pixels on each side.
vk::VkExtent2D imageSize (deUint32 sampleCount)
{
assertSquareSampleCount(sampleCount);
// Special case: 2x1 image (not actually square).
if (sampleCount == vk::VK_SAMPLE_COUNT_1_BIT)
return vk::VkExtent2D{2u, 1u};
// Other cases: square image as described above.
const auto dim = (1u<<(sampleCount>>1u));
return vk::VkExtent2D{dim, dim};
}
vk::VkExtent3D getExtent3D (deUint32 sampleCount)
{
const auto size = imageSize(sampleCount);
return vk::VkExtent3D{size.width, size.height, 1u};
}
std::string getShaderDecl (const tcu::Vec4& color)
{
std::ostringstream declaration;
declaration << "vec4(" << color.x() << ", " << color.y() << ", " << color.z() << ", " << color.w() << ")";
return declaration.str();
}
struct WriteSampleParams
{
vk::VkSampleCountFlagBits sampleCount;
};
class WriteSampleTest : public vkt::TestCase
{
public:
WriteSampleTest (tcu::TestContext& testCtx, const std::string& name, const std::string& desc, const WriteSampleParams& params)
: vkt::TestCase(testCtx, name, desc), m_params(params)
{}
virtual ~WriteSampleTest (void) {}
virtual void initPrograms (vk::SourceCollections& programCollection) const;
virtual vkt::TestInstance* createInstance (Context& context) const;
virtual void checkSupport (Context& context) const;
static const tcu::Vec4 kClearColor;
static const tcu::Vec4 kBadColor;
static const tcu::Vec4 kGoodColor;
static const tcu::Vec4 kWriteColor;
static constexpr vk::VkFormat kImageFormat = vk::VK_FORMAT_R8G8B8A8_UNORM;
// Keep these two in sync.
static constexpr vk::VkImageUsageFlags kUsageFlags = (vk::VK_IMAGE_USAGE_STORAGE_BIT | vk::VK_IMAGE_USAGE_TRANSFER_SRC_BIT | vk::VK_IMAGE_USAGE_TRANSFER_DST_BIT);
static constexpr vk::VkFormatFeatureFlags kFeatureFlags = (vk::VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | vk::VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | vk::VK_FORMAT_FEATURE_TRANSFER_DST_BIT);
private:
WriteSampleParams m_params;
};
const tcu::Vec4 WriteSampleTest::kClearColor {0.0f, 0.0f, 0.0f, 1.0f};
const tcu::Vec4 WriteSampleTest::kBadColor {1.0f, 0.0f, 0.0f, 1.0f};
const tcu::Vec4 WriteSampleTest::kGoodColor {0.0f, 1.0f, 0.0f, 1.0f};
const tcu::Vec4 WriteSampleTest::kWriteColor {0.0f, 0.0f, 1.0f, 1.0f};
class WriteSampleTestInstance : public vkt::TestInstance
{
public:
WriteSampleTestInstance (vkt::Context& context, const WriteSampleParams& params)
: vkt::TestInstance(context), m_params(params)
{}
virtual ~WriteSampleTestInstance (void) {}
virtual tcu::TestStatus iterate (void);
private:
WriteSampleParams m_params;
};
void WriteSampleTest::checkSupport (Context& context) const
{
const auto& vki = context.getInstanceInterface();
const auto physicalDevice = context.getPhysicalDevice();
// Check multisample storage images support.
const auto features = vk::getPhysicalDeviceFeatures(vki, physicalDevice);
if (!features.shaderStorageImageMultisample)
TCU_THROW(NotSupportedError, "Using multisample images as storage is not supported");
// Check the specific image format.
const auto properties = vk::getPhysicalDeviceFormatProperties(vki, physicalDevice, kImageFormat);
if (!(properties.optimalTilingFeatures & kFeatureFlags))
TCU_THROW(NotSupportedError, "Format does not support the required features");
// Check the supported sample count.
const auto imgProps = vk::getPhysicalDeviceImageFormatProperties(vki, physicalDevice, kImageFormat, vk::VK_IMAGE_TYPE_2D, vk::VK_IMAGE_TILING_OPTIMAL, kUsageFlags, 0u);
if (!(imgProps.sampleCounts & m_params.sampleCount))
TCU_THROW(NotSupportedError, "Format does not support the required sample count");
}
void WriteSampleTest::initPrograms (vk::SourceCollections& programCollection) const
{
std::ostringstream writeColorDecl, goodColorDecl, badColorDecl, clearColorDecl, allColorDecl;
writeColorDecl << " vec4 wcolor = " << getShaderDecl(kWriteColor) << ";\n";
goodColorDecl << " vec4 bcolor = " << getShaderDecl(kBadColor) << ";\n";
badColorDecl << " vec4 gcolor = " << getShaderDecl(kGoodColor) << ";\n";
clearColorDecl << " vec4 ccolor = " << getShaderDecl(kClearColor) << ";\n";
allColorDecl << writeColorDecl.str() << goodColorDecl.str() << badColorDecl.str() << clearColorDecl.str();
std::ostringstream shaderWrite;
const auto bpc = de::toString(bitsPerCoord(m_params.sampleCount));
const auto count = de::toString(m_params.sampleCount);
shaderWrite
<< "#version 450\n"
<< "\n"
<< "layout (rgba8, set=0, binding=0) uniform image2DMS writeImg;\n"
<< "layout (rgba8, set=0, binding=1) uniform image2D verificationImg;\n"
<< "\n"
<< "void main()\n"
<< "{\n"
<< writeColorDecl.str()
<< " uvec2 ucoords = uvec2(gl_GlobalInvocationID.xy);\n"
<< " ivec2 icoords = ivec2(ucoords);\n"
<< " uint writeMask = ((ucoords.x << " << bpc << ") | ucoords.y);\n"
<< " for (uint i = 0; i < " << count << "; ++i)\n"
<< " {\n"
<< " if ((writeMask & (1 << i)) != 0)\n"
<< " imageStore(writeImg, icoords, int(i), wcolor);\n"
<< " }\n"
<< "}\n"
;
std::ostringstream shaderVerify;
shaderVerify
<< "#version 450\n"
<< "\n"
<< "layout (rgba8, set=0, binding=0) uniform image2DMS writeImg;\n"
<< "layout (rgba8, set=0, binding=1) uniform image2D verificationImg;\n"
<< "\n"
<< "void main()\n"
<< "{\n"
<< allColorDecl.str()
<< " uvec2 ucoords = uvec2(gl_GlobalInvocationID.xy);\n"
<< " ivec2 icoords = ivec2(ucoords);\n"
<< " uint writeMask = ((ucoords.x << " << bpc << ") | ucoords.y);\n"
<< " bool ok = true;\n"
<< " for (uint i = 0; i < " << count << "; ++i)\n"
<< " {\n"
<< " bool expectWrite = ((writeMask & (1 << i)) != 0);\n"
<< " vec4 sampleColor = imageLoad(writeImg, icoords, int(i));\n"
<< " vec4 wantedColor = (expectWrite ? wcolor : ccolor);\n"
<< " ok = ok && (sampleColor == wantedColor);\n"
<< " }\n"
<< " vec4 resultColor = (ok ? gcolor : bcolor);\n"
<< " imageStore(verificationImg, icoords, resultColor);\n"
<< "}\n"
;
programCollection.glslSources.add("write") << glu::ComputeSource(shaderWrite.str());
programCollection.glslSources.add("verify") << glu::ComputeSource(shaderVerify.str());
}
vkt::TestInstance* WriteSampleTest::createInstance (Context& context) const
{
return new WriteSampleTestInstance{context, m_params};
}
tcu::TestStatus WriteSampleTestInstance::iterate (void)
{
const auto& vkd = m_context.getDeviceInterface();
const auto device = m_context.getDevice();
auto& allocator = m_context.getDefaultAllocator();
const auto queue = m_context.getUniversalQueue();
const auto queueIndex = m_context.getUniversalQueueFamilyIndex();
const auto extent3D = getExtent3D(m_params.sampleCount);
// Create storage image and verification image.
const vk::VkImageCreateInfo storageImageInfo =
{
vk::VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType;
nullptr, // const void* pNext;
0u, // VkImageCreateFlags flags;
vk::VK_IMAGE_TYPE_2D, // VkImageType imageType;
WriteSampleTest::kImageFormat, // VkFormat format;
extent3D, // VkExtent3D extent;
1u, // deUint32 mipLevels;
1u, // deUint32 arrayLayers;
m_params.sampleCount, // VkSampleCountFlagBits samples;
vk::VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling;
WriteSampleTest::kUsageFlags, // VkImageUsageFlags usage;
vk::VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
1u, // deUint32 queueFamilyIndexCount;
&queueIndex, // const deUint32* pQueueFamilyIndices;
vk::VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout;
};
const vk::VkImageCreateInfo verificationImageInfo =
{
vk::VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType;
nullptr, // const void* pNext;
0u, // VkImageCreateFlags flags;
vk::VK_IMAGE_TYPE_2D, // VkImageType imageType;
WriteSampleTest::kImageFormat, // VkFormat format;
extent3D, // VkExtent3D extent;
1u, // deUint32 mipLevels;
1u, // deUint32 arrayLayers;
vk::VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples;
vk::VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling;
WriteSampleTest::kUsageFlags, // VkImageUsageFlags usage;
vk::VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
1u, // deUint32 queueFamilyIndexCount;
&queueIndex, // const deUint32* pQueueFamilyIndices;
vk::VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout;
};
vk::ImageWithMemory storageImgPrt {vkd, device, allocator, storageImageInfo, vk::MemoryRequirement::Any};
vk::ImageWithMemory verificationImgPtr {vkd, device, allocator, verificationImageInfo, vk::MemoryRequirement::Any};
const vk::VkImageSubresourceRange kSubresourceRange =
{
vk::VK_IMAGE_ASPECT_COLOR_BIT, // VkImageAspectFlags aspectMask;
0u, // deUint32 baseMipLevel;
1u, // deUint32 levelCount;
0u, // deUint32 baseArrayLayer;
1u, // deUint32 layerCount;
};
auto storageImgViewPtr = vk::makeImageView(vkd, device, storageImgPrt.get(), vk::VK_IMAGE_VIEW_TYPE_2D, WriteSampleTest::kImageFormat, kSubresourceRange);
auto verificationImgViewPtr = vk::makeImageView(vkd, device, verificationImgPtr.get(), vk::VK_IMAGE_VIEW_TYPE_2D, WriteSampleTest::kImageFormat, kSubresourceRange);
// Prepare a staging buffer to check verification image.
const auto tcuFormat = vk::mapVkFormat(WriteSampleTest::kImageFormat);
const VkDeviceSize bufferSize = extent3D.width * extent3D.height * extent3D.depth * tcu::getPixelSize(tcuFormat);
const auto stagingBufferInfo = vk::makeBufferCreateInfo(bufferSize, vk::VK_BUFFER_USAGE_TRANSFER_DST_BIT);
vk::BufferWithMemory stagingBuffer {vkd, device, allocator, stagingBufferInfo, MemoryRequirement::HostVisible};
// Descriptor set layout.
vk::DescriptorSetLayoutBuilder layoutBuilder;
layoutBuilder.addSingleBinding(vk::VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, vk::VK_SHADER_STAGE_COMPUTE_BIT);
layoutBuilder.addSingleBinding(vk::VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, vk::VK_SHADER_STAGE_COMPUTE_BIT);
auto descriptorSetLayout = layoutBuilder.build(vkd, device);
// Descriptor pool.
vk::DescriptorPoolBuilder poolBuilder;
poolBuilder.addType(vk::VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 2u);
auto descriptorPool = poolBuilder.build(vkd, device, vk::VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u);
// Descriptor set.
const auto descriptorSet = vk::makeDescriptorSet(vkd, device, descriptorPool.get(), descriptorSetLayout.get());
// Update descriptor set using the images.
const auto storageImgDescriptorInfo = vk::makeDescriptorImageInfo(DE_NULL, storageImgViewPtr.get(), vk::VK_IMAGE_LAYOUT_GENERAL);
const auto verificationImgDescriptorInfo = vk::makeDescriptorImageInfo(DE_NULL, verificationImgViewPtr.get(), vk::VK_IMAGE_LAYOUT_GENERAL);
vk::DescriptorSetUpdateBuilder updateBuilder;
updateBuilder.writeSingle(descriptorSet.get(), vk::DescriptorSetUpdateBuilder::Location::binding(0u), vk::VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, &storageImgDescriptorInfo);
updateBuilder.writeSingle(descriptorSet.get(), vk::DescriptorSetUpdateBuilder::Location::binding(1u), vk::VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, &verificationImgDescriptorInfo);
updateBuilder.update(vkd, device);
// Create write and verification compute pipelines.
auto shaderWriteModule = vk::createShaderModule(vkd, device, m_context.getBinaryCollection().get("write"), 0u);
auto shaderVerifyModule = vk::createShaderModule(vkd, device, m_context.getBinaryCollection().get("verify"), 0u);
auto pipelineLayout = vk::makePipelineLayout(vkd, device, descriptorSetLayout.get());
const vk::VkComputePipelineCreateInfo writePipelineCreateInfo =
{
vk::VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,
nullptr,
0u, // flags
{ // compute shader
vk::VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, // VkStructureType sType;
nullptr, // const void* pNext;
0u, // VkPipelineShaderStageCreateFlags flags;
vk::VK_SHADER_STAGE_COMPUTE_BIT, // VkShaderStageFlagBits stage;
shaderWriteModule.get(), // VkShaderModule module;
"main", // const char* pName;
nullptr, // const VkSpecializationInfo* pSpecializationInfo;
},
pipelineLayout.get(), // layout
DE_NULL, // basePipelineHandle
0, // basePipelineIndex
};
auto verificationPipelineCreateInfo = writePipelineCreateInfo;
verificationPipelineCreateInfo.stage.module = shaderVerifyModule.get();
auto writePipeline = vk::createComputePipeline(vkd, device, DE_NULL, &writePipelineCreateInfo);
auto verificationPipeline = vk::createComputePipeline(vkd, device, DE_NULL, &verificationPipelineCreateInfo);
// Transition images to the correct layout and buffers at different stages.
auto storageImgPreClearBarrier = vk::makeImageMemoryBarrier(0, vk::VK_ACCESS_TRANSFER_WRITE_BIT, vk::VK_IMAGE_LAYOUT_UNDEFINED, vk::VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, storageImgPrt.get(), kSubresourceRange);
auto storageImgPreShaderBarrier = vk::makeImageMemoryBarrier(vk::VK_ACCESS_TRANSFER_WRITE_BIT, vk::VK_ACCESS_SHADER_WRITE_BIT, vk::VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, vk::VK_IMAGE_LAYOUT_GENERAL, storageImgPrt.get(), kSubresourceRange);
auto verificationImgPreShaderBarrier = vk::makeImageMemoryBarrier(0, vk::VK_ACCESS_SHADER_WRITE_BIT, vk::VK_IMAGE_LAYOUT_UNDEFINED, vk::VK_IMAGE_LAYOUT_GENERAL, verificationImgPtr.get(), kSubresourceRange);
auto storageImgPreVerificationBarrier = vk::makeImageMemoryBarrier(vk::VK_ACCESS_SHADER_WRITE_BIT, vk::VK_ACCESS_SHADER_READ_BIT, vk::VK_IMAGE_LAYOUT_GENERAL, vk::VK_IMAGE_LAYOUT_GENERAL, storageImgPrt.get(), kSubresourceRange);
auto verificationImgPostBarrier = vk::makeImageMemoryBarrier(vk::VK_ACCESS_SHADER_WRITE_BIT, vk::VK_ACCESS_TRANSFER_READ_BIT, vk::VK_IMAGE_LAYOUT_GENERAL, vk::VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, verificationImgPtr.get(), kSubresourceRange);
auto bufferBarrier = vk::makeBufferMemoryBarrier(vk::VK_ACCESS_TRANSFER_WRITE_BIT, vk::VK_ACCESS_HOST_READ_BIT, stagingBuffer.get(), 0ull, bufferSize);
// Command buffer.
auto cmdPool = vk::makeCommandPool(vkd, device, queueIndex);
auto cmdBufferPtr = vk::allocateCommandBuffer(vkd, device, cmdPool.get(), vk::VK_COMMAND_BUFFER_LEVEL_PRIMARY);
auto cmdBuffer = cmdBufferPtr.get();
// Clear color for the storage image.
const auto clearColor = vk::makeClearValueColor(WriteSampleTest::kClearColor);
const vk::VkBufferImageCopy copyRegion =
{
0ull, // VkDeviceSize bufferOffset;
extent3D.width, // deUint32 bufferRowLength;
extent3D.height, // deUint32 bufferImageHeight;
{ // VkImageSubresourceLayers imageSubresource;
vk::VK_IMAGE_ASPECT_COLOR_BIT, // VkImageAspectFlags aspectMask;
0u, // deUint32 mipLevel;
0u, // deUint32 baseArrayLayer;
1u, // deUint32 layerCount;
},
{ 0, 0, 0 }, // VkOffset3D imageOffset;
extent3D, // VkExtent3D imageExtent;
};
// Record and submit commands.
vk::beginCommandBuffer(vkd, cmdBuffer);
// Clear storage image.
vkd.cmdPipelineBarrier(cmdBuffer, vk::VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, vk::VK_PIPELINE_STAGE_TRANSFER_BIT, 0u, 0u, nullptr, 0u, nullptr, 1u, &storageImgPreClearBarrier);
vkd.cmdClearColorImage(cmdBuffer, storageImgPrt.get(), vk::VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clearColor.color, 1u, &kSubresourceRange);
// Bind write pipeline and descriptor set.
vkd.cmdBindPipeline(cmdBuffer, vk::VK_PIPELINE_BIND_POINT_COMPUTE, writePipeline.get());
vkd.cmdBindDescriptorSets(cmdBuffer, vk::VK_PIPELINE_BIND_POINT_COMPUTE, pipelineLayout.get(), 0, 1u, &descriptorSet.get(), 0u, nullptr);
// Transition images to the appropriate layout before running the shader.
vkd.cmdPipelineBarrier(cmdBuffer, vk::VK_PIPELINE_STAGE_TRANSFER_BIT, vk::VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0u, 0u, nullptr, 0u, nullptr, 1u, &storageImgPreShaderBarrier);
vkd.cmdPipelineBarrier(cmdBuffer, vk::VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, vk::VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0u, 0u, nullptr, 0u, nullptr, 1u, &verificationImgPreShaderBarrier);
// Run shader.
vkd.cmdDispatch(cmdBuffer, extent3D.width, extent3D.height, extent3D.depth);
// Bind verification pipeline.
vkd.cmdBindPipeline(cmdBuffer, vk::VK_PIPELINE_BIND_POINT_COMPUTE, verificationPipeline.get());
// Make sure writes happen before reads in the second dispatch for the storage image.
vkd.cmdPipelineBarrier(cmdBuffer, vk::VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, vk::VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0u, 0u, nullptr, 0u, nullptr, 1u, &storageImgPreVerificationBarrier);
// Run verification shader.
vkd.cmdDispatch(cmdBuffer, extent3D.width, extent3D.height, extent3D.depth);
// Change verification image layout to prepare the transfer.
vkd.cmdPipelineBarrier(cmdBuffer, vk::VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, vk::VK_PIPELINE_STAGE_TRANSFER_BIT, 0u, 0u, nullptr, 0u, nullptr, 1u, &verificationImgPostBarrier);
// Copy verification image to staging buffer.
vkd.cmdCopyImageToBuffer(cmdBuffer, verificationImgPtr.get(), vk::VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, stagingBuffer.get(), 1u, &copyRegion);
vkd.cmdPipelineBarrier(cmdBuffer, vk::VK_PIPELINE_STAGE_TRANSFER_BIT, vk::VK_PIPELINE_STAGE_HOST_BIT, 0, 0u, nullptr, 1u, &bufferBarrier, 0u, nullptr);
vk::endCommandBuffer(vkd, cmdBuffer);
// Run shaders.
vk::submitCommandsAndWait(vkd, device, queue, cmdBuffer);
// Read buffer pixels.
const auto& bufferAlloc = stagingBuffer.getAllocation();
vk::invalidateAlloc(vkd, device, bufferAlloc);
// Copy buffer data to texture level and verify all pixels have the proper color.
tcu::TextureLevel texture {tcuFormat, static_cast<int>(extent3D.width), static_cast<int>(extent3D.height), static_cast<int>(extent3D.depth)};
const auto access = texture.getAccess();
deMemcpy(access.getDataPtr(), reinterpret_cast<char*>(bufferAlloc.getHostPtr()) + bufferAlloc.getOffset(), static_cast<size_t>(bufferSize));
for (int i = 0; i < access.getWidth(); ++i)
for (int j = 0; j < access.getHeight(); ++j)
for (int k = 0; k < access.getDepth(); ++k)
{
if (access.getPixel(i, j, k) != WriteSampleTest::kGoodColor)
{
std::ostringstream msg;
msg << "Invalid result at pixel (" << i << ", " << j << ", " << k << "); check error mask for more details";
m_context.getTestContext().getLog() << tcu::TestLog::Image("ErrorMask", "Indicates which pixels have unexpected values", access);
return tcu::TestStatus::fail(msg.str());
}
}
return tcu::TestStatus::pass("Pass");
}
using WriteSampleMaskParams = WriteSampleParams;
class WriteSampleMaskTestCase : public vkt::TestCase
{
public:
WriteSampleMaskTestCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const WriteSampleMaskParams& params);
virtual ~WriteSampleMaskTestCase (void) {}
virtual void checkSupport (Context& context) const;
virtual void initPrograms (vk::SourceCollections& programCollection) const;
virtual TestInstance* createInstance (Context& context) const;
static deUint32 getBufferElems (deUint32 sampleCount);
static const tcu::Vec4 kClearColor;
static const tcu::Vec4 kWriteColor;
static constexpr vk::VkFormat kImageFormat = vk::VK_FORMAT_R8G8B8A8_UNORM;
static constexpr vk::VkImageUsageFlags kUsageFlags = (vk::VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | vk::VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT);
static constexpr vk::VkFormatFeatureFlags kFeatureFlags = (vk::VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT);
private:
WriteSampleMaskParams m_params;
};
const tcu::Vec4 WriteSampleMaskTestCase::kClearColor {0.0f, 0.0f, 0.0f, 1.0f};
const tcu::Vec4 WriteSampleMaskTestCase::kWriteColor {0.0f, 0.0f, 1.0f, 1.0f};
class WriteSampleMaskTestInstance : public vkt::TestInstance
{
public:
WriteSampleMaskTestInstance (Context& context, const WriteSampleMaskParams& params);
virtual ~WriteSampleMaskTestInstance (void) {}
virtual tcu::TestStatus iterate (void);
private:
WriteSampleMaskParams m_params;
};
WriteSampleMaskTestCase::WriteSampleMaskTestCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const WriteSampleMaskParams& params)
: vkt::TestCase (testCtx, name, description)
, m_params (params)
{}
void WriteSampleMaskTestCase::checkSupport (Context& context) const
{
const auto& vki = context.getInstanceInterface();
const auto physicalDevice = context.getPhysicalDevice();
// Check the specific image format.
const auto properties = vk::getPhysicalDeviceFormatProperties(vki, physicalDevice, kImageFormat);
if (!(properties.optimalTilingFeatures & kFeatureFlags))
TCU_THROW(NotSupportedError, "Format does not support the required features");
// Check the supported sample count.
const auto imgProps = vk::getPhysicalDeviceImageFormatProperties(vki, physicalDevice, kImageFormat, vk::VK_IMAGE_TYPE_2D, vk::VK_IMAGE_TILING_OPTIMAL, kUsageFlags, 0u);
if (!(imgProps.sampleCounts & m_params.sampleCount))
TCU_THROW(NotSupportedError, "Format does not support the required sample count");
}
void WriteSampleMaskTestCase::initPrograms (vk::SourceCollections& programCollection) const
{
const auto bpc = de::toString(bitsPerCoord(m_params.sampleCount));
const auto size = imageSize(m_params.sampleCount);
const auto bufferElems = getBufferElems(m_params.sampleCount);
// Passthrough vertex shader.
std::ostringstream vertShader;
vertShader
<< "#version 450\n"
<< "layout (location=0) in vec2 inPos;\n"
<< "void main()\n"
<< "{\n"
<< " gl_Position = vec4(inPos, 0.0, 1.0);\n"
<< "}\n"
;
// Fragment shader common header.
std::ostringstream fragHeader;
fragHeader
<< "#version 450\n"
<< "\n"
// The color attachment is useless for the second subpass but avoids having to use an empty subpass and verifying the sample
// count is valid for it.
<< "layout (location=0) out vec4 outColor;\n"
<< "\n"
<< "vec4 wcolor = " << getShaderDecl(kWriteColor) << ";\n"
<< "vec4 ccolor = " << getShaderDecl(kClearColor) << ";\n"
<< "\n"
;
const auto fragHeaderStr = fragHeader.str();
// Fragment shader setting the sample mask and writing to the output color attachment. The sample mask will guarantee each image
// pixel gets a different combination of sample bits set, allowing the fragment shader to write in that sample or not, from all
// zeros in pixel (0, 0) to all ones in the opposite corner.
std::ostringstream fragShaderWrite;
fragShaderWrite
<< fragHeaderStr
<< "void main()\n"
<< "{\n"
<< " uvec2 ucoords = uvec2(gl_FragCoord);\n"
<< " ivec2 icoords = ivec2(ucoords);\n"
<< " gl_SampleMask[0] = int((ucoords.x << " << bpc << ") | ucoords.y);\n"
<< " outColor = wcolor;\n"
<< "}\n"
;
// Fragment shader reading from the previous output color attachment and copying the state to an SSBO for verification.
std::ostringstream fragShaderCheck;
const bool isMultiSample = (m_params.sampleCount != vk::VK_SAMPLE_COUNT_1_BIT);
fragShaderCheck
<< fragHeaderStr
<< "layout(set=0, binding=0, input_attachment_index=0) uniform subpassInput" << (isMultiSample ? "MS" : "") << " inputAttachment;\n"
<< "layout(set=0, binding=1, std430) buffer StorageBuffer {\n"
<< " int writeFlags[" << bufferElems << "];\n"
<< "} sb;\n"
<< "\n"
<< "void main()\n"
<< "{\n"
<< " uvec2 ucoords = uvec2(gl_FragCoord);\n"
<< " ivec2 icoords = ivec2(ucoords);\n"
<< " uint bufferp = ((ucoords.y * " << size.width << " + ucoords.x) * " << m_params.sampleCount << ") + uint(gl_SampleID);\n"
<< " vec4 storedc = subpassLoad(inputAttachment" << (isMultiSample ? ", gl_SampleID" : "") << ");\n"
<< " sb.writeFlags[bufferp] = ((storedc == wcolor) ? 1 : ((storedc == ccolor) ? 0 : 2));\n"
<< " outColor = storedc;\n"
<< "}\n"
;
programCollection.glslSources.add("vert") << glu::VertexSource(vertShader.str());
programCollection.glslSources.add("frag_write") << glu::FragmentSource(fragShaderWrite.str());
programCollection.glslSources.add("frag_check") << glu::FragmentSource(fragShaderCheck.str());
}
TestInstance* WriteSampleMaskTestCase::createInstance (Context& context) const
{
return new WriteSampleMaskTestInstance(context, m_params);
}
deUint32 WriteSampleMaskTestCase::getBufferElems (deUint32 sampleCount)
{
const auto imgSize = imageSize(sampleCount);
return (imgSize.width * imgSize.height * sampleCount);
}
WriteSampleMaskTestInstance::WriteSampleMaskTestInstance (Context& context, const WriteSampleMaskParams& params)
: vkt::TestInstance (context)
, m_params (params)
{}
tcu::TestStatus WriteSampleMaskTestInstance::iterate (void)
{
const auto& vkd = m_context.getDeviceInterface();
const auto device = m_context.getDevice();
auto& alloc = m_context.getDefaultAllocator();
const auto queueFamilyIndex = m_context.getUniversalQueueFamilyIndex();
const auto queue = m_context.getUniversalQueue();
static constexpr auto kImageFormat = WriteSampleMaskTestCase::kImageFormat;
static constexpr auto kImageUsage = WriteSampleMaskTestCase::kUsageFlags;
const auto kImageExtent = getExtent3D(m_params.sampleCount);
const auto kBufferElems = WriteSampleMaskTestCase::getBufferElems(m_params.sampleCount);
const auto kBufferSize = static_cast<vk::VkDeviceSize>(kBufferElems * sizeof(deInt32));
// Create image.
const vk::VkImageCreateInfo imageCreateInfo =
{
vk::VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType;
nullptr, // const void* pNext;
0u, // VkImageCreateFlags flags;
vk::VK_IMAGE_TYPE_2D, // VkImageType imageType;
kImageFormat, // VkFormat format;
kImageExtent, // VkExtent3D extent;
1u, // deUint32 mipLevels;
1u, // deUint32 arrayLayers;
m_params.sampleCount, // VkSampleCountFlagBits samples;
vk::VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling;
kImageUsage, // VkImageUsageFlags usage;
vk::VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
0u, // deUint32 queueFamilyIndexCount;
nullptr, // const deUint32* pQueueFamilyIndices;
vk::VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout;
};
const vk::ImageWithMemory colorImage (vkd, device, alloc, imageCreateInfo, vk::MemoryRequirement::Any);
const vk::ImageWithMemory auxiliarImage (vkd, device, alloc, imageCreateInfo, vk::MemoryRequirement::Any); // For the second subpass.
// Image views.
const auto subresourceRange = vk::makeImageSubresourceRange(vk::VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u);
const auto colorImageView = vk::makeImageView(vkd, device, colorImage.get(), vk::VK_IMAGE_VIEW_TYPE_2D, kImageFormat, subresourceRange);
const auto auxiliarImageView = vk::makeImageView(vkd, device, auxiliarImage.get(), vk::VK_IMAGE_VIEW_TYPE_2D, kImageFormat, subresourceRange);
// Create storage buffer used to verify results.
const vk::BufferWithMemory storageBuffer(vkd, device, alloc, vk::makeBufferCreateInfo(kBufferSize, vk::VK_BUFFER_USAGE_STORAGE_BUFFER_BIT), vk::MemoryRequirement::HostVisible);
// Full-screen quad.
const std::vector<tcu::Vec2> quadVertices =
{
tcu::Vec2(-1.0f, 1.0f), // Lower left
tcu::Vec2( 1.0f, 1.0f), // Lower right
tcu::Vec2( 1.0f, -1.0f), // Top right.
tcu::Vec2(-1.0f, 1.0f), // Lower left
tcu::Vec2( 1.0f, -1.0f), // Top right.
tcu::Vec2(-1.0f, -1.0f), // Top left.
};
// Vertex buffer.
const auto vertexBufferSize = static_cast<vk::VkDeviceSize>(quadVertices.size() * sizeof(decltype(quadVertices)::value_type));
const vk::BufferWithMemory vertexBuffer (vkd, device, alloc, vk::makeBufferCreateInfo(vertexBufferSize, vk::VK_BUFFER_USAGE_VERTEX_BUFFER_BIT), vk::MemoryRequirement::HostVisible);
const auto& vertexBufferAlloc = vertexBuffer.getAllocation();
void* vertexBufferPtr = vertexBufferAlloc.getHostPtr();
const vk::VkDeviceSize vertexBufferOffset = 0;
deMemcpy(vertexBufferPtr, quadVertices.data(), static_cast<size_t>(vertexBufferSize));
vk::flushAlloc(vkd, device, vertexBufferAlloc);
// Descriptor set layout.
vk::DescriptorSetLayoutBuilder setLayoutBuilder;
setLayoutBuilder.addSingleBinding(vk::VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, vk::VK_SHADER_STAGE_FRAGMENT_BIT);
setLayoutBuilder.addSingleBinding(vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, vk::VK_SHADER_STAGE_FRAGMENT_BIT);
const auto descriptorSetLayout = setLayoutBuilder.build(vkd, device);
// Descriptor pool and set.
vk::DescriptorPoolBuilder poolBuilder;
poolBuilder.addType(vk::VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1u);
poolBuilder.addType(vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1u);
const auto descriptorPool = poolBuilder.build(vkd, device, vk::VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u);
const auto descriptorSet = vk::makeDescriptorSet(vkd, device, descriptorPool.get(), descriptorSetLayout.get());
// Render pass.
const std::vector<vk::VkAttachmentDescription> attachments =
{
// Main color attachment.
{
0u, // VkAttachmentDescriptionFlags flags;
kImageFormat, // VkFormat format;
m_params.sampleCount, // VkSampleCountFlagBits samples;
vk::VK_ATTACHMENT_LOAD_OP_CLEAR, // VkAttachmentLoadOp loadOp;
vk::VK_ATTACHMENT_STORE_OP_STORE, // VkAttachmentStoreOp storeOp;
vk::VK_ATTACHMENT_LOAD_OP_DONT_CARE, // VkAttachmentLoadOp stencilLoadOp;
vk::VK_ATTACHMENT_STORE_OP_DONT_CARE, // VkAttachmentStoreOp stencilStoreOp;
vk::VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout;
vk::VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, // VkImageLayout finalLayout;
},
// Auxiliar color attachment for the check pass.
{
0u, // VkAttachmentDescriptionFlags flags;
kImageFormat, // VkFormat format;
m_params.sampleCount, // VkSampleCountFlagBits samples;
vk::VK_ATTACHMENT_LOAD_OP_DONT_CARE, // VkAttachmentLoadOp loadOp;
vk::VK_ATTACHMENT_STORE_OP_DONT_CARE, // VkAttachmentStoreOp storeOp;
vk::VK_ATTACHMENT_LOAD_OP_DONT_CARE, // VkAttachmentLoadOp stencilLoadOp;
vk::VK_ATTACHMENT_STORE_OP_DONT_CARE, // VkAttachmentStoreOp stencilStoreOp;
vk::VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout;
vk::VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // VkImageLayout finalLayout;
},
};
const vk::VkAttachmentReference colorAttachmentReference =
{
0u, // deUint32 attachment;
vk::VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // VkImageLayout layout;
};
const vk::VkAttachmentReference colorAsInputAttachment =
{
0u, // deUint32 attachment;
vk::VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, // VkImageLayout layout;
};
const vk::VkAttachmentReference auxiliarAttachmentReference =
{
1u, // deUint32 attachment;
vk::VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // VkImageLayout layout;
};
const std::vector<vk::VkSubpassDescription> subpasses =
{
// First subpass writing to the main attachment.
{
0u, // VkSubpassDescriptionFlags flags;
vk::VK_PIPELINE_BIND_POINT_GRAPHICS, // VkPipelineBindPoint pipelineBindPoint;
0u, // deUint32 inputAttachmentCount;
nullptr, // const VkAttachmentReference* pInputAttachments;
1u, // deUint32 colorAttachmentCount;
&colorAttachmentReference, // const VkAttachmentReference* pColorAttachments;
nullptr, // const VkAttachmentReference* pResolveAttachments;
nullptr, // const VkAttachmentReference* pDepthStencilAttachment;
0u, // deUint32 preserveAttachmentCount;
nullptr, // const deUint32* pPreserveAttachments;
},
// Second subpass writing to the auxiliar attachment.
{
0u, // VkSubpassDescriptionFlags flags;
vk::VK_PIPELINE_BIND_POINT_GRAPHICS, // VkPipelineBindPoint pipelineBindPoint;
1u, // deUint32 inputAttachmentCount;
&colorAsInputAttachment, // const VkAttachmentReference* pInputAttachments;
1u, // deUint32 colorAttachmentCount;
&auxiliarAttachmentReference, // const VkAttachmentReference* pColorAttachments;
nullptr, // const VkAttachmentReference* pResolveAttachments;
nullptr, // const VkAttachmentReference* pDepthStencilAttachment;
0u, // deUint32 preserveAttachmentCount;
nullptr, // const deUint32* pPreserveAttachments;
},
};
const std::vector<vk::VkSubpassDependency> subpassDependencies =
{
// First subpass writes to the color attachment and second subpass reads it as an input attachment.
{
0u, // deUint32 srcSubpass;
1u, // deUint32 dstSubpass;
vk::VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, // VkPipelineStageFlags srcStageMask;
vk::VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, // VkPipelineStageFlags dstStageMask;
vk::VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, // VkAccessFlags srcAccessMask;
vk::VK_ACCESS_INPUT_ATTACHMENT_READ_BIT, // VkAccessFlags dstAccessMask;
0u, // VkDependencyFlags dependencyFlags;
},
};
const vk::VkRenderPassCreateInfo renderPassInfo =
{
vk::VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, // VkStructureType sType;
nullptr, // const void* pNext;
0u, // VkRenderPassCreateFlags flags;
static_cast<deUint32>(attachments.size()), // deUint32 attachmentCount;
attachments.data(), // const VkAttachmentDescription* pAttachments;
static_cast<deUint32>(subpasses.size()), // deUint32 subpassCount;
subpasses.data(), // const VkSubpassDescription* pSubpasses;
static_cast<deUint32>(subpassDependencies.size()), // deUint32 dependencyCount;
subpassDependencies.data(), // const VkSubpassDependency* pDependencies;
};
const auto renderPass = vk::createRenderPass(vkd, device, &renderPassInfo);
// Framebuffer.
const std::vector<vk::VkImageView> imageViews =
{
colorImageView.get(),
auxiliarImageView.get(),
};
const auto framebuffer = vk::makeFramebuffer(vkd, device, renderPass.get(), static_cast<deUint32>(imageViews.size()), imageViews.data(), kImageExtent.width, kImageExtent.height);
// Empty pipeline layout for the first subpass.
const auto emptyPipelineLayout = vk::makePipelineLayout(vkd, device);
// Pipeline layout for the second subpass.
const auto checkPipelineLayout = vk::makePipelineLayout(vkd, device, descriptorSetLayout.get());
// Shader modules.
const auto vertModule = vk::createShaderModule(vkd, device, m_context.getBinaryCollection().get("vert"), 0u);
const auto writeModule = vk::createShaderModule(vkd, device, m_context.getBinaryCollection().get("frag_write"), 0u);
const auto checkModule = vk::createShaderModule(vkd, device, m_context.getBinaryCollection().get("frag_check"), 0u);
const std::vector<vk::VkVertexInputBindingDescription> vertexBindings =
{
{
0u, // deUint32 binding;
static_cast<deUint32>(sizeof(decltype(quadVertices)::value_type)), // deUint32 stride;
vk::VK_VERTEX_INPUT_RATE_VERTEX, // VkVertexInputRate inputRate;
},
};
const std::vector<vk::VkVertexInputAttributeDescription> vertexAttributes =
{
{
0u, // deUint32 location;
0u, // deUint32 binding;
vk::VK_FORMAT_R32G32_SFLOAT, // VkFormat format;
0u, // deUint32 offset;
},
};
const vk::VkPipelineVertexInputStateCreateInfo vertexInputInfo =
{
vk::VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, // VkStructureType sType;
nullptr, // const void* pNext;
0u, // VkPipelineVertexInputStateCreateFlags flags;
static_cast<deUint32>(vertexBindings.size()), // deUint32 vertexBindingDescriptionCount;
vertexBindings.data(), // const VkVertexInputBindingDescription* pVertexBindingDescriptions;
static_cast<deUint32>(vertexAttributes.size()), // deUint32 vertexAttributeDescriptionCount;
vertexAttributes.data(), // const VkVertexInputAttributeDescription* pVertexAttributeDescriptions;
};
const vk::VkPipelineInputAssemblyStateCreateInfo inputAssemblyInfo =
{
vk::VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, // VkStructureType sType;
nullptr, // const void* pNext;
0u, // VkPipelineInputAssemblyStateCreateFlags flags;
vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, // VkPrimitiveTopology topology;
VK_FALSE, // VkBool32 primitiveRestartEnable;
};
const auto viewport = vk::makeViewport(kImageExtent);
const auto scissor = vk::makeRect2D(kImageExtent);
const vk::VkPipelineViewportStateCreateInfo viewportInfo =
{
vk::VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, // VkStructureType sType;
nullptr, // const void* pNext;
0u, // VkPipelineViewportStateCreateFlags flags;
1u, // deUint32 viewportCount;
&viewport, // const VkViewport* pViewports;
1u, // deUint32 scissorCount;
&scissor, // const VkRect2D* pScissors;
};
const vk::VkPipelineRasterizationStateCreateInfo rasterizationInfo =
{
vk::VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, // VkStructureType sType;
nullptr, // const void* pNext;
0u, // VkPipelineRasterizationStateCreateFlags flags;
VK_FALSE, // VkBool32 depthClampEnable;
VK_FALSE, // VkBool32 rasterizerDiscardEnable;
vk::VK_POLYGON_MODE_FILL, // VkPolygonMode polygonMode;
vk::VK_CULL_MODE_NONE, // VkCullModeFlags cullMode;
vk::VK_FRONT_FACE_COUNTER_CLOCKWISE, // VkFrontFace frontFace;
VK_FALSE, // VkBool32 depthBiasEnable;
0.0f, // float depthBiasConstantFactor;
0.0f, // float depthBiasClamp;
0.0f, // float depthBiasSlopeFactor;
1.0f, // float lineWidth;
};
const vk::VkPipelineMultisampleStateCreateInfo multisampleInfo =
{
vk::VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, // VkStructureType sType;
nullptr, // const void* pNext;
0u, // VkPipelineMultisampleStateCreateFlags flags;
m_params.sampleCount, // VkSampleCountFlagBits rasterizationSamples;
VK_FALSE, // VkBool32 sampleShadingEnable;
1.0f, // float minSampleShading;
nullptr, // const VkSampleMask* pSampleMask;
VK_FALSE, // VkBool32 alphaToCoverageEnable;
VK_FALSE, // VkBool32 alphaToOneEnable;
};
const auto stencilState = vk::makeStencilOpState(vk::VK_STENCIL_OP_KEEP, vk::VK_STENCIL_OP_KEEP, vk::VK_STENCIL_OP_KEEP, vk::VK_COMPARE_OP_ALWAYS, 0xFFu, 0xFFu, 0u);
const vk::VkPipelineDepthStencilStateCreateInfo depthStencilInfo =
{
vk::VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, // VkStructureType sType;
nullptr, // const void* pNext;
0u, // VkPipelineDepthStencilStateCreateFlags flags;
VK_FALSE, // VkBool32 depthTestEnable;
VK_FALSE, // VkBool32 depthWriteEnable;
vk::VK_COMPARE_OP_ALWAYS, // VkCompareOp depthCompareOp;
VK_FALSE, // VkBool32 depthBoundsTestEnable;
VK_FALSE, // VkBool32 stencilTestEnable;
stencilState, // VkStencilOpState front;
stencilState, // VkStencilOpState back;
0.0f, // float minDepthBounds;
1.0f, // float maxDepthBounds;
};
const vk::VkPipelineColorBlendAttachmentState colorBlendAttachmentState =
{
VK_FALSE, // VkBool32 blendEnable;
vk::VK_BLEND_FACTOR_ZERO, // VkBlendFactor srcColorBlendFactor;
vk::VK_BLEND_FACTOR_ZERO, // VkBlendFactor dstColorBlendFactor;
vk::VK_BLEND_OP_ADD, // VkBlendOp colorBlendOp;
vk::VK_BLEND_FACTOR_ZERO, // VkBlendFactor srcAlphaBlendFactor;
vk::VK_BLEND_FACTOR_ZERO, // VkBlendFactor dstAlphaBlendFactor;
vk::VK_BLEND_OP_ADD, // VkBlendOp alphaBlendOp;
( // VkColorComponentFlags colorWriteMask;
vk::VK_COLOR_COMPONENT_R_BIT |
vk::VK_COLOR_COMPONENT_G_BIT |
vk::VK_COLOR_COMPONENT_B_BIT |
vk::VK_COLOR_COMPONENT_A_BIT ),
};
const vk::VkPipelineColorBlendStateCreateInfo colorBlendInfo =
{
vk::VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, // VkStructureType sType;
nullptr, // const void* pNext;
0u, // VkPipelineColorBlendStateCreateFlags flags;
VK_FALSE, // VkBool32 logicOpEnable;
vk::VK_LOGIC_OP_NO_OP, // VkLogicOp logicOp;
1u, // deUint32 attachmentCount;
&colorBlendAttachmentState, // const VkPipelineColorBlendAttachmentState* pAttachments;
{ .0f, .0f, .0f, .0f }, // float blendConstants[4];
};
const vk::VkPipelineDynamicStateCreateInfo dynamicStateInfo =
{
vk::VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, // VkStructureType sType;
nullptr, // const void* pNext;
0u, // VkPipelineDynamicStateCreateFlags flags;
0u, // deUint32 dynamicStateCount;
nullptr, // const VkDynamicState* pDynamicStates;
};
// Pipeline for the first subpass.
const auto firstSubpassPipeline = vk::makeGraphicsPipeline(
vkd, device, emptyPipelineLayout.get(),
vertModule.get(), DE_NULL, DE_NULL, DE_NULL, writeModule.get(),
renderPass.get(), 0u,
&vertexInputInfo, &inputAssemblyInfo, nullptr, &viewportInfo, &rasterizationInfo,
&multisampleInfo, &depthStencilInfo, &colorBlendInfo, &dynamicStateInfo
);
// Pipeline for the second subpass.
const auto secondSubpassPipeline = vk::makeGraphicsPipeline(
vkd, device, checkPipelineLayout.get(),
vertModule.get(), DE_NULL, DE_NULL, DE_NULL, checkModule.get(),
renderPass.get(), 1u,
&vertexInputInfo, &inputAssemblyInfo, nullptr, &viewportInfo, &rasterizationInfo,
&multisampleInfo, &depthStencilInfo, &colorBlendInfo, &dynamicStateInfo
);
// Command pool and command buffer.
const auto cmdPool = vk::makeCommandPool(vkd, device, queueFamilyIndex);
const auto cmdBufferPtr = vk::allocateCommandBuffer(vkd, device, cmdPool.get(), vk::VK_COMMAND_BUFFER_LEVEL_PRIMARY);
const auto cmdBuffer = cmdBufferPtr.get();
// Update descriptor set.
vk::DescriptorSetUpdateBuilder updateBuilder;
const auto imageInfo = vk::makeDescriptorImageInfo(DE_NULL, colorImageView.get(), vk::VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
const auto bufferInfo = vk::makeDescriptorBufferInfo(storageBuffer.get(), 0u, VK_WHOLE_SIZE);
updateBuilder.writeSingle(descriptorSet.get(), vk::DescriptorSetUpdateBuilder::Location::binding(0u), vk::VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, &imageInfo);
updateBuilder.writeSingle(descriptorSet.get(), vk::DescriptorSetUpdateBuilder::Location::binding(1u), vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &bufferInfo);
updateBuilder.update(vkd, device);
// Output buffer pipeline barrier.
const auto bufferBarrier = vk::makeBufferMemoryBarrier(vk::VK_ACCESS_SHADER_WRITE_BIT, vk::VK_ACCESS_HOST_READ_BIT, storageBuffer.get(), 0ull, VK_WHOLE_SIZE);
// Run pipelines.
vk::beginCommandBuffer(vkd, cmdBuffer);
vk::beginRenderPass(vkd, cmdBuffer, renderPass.get(), framebuffer.get(), vk::makeRect2D(kImageExtent), WriteSampleMaskTestCase::kClearColor);
vkd.cmdBindVertexBuffers(cmdBuffer, 0u, 1u, &vertexBuffer.get(), &vertexBufferOffset);
vkd.cmdBindPipeline(cmdBuffer, vk::VK_PIPELINE_BIND_POINT_GRAPHICS, firstSubpassPipeline.get());
vkd.cmdDraw(cmdBuffer, static_cast<deUint32>(quadVertices.size()), 1u, 0u, 0u);
vkd.cmdNextSubpass(cmdBuffer, vk::VK_SUBPASS_CONTENTS_INLINE);
vkd.cmdBindPipeline(cmdBuffer, vk::VK_PIPELINE_BIND_POINT_GRAPHICS, secondSubpassPipeline.get());
vkd.cmdBindDescriptorSets(cmdBuffer, vk::VK_PIPELINE_BIND_POINT_GRAPHICS, checkPipelineLayout.get(), 0u, 1u, &descriptorSet.get(), 0u, nullptr);
vkd.cmdDraw(cmdBuffer, static_cast<deUint32>(quadVertices.size()), 1u, 0u, 0u);
vk::endRenderPass(vkd, cmdBuffer);
vkd.cmdPipelineBarrier(cmdBuffer, vk::VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, vk::VK_PIPELINE_STAGE_HOST_BIT, 0u, 0u, nullptr, 1u, &bufferBarrier, 0u, nullptr);
vk::endCommandBuffer(vkd, cmdBuffer);
vk::submitCommandsAndWait(vkd, device, queue, cmdBuffer);
// Check buffer contents.
auto& bufferAlloc = storageBuffer.getAllocation();
const void* bufferPtr = bufferAlloc.getHostPtr();
std::vector<deInt32> bufferContents (kBufferElems, 0);
vk::invalidateAlloc(vkd, device, bufferAlloc);
deMemcpy(bufferContents.data(), bufferPtr, static_cast<size_t>(kBufferSize));
const auto sampleCount = static_cast<deUint32>(m_params.sampleCount);
const auto bpc = bitsPerCoord(sampleCount);
for (deUint32 x = 0; x < kImageExtent.width; ++x)
for (deUint32 y = 0; y < kImageExtent.height; ++y)
{
// Samples on which we expect writes.
const deUint32 sampleMask = ((x << bpc) | y);
// Starting location for the pixel sample values in the buffer.
const deUint32 pixelOffset = (y * kImageExtent.width + x) * sampleCount;
for (deUint32 s = 0; s < sampleCount; ++s)
{
const deUint32 sampleIndex = pixelOffset + s;
const deInt32& value = bufferContents[sampleIndex];
if (value != 0 && value != 1)
{
// Garbage!
std::ostringstream msg;
msg << "Found garbage value " << value << " in buffer position " << sampleIndex << " (x=" << x << ", y=" << y << ", sample=" << s << ")";
return tcu::TestStatus::fail(msg.str());
}
const deInt32 expected = (((sampleMask & (1u << s)) != 0u) ? 1 : 0);
if (value != expected)
{
std::ostringstream msg;
msg << "Read " << value << " while expecting " << expected << " in buffer position " << sampleIndex << " (x=" << x << ", y=" << y << ", sample=" << s << ")";
return tcu::TestStatus::fail(msg.str());
}
}
}
return tcu::TestStatus::pass("Pass");
}
} // multisample
tcu::TestCaseGroup* createMultisampleShaderBuiltInTests (tcu::TestContext& testCtx)
{
de::MovePtr<tcu::TestCaseGroup> testGroup(new tcu::TestCaseGroup(testCtx, "multisample_shader_builtin", "Multisample Shader BuiltIn Tests"));
const tcu::UVec3 imageSizes[] =
{
tcu::UVec3(128u, 128u, 1u),
tcu::UVec3(137u, 191u, 1u),
};
const deUint32 sizesElemCount = static_cast<deUint32>(sizeof(imageSizes) / sizeof(tcu::UVec3));
const vk::VkSampleCountFlagBits samplesSetFull[] =
{
vk::VK_SAMPLE_COUNT_2_BIT,
vk::VK_SAMPLE_COUNT_4_BIT,
vk::VK_SAMPLE_COUNT_8_BIT,
vk::VK_SAMPLE_COUNT_16_BIT,
vk::VK_SAMPLE_COUNT_32_BIT,
vk::VK_SAMPLE_COUNT_64_BIT,
};
const deUint32 samplesSetFullCount = static_cast<deUint32>(sizeof(samplesSetFull) / sizeof(vk::VkSampleCountFlagBits));
testGroup->addChild(makeMSGroup<multisample::MSCase<multisample::MSCaseSampleID> >(testCtx, "sample_id", imageSizes, sizesElemCount, samplesSetFull, samplesSetFullCount));
de::MovePtr<tcu::TestCaseGroup> samplePositionGroup(new tcu::TestCaseGroup(testCtx, "sample_position", "Sample Position Tests"));
samplePositionGroup->addChild(makeMSGroup<multisample::MSCase<multisample::MSCaseSamplePosDistribution> >(testCtx, "distribution", imageSizes, sizesElemCount, samplesSetFull, samplesSetFullCount));
samplePositionGroup->addChild(makeMSGroup<multisample::MSCase<multisample::MSCaseSamplePosCorrectness> > (testCtx, "correctness", imageSizes, sizesElemCount, samplesSetFull, samplesSetFullCount));
testGroup->addChild(samplePositionGroup.release());
const vk::VkSampleCountFlagBits samplesSetReduced[] =
{
vk::VK_SAMPLE_COUNT_2_BIT,
vk::VK_SAMPLE_COUNT_4_BIT,
vk::VK_SAMPLE_COUNT_8_BIT,
vk::VK_SAMPLE_COUNT_16_BIT,
vk::VK_SAMPLE_COUNT_32_BIT,
};
const deUint32 samplesSetReducedCount = static_cast<deUint32>(DE_LENGTH_OF_ARRAY(samplesSetReduced));
de::MovePtr<tcu::TestCaseGroup> sampleMaskGroup(new tcu::TestCaseGroup(testCtx, "sample_mask", "Sample Mask Tests"));
sampleMaskGroup->addChild(makeMSGroup<multisample::MSCase<multisample::MSCaseSampleMaskPattern> > (testCtx, "pattern", imageSizes, sizesElemCount, samplesSetReduced, samplesSetReducedCount));
sampleMaskGroup->addChild(makeMSGroup<multisample::MSCase<multisample::MSCaseSampleMaskBitCount> > (testCtx, "bit_count", imageSizes, sizesElemCount, samplesSetReduced, samplesSetReducedCount));
sampleMaskGroup->addChild(makeMSGroup<multisample::MSCase<multisample::MSCaseSampleMaskCorrectBit> >(testCtx, "correct_bit",imageSizes, sizesElemCount, samplesSetReduced, samplesSetReducedCount));
sampleMaskGroup->addChild(makeMSGroup<multisample::MSCase<multisample::MSCaseSampleMaskWrite> > (testCtx, "write", imageSizes, sizesElemCount, samplesSetReduced, samplesSetReducedCount));
testGroup->addChild(sampleMaskGroup.release());
// Write image sample tests using a storage images.
{
de::MovePtr<tcu::TestCaseGroup> imageWriteSampleGroup(new tcu::TestCaseGroup(testCtx, "image_write_sample", "Test OpImageWrite with a sample ID"));
for (auto count : multisample::kValidSquareSampleCounts)
{
if (count == vk::VK_SAMPLE_COUNT_1_BIT)
continue;
multisample::WriteSampleParams params { static_cast<vk::VkSampleCountFlagBits>(count) };
const auto countStr = de::toString(count);
imageWriteSampleGroup->addChild(new multisample::WriteSampleTest(testCtx, countStr + "_samples", "Test image with " + countStr + " samples", params));
}
testGroup->addChild(imageWriteSampleGroup.release());
}
// Write to gl_SampleMask from the fragment shader.
{
de::MovePtr<tcu::TestCaseGroup> writeSampleMaskGroup(new tcu::TestCaseGroup(testCtx, "write_sample_mask", "Test writes to SampleMask variable"));
for (auto count : multisample::kValidSquareSampleCounts)
{
multisample::WriteSampleMaskParams params { static_cast<vk::VkSampleCountFlagBits>(count) };
const auto countStr = de::toString(count);
writeSampleMaskGroup->addChild(new multisample::WriteSampleMaskTestCase(testCtx, countStr + "_samples", "Test image with " + countStr + " samples", params));
}
testGroup->addChild(writeSampleMaskGroup.release());
}
return testGroup.release();
}
} // pipeline
} // vkt