blob: d7120685e4e497d67fb3d2dd58aebc00b9bd24f8 [file] [log] [blame]
/*------------------------------------------------------------------------
* Vulkan Conformance Tests
* ------------------------
*
* Copyright (c) 2019 The Khronos Group Inc.
* Copyright (c) 2018 The Khronos Group Inc.
* Copyright (c) 2018 Google 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 stack tests
*//*--------------------------------------------------------------------*/
#include "vktProtectedMemStackTests.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
{
struct Params
{
deUint32 stackSize;
deUint32 imageWidth;
deUint32 imageHeight;
Params (deUint32 stackSize_)
: stackSize (stackSize_)
{
// Find suitable image dimensions based on stack memory size
imageWidth = 1;
imageHeight = 1;
bool increaseWidth = true;
while (imageWidth * imageHeight < stackSize)
{
if (increaseWidth)
imageWidth *= 2;
else
imageHeight *= 2;
increaseWidth = !increaseWidth;
}
}
};
deUint32 getSeedValue (const Params& params)
{
return deInt32Hash(params.stackSize);
}
class StackTestInstance : public ProtectedTestInstance
{
public:
StackTestInstance (Context& ctx,
const ImageValidator& validator,
const Params& params);
virtual tcu::TestStatus iterate (void);
private:
de::MovePtr<tcu::Texture2D> createTestTexture2D (void);
bool validateResult (vk::VkImage image,
vk::VkImageLayout imageLayout,
const tcu::Texture2D& texture2D,
const tcu::Sampler& refSampler);
void calculateRef (tcu::Texture2D& texture2D);
const ImageValidator& m_validator;
const Params& m_params;
};
class StackTestCase : public TestCase
{
public:
StackTestCase (tcu::TestContext& testCtx,
const std::string& name,
const std::string& description,
const Params& params)
: TestCase (testCtx, name, description)
, m_validator (vk::VK_FORMAT_R8G8B8A8_UNORM)
, m_params (params)
{
}
virtual ~StackTestCase (void) {}
virtual TestInstance* createInstance (Context& ctx) const
{
return new StackTestInstance(ctx, m_validator, m_params);
}
virtual void initPrograms (vk::SourceCollections& programCollection) const;
private:
ImageValidator m_validator;
Params m_params;
};
void StackTestCase::initPrograms (vk::SourceCollections& programCollection) const
{
m_validator.initPrograms(programCollection);
// Test validates handling of protected memory allocated on stack.
// The test copies protected memory content into temporary variable allocated inside function p.
// Thus test forces protected content to appear on stack.
// Function p() returns specified protected memory element from the variable allocated on stack.
// Function u() returns specified protected memory element from the global variable.
// Values returned by p() and u() should be same.
// Test is repeated 2 times () in shader to avoid coincidental matches.
// In case of any mismatches it is signalized to inherited verifier function by setting 0 in result store image.
// Each invocation validates particular element (bytes) on stack.
// Number of invocations matches stack size specified in test parameters.
std::string comp =
std::string() +
"#version 450\n"
"layout(local_size_x = " + de::toString(m_params.imageWidth) + ", local_size_y = " + de::toString(m_params.imageHeight) + ", local_size_z = 1) in;\n"
"layout(set = 0, binding = 0, rgba8) writeonly uniform highp image2D u_resultImage;\n"
"layout(set = 0, binding = 1, rgba8) readonly uniform highp image2D u_srcImage;\n"
"vec4 protectedData[" + de::toString(m_params.stackSize) + "];\n"
"\n"
"vec4 p(int idx)\n"
"{\n"
" vec4 localData[" + de::toString(m_params.stackSize) + "];\n"
" for (int i = 0; i < " + de::toString(m_params.stackSize) + "; i++)\n"
" localData[i] = protectedData[i];\n"
" return localData[idx];\n"
"}\n"
"\n"
"vec4 u(int idx)\n"
"{\n"
" return protectedData[idx];\n"
"}\n"
"\n"
"void main() {\n"
" const int n = " + de::toString(m_params.stackSize) + ";\n"
" int m = 0;\n"
" int w = " + de::toString(m_params.imageWidth) + ";\n"
" int gx = int(gl_GlobalInvocationID.x);\n"
" int gy = int(gl_GlobalInvocationID.y);\n"
" int checked_ndx = gy * w + gx;\n"
" vec4 outColor;\n"
"\n"
" for (int j = 0; j < 2; j++)\n"
" {\n"
" for (int i = 0; i < n; i++)\n"
" {\n"
" const int idx = (i + j) % n;\n"
" protectedData[i] = imageLoad(u_srcImage, ivec2(idx % w, idx / w));\n"
" }\n"
"\n"
" vec4 vp = p(checked_ndx);\n"
" vec4 vu = u(checked_ndx);\n"
" if (any(notEqual(vp,vu)))\n"
" m++;\n"
" }\n"
"\n"
" if (m <= 0)\n"
" outColor = vec4(0.0f);\n"
" else\n"
" outColor = vec4(1.0f);\n"
" imageStore(u_resultImage, ivec2(gx, gy), outColor);\n"
"}\n";
programCollection.glslSources.add("comp") << glu::ComputeSource(comp);
}
StackTestInstance::StackTestInstance (Context& ctx,
const ImageValidator& validator,
const Params& params)
: ProtectedTestInstance (ctx)
, m_validator (validator)
, m_params (params)
{
}
de::MovePtr<tcu::Texture2D> StackTestInstance::createTestTexture2D (void)
{
const tcu::TextureFormat texFmt = mapVkFormat(vk::VK_FORMAT_R8G8B8A8_UNORM);
de::MovePtr<tcu::Texture2D> texture2D (new tcu::Texture2D(texFmt, m_params.imageWidth, m_params.imageHeight));
texture2D->allocLevel(0);
const tcu::PixelBufferAccess& level = texture2D->getLevel(0);
fillWithUniqueColors(level, getSeedValue(m_params));
return texture2D;
}
tcu::TestStatus StackTestInstance::iterate (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();
const vk::VkPhysicalDeviceProperties properties = vk::getPhysicalDeviceProperties(ctx.getInstanceDriver(), ctx.getPhysicalDevice());
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;
// Check the number of invocations supported
if (properties.limits.maxComputeWorkGroupInvocations < m_params.imageWidth * m_params.imageHeight)
throw tcu::NotSupportedError("Not enough compute workgroup invocations supported.");
// 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,
m_params.imageWidth, m_params.imageHeight,
vk::VK_FORMAT_R8G8B8A8_UNORM,
imageUsageFlags);
imageDst = createImage2D(ctx, PROTECTION_ENABLED, queueFamilyIndex,
m_params.imageWidth, m_params.imageHeight,
vk::VK_FORMAT_R8G8B8A8_UNORM,
imageUsageFlags);
}
// Upload source image
{
de::MovePtr<vk::ImageWithMemory> unprotectedImage = createImage2D(ctx, PROTECTION_DISABLED, queueFamilyIndex,
m_params.imageWidth, m_params.imageHeight,
vk::VK_FORMAT_R8G8B8A8_UNORM,
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, vk::VK_IMAGE_LAYOUT_GENERAL, m_params.imageWidth, m_params.imageHeight);
}
// Clear dst image
clearImage(m_protectedContext, **imageDst);
// Create descriptors
{
vk::DescriptorSetLayoutBuilder layoutBuilder;
vk::DescriptorPoolBuilder poolBuilder;
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);
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 image views
{
imageViewSrc = createImageView(ctx, **imageSrc, vk::VK_FORMAT_R8G8B8A8_UNORM);
imageViewDst = createImageView(ctx, **imageDst, vk::VK_FORMAT_R8G8B8A8_UNORM);
}
// Update descriptor set information
{
vk::DescriptorSetUpdateBuilder updateBuilder;
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);
updateBuilder.update(vk, device);
}
// Calculate reference image
calculateRef(*texture2D);
bool result = true;
// Create compute commands & submit
// Command buffer load is repeated 8 times () to avoid coincidental matches.
for (int i = 0; (i < 8) && (result == true); i++)
{
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, 1u, 1u, 1u);
endCommandBuffer(vk, *cmdBuffer);
VK_CHECK(queueSubmit(ctx, PROTECTION_ENABLED, queue, *cmdBuffer, *fence, ~0ull));
VK_CHECK(vk.waitForFences(device, 1u, &*fence, VK_TRUE, ~0ull));
result = validateResult(**imageDst, vk::VK_IMAGE_LAYOUT_GENERAL, *texture2D, refSampler);
}
if (result == true)
return tcu::TestStatus::pass("Pass");
else
return tcu::TestStatus::fail("Result validation failed");
}
void StackTestInstance::calculateRef (tcu::Texture2D& texture2D)
{
const tcu::PixelBufferAccess& reference = texture2D.getLevel(0);
const tcu::IVec4 zero;
for (int x = 0; x < reference.getWidth(); ++x)
for (int y = 0; y < reference.getHeight(); ++y)
reference.setPixel(zero, x, y);
}
bool StackTestInstance::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, vk::VK_FORMAT_R8G8B8A8_UNORM, imageLayout))
return false;
else
return true;
}
} // anonymous
tcu::TestCaseGroup* createStackTests (tcu::TestContext& testCtx)
{
de::MovePtr<tcu::TestCaseGroup> stackGroup (new tcu::TestCaseGroup(testCtx, "stack", "Protected memory stack tests"));
static const deUint32 stackMemSizes[] = { 32, 64, 128, 256, 512, 1024 };
for (int stackMemSizeIdx = 0; stackMemSizeIdx < DE_LENGTH_OF_ARRAY(stackMemSizes); ++stackMemSizeIdx)
{
std::string testName = std::string("stacksize_") + de::toString(stackMemSizes[stackMemSizeIdx]);
stackGroup->addChild(new StackTestCase(testCtx, testName, "", Params(stackMemSizes[stackMemSizeIdx])));
}
return stackGroup.release();
}
} // ProtectedMem
} // vkt