blob: efb4cbf42a8b001ba28e6f1754bc6a234b4b84e9 [file] [log] [blame]
/*------------------------------------------------------------------------
* Vulkan Conformance Tests
* ------------------------
*
* Copyright (c) 2017 The Khronos Group Inc.
* Copyright (c) 2017 Samsung Electronics Co., Ltd.
*
* 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 Protected memory image access tests
*//*--------------------------------------------------------------------*/
#include "vktProtectedMemShaderImageAccessTests.hpp"
#include "vktProtectedMemContext.hpp"
#include "vktProtectedMemUtils.hpp"
#include "vktProtectedMemImageValidator.hpp"
#include "vktTestCase.hpp"
#include "vktTestGroupUtil.hpp"
#include "vkPrograms.hpp"
#include "vkTypeUtil.hpp"
#include "vkBuilderUtil.hpp"
#include "vkImageUtil.hpp"
#include "vkCmdUtil.hpp"
#include "vkObjUtil.hpp"
#include "tcuTestLog.hpp"
#include "tcuVector.hpp"
#include "tcuTextureUtil.hpp"
#include "tcuStringTemplate.hpp"
#include "gluTextureTestUtil.hpp"
#include "deRandom.hpp"
namespace vkt
{
namespace ProtectedMem
{
namespace
{
enum
{
RENDER_WIDTH = 128,
RENDER_HEIGHT = 128,
IMAGE_WIDTH = 128,
IMAGE_HEIGHT = 128,
};
enum AccessType
{
ACCESS_TYPE_SAMPLING = 0,
ACCESS_TYPE_TEXEL_FETCH,
ACCESS_TYPE_IMAGE_LOAD,
ACCESS_TYPE_IMAGE_STORE,
ACCESS_TYPE_IMAGE_ATOMICS,
ACCESS_TYPE_LAST
};
enum AtomicOperation
{
ATOMIC_OPERATION_ADD = 0,
ATOMIC_OPERATION_MIN,
ATOMIC_OPERATION_MAX,
ATOMIC_OPERATION_AND,
ATOMIC_OPERATION_OR,
ATOMIC_OPERATION_XOR,
ATOMIC_OPERATION_EXCHANGE,
ATOMIC_OPERATION_LAST
};
struct Params
{
glu::ShaderType shaderType;
AccessType accessType;
vk::VkFormat imageFormat;
AtomicOperation atomicOperation;
Params (void)
: shaderType (glu::SHADERTYPE_LAST)
, accessType (ACCESS_TYPE_LAST)
, imageFormat (vk::VK_FORMAT_UNDEFINED)
, atomicOperation (ATOMIC_OPERATION_LAST)
{}
Params (const glu::ShaderType shaderType_,
const AccessType accessType_,
const vk::VkFormat imageFormat_,
const AtomicOperation atomicOperation_ = ATOMIC_OPERATION_LAST)
: shaderType (shaderType_)
, accessType (accessType_)
, imageFormat (imageFormat_)
, atomicOperation (atomicOperation_)
{}
};
static deUint32 getSeedValue (const Params& params)
{
return deInt32Hash(params.shaderType) ^ deInt32Hash(params.accessType) ^ deInt32Hash(params.imageFormat) ^ deInt32Hash(params.atomicOperation);
}
static std::string getAtomicOperationCaseName (const AtomicOperation op)
{
switch (op)
{
case ATOMIC_OPERATION_ADD: return "add";
case ATOMIC_OPERATION_MIN: return "min";
case ATOMIC_OPERATION_MAX: return "max";
case ATOMIC_OPERATION_AND: return "and";
case ATOMIC_OPERATION_OR: return "or";
case ATOMIC_OPERATION_XOR: return "xor";
case ATOMIC_OPERATION_EXCHANGE: return "exchange";
default:
DE_FATAL("Impossible");
return "";
}
}
static std::string getAtomicOperationShaderFuncName (const AtomicOperation op)
{
switch (op)
{
case ATOMIC_OPERATION_ADD: return "imageAtomicAdd";
case ATOMIC_OPERATION_MIN: return "imageAtomicMin";
case ATOMIC_OPERATION_MAX: return "imageAtomicMax";
case ATOMIC_OPERATION_AND: return "imageAtomicAnd";
case ATOMIC_OPERATION_OR: return "imageAtomicOr";
case ATOMIC_OPERATION_XOR: return "imageAtomicXor";
case ATOMIC_OPERATION_EXCHANGE: return "imageAtomicExchange";
default:
DE_FATAL("Impossible");
return "";
}
}
//! Computes the result of an atomic operation where "a" is the data operated on and "b" is the parameter to the atomic function.
static deInt32 computeBinaryAtomicOperationResult (const AtomicOperation op, const deInt32 a, const deInt32 b)
{
switch (op)
{
case ATOMIC_OPERATION_ADD: return a + b;
case ATOMIC_OPERATION_MIN: return de::min(a, b);
case ATOMIC_OPERATION_MAX: return de::max(a, b);
case ATOMIC_OPERATION_AND: return a & b;
case ATOMIC_OPERATION_OR: return a | b;
case ATOMIC_OPERATION_XOR: return a ^ b;
case ATOMIC_OPERATION_EXCHANGE: return b;
default:
DE_FATAL("Impossible");
return -1;
}
}
static std::string getShaderImageFormatQualifier (const tcu::TextureFormat& format)
{
const char* orderPart;
const char* typePart;
switch (format.order)
{
case tcu::TextureFormat::R: orderPart = "r"; break;
case tcu::TextureFormat::RG: orderPart = "rg"; break;
case tcu::TextureFormat::RGB: orderPart = "rgb"; break;
case tcu::TextureFormat::RGBA: orderPart = "rgba"; break;
default:
DE_FATAL("Impossible");
orderPart = DE_NULL;
}
switch (format.type)
{
case tcu::TextureFormat::FLOAT: typePart = "32f"; break;
case tcu::TextureFormat::HALF_FLOAT: typePart = "16f"; break;
case tcu::TextureFormat::UNSIGNED_INT32: typePart = "32ui"; break;
case tcu::TextureFormat::UNSIGNED_INT16: typePart = "16ui"; break;
case tcu::TextureFormat::UNSIGNED_INT8: typePart = "8ui"; break;
case tcu::TextureFormat::SIGNED_INT32: typePart = "32i"; break;
case tcu::TextureFormat::SIGNED_INT16: typePart = "16i"; break;
case tcu::TextureFormat::SIGNED_INT8: typePart = "8i"; break;
case tcu::TextureFormat::UNORM_INT16: typePart = "16"; break;
case tcu::TextureFormat::UNORM_INT8: typePart = "8"; break;
case tcu::TextureFormat::SNORM_INT16: typePart = "16_snorm"; break;
case tcu::TextureFormat::SNORM_INT8: typePart = "8_snorm"; break;
default:
DE_FATAL("Impossible");
typePart = DE_NULL;
}
return std::string() + orderPart + typePart;
}
static std::string getShaderSamplerOrImageType (const tcu::TextureFormat& format, bool isSampler)
{
const std::string formatPart = tcu::getTextureChannelClass(format.type) == tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER ? "u" :
tcu::getTextureChannelClass(format.type) == tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER ? "i" : "";
return formatPart + (isSampler ? "sampler2D" : "image2D");
}
class ImageAccessTestInstance : public ProtectedTestInstance
{
public:
ImageAccessTestInstance (Context& ctx,
const ImageValidator& validator,
const Params& params);
virtual tcu::TestStatus iterate (void);
private:
de::MovePtr<tcu::Texture2D> createTestTexture2D (void);
void calculateAtomicRef (tcu::Texture2D& texture2D);
tcu::TestStatus validateResult (vk::VkImage image,
vk::VkImageLayout imageLayout,
const tcu::Texture2D& texture2D,
const tcu::Sampler& refSampler);
tcu::TestStatus executeFragmentTest (void);
tcu::TestStatus executeComputeTest (void);
const ImageValidator& m_validator;
const Params& m_params;
};
class ImageAccessTestCase : public TestCase
{
public:
ImageAccessTestCase (tcu::TestContext& testCtx,
const std::string& name,
const std::string& description,
const Params& params)
: TestCase (testCtx, name, description)
, m_validator (params.imageFormat)
, m_params (params)
{
}
virtual ~ImageAccessTestCase (void) {}
virtual TestInstance* createInstance (Context& ctx) const
{
return new ImageAccessTestInstance(ctx, m_validator, m_params);
}
virtual void initPrograms (vk::SourceCollections& programCollection) const;
virtual void checkSupport (Context& context) const
{
checkProtectedQueueSupport(context);
}
private:
ImageValidator m_validator;
Params m_params;
};
void ImageAccessTestCase::initPrograms (vk::SourceCollections& programCollection) const
{
const tcu::TextureFormat& texFormat = mapVkFormat(m_params.imageFormat);
const std::string imageFormat = getShaderImageFormatQualifier(texFormat);
const std::string imageType = getShaderSamplerOrImageType(texFormat, false);
const std::string samplerType = getShaderSamplerOrImageType(texFormat, true);
const std::string colorVecType = isIntFormat(m_params.imageFormat) ? "ivec4" :
isUintFormat(m_params.imageFormat) ? "uvec4" : "vec4";
m_validator.initPrograms(programCollection);
if (m_params.shaderType == glu::SHADERTYPE_FRAGMENT)
{
{
// Vertex shader
const char* vert = "#version 450\n"
"layout(location = 0) in mediump vec2 a_position;\n"
"layout(location = 1) in mediump vec2 a_texCoord;\n"
"layout(location = 0) out mediump vec2 v_texCoord;\n"
"\n"
"void main() {\n"
" gl_Position = vec4(a_position, 0.0, 1.0);\n"
" v_texCoord = a_texCoord;\n"
"}\n";
programCollection.glslSources.add("vert") << glu::VertexSource(vert);
}
{
// Fragment shader
std::ostringstream frag;
frag << "#version 450\n"
"layout(location = 0) in mediump vec2 v_texCoord;\n"
"layout(location = 0) out highp ${COLOR_VEC_TYPE} o_color;\n";
switch (m_params.accessType)
{
case ACCESS_TYPE_SAMPLING:
case ACCESS_TYPE_TEXEL_FETCH:
frag << "layout(set = 0, binding = 0) uniform highp ${SAMPLER_TYPE} u_sampler;\n";
break;
case ACCESS_TYPE_IMAGE_LOAD:
frag << "layout(set = 0, binding = 0, ${IMAGE_FORMAT}) readonly uniform highp ${IMAGE_TYPE} u_image;\n";
break;
case ACCESS_TYPE_IMAGE_STORE:
frag << "layout(set = 0, binding = 0, ${IMAGE_FORMAT}) readonly uniform highp ${IMAGE_TYPE} u_imageA;\n";
frag << "layout(set = 0, binding = 1, ${IMAGE_FORMAT}) writeonly uniform highp ${IMAGE_TYPE} u_imageB;\n";
break;
case ACCESS_TYPE_IMAGE_ATOMICS:
frag << "layout(set = 0, binding = 0, ${IMAGE_FORMAT}) coherent uniform highp ${IMAGE_TYPE} u_image;\n";
break;
default:
DE_FATAL("Impossible");
break;
}
frag << "\n"
"void main() {\n";
switch (m_params.accessType)
{
case ACCESS_TYPE_SAMPLING:
frag << " o_color = texture(u_sampler, v_texCoord);\n";
break;
case ACCESS_TYPE_TEXEL_FETCH:
frag << " const highp int lod = 0;\n";
frag << " o_color = texelFetch(u_sampler, ivec2(v_texCoord), lod);\n";
break;
case ACCESS_TYPE_IMAGE_LOAD:
frag << " o_color = imageLoad(u_image, ivec2(v_texCoord));\n";
break;
case ACCESS_TYPE_IMAGE_STORE:
frag << " o_color = imageLoad(u_imageA, ivec2(v_texCoord));\n";
frag << " imageStore(u_imageB, ivec2(v_texCoord), o_color);\n";
break;
case ACCESS_TYPE_IMAGE_ATOMICS:
frag << " int gx = int(v_texCoord.x);\n";
frag << " int gy = int(v_texCoord.y);\n";
frag << " "
<< getAtomicOperationShaderFuncName(m_params.atomicOperation)
<< "(u_image, ivec2(v_texCoord), "
<< (isUintFormat(m_params.imageFormat) ? "uint" : "int")
<< "(gx*gx + gy*gy));\n";
frag << " o_color = imageLoad(u_image, ivec2(v_texCoord));\n";
break;
default:
DE_FATAL("Impossible");
break;
}
frag << "}\n";
std::map<std::string, std::string> fragParams;
fragParams["IMAGE_FORMAT"] = imageFormat;
fragParams["IMAGE_TYPE"] = imageType;
fragParams["SAMPLER_TYPE"] = samplerType;
fragParams["COLOR_VEC_TYPE"] = colorVecType;
programCollection.glslSources.add("frag") << glu::FragmentSource(tcu::StringTemplate(frag.str()).specialize(fragParams));
}
}
else if (m_params.shaderType == glu::SHADERTYPE_COMPUTE)
{
// Compute shader
std::ostringstream comp;
comp << "#version 450\n"
"layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n"
"layout(set = 0, binding = 0, ${IMAGE_FORMAT}) ${RES_MEM_QUALIFIER} uniform highp ${IMAGE_TYPE} u_resultImage;\n";
switch (m_params.accessType)
{
case ACCESS_TYPE_SAMPLING:
case ACCESS_TYPE_TEXEL_FETCH:
comp << "layout(set = 0, binding = 1) uniform highp ${SAMPLER_TYPE} u_sampler;\n";
break;
case ACCESS_TYPE_IMAGE_LOAD:
case ACCESS_TYPE_IMAGE_STORE:
comp << "layout(set = 0, binding = 1, ${IMAGE_FORMAT}) readonly uniform highp ${IMAGE_TYPE} u_srcImage;\n";
break;
case ACCESS_TYPE_IMAGE_ATOMICS:
break;
default:
DE_FATAL("Impossible");
break;
}
comp << "\n"
"void main() {\n"
" int gx = int(gl_GlobalInvocationID.x);\n"
" int gy = int(gl_GlobalInvocationID.y);\n";
switch (m_params.accessType)
{
case ACCESS_TYPE_SAMPLING:
comp << " ${COLOR_VEC_TYPE} color = texture(u_sampler, vec2(float(gx)/" << de::toString((int)IMAGE_WIDTH) << ", float(gy)/" << de::toString((int)IMAGE_HEIGHT) << "));\n";
comp << " imageStore(u_resultImage, ivec2(gx, gy), color);\n";
break;
case ACCESS_TYPE_TEXEL_FETCH:
comp << " const highp int lod = 0;\n";
comp << " ${COLOR_VEC_TYPE} color = texelFetch(u_sampler, ivec2(gx, gy), lod);\n";
comp << " imageStore(u_resultImage, ivec2(gx, gy), color);\n";
break;
case ACCESS_TYPE_IMAGE_LOAD:
case ACCESS_TYPE_IMAGE_STORE:
comp << " ${COLOR_VEC_TYPE} color = imageLoad(u_srcImage, ivec2(gx, gy));\n";
comp << " imageStore(u_resultImage, ivec2(gx, gy), color);\n";
break;
case ACCESS_TYPE_IMAGE_ATOMICS:
comp << " "
<< getAtomicOperationShaderFuncName(m_params.atomicOperation)
<< "(u_resultImage, ivec2(gx, gy), "
<< (isUintFormat(m_params.imageFormat) ? "uint" : "int")
<< "(gx*gx + gy*gy));\n";
break;
default:
DE_FATAL("Impossible");
break;
}
comp << "}\n";
std::map<std::string, std::string> compParams;
compParams["IMAGE_FORMAT"] = imageFormat;
compParams["IMAGE_TYPE"] = imageType;
compParams["SAMPLER_TYPE"] = samplerType;
compParams["COLOR_VEC_TYPE"] = colorVecType;
compParams["RES_MEM_QUALIFIER"] = m_params.accessType == ACCESS_TYPE_IMAGE_ATOMICS ? "coherent" : "writeonly";
programCollection.glslSources.add("comp") << glu::ComputeSource(tcu::StringTemplate(comp.str()).specialize(compParams));
}
else
DE_FATAL("Impossible");
}
ImageAccessTestInstance::ImageAccessTestInstance (Context& ctx,
const ImageValidator& validator,
const Params& params)
: ProtectedTestInstance (ctx)
, m_validator (validator)
, m_params (params)
{
}
de::MovePtr<tcu::Texture2D> ImageAccessTestInstance::createTestTexture2D (void)
{
const tcu::TextureFormat texFmt = mapVkFormat(m_params.imageFormat);
const tcu::TextureFormatInfo fmtInfo = tcu::getTextureFormatInfo(texFmt);
de::MovePtr<tcu::Texture2D> texture2D (new tcu::Texture2D(texFmt, IMAGE_WIDTH, IMAGE_HEIGHT));
// \note generate only the base level
texture2D->allocLevel(0);
const tcu::PixelBufferAccess& level = texture2D->getLevel(0);
if (m_params.accessType == ACCESS_TYPE_IMAGE_ATOMICS)
{
// use a smaller range than the format would allow
const float cMin = isIntFormat(m_params.imageFormat) ? -1000.0f : 0.0f;
const float cMax = +1000.0f;
fillWithRandomColorTiles(level, tcu::Vec4(cMin, 0, 0, 0), tcu::Vec4(cMax, 0, 0, 0), getSeedValue(m_params));
}
else
fillWithRandomColorTiles(level, fmtInfo.valueMin, fmtInfo.valueMax, getSeedValue(m_params));
return texture2D;
}
tcu::TestStatus ImageAccessTestInstance::iterate (void)
{
switch (m_params.shaderType)
{
case glu::SHADERTYPE_FRAGMENT: return executeFragmentTest();
case glu::SHADERTYPE_COMPUTE: return executeComputeTest();
default:
DE_FATAL("Impossible");
return tcu::TestStatus::fail("");
}
}
tcu::TestStatus ImageAccessTestInstance::executeComputeTest (void)
{
ProtectedContext& ctx (m_protectedContext);
const vk::DeviceInterface& vk = ctx.getDeviceInterface();
const vk::VkDevice device = ctx.getDevice();
const vk::VkQueue queue = ctx.getQueue();
const deUint32 queueFamilyIndex = ctx.getQueueFamilyIndex();
vk::Unique<vk::VkCommandPool> cmdPool (makeCommandPool(vk, device, PROTECTION_ENABLED, queueFamilyIndex));
de::MovePtr<tcu::Texture2D> texture2D = createTestTexture2D();
const tcu::Sampler refSampler = tcu::Sampler(tcu::Sampler::CLAMP_TO_EDGE, tcu::Sampler::CLAMP_TO_EDGE, tcu::Sampler::CLAMP_TO_EDGE,
tcu::Sampler::NEAREST, tcu::Sampler::NEAREST);
vk::Unique<vk::VkShaderModule> computeShader (vk::createShaderModule(vk, device, ctx.getBinaryCollection().get("comp"), 0));
de::MovePtr<vk::ImageWithMemory> imageSrc;
de::MovePtr<vk::ImageWithMemory> imageDst;
vk::Move<vk::VkSampler> sampler;
vk::Move<vk::VkImageView> imageViewSrc;
vk::Move<vk::VkImageView> imageViewDst;
vk::Move<vk::VkDescriptorSetLayout> descriptorSetLayout;
vk::Move<vk::VkDescriptorPool> descriptorPool;
vk::Move<vk::VkDescriptorSet> descriptorSet;
// Create src and dst images
{
vk::VkImageUsageFlags imageUsageFlags = vk::VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
vk::VK_IMAGE_USAGE_TRANSFER_DST_BIT |
vk::VK_IMAGE_USAGE_SAMPLED_BIT |
vk::VK_IMAGE_USAGE_STORAGE_BIT;
imageSrc = createImage2D(ctx, PROTECTION_ENABLED, queueFamilyIndex,
IMAGE_WIDTH, IMAGE_HEIGHT,
m_params.imageFormat,
imageUsageFlags);
if (m_params.accessType != ACCESS_TYPE_IMAGE_ATOMICS)
{
imageDst = createImage2D(ctx, PROTECTION_ENABLED, queueFamilyIndex,
IMAGE_WIDTH, IMAGE_HEIGHT,
m_params.imageFormat,
imageUsageFlags);
}
}
// Upload source image
{
de::MovePtr<vk::ImageWithMemory> unprotectedImage = createImage2D(ctx, PROTECTION_DISABLED, queueFamilyIndex,
IMAGE_WIDTH, IMAGE_HEIGHT,
m_params.imageFormat,
vk::VK_IMAGE_USAGE_TRANSFER_SRC_BIT | vk::VK_IMAGE_USAGE_TRANSFER_DST_BIT);
// Upload data to an unprotected image
uploadImage(m_protectedContext, **unprotectedImage, *texture2D);
// Select vkImageLayout based upon accessType
vk::VkImageLayout imageSrcLayout = vk::VK_IMAGE_LAYOUT_UNDEFINED;
switch (m_params.accessType)
{
case ACCESS_TYPE_SAMPLING:
case ACCESS_TYPE_TEXEL_FETCH:
{
imageSrcLayout = vk::VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
break;
}
case ACCESS_TYPE_IMAGE_LOAD:
case ACCESS_TYPE_IMAGE_STORE:
case ACCESS_TYPE_IMAGE_ATOMICS:
{
imageSrcLayout = vk::VK_IMAGE_LAYOUT_GENERAL;
break;
}
default:
DE_FATAL("Impossible");
break;
}
// Copy unprotected image to protected image
copyToProtectedImage(m_protectedContext, **unprotectedImage, **imageSrc, imageSrcLayout, IMAGE_WIDTH, IMAGE_HEIGHT);
}
// Clear dst image
if (m_params.accessType != ACCESS_TYPE_IMAGE_ATOMICS)
clearImage(m_protectedContext, **imageDst);
// Create descriptors
{
vk::DescriptorSetLayoutBuilder layoutBuilder;
vk::DescriptorPoolBuilder poolBuilder;
switch (m_params.accessType)
{
case ACCESS_TYPE_SAMPLING:
case ACCESS_TYPE_TEXEL_FETCH:
layoutBuilder.addSingleBinding(vk::VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, vk::VK_SHADER_STAGE_COMPUTE_BIT);
layoutBuilder.addSingleSamplerBinding(vk::VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, vk::VK_SHADER_STAGE_COMPUTE_BIT, DE_NULL);
poolBuilder.addType(vk::VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1u);
poolBuilder.addType(vk::VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1u);
break;
case ACCESS_TYPE_IMAGE_LOAD:
case ACCESS_TYPE_IMAGE_STORE:
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);
poolBuilder.addType(vk::VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 2u);
break;
case ACCESS_TYPE_IMAGE_ATOMICS:
layoutBuilder.addSingleBinding(vk::VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, vk::VK_SHADER_STAGE_COMPUTE_BIT);
poolBuilder.addType(vk::VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1u);
break;
default:
DE_FATAL("Impossible");
break;
}
descriptorSetLayout = layoutBuilder.build(vk, device);
descriptorPool = poolBuilder.build(vk, device, vk::VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u);
descriptorSet = makeDescriptorSet(vk, device, *descriptorPool, *descriptorSetLayout);
}
// Create pipeline layout
vk::Unique<vk::VkPipelineLayout> pipelineLayout (makePipelineLayout(vk, device, *descriptorSetLayout));
// Create sampler and image views
{
if (m_params.accessType == ACCESS_TYPE_SAMPLING || m_params.accessType == ACCESS_TYPE_TEXEL_FETCH)
{
const tcu::TextureFormat texFormat = mapVkFormat(m_params.imageFormat);
const vk::VkSamplerCreateInfo samplerParams = vk::mapSampler(refSampler, texFormat);
sampler = createSampler(vk, device, &samplerParams);
}
imageViewSrc = createImageView(ctx, **imageSrc, m_params.imageFormat);
if (m_params.accessType != ACCESS_TYPE_IMAGE_ATOMICS)
imageViewDst = createImageView(ctx, **imageDst, m_params.imageFormat);
}
// Update descriptor set information
{
vk::DescriptorSetUpdateBuilder updateBuilder;
switch (m_params.accessType)
{
case ACCESS_TYPE_SAMPLING:
case ACCESS_TYPE_TEXEL_FETCH:
{
vk::VkDescriptorImageInfo descStorageImgDst = makeDescriptorImageInfo((vk::VkSampler)0, *imageViewDst, vk::VK_IMAGE_LAYOUT_GENERAL);
vk::VkDescriptorImageInfo descSampledImgSrc = makeDescriptorImageInfo(*sampler, *imageViewSrc, vk::VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
updateBuilder.writeSingle(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(0u), vk::VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, &descStorageImgDst);
updateBuilder.writeSingle(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(1u), vk::VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, &descSampledImgSrc);
break;
}
case ACCESS_TYPE_IMAGE_LOAD:
case ACCESS_TYPE_IMAGE_STORE:
{
vk::VkDescriptorImageInfo descStorageImgDst = makeDescriptorImageInfo((vk::VkSampler)0, *imageViewDst, vk::VK_IMAGE_LAYOUT_GENERAL);
vk::VkDescriptorImageInfo descStorageImgSrc = makeDescriptorImageInfo((vk::VkSampler)0, *imageViewSrc, vk::VK_IMAGE_LAYOUT_GENERAL);
updateBuilder.writeSingle(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(0u), vk::VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, &descStorageImgDst);
updateBuilder.writeSingle(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(1u), vk::VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, &descStorageImgSrc);
break;
}
case ACCESS_TYPE_IMAGE_ATOMICS:
{
vk::VkDescriptorImageInfo descStorageImg = makeDescriptorImageInfo((vk::VkSampler)0, *imageViewSrc, vk::VK_IMAGE_LAYOUT_GENERAL);
updateBuilder.writeSingle(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(0u), vk::VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, &descStorageImg);
break;
}
default:
DE_FATAL("Impossible");
break;
}
updateBuilder.update(vk, device);
}
// Create validation compute commands & submit
{
const vk::Unique<vk::VkFence> fence (vk::createFence(vk, device));
vk::Unique<vk::VkPipeline> pipeline (makeComputePipeline(vk, device, *pipelineLayout, *computeShader, DE_NULL));
vk::Unique<vk::VkCommandBuffer> cmdBuffer (vk::allocateCommandBuffer(vk, device, *cmdPool, vk::VK_COMMAND_BUFFER_LEVEL_PRIMARY));
beginCommandBuffer(vk, *cmdBuffer);
vk.cmdBindPipeline(*cmdBuffer, vk::VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline);
vk.cmdBindDescriptorSets(*cmdBuffer, vk::VK_PIPELINE_BIND_POINT_COMPUTE, *pipelineLayout, 0u, 1u, &*descriptorSet, 0u, DE_NULL);
vk.cmdDispatch(*cmdBuffer, (deUint32)IMAGE_WIDTH, (deUint32)IMAGE_HEIGHT, 1u);
endCommandBuffer(vk, *cmdBuffer);
VK_CHECK(queueSubmit(ctx, PROTECTION_ENABLED, queue, *cmdBuffer, *fence, ~0ull));
}
// Calculate reference image
if (m_params.accessType == ACCESS_TYPE_IMAGE_ATOMICS)
calculateAtomicRef(*texture2D);
// Validate result
{
const vk::VkImage resultImage = m_params.accessType == ACCESS_TYPE_IMAGE_ATOMICS ? **imageSrc : **imageDst;
return validateResult(resultImage, vk::VK_IMAGE_LAYOUT_GENERAL, *texture2D, refSampler);
}
}
tcu::TestStatus ImageAccessTestInstance::executeFragmentTest (void)
{
ProtectedContext& ctx (m_protectedContext);
const vk::DeviceInterface& vk = ctx.getDeviceInterface();
const vk::VkDevice device = ctx.getDevice();
const vk::VkQueue queue = ctx.getQueue();
const deUint32 queueFamilyIndex = ctx.getQueueFamilyIndex();
// Create output image
de::MovePtr<vk::ImageWithMemory> colorImage (createImage2D(ctx, PROTECTION_ENABLED, queueFamilyIndex,
RENDER_WIDTH, RENDER_HEIGHT,
m_params.imageFormat,
vk::VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT|vk::VK_IMAGE_USAGE_SAMPLED_BIT));
vk::Unique<vk::VkImageView> colorImageView (createImageView(ctx, **colorImage, m_params.imageFormat));
vk::Unique<vk::VkRenderPass> renderPass (createRenderPass(ctx, m_params.imageFormat));
vk::Unique<vk::VkFramebuffer> framebuffer (createFramebuffer(ctx, RENDER_WIDTH, RENDER_HEIGHT, *renderPass, *colorImageView));
vk::Unique<vk::VkCommandPool> cmdPool (makeCommandPool(vk, device, PROTECTION_ENABLED, queueFamilyIndex));
vk::Unique<vk::VkCommandBuffer> cmdBuffer (vk::allocateCommandBuffer(vk, device, *cmdPool, vk::VK_COMMAND_BUFFER_LEVEL_PRIMARY));
de::MovePtr<tcu::Texture2D> texture2D = createTestTexture2D();
const tcu::Sampler refSampler = tcu::Sampler(tcu::Sampler::CLAMP_TO_EDGE, tcu::Sampler::CLAMP_TO_EDGE, tcu::Sampler::CLAMP_TO_EDGE,
tcu::Sampler::NEAREST, tcu::Sampler::NEAREST);
vk::Move<vk::VkShaderModule> vertexShader = createShaderModule(vk, device, ctx.getBinaryCollection().get("vert"), 0);
vk::Move<vk::VkShaderModule> fragmentShader = createShaderModule(vk, device, ctx.getBinaryCollection().get("frag"), 0);
de::MovePtr<vk::ImageWithMemory> imageSrc;
de::MovePtr<vk::ImageWithMemory> imageDst;
vk::Move<vk::VkSampler> sampler;
vk::Move<vk::VkImageView> imageViewSrc;
vk::Move<vk::VkImageView> imageViewDst;
vk::Move<vk::VkPipeline> graphicsPipeline;
vk::Move<vk::VkDescriptorSetLayout> descriptorSetLayout;
vk::Move<vk::VkDescriptorPool> descriptorPool;
vk::Move<vk::VkDescriptorSet> descriptorSet;
// Create src and dst images
{
vk::VkImageUsageFlags imageUsageFlags = vk::VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
vk::VK_IMAGE_USAGE_TRANSFER_DST_BIT |
vk::VK_IMAGE_USAGE_SAMPLED_BIT;
switch (m_params.accessType)
{
case ACCESS_TYPE_IMAGE_LOAD:
case ACCESS_TYPE_IMAGE_STORE:
case ACCESS_TYPE_IMAGE_ATOMICS:
imageUsageFlags |= vk::VK_IMAGE_USAGE_STORAGE_BIT;
break;
default:
break;
}
imageSrc = createImage2D(ctx, PROTECTION_ENABLED, queueFamilyIndex,
IMAGE_WIDTH, IMAGE_HEIGHT,
m_params.imageFormat,
imageUsageFlags);
if (m_params.accessType == ACCESS_TYPE_IMAGE_STORE)
{
imageDst = createImage2D(ctx, PROTECTION_ENABLED, queueFamilyIndex,
IMAGE_WIDTH, IMAGE_HEIGHT,
m_params.imageFormat,
imageUsageFlags);
}
}
// Select vkImageLayout based upon accessType
vk::VkImageLayout imageLayout = vk::VK_IMAGE_LAYOUT_UNDEFINED;
switch (m_params.accessType)
{
case ACCESS_TYPE_SAMPLING:
case ACCESS_TYPE_TEXEL_FETCH:
{
imageLayout = vk::VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
break;
}
case ACCESS_TYPE_IMAGE_LOAD:
case ACCESS_TYPE_IMAGE_STORE:
case ACCESS_TYPE_IMAGE_ATOMICS:
{
imageLayout = vk::VK_IMAGE_LAYOUT_GENERAL;
break;
}
default:
DE_FATAL("Impossible");
break;
}
// Upload source image
{
de::MovePtr<vk::ImageWithMemory> unprotectedImage = createImage2D(ctx, PROTECTION_DISABLED, queueFamilyIndex,
IMAGE_WIDTH, IMAGE_HEIGHT,
m_params.imageFormat,
vk::VK_IMAGE_USAGE_TRANSFER_SRC_BIT | vk::VK_IMAGE_USAGE_TRANSFER_DST_BIT);
// Upload data to an unprotected image
uploadImage(m_protectedContext, **unprotectedImage, *texture2D);
// Copy unprotected image to protected image
copyToProtectedImage(m_protectedContext, **unprotectedImage, **imageSrc, imageLayout, IMAGE_WIDTH, IMAGE_HEIGHT);
}
// Clear dst image
if (m_params.accessType == ACCESS_TYPE_IMAGE_STORE)
clearImage(m_protectedContext, **imageDst);
// Create descriptors
{
vk::DescriptorSetLayoutBuilder layoutBuilder;
vk::DescriptorPoolBuilder poolBuilder;
switch (m_params.accessType)
{
case ACCESS_TYPE_SAMPLING:
case ACCESS_TYPE_TEXEL_FETCH:
layoutBuilder.addSingleSamplerBinding(vk::VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, vk::VK_SHADER_STAGE_FRAGMENT_BIT, DE_NULL);
poolBuilder.addType(vk::VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1u);
break;
case ACCESS_TYPE_IMAGE_LOAD:
layoutBuilder.addSingleBinding(vk::VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, vk::VK_SHADER_STAGE_FRAGMENT_BIT);
poolBuilder.addType(vk::VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1u);
break;
case ACCESS_TYPE_IMAGE_STORE:
layoutBuilder.addSingleBinding(vk::VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, vk::VK_SHADER_STAGE_FRAGMENT_BIT);
layoutBuilder.addSingleBinding(vk::VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, vk::VK_SHADER_STAGE_FRAGMENT_BIT);
poolBuilder.addType(vk::VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 2u);
break;
case ACCESS_TYPE_IMAGE_ATOMICS:
layoutBuilder.addSingleBinding(vk::VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, vk::VK_SHADER_STAGE_FRAGMENT_BIT);
poolBuilder.addType(vk::VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1u);
break;
default:
DE_FATAL("Impossible");
break;
}
descriptorSetLayout = layoutBuilder.build(vk, device);
descriptorPool = poolBuilder.build(vk, device, vk::VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u);
descriptorSet = makeDescriptorSet(vk, device, *descriptorPool, *descriptorSetLayout);
}
// Create pipeline layout
vk::Unique<vk::VkPipelineLayout> pipelineLayout (makePipelineLayout(vk, device, *descriptorSetLayout));
// Create sampler and image views
{
if (m_params.accessType == ACCESS_TYPE_SAMPLING || m_params.accessType == ACCESS_TYPE_TEXEL_FETCH)
{
const tcu::TextureFormat texFormat = mapVkFormat(m_params.imageFormat);
const vk::VkSamplerCreateInfo samplerParams = vk::mapSampler(refSampler, texFormat);
sampler = createSampler(vk, device, &samplerParams);
}
imageViewSrc = createImageView(ctx, **imageSrc, m_params.imageFormat);
if (m_params.accessType == ACCESS_TYPE_IMAGE_STORE)
imageViewDst = createImageView(ctx, **imageDst, m_params.imageFormat);
}
// Update descriptor set information
{
vk::DescriptorSetUpdateBuilder updateBuilder;
switch (m_params.accessType)
{
case ACCESS_TYPE_SAMPLING:
case ACCESS_TYPE_TEXEL_FETCH:
{
vk::VkDescriptorImageInfo descSampledImg = makeDescriptorImageInfo(*sampler, *imageViewSrc, vk::VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
updateBuilder.writeSingle(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(0u), vk::VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, &descSampledImg);
break;
}
case ACCESS_TYPE_IMAGE_LOAD:
{
vk::VkDescriptorImageInfo descStorageImg = makeDescriptorImageInfo((vk::VkSampler)0, *imageViewSrc, vk::VK_IMAGE_LAYOUT_GENERAL);
updateBuilder.writeSingle(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(0u), vk::VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, &descStorageImg);
break;
}
case ACCESS_TYPE_IMAGE_STORE:
{
vk::VkDescriptorImageInfo descStorageImgSrc = makeDescriptorImageInfo((vk::VkSampler)0, *imageViewSrc, vk::VK_IMAGE_LAYOUT_GENERAL);
vk::VkDescriptorImageInfo descStorageImgDst = makeDescriptorImageInfo((vk::VkSampler)0, *imageViewDst, vk::VK_IMAGE_LAYOUT_GENERAL);
updateBuilder.writeSingle(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(0u), vk::VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, &descStorageImgSrc);
updateBuilder.writeSingle(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(1u), vk::VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, &descStorageImgDst);
break;
}
case ACCESS_TYPE_IMAGE_ATOMICS:
{
vk::VkDescriptorImageInfo descStorageImg = makeDescriptorImageInfo((vk::VkSampler)0, *imageViewSrc, vk::VK_IMAGE_LAYOUT_GENERAL);
updateBuilder.writeSingle(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(0u), vk::VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, &descStorageImg);
break;
}
default:
DE_FATAL("Impossible");
break;
}
updateBuilder.update(vk, device);
}
// Create vertex buffer and vertex input descriptors
VertexBindings vertexBindings;
VertexAttribs vertexAttribs;
de::MovePtr<vk::BufferWithMemory> vertexBuffer;
{
const float positions[] =
{
-1.0f, -1.0f,
-1.0f, +1.0f,
+1.0f, -1.0f,
+1.0f, +1.0f,
};
std::vector<float> texCoord;
{
const tcu::Vec2 minCoords (0.0f, 0.0f);
const tcu::Vec2 maxCoords = m_params.accessType == ACCESS_TYPE_SAMPLING ?
tcu::Vec2(1.0f, 1.0f) :
tcu::Vec2((float)IMAGE_WIDTH - 0.1f, (float)IMAGE_HEIGHT - 0.1f);
glu::TextureTestUtil::computeQuadTexCoord2D(texCoord, minCoords, maxCoords);
}
const deUint32 vertexPositionStrideSize = (deUint32)sizeof(tcu::Vec2);
const deUint32 vertexTextureStrideSize = (deUint32)sizeof(tcu::Vec2);
const deUint32 positionDataSize = 4 * vertexPositionStrideSize;
const deUint32 textureCoordDataSize = 4 * vertexTextureStrideSize;
const deUint32 vertexBufferSize = positionDataSize + textureCoordDataSize;
{
const vk::VkVertexInputBindingDescription vertexInputBindingDescriptions[2] =
{
{
0u, // deUint32 binding;
vertexPositionStrideSize, // deUint32 strideInBytes;
vk::VK_VERTEX_INPUT_RATE_VERTEX // VkVertexInputStepRate inputRate;
},
{
1u, // deUint32 binding;
vertexTextureStrideSize, // deUint32 strideInBytes;
vk::VK_VERTEX_INPUT_RATE_VERTEX // VkVertexInputStepRate inputRate;
}
};
vertexBindings.push_back(vertexInputBindingDescriptions[0]);
vertexBindings.push_back(vertexInputBindingDescriptions[1]);
const vk::VkVertexInputAttributeDescription vertexInputAttributeDescriptions[2] =
{
{
0u, // deUint32 location;
0u, // deUint32 binding;
vk::VK_FORMAT_R32G32_SFLOAT, // VkFormat format;
0u // deUint32 offsetInBytes;
},
{
1u, // deUint32 location;
1u, // deUint32 binding;
vk::VK_FORMAT_R32G32_SFLOAT, // VkFormat format;
positionDataSize // deUint32 offsetInBytes;
}
};
vertexAttribs.push_back(vertexInputAttributeDescriptions[0]);
vertexAttribs.push_back(vertexInputAttributeDescriptions[1]);
}
vertexBuffer = makeBuffer(ctx,
PROTECTION_DISABLED,
queueFamilyIndex,
vertexBufferSize,
vk::VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
vk::MemoryRequirement::HostVisible);
deMemcpy(vertexBuffer->getAllocation().getHostPtr(), positions, positionDataSize);
deMemcpy(reinterpret_cast<deUint8*>(vertexBuffer->getAllocation().getHostPtr()) + positionDataSize, texCoord.data(), textureCoordDataSize);
vk::flushMappedMemoryRange(vk, device, vertexBuffer->getAllocation().getMemory(), vertexBuffer->getAllocation().getOffset(), vertexBufferSize);
}
// Create pipeline
graphicsPipeline = makeGraphicsPipeline(vk,
device,
*pipelineLayout,
*renderPass,
*vertexShader,
*fragmentShader,
vertexBindings,
vertexAttribs,
tcu::UVec2(RENDER_WIDTH, RENDER_HEIGHT),
vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP);
// Begin cmd buffer
beginCommandBuffer(vk, *cmdBuffer);
// Start image barrier
{
const vk::VkImageMemoryBarrier startImgBarrier =
{
vk::VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // sType
DE_NULL, // pNext
0, // srcAccessMask
vk::VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, // dstAccessMask
vk::VK_IMAGE_LAYOUT_UNDEFINED, // oldLayout
vk::VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // newLayout
queueFamilyIndex, // srcQueueFamilyIndex
queueFamilyIndex, // dstQueueFamilyIndex
**colorImage, // image
{
vk::VK_IMAGE_ASPECT_COLOR_BIT, // aspectMask
0u, // baseMipLevel
1u, // mipLevels
0u, // baseArraySlice
1u, // subresourceRange
}
};
vk.cmdPipelineBarrier(*cmdBuffer,
vk::VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, // srcStageMask
vk::VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, // dstStageMask
(vk::VkDependencyFlags)0,
0, (const vk::VkMemoryBarrier*)DE_NULL,
0, (const vk::VkBufferMemoryBarrier*)DE_NULL,
1, &startImgBarrier);
}
beginRenderPass(vk, *cmdBuffer, *renderPass, *framebuffer, vk::makeRect2D(0, 0, RENDER_WIDTH, RENDER_HEIGHT), tcu::Vec4(0.0f));
vk.cmdBindPipeline(*cmdBuffer, vk::VK_PIPELINE_BIND_POINT_GRAPHICS, *graphicsPipeline);
vk.cmdBindDescriptorSets(*cmdBuffer, vk::VK_PIPELINE_BIND_POINT_GRAPHICS, *pipelineLayout, 0u, 1u, &*descriptorSet, 0u, DE_NULL);
{
const vk::VkDeviceSize vertexBufferOffset = 0;
vk.cmdBindVertexBuffers(*cmdBuffer, 0u, 1u, &vertexBuffer->get(), &vertexBufferOffset);
vk.cmdBindVertexBuffers(*cmdBuffer, 1u, 1u, &vertexBuffer->get(), &vertexBufferOffset);
}
vk.cmdDraw(*cmdBuffer, /*vertexCount*/ 4u, 1u, 0u, 1u);
endRenderPass(vk, *cmdBuffer);
{
const vk::VkImageMemoryBarrier endImgBarrier =
{
vk::VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // sType
DE_NULL, // pNext
vk::VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, // srcAccessMask
vk::VK_ACCESS_SHADER_READ_BIT, // dstAccessMask
vk::VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // oldLayout
imageLayout, // newLayout
queueFamilyIndex, // srcQueueFamilyIndex
queueFamilyIndex, // dstQueueFamilyIndex
**colorImage, // image
{
vk::VK_IMAGE_ASPECT_COLOR_BIT, // aspectMask
0u, // baseMipLevel
1u, // mipLevels
0u, // baseArraySlice
1u, // subresourceRange
}
};
vk.cmdPipelineBarrier(*cmdBuffer,
vk::VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, // srcStageMask
vk::VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, // dstStageMask
(vk::VkDependencyFlags)0,
0, (const vk::VkMemoryBarrier*)DE_NULL,
0, (const vk::VkBufferMemoryBarrier*)DE_NULL,
1, &endImgBarrier);
}
endCommandBuffer(vk, *cmdBuffer);
// Submit command buffer
{
const vk::Unique<vk::VkFence> fence (vk::createFence(vk, device));
VK_CHECK(queueSubmit(ctx, PROTECTION_ENABLED, queue, *cmdBuffer, *fence, ~0ull));
}
// Calculate reference image
if (m_params.accessType == ACCESS_TYPE_IMAGE_ATOMICS)
calculateAtomicRef(*texture2D);
// Validate result
{
const vk::VkImage resultImage = m_params.accessType == ACCESS_TYPE_IMAGE_ATOMICS ? **imageSrc :
m_params.accessType == ACCESS_TYPE_IMAGE_STORE ? **imageDst : **colorImage;
return validateResult(resultImage, imageLayout, *texture2D, refSampler);
}
}
void ImageAccessTestInstance::calculateAtomicRef (tcu::Texture2D& texture2D)
{
DE_ASSERT(m_params.accessType == ACCESS_TYPE_IMAGE_ATOMICS);
const tcu::PixelBufferAccess& reference = texture2D.getLevel(0);
for (int x = 0; x < reference.getWidth(); ++x)
for (int y = 0; y < reference.getHeight(); ++y)
{
const deInt32 oldX = reference.getPixelInt(x, y).x();
const deInt32 atomicArg = x*x + y*y;
const deInt32 newX = computeBinaryAtomicOperationResult(m_params.atomicOperation, oldX, atomicArg);
reference.setPixel(tcu::IVec4(newX, 0, 0, 0), x, y);
}
}
tcu::TestStatus ImageAccessTestInstance::validateResult (vk::VkImage image, vk::VkImageLayout imageLayout, const tcu::Texture2D& texture2D, const tcu::Sampler& refSampler)
{
de::Random rnd (getSeedValue(m_params));
ValidationData refData;
for (int ndx = 0; ndx < 4; ++ndx)
{
const float lod = 0.0f;
const float cx = rnd.getFloat(0.0f, 1.0f);
const float cy = rnd.getFloat(0.0f, 1.0f);
refData.coords[ndx] = tcu::Vec4(cx, cy, 0.0f, 0.0f);
refData.values[ndx] = texture2D.sample(refSampler, cx, cy, lod);
}
if (!m_validator.validateImage(m_protectedContext, refData, image, m_params.imageFormat, imageLayout))
return tcu::TestStatus::fail("Something went really wrong");
else
return tcu::TestStatus::pass("Everything went OK");
}
} // anonymous
tcu::TestCaseGroup* createShaderImageAccessTests (tcu::TestContext& testCtx)
{
de::MovePtr<tcu::TestCaseGroup> accessGroup (new tcu::TestCaseGroup(testCtx, "access", "Shader Image Access Tests"));
static const struct
{
glu::ShaderType type;
const char* name;
const char* desc;
} shaderTypes[] =
{
{ glu::SHADERTYPE_FRAGMENT, "fragment", "Image access from fragment shader" },
{ glu::SHADERTYPE_COMPUTE, "compute", "Image access from compute shader" },
};
static const struct
{
AccessType type;
const char* name;
const char* desc;
} accessTypes[] =
{
{ ACCESS_TYPE_SAMPLING, "sampling", "Sampling test" },
{ ACCESS_TYPE_TEXEL_FETCH, "texelfetch", "Texel fetch test" },
{ ACCESS_TYPE_IMAGE_LOAD, "imageload", "Image load test" },
{ ACCESS_TYPE_IMAGE_STORE, "imagestore", "Image store test" },
{ ACCESS_TYPE_IMAGE_ATOMICS, "imageatomics", "Image atomics test" },
};
static const struct
{
vk::VkFormat format;
const char* name;
} formats[] =
{
{ vk::VK_FORMAT_R8G8B8A8_UNORM, "rgba8" },
{ vk::VK_FORMAT_R32_SINT, "r32i" },
{ vk::VK_FORMAT_R32_UINT, "r32ui" },
};
for (int shaderTypeNdx = 0; shaderTypeNdx < DE_LENGTH_OF_ARRAY(shaderTypes); ++shaderTypeNdx)
{
const glu::ShaderType shaderType = shaderTypes[shaderTypeNdx].type;
de::MovePtr<tcu::TestCaseGroup> shaderGroup (new tcu::TestCaseGroup(testCtx, shaderTypes[shaderTypeNdx].name, shaderTypes[shaderTypeNdx].desc));
for (int accessNdx = 0; accessNdx < DE_LENGTH_OF_ARRAY(accessTypes); ++accessNdx)
{
const AccessType accessType = accessTypes[accessNdx].type;
if (shaderType == glu::SHADERTYPE_COMPUTE && accessType == ACCESS_TYPE_IMAGE_STORE) // \note already tested in other tests
continue;
de::MovePtr<tcu::TestCaseGroup> accessTypeGroup (new tcu::TestCaseGroup(testCtx, accessTypes[accessNdx].name, accessTypes[accessNdx].desc));
if (accessType == ACCESS_TYPE_IMAGE_ATOMICS)
{
for (deUint32 atomicOpI = 0; atomicOpI < ATOMIC_OPERATION_LAST; ++atomicOpI)
{
const AtomicOperation atomicOp = (AtomicOperation)atomicOpI;
de::MovePtr<tcu::TestCaseGroup> operationGroup (new tcu::TestCaseGroup(testCtx, getAtomicOperationCaseName(atomicOp).c_str(), ""));
for (deUint32 formatNdx = 0; formatNdx < DE_LENGTH_OF_ARRAY(formats); formatNdx++)
{
const vk::VkFormat format = formats[formatNdx].format;
if (format != vk::VK_FORMAT_R32_UINT && format != vk::VK_FORMAT_R32_SINT)
continue;
operationGroup->addChild(new ImageAccessTestCase(testCtx, formats[formatNdx].name, "", Params(shaderType, accessType, format, atomicOp)));
}
accessTypeGroup->addChild(operationGroup.release());
}
}
else
{
for (deUint32 formatNdx = 0; formatNdx < DE_LENGTH_OF_ARRAY(formats); formatNdx++)
{
const vk::VkFormat format = formats[formatNdx].format;
accessTypeGroup->addChild(new ImageAccessTestCase(testCtx, formats[formatNdx].name, "", Params(shaderType, accessType, format)));
}
}
shaderGroup->addChild(accessTypeGroup.release());
}
accessGroup->addChild(shaderGroup.release());
}
return accessGroup.release();
}
} // ProtectedMem
} // vkt