--- /dev/null
+/*------------------------------------------------------------------------
+ * 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);
+ tcu::TestStatus 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 several times (16) 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 < 16; 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);
+ }
+
+ // Create 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, 1u, 1u, 1u);
+ endCommandBuffer(vk, *cmdBuffer);
+
+ VK_CHECK(queueSubmit(ctx, PROTECTION_ENABLED, queue, *cmdBuffer, *fence, ~0ull));
+ }
+
+ // Calculate reference image
+ calculateRef(*texture2D);
+
+ // Validate result
+ return validateResult(**imageDst, vk::VK_IMAGE_LAYOUT_GENERAL, *texture2D, refSampler);
+}
+
+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);
+}
+
+tcu::TestStatus 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 tcu::TestStatus::fail("Result validation failed");
+ else
+ return tcu::TestStatus::pass("Pass");
+}
+
+} // 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