--- /dev/null
+/*------------------------------------------------------------------------
+ * Vulkan Conformance Tests
+ * ------------------------
+ *
+ * Copyright (c) 2022 The Khronos Group Inc.
+ * Copyright (c) 2022 Google 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.
+ *
+ *//*!
+ * \brief Robustness1 vertex access out of range tests
+ *//*--------------------------------------------------------------------*/
+
+#include "vktRobustness1VertexAccessTests.hpp"
+#include "deDefs.h"
+#include "deMemory.h"
+#include "gluShaderProgram.hpp"
+#include "gluShaderUtil.hpp"
+#include "image/vktImageLoadStoreUtil.hpp"
+#include "pipeline/vktPipelineSpecConstantUtil.hpp"
+#include "qpTestLog.h"
+#include "tcuTestCase.hpp"
+#include "tcuTestContext.hpp"
+#include "tcuTexture.hpp"
+#include "tcuTextureUtil.hpp"
+#include "tcuVectorType.hpp"
+#include "tcuVectorUtil.hpp"
+#include "vkBarrierUtil.hpp"
+#include "vkCmdUtil.hpp"
+#include "vkDefs.hpp"
+#include "vkObjUtil.hpp"
+#include "vkShaderProgram.hpp"
+#include "vktRobustnessUtil.hpp"
+#include "vktTestCase.hpp"
+#include "vktTestCaseUtil.hpp"
+#include "vkBuilderUtil.hpp"
+#include "vkImageUtil.hpp"
+#include "vkMemUtil.hpp"
+#include "vkPrograms.hpp"
+#include "vkQueryUtil.hpp"
+#include "vkRef.hpp"
+#include "vkRefUtil.hpp"
+#include "vkTypeUtil.hpp"
+#include "tcuTestLog.hpp"
+#include "deMath.h"
+#include "deUniquePtr.hpp"
+#include "vkRefUtil.hpp"
+#include <algorithm>
+#include <cstdint>
+#include <cstdio>
+#include <iterator>
+#include <sstream>
+#include <tuple>
+#include <vector>
+#include <array>
+#include <functional>
+#include <cmath>
+#include <limits>
+#include "pipeline/vktPipelineImageUtil.hpp"
+
+namespace vkt
+{
+namespace robustness
+{
+namespace
+{
+
+using namespace vk;
+using namespace de;
+using namespace tcu;
+
+using std::function;
+using std::ostringstream;
+using std::string;
+using std::vector;
+using std::numeric_limits;
+using std::setprecision;
+using std::fixed;
+
+typedef function<deUint32(Vec4, Vec4)> AllocateVertexFn;
+typedef function<void(deUint32)> WriteIndexFn;
+struct ValidityInfo
+{
+ bool color0;
+ bool color1;
+};
+
+deUint32 GetVerticesCountForTriangles(deUint32 tilesX, deUint32 tilesY);
+void GenerateTriangles(deUint32 tilesX, deUint32 tilesY, vector<Vec4> colors, const vector<deUint32>& invalidIndices, AllocateVertexFn allocateVertex, WriteIndexFn writeIndex = [](deUint32) { });
+
+typedef vector<VkVertexInputBindingDescription> VertexBindings;
+typedef vector<VkVertexInputAttributeDescription> VertexAttributes;
+struct AttributeData
+{
+ const void* data;
+ deUint32 size;
+};
+
+struct InputInfo
+{
+ VertexBindings vertexBindings;
+ VertexAttributes vertexAttributes;
+ vector<AttributeData> data;
+ deUint32 vertexCount;
+ vector<deUint32> indices;
+};
+typedef function<TestStatus(const ConstPixelBufferAccess &)> ValidationFn;
+
+
+static const auto expectedColor = Vec4(0.25f, 0.0f, 0.75f, 1.0f); // Expected color input
+static const auto unusedColor = Vec4(0.75f, 0.0f, 0.25f, 1.0f); // Unused color attributes
+static const auto outOfRangeColor = Vec4(0.2f , 0.2f, 0.2f , 1.0f); // Padding, out of range accesses - never accepted as output
+static const auto validColors = vector<Vec4> // Colors accepted as valid in verification shader
+{
+ expectedColor, unusedColor
+};
+static const auto invalidColors = vector<Vec4> // Colors accepted as oob access in verification shader
+{
+ expectedColor, unusedColor, Vec4(0.0f), Vec4(0.0f, 0.0f, 0.0f, 1.0f)
+};
+
+static TestStatus robustness1TestFn(TestContext& testCtx, Context& context, const VkDevice device, const vector<InputInfo>& inputs, const IVec2& renderSize);
+
+template<typename T>
+class PaddedAlloc
+{
+ deUint32 m_count, m_paddingCount;
+ vector<T> m_data;
+public:
+ PaddedAlloc (deUint32 count, deUint32 paddingCount, const T& paddingValue);
+ PaddedAlloc (const PaddedAlloc<T>&) = delete;
+
+ deUint32 paddedSize() const { return static_cast<deUint32>(m_data.size()); }
+ deUint32 paddedStart() const { return m_paddingCount; }
+ const T* paddedData() const { return m_data.data(); }
+
+ deUint32 size() const { return m_count; }
+ const T* data() const { return m_data.data() + m_paddingCount; }
+
+ T& operator[](const deUint32 index) { return m_data[m_paddingCount + index]; }
+ const T& operator[](const deUint32 index) const { return m_data[m_paddingCount + index]; }
+
+ PaddedAlloc& operator= (PaddedAlloc&) = delete;
+};
+
+template <typename T>
+PaddedAlloc<T>::PaddedAlloc (deUint32 count, deUint32 paddingCount, const T& paddingValue)
+ : m_count (count)
+ , m_paddingCount (paddingCount)
+{
+ DE_ASSERT((count + 2 * paddingCount) * sizeof(T) <= numeric_limits<deUint32>::max());
+ m_data.resize(count + 2 * paddingCount);
+ const auto end = m_data.size() - 1;
+ for(deUint32 i = 0; i < paddingCount; ++i)
+ {
+ m_data[i] = paddingValue;
+ m_data[end - i] = paddingValue;
+ }
+}
+
+typedef function<TestStatus (TestContext&, Context&, const VkDevice)> TestFn;
+struct Robustness1TestInfo
+{
+ string name;
+ string description;
+ TestFn testFn;
+};
+static const auto renderTargetSize = IVec2(12, 12);
+static const auto robustness1Tests = vector<Robustness1TestInfo>
+{
+ /* Layout of generated vertices vs location invalid vertices always at middle,
+ (3x3 tiles = 4x4 vertices):
+ 0 1 2 3 -> 0 1 2 3
+ 4 * 5 * 6 7 -> 4 7 8 11
+ 8 * 9 *10 11 -> 12 13 14 15
+ 12 13 14 15 -> * 5 * 6 * 9 *10
+ */
+ {
+ "out_of_bounds_stride_0", // string name
+ "Last elements 4 out of bounds, color with stride 0", // string description
+ [](TestContext& testContext, Context& context, const VkDevice device) // TestFn testFn
+ {
+ struct Color
+ {
+ Vec4 unused;
+ Vec4 color;
+ };
+ const deUint32 totalCount = GetVerticesCountForTriangles(3, 3);
+ PaddedAlloc<Vec4> positions (totalCount, 8, outOfRangeColor);
+ PaddedAlloc<Color> colors (totalCount, 8, { outOfRangeColor, outOfRangeColor });
+ PaddedAlloc<Vec4> color0 (1, 4, outOfRangeColor);
+ color0[0] = expectedColor;
+ vector<deUint32> indices;
+ deUint32 writeIndex = 0;
+ GenerateTriangles(
+ 3u,
+ 3u,
+ { unusedColor },
+ { 5, 6, 9, 10 },
+ [&positions, &colors, &writeIndex](Vec4 position, Vec4 color)
+ {
+ positions[writeIndex] = position;
+ colors[writeIndex] = { color, color };
+ return writeIndex++;
+ },
+ [&indices](deUint32 index) { indices.push_back(index); });
+ auto bindings =
+ {
+ makeVertexInputBindingDescription(0u, sizeof(positions[0]), VK_VERTEX_INPUT_RATE_VERTEX),
+ makeVertexInputBindingDescription(1u, 0u, VK_VERTEX_INPUT_RATE_VERTEX),
+ makeVertexInputBindingDescription(2u, sizeof(colors[0]), VK_VERTEX_INPUT_RATE_VERTEX)
+ };
+ auto attributes =
+ {
+ makeVertexInputAttributeDescription(0u, 0u, VK_FORMAT_R32G32B32A32_SFLOAT, 0u),
+ makeVertexInputAttributeDescription(1u, 1u, VK_FORMAT_R32G32B32A32_SFLOAT, 0u),
+ makeVertexInputAttributeDescription(2u, 2u, VK_FORMAT_R32G32B32A32_SFLOAT, offsetof(Color, color))
+ };
+ return robustness1TestFn(
+ testContext, context, device,
+ {
+ {
+ bindings,
+ attributes,
+ {
+ { positions.data() , positions.size() * static_cast<deUint32>(sizeof(positions[0])) },
+ { color0.data() , color0.size() * static_cast<deUint32>(sizeof(color0[0])) },
+ { colors.data() , (colors.size() - 3) * static_cast<deUint32>(sizeof(colors[0])) - static_cast<deUint32>(sizeof(Color::color) / 2) }
+ },
+ static_cast<deUint32>(positions.size()),
+ indices
+ }
+ },
+ renderTargetSize);
+ }
+ },
+ {
+ "out_of_bounds_stride_16_single_buffer", // string name
+ "Last 4 elements out of bounds, color with stride 16", // string description
+ [](TestContext& testContext, Context& context, const VkDevice device) // TestFn testFn
+ {
+ struct Vertex
+ {
+ Vec4 position;
+ Vec4 unused1;
+ Vec4 color1;
+ Vec4 color2;
+ };
+ const deUint32 totalCount = GetVerticesCountForTriangles(3, 3);
+ PaddedAlloc<Vertex> vertices (totalCount, 8, { outOfRangeColor, outOfRangeColor, outOfRangeColor, outOfRangeColor });
+ deUint32 writeIndex = 0;
+ vector<deUint32> indices;
+ GenerateTriangles(
+ 3u,
+ 3u,
+ { expectedColor },
+ { 5, 6, 9, 10 },
+ [&vertices, &writeIndex](Vec4 position, Vec4 color)
+ {
+ vertices[writeIndex] = { position, unusedColor, color, color };
+ return writeIndex++;
+ },
+ [&indices](deUint32 index) { indices.push_back(index); });
+ auto bindings =
+ {
+ makeVertexInputBindingDescription(0u, sizeof(Vertex), VK_VERTEX_INPUT_RATE_VERTEX),
+ makeVertexInputBindingDescription(1u, sizeof(Vertex), VK_VERTEX_INPUT_RATE_VERTEX)
+ };
+ auto attributes =
+ {
+ makeVertexInputAttributeDescription(0u, 0u, VK_FORMAT_R32G32B32A32_SFLOAT, offsetof(Vertex, position)),
+ makeVertexInputAttributeDescription(1u, 1u, VK_FORMAT_R32G32B32A32_SFLOAT, offsetof(Vertex, color1)),
+ makeVertexInputAttributeDescription(2u, 1u, VK_FORMAT_R32G32B32A32_SFLOAT, offsetof(Vertex, color2))
+ };
+ return robustness1TestFn(
+ testContext, context, device,
+ {
+ {
+ bindings,
+ attributes,
+ {
+ { vertices.data(), static_cast<deUint32>(vertices.size() * sizeof(vertices[0])) },
+ { vertices.data(), static_cast<deUint32>((vertices.size() - 3) * sizeof(vertices[0]) - sizeof(Vertex::color2)) }
+ },
+ static_cast<deUint32>(vertices.size()),
+ indices,
+ }
+ },
+ renderTargetSize);
+ }
+ },
+ {
+ "out_of_bounds_stride_30_middle_of_buffer", // string name
+ "Last elements 4 out of bounds, color with stride 30, data middle of buffer", // string description
+ [](TestContext& testContext, Context& context, const VkDevice device) // TestFn testFn
+ {
+ const vector<deUint32> invalidIndices = { 5, 6, 9, 10 };
+ const deUint32 invalidCount = static_cast<deUint32>(invalidIndices.size());
+ const deUint32 totalCount = GetVerticesCountForTriangles(3, 3);
+ struct Vertex
+ {
+ Vec4 position;
+ Vec4 color1;
+ Vec4 unused1;
+ Vec4 color2;
+ Vec4 unused2;
+ };
+ PaddedAlloc<Vertex> vertices (totalCount, 8, { outOfRangeColor, outOfRangeColor, outOfRangeColor, outOfRangeColor, outOfRangeColor });
+ deUint32 writeIndex = 0;
+ vector<deUint32> indices;
+ GenerateTriangles(
+ 3u,
+ 3u,
+ { expectedColor },
+ invalidIndices,
+ [&vertices, &writeIndex](Vec4 position, Vec4 color)
+ {
+ vertices[writeIndex] = { position, color, unusedColor, unusedColor, unusedColor };
+ return writeIndex++;
+ },
+ [&indices](deUint32 index) { indices.push_back(index); });
+ const auto elementSize = static_cast<deUint32>(sizeof(Vertex));
+ auto bindings =
+ {
+ makeVertexInputBindingDescription(0u, elementSize, VK_VERTEX_INPUT_RATE_VERTEX),
+ makeVertexInputBindingDescription(1u, elementSize, VK_VERTEX_INPUT_RATE_VERTEX),
+ };
+ auto attributes =
+ {
+ makeVertexInputAttributeDescription(0u, 0u, VK_FORMAT_R32G32B32A32_SFLOAT, vertices.paddedStart() * elementSize + static_cast<deUint32>(offsetof(Vertex, position))),
+ makeVertexInputAttributeDescription(1u, 1u, VK_FORMAT_R32G32B32A32_SFLOAT, vertices.paddedStart() * elementSize + static_cast<deUint32>(offsetof(Vertex, color1))),
+ makeVertexInputAttributeDescription(2u, 1u, VK_FORMAT_R32G32B32A32_SFLOAT, vertices.paddedStart() * elementSize + static_cast<deUint32>(offsetof(Vertex, color2))),
+ };
+ return robustness1TestFn(
+ testContext, context, device,
+ {
+ {
+ bindings,
+ attributes,
+ {
+ { vertices.paddedData() , vertices.paddedSize() * elementSize },
+ { vertices.paddedData() , (vertices.paddedSize() - invalidCount) * elementSize },
+ },
+ static_cast<deUint32>(vertices.size()),
+ indices
+ }
+ },
+ renderTargetSize);
+ }
+ },
+ {
+ "out_of_bounds_stride_8_middle_of_buffer_separate", // string name
+ "Last elements 4 out of bounds, color with stride 8, data middle of buffer", // string description
+ [](TestContext& testContext, Context& context, const VkDevice device) // TestFn testFn
+ {
+ /* NOTE: Out of range entries ('padding') need to be initialized with unusedColor as the spec
+ allows out of range to return any value from within the bound memory range. */
+ const vector<deUint32> invalidIndices = { 5, 6, 9, 10 };
+ const deUint32 invalidCount = static_cast<deUint32>(invalidIndices.size());
+ const deUint32 totalCount = GetVerticesCountForTriangles(3, 3);
+ PaddedAlloc<Vec4> vertices (totalCount, 8, unusedColor);
+ PaddedAlloc<Vec4> colors (2 * totalCount - invalidCount, 8, unusedColor);
+ deUint32 writeIndex = 0;
+ vector<deUint32> indices;
+ GenerateTriangles(
+ 3u,
+ 3u,
+ { expectedColor },
+ invalidIndices,
+ [&vertices, &colors, &writeIndex, totalCount](Vec4 position, Vec4 color)
+ {
+ vertices[writeIndex] = position;
+ colors[writeIndex] = color;
+ if (totalCount + writeIndex < colors.size())
+ {
+ colors[totalCount + writeIndex] = color;
+ }
+ return writeIndex++;
+ },
+ [&indices](deUint32 index) { indices.push_back(index); });
+ const auto elementSize = static_cast<deUint32>(sizeof(Vec4));
+ auto bindings =
+ {
+ makeVertexInputBindingDescription(0u, elementSize, VK_VERTEX_INPUT_RATE_VERTEX),
+ makeVertexInputBindingDescription(1u, elementSize, VK_VERTEX_INPUT_RATE_VERTEX)
+ };
+ auto attributes =
+ {
+ makeVertexInputAttributeDescription(0u, 0u, VK_FORMAT_R32G32B32A32_SFLOAT, vertices.paddedStart() * elementSize),
+ makeVertexInputAttributeDescription(1u, 1u, VK_FORMAT_R32G32B32A32_SFLOAT, colors.paddedStart() * elementSize),
+ makeVertexInputAttributeDescription(2u, 1u, VK_FORMAT_R32G32B32A32_SFLOAT, (colors.paddedStart() + totalCount) * elementSize)
+ };
+ return robustness1TestFn(
+ testContext, context, device,
+ {
+ {
+ bindings,
+ attributes,
+ {
+ { vertices.paddedData() , vertices.paddedSize() * elementSize },
+ { colors.paddedData() , colors.paddedSize() * elementSize }
+ },
+ static_cast<deUint32>(vertices.size()),
+ indices
+ }
+ },
+ renderTargetSize);
+ }
+ }
+};
+
+deUint32 GetVerticesCountForTriangles(deUint32 tilesX, deUint32 tilesY)
+{
+ return (tilesX + 1) * (tilesY + 1);
+}
+
+// Generate triangles with invalid vertices placed at end of buffer. NOTE: Assumes invalidIndices to be in ascending order!
+void GenerateTriangles (deUint32 tilesX, deUint32 tilesY, vector<Vec4> colors, const vector<deUint32>& invalidIndices, AllocateVertexFn allocateVertex, WriteIndexFn writeIndex)
+{
+ const auto tilesStride = (tilesX + 1);
+ const auto total = tilesStride * (tilesY + 1);
+ const auto lastValidIndex = total - 1 - static_cast<deUint32>(invalidIndices.size());
+ const Vec2 step (1.0f / static_cast<float>(tilesX), 1.0f / static_cast<float>(tilesY));
+
+ vector<deUint32> indexMappings (total);
+ deUint32 nextInvalid = 0;
+ deUint32 writeOffset = 0;
+ deUint32 nextInvalidValue = nextInvalid < invalidIndices.size() ? invalidIndices[nextInvalid] : total;
+ for(deUint32 i = 0; i < total; ++i)
+ {
+ if (i < nextInvalidValue)
+ {
+ indexMappings[writeOffset++] = i;
+ }
+ else
+ {
+ ++nextInvalid;
+ nextInvalidValue = nextInvalid < invalidIndices.size() ? invalidIndices[nextInvalid] : total;
+ }
+ }
+ for(deUint32 i = 0; i < static_cast<deUint32>(invalidIndices.size()); ++i)
+ {
+ indexMappings[writeOffset++] = invalidIndices[i];
+ }
+ deUint32 count = 0;
+ const auto vertexFn = [lastValidIndex, &step, allocateVertex, &count](deUint32 x, deUint32 y, Vec4 color)
+ {
+ const auto result = allocateVertex(
+ Vec4(
+ 2.0f * static_cast<float>(x) * step.x() - 1.0f,
+ 2.0f * static_cast<float>(y) * step.y() - 1.0f,
+ (count <= lastValidIndex) ? 1.0f : 0.0f,
+ 1.0f
+ ), color);
+ ++count;
+ return result;
+ };
+ vector<deUint32> indices(total);
+ for(deUint32 index = 0; index < total; ++index)
+ {
+ const auto mapped = indexMappings[index];
+ const auto x = mapped % tilesStride;
+ const auto y = mapped / tilesStride;
+ const auto color = colors[(x + y) % colors.size()];
+ indices[y * tilesStride + x] = vertexFn(x, y, color);
+ }
+ for(deUint32 y = 0; y < tilesY; ++y)
+ {
+ for(deUint32 x = 0; x < tilesX; ++x)
+ {
+ writeIndex(indices[(y ) * tilesStride + x ]);
+ writeIndex(indices[(y + 1) * tilesStride + x ]);
+ writeIndex(indices[(y ) * tilesStride + x + 1]);
+ writeIndex(indices[(y ) * tilesStride + x + 1]);
+ writeIndex(indices[(y + 1) * tilesStride + x + 1]);
+ writeIndex(indices[(y + 1) * tilesStride + x ]);
+ }
+ }
+}
+
+static TestStatus robustness1TestFn (TestContext& testCtx, Context& context, const VkDevice device, const vector<InputInfo>& inputs, const IVec2& renderSize)
+{
+ const auto colorFormat = VK_FORMAT_R8G8B8A8_UNORM;
+ const auto& vk = context.getDeviceInterface();
+ auto allocator = SimpleAllocator(vk, device, getPhysicalDeviceMemoryProperties(context.getInstanceInterface(), context.getPhysicalDevice()));
+
+ const auto queueFamilyIndex = context.getUniversalQueueFamilyIndex();
+ VkQueue queue;
+ vk.getDeviceQueue(device, queueFamilyIndex, 0, &queue);
+
+ vector<Move<VkImage>> colorImages;
+ vector<MovePtr<Allocation>> colorImageAllocs;
+ vector<Move<VkImageView>> colorViews;
+ vector<VkImageView> attachmentViews;
+ VkImageCreateInfo imageCreateInfos[] =
+ {
+ pipeline::makeImageCreateInfo(renderSize, colorFormat, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT),
+ };
+ for(const auto& params : imageCreateInfos)
+ {
+ auto image = createImage(vk, device, ¶ms);
+ auto imageAlloc = allocator.allocate(getImageMemoryRequirements(vk, device, *image), MemoryRequirement::Any);
+ VK_CHECK(vk.bindImageMemory(device, *image, imageAlloc->getMemory(), imageAlloc->getOffset()));
+ const auto createInfo = VkImageViewCreateInfo
+ {
+ VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ 0u, // VkImageViewCreateFlags flags;
+ *image, // VkImage image;
+ VK_IMAGE_VIEW_TYPE_2D, // VkImageViewType viewType;
+ colorFormat, // VkFormat format;
+ { VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY,
+ VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY }, // VkComponentMapping components;
+ { VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u } // VkImageSubresourceRange subresourceRange;
+ };
+ auto imageView = createImageView(vk, device, &createInfo);
+ attachmentViews.push_back(*imageView);
+ colorImageAllocs.emplace_back(imageAlloc);
+ colorViews.emplace_back(imageView);
+ colorImages.emplace_back(image);
+ }
+
+ const auto colorAttachmentDescs = vector<VkAttachmentDescription>
+ {
+ {
+ (VkAttachmentDescriptionFlags)0, // VkAttachmentDescriptionFlags flags
+ colorFormat, // VkFormat format
+ VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples
+ VK_ATTACHMENT_LOAD_OP_CLEAR, // VkAttachmentLoadOp loadOp
+ VK_ATTACHMENT_STORE_OP_STORE, // VkAttachmentStoreOp storeOp
+ VK_ATTACHMENT_LOAD_OP_DONT_CARE, // VkAttachmentLoadOp stencilLoadOp
+ VK_ATTACHMENT_STORE_OP_DONT_CARE, // VkAttachmentStoreOp stencilStoreOp
+ VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout
+ VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL // VkImageLayout finalLayout
+ },
+ };
+ const auto attachmentRefs = vector<vector<VkAttachmentReference>>
+ {
+ { { 0u, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL } }, // pass 0 color
+ };
+ const auto subpassDescs = vector<VkSubpassDescription>
+ {
+ {
+ static_cast<VkSubpassDescriptionFlags>(0), // VkSubpassDescriptionFlags flags;
+ VK_PIPELINE_BIND_POINT_GRAPHICS, // VkPipelineBindPoint pipelineBindPoint;
+ 0u, // uint32_t inputAttachmentCount;
+ DE_NULL, // const VkAttachmentReference* pInputAttachments;
+ static_cast<deUint32>(attachmentRefs[0].size()), // uint32_t colorAttachmentCount;
+ attachmentRefs[0].data(), // const VkAttachmentReference* pColorAttachments;
+ DE_NULL, // const VkAttachmentReference* pResolveAttachments;
+ DE_NULL, // const VkAttachmentReference* pDepthStencilAttachment;
+ 0u, // uint32_t preserveAttachmentCount;
+ DE_NULL // const uint32_t* pPreserveAttachments;
+ }
+ };
+ const auto subpassDeps = vector<VkSubpassDependency>
+ {
+ {
+ VK_SUBPASS_EXTERNAL, // uint32_t srcSubpass;
+ 0u, // uint32_t dstSubpass;
+ VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, // VkPipelineStageFlags srcStageMask;
+ VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, // VkPipelineStageFlags dstStageMask;
+ 0u, // VkAccessFlags srcAccessMask;
+ VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, // VkAccessFlags dstAccessMask;
+ VK_DEPENDENCY_BY_REGION_BIT // VkDependencyFlags dependencyFlags;
+ },
+ {
+ 0u, // uint32_t srcSubpass;
+ VK_SUBPASS_EXTERNAL, // uint32_t dstSubpass;
+ VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, // VkPipelineStageFlags srcStageMask;
+ VK_PIPELINE_STAGE_TRANSFER_BIT, // VkPipelineStageFlags dstStageMask;
+ VK_ACCESS_COLOR_ATTACHMENT_READ_BIT, // VkAccessFlags srcAccessMask;
+ VK_ACCESS_TRANSFER_READ_BIT, // VkAccessFlags dstAccessMask;
+ VK_DEPENDENCY_BY_REGION_BIT // VkDependencyFlags dependencyFlags;
+ }
+ };
+ const auto renderPassInfo = VkRenderPassCreateInfo
+ {
+ VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, // VkStructureType sType
+ DE_NULL, // const void* pNext
+ static_cast<VkRenderPassCreateFlags>(0), // VkRenderPassCreateFlags flags
+ static_cast<deUint32>(colorAttachmentDescs.size()), // deUint32 attachmentCount
+ colorAttachmentDescs.data(), // const VkAttachmentDescription* pAttachments
+ static_cast<deUint32>(subpassDescs.size()), // deUint32 subpassCount
+ subpassDescs.data(), // const VkSubpassDescription* pSubpasses
+ static_cast<deUint32>(subpassDeps.size()), // deUint32 dependencyCount
+ subpassDeps.data() // const VkSubpassDependency* pDependencies
+ };
+ const Unique<VkRenderPass> pass (createRenderPass(vk, device, &renderPassInfo, DE_NULL));
+
+ vector<Move<VkBuffer>> vertexBuffers;
+ vector<MovePtr<Allocation>> vertexBufferAllocs;
+ vector<vector<VkBuffer>> vertexBufferPtrs;
+ vector<vector<VkDeviceSize>> vertexBufferOffsets;
+ vector<Move<VkBuffer>> indexBuffers;
+ vector<MovePtr<Allocation>> indexBufferAllocs;
+ vector<Move<VkPipelineLayout>> pipelineLayouts;
+ vector<Move<VkPipeline>> pipelines;
+
+ const auto descriptorPool = DescriptorPoolBuilder()
+ .addType(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, static_cast<deUint32>(inputs.size() * 4u))
+ .build(vk, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u);
+ vector<Move<VkDescriptorSetLayout>> descriptorSetLayouts;
+ vector<Move<VkDescriptorSet>> descriptorSets;
+ vector<vector<VkDescriptorSet>> descriptorSetPtrs;
+ vector<Move<VkShaderModule>> shaderModules;
+ const vector<VkViewport> viewports = { makeViewport(renderSize) };
+ const vector<VkRect2D> scissors = { makeRect2D(renderSize) };
+ const vector<string> vertexNames = { "vertex-test" };
+ const vector<string> fragmentNames = { "fragment-test" };
+ for(vector<InputInfo>::size_type i = 0; i < inputs.size(); ++i)
+ {
+ const auto& input = inputs[i];
+ vector<VkDescriptorSet> inputDescriptorSets;
+ vector<VkDescriptorSetLayout> setLayouts;
+ DescriptorSetLayoutBuilder builder;
+ for(size_t j = 0; j < input.vertexBindings.size(); ++j)
+ {
+ builder.addSingleBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_ALL);
+ }
+ auto descriptorSetLayout = builder.build(vk, device);
+ setLayouts.push_back(*descriptorSetLayout);
+ VkDescriptorSetAllocateInfo descriptorSetAllocateInfo =
+ {
+ VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ *descriptorPool, // VkDescriptorPool descriptorPool;
+ 1u, // deUint32 setLayoutCount;
+ &*descriptorSetLayout // const VkDescriptorSetLayout* pSetLayouts;
+ };
+ auto descriptorSet = allocateDescriptorSet(vk, device, &descriptorSetAllocateInfo);
+ inputDescriptorSets.push_back(*descriptorSet);
+
+ VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo =
+ {
+ VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ 0u, // VkPipelineLayoutCreateFlags flags;
+ static_cast<deUint32>(setLayouts.size()), // deUint32 setLayoutCount;
+ setLayouts.data(), // const VkDescriptorSetLayout* pSetLayouts;
+ 0u, // deUint32 pushConstantRangeCount;
+ DE_NULL // const VkPushConstantRange* pPushConstantRanges;
+ };
+ auto pipelineLayout = createPipelineLayout(vk, device, &pipelineLayoutCreateInfo);
+
+ descriptorSetPtrs.push_back(inputDescriptorSets);
+ descriptorSetLayouts.emplace_back(descriptorSetLayout);
+ descriptorSets.emplace_back(descriptorSet);
+
+ vector<VkBuffer> inputVertexBufferPtrs;
+ for(const auto& data : input.data)
+ {
+ const auto createInfo = makeBufferCreateInfo(data.size, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT);
+ auto buffer = createBuffer(vk, device, &createInfo);
+ auto bufferAlloc = allocator.allocate(getBufferMemoryRequirements(vk, device, *buffer), MemoryRequirement::HostVisible);
+ VK_CHECK(vk.bindBufferMemory(device, *buffer, bufferAlloc->getMemory(), bufferAlloc->getOffset()));
+ deMemcpy(bufferAlloc->getHostPtr(), data.data, data.size);
+ flushMappedMemoryRange(vk, device, bufferAlloc->getMemory(), bufferAlloc->getOffset(), VK_WHOLE_SIZE);
+ inputVertexBufferPtrs.push_back(*buffer);
+ vertexBufferAllocs.emplace_back(bufferAlloc);
+ vertexBuffers.emplace_back(buffer);
+ }
+ vertexBufferOffsets.push_back(vector<VkDeviceSize>(inputVertexBufferPtrs.size(), 0ull));
+ vertexBufferPtrs.push_back(inputVertexBufferPtrs);
+
+ if (input.indices.size() > 0u)
+ {
+ const auto indexDataSize = input.indices.size() * sizeof(input.indices[0]);
+ const auto createInfo = makeBufferCreateInfo(indexDataSize, VK_BUFFER_USAGE_INDEX_BUFFER_BIT);
+ auto indexBuffer = createBuffer(vk, device, &createInfo);
+ auto indexBufferAlloc = allocator.allocate(getBufferMemoryRequirements(vk, device, *indexBuffer), MemoryRequirement::HostVisible);
+ VK_CHECK(vk.bindBufferMemory(device, *indexBuffer, indexBufferAlloc->getMemory(), indexBufferAlloc->getOffset()));
+ deMemcpy(indexBufferAlloc->getHostPtr(), input.indices.data(), indexDataSize);
+ flushMappedMemoryRange(vk, device, indexBufferAlloc->getMemory(), indexBufferAlloc->getOffset(), VK_WHOLE_SIZE);
+ indexBufferAllocs.emplace_back(indexBufferAlloc);
+ indexBuffers.emplace_back(indexBuffer);
+ }
+ const auto& bindings = input.vertexBindings;
+ const auto& attributes = input.vertexAttributes;
+ const auto vertexInputCreateInfo = VkPipelineVertexInputStateCreateInfo
+ {
+ VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ 0u, // VkPipelineVertexInputStateCreateFlags flags;
+ static_cast<deUint32>(bindings.size()), // deUint32 vertexBindingDescriptionCount;
+ bindings.data(), // const VkVertexInputBindingDescription* pVertexBindingDescriptions;
+ static_cast<deUint32>(attributes.size()), // deUint32 vertexAttributeDescriptionCount;
+ attributes.data() // const VkVertexInputAttributeDescription* pVertexAttributeDescriptions;
+ };
+ auto vertexShaderModule = createShaderModule(vk, device, context.getBinaryCollection().get(vertexNames[i % vertexNames.size()]), 0u);
+ auto fragmentShaderModule = createShaderModule(vk, device, context.getBinaryCollection().get(fragmentNames[i % fragmentNames.size()]), 0u);
+ auto graphicsPipeline = makeGraphicsPipeline(
+ vk, // const DeviceInterface& vk,
+ device, // const VkDevice device,
+ *pipelineLayout, // const VkPipelineLayout pipelineLayout,
+ *vertexShaderModule, // const VkShaderModule vertexShaderModule,
+ DE_NULL, // const VkShaderModule tessellationControlShaderModule,
+ DE_NULL, // const VkShaderModule tessellationEvalShaderModule,
+ DE_NULL, // const VkShaderModule geometryShaderModule,
+ *fragmentShaderModule, // const VkShaderModule fragmentShaderModule,
+ *pass, // const VkRenderPass renderPass,
+ viewports, // const std::vector<VkViewport>& viewports,
+ scissors, // const std::vector<VkRect2D>& scissors,
+ VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, // const VkPrimitiveTopology topology,
+ static_cast<deUint32>(i), // const deUint32 subpass,
+ 0u, // const deUint32 patchControlPoints,
+ &vertexInputCreateInfo); // const VkPipelineVertexInputStateCreateInfo* vertexInputStateCreateInfo,
+
+ pipelineLayouts.emplace_back(pipelineLayout);
+ pipelines.emplace_back(graphicsPipeline);
+ shaderModules.emplace_back(vertexShaderModule);
+ shaderModules.emplace_back(fragmentShaderModule);
+ }
+
+ const auto framebufferCreateInfo = VkFramebufferCreateInfo
+ {
+ VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ 0u, // VkFramebufferCreateFlags flags;
+ *pass, // VkRenderPass renderPass;
+ static_cast<deUint32>(attachmentViews.size()), // deUint32 attachmentCount;
+ attachmentViews.data(), // const VkImageView* pAttachments;
+ (deUint32)renderSize.x(), // deUint32 width;
+ (deUint32)renderSize.y(), // deUint32 height;
+ 1u // deUint32 layers;
+ };
+ const Unique<VkFramebuffer> framebuffer (createFramebuffer(vk, device, &framebufferCreateInfo));
+
+ const Unique<VkCommandPool> commandPool (createCommandPool(vk, device, VK_COMMAND_POOL_CREATE_TRANSIENT_BIT, queueFamilyIndex));
+ const Unique<VkCommandBuffer> commandBuffer (allocateCommandBuffer(vk, device, *commandPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY));
+ beginCommandBuffer(vk, *commandBuffer, 0u);
+ beginRenderPass(vk, *commandBuffer, *pass, *framebuffer, makeRect2D(renderSize), Vec4(0.0f));
+ deUint32 nextIndex = 0;
+ for(vector<InputInfo>::size_type i = 0; i < inputs.size(); ++i)
+ {
+ const auto& input = inputs[i];
+ vk.cmdBindPipeline(*commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipelines[i]);
+ vk.cmdBindDescriptorSets(*commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipelineLayouts[i], 0, static_cast<deUint32>(descriptorSetPtrs[i].size()), descriptorSetPtrs[i].data(), 0, DE_NULL);
+ vk.cmdBindVertexBuffers(*commandBuffer, 0, (deUint32)vertexBufferPtrs[i].size(), vertexBufferPtrs[i].data(), vertexBufferOffsets[i].data());
+ if (!input.indices.empty())
+ {
+ vk.cmdBindIndexBuffer(*commandBuffer, *indexBuffers[nextIndex], 0u, VK_INDEX_TYPE_UINT32);
+ vk.cmdDrawIndexed(*commandBuffer, static_cast<deUint32>(input.indices.size()), 1u, 0, 0, 0u);
+ ++nextIndex;
+ }
+ else
+ {
+ vk.cmdDraw(*commandBuffer, input.vertexCount, 1u, 0, 0);
+ }
+ if (i + 1 < inputs.size())
+ {
+ vk.cmdNextSubpass(*commandBuffer, VK_SUBPASS_CONTENTS_INLINE);
+ }
+ }
+ endRenderPass(vk, *commandBuffer);
+
+ endCommandBuffer(vk, *commandBuffer);
+ submitCommandsAndWait(vk, device, queue, *commandBuffer);
+
+ const auto texture0 = pipeline::readColorAttachment(vk, device, queue, queueFamilyIndex, allocator, *colorImages[0], colorFormat, UVec2(renderSize.x(), renderSize.y()));
+
+ const auto tex1Access = texture0->getAccess();
+ for(deInt32 y = 0; y < tex1Access.getHeight(); ++y)
+ {
+ for(deInt32 x = 0; x < tex1Access.getWidth(); ++x)
+ {
+ if (tex1Access.getPixel(x, y) != Vec4(0.0f, 1.0f, 0.0f, 1.0f))
+ {
+ testCtx.getLog()
+ << TestLog::ImageSet("Result Images", "")
+ << TestLog::Image("Texture 0 (source)", "", texture0->getAccess())
+ << TestLog::EndImageSet;
+
+ return TestStatus::fail("Image comparison failed.");
+ }
+ }
+ }
+ return TestStatus::pass("OK");
+}
+} // namespace
+
+// Robustness1AccessInstance
+
+template<typename T>
+class Robustness1AccessInstance : public vkt::TestInstance
+{
+public:
+ Robustness1AccessInstance (TestContext& testCtx, Context& context, T device, const Robustness1TestInfo& testInfo);
+ virtual ~Robustness1AccessInstance () {}
+ virtual TestStatus iterate() override;
+
+private:
+ TestContext& m_testCtx;
+ T m_device;
+ const Robustness1TestInfo& m_testInfo;
+};
+
+template<typename T>
+Robustness1AccessInstance<T>::Robustness1AccessInstance (TestContext& testCtx, Context& context, T device, const Robustness1TestInfo& testInfo)
+ : vkt::TestInstance(context), m_testCtx(testCtx), m_device(device), m_testInfo(testInfo)
+{
+}
+
+template<typename T>
+TestStatus Robustness1AccessInstance<T>::iterate ()
+{
+ return m_testInfo.testFn(m_testCtx, m_context, m_device);
+}
+
+// Robustness1AccessTest
+
+class Robustness1AccessTest : public vkt::TestCase
+{
+public:
+ Robustness1AccessTest (TestContext& testContext, const Robustness1TestInfo &testInfo);
+ virtual ~Robustness1AccessTest () {}
+
+ virtual TestInstance* createInstance (Context& context) const override;
+
+protected:
+ virtual void initPrograms (SourceCollections& programCollection) const override;
+
+private:
+ Robustness1TestInfo m_testInfo;
+};
+
+Robustness1AccessTest::Robustness1AccessTest (TestContext &testContext, const Robustness1TestInfo& testInfo)
+ : vkt::TestCase(testContext, testInfo.name, testInfo.description),
+ m_testInfo(testInfo)
+{
+}
+
+TestInstance *Robustness1AccessTest::createInstance (Context &context) const
+{
+ return new Robustness1AccessInstance<VkDevice>(m_testCtx, context, context.getDevice(), m_testInfo);
+}
+
+void Robustness1AccessTest::initPrograms (SourceCollections& programCollection) const
+{
+ ostringstream vertexTestSource;
+ vertexTestSource
+ << "#version 310 es\n"
+ << "precision highp float;\n"
+ << "layout(location = 0) in vec4 in_position;\n"
+ << "layout(location = 1) in vec4 in_color0;\n"
+ << "layout(location = 2) in vec4 in_color1;\n"
+ << "layout(location = 0) out vec4 out_color;\n"
+ << "bool is_valid(vec4 color)\n"
+ << "{\n"
+ << " return\n";
+ const auto compare_color = [](ostringstream& out, const string& variable, const Vec4& color)
+ {
+ out << setprecision(5) << fixed
+ << " ("
+ << variable << ".r - " << color.x() << " < 0.00001 && "
+ << variable << ".g - " << color.y() << " < 0.00001 && "
+ << variable << ".b - " << color.z() << " < 0.00001 && "
+ << variable << ".a - " << color.w() << " < 0.00001"
+ << ")";
+ };
+ for(vector<Vec4>::size_type i = 0; i < validColors.size(); ++i)
+ {
+ compare_color(vertexTestSource, "color", validColors[i]);
+ vertexTestSource << ((i < validColors.size() - 1) ? " ||\n" : ";\n");
+ }
+ vertexTestSource
+ << "}\n"
+ << "bool is_invalid(vec4 color)\n"
+ << "{\n"
+ << " return\n";
+ for(vector<Vec4>::size_type i = 0; i < invalidColors.size(); ++i)
+ {
+ compare_color(vertexTestSource, "color", invalidColors[i]);
+ vertexTestSource << ((i < invalidColors.size() - 1) ? " ||\n" : ";\n");
+ }
+ vertexTestSource
+ << "}\n"
+ << "bool validate(bool should_be_valid, vec4 color0, vec4 color1)\n"
+ << "{\n"
+ << " return (should_be_valid && is_valid(color0) && is_valid(color1)) || (is_invalid(color0) && is_invalid(color1));\n"
+ << "}\n"
+ << "void main()\n"
+ << "{\n"
+ << " out_color = validate(in_position.z >= 1.0, in_color0, in_color1) ? vec4(0,1,0,1) : in_color0;"
+ << " gl_Position = vec4(in_position.xy, 0.0, 1.0);\n"
+ << "}\n";
+ programCollection.glslSources.add("vertex-test") << glu::VertexSource(vertexTestSource.str());
+ programCollection.glslSources.add("fragment-test") << glu::FragmentSource(
+ "#version 310 es\n"
+ "precision highp float;\n"
+ "layout(location = 0) in vec4 in_color;\n"
+ "layout(location = 0) out vec4 out_color;\n"
+ "void main() {\n"
+ " out_color = in_color;\n"
+ "}\n");
+}
+
+TestCaseGroup* createRobustness1VertexAccessTests (TestContext& testCtx)
+{
+ MovePtr<TestCaseGroup> robustness1AccessTests (new TestCaseGroup(testCtx, "robustness1_vertex_access", ""));
+ for(const auto& info : robustness1Tests)
+ {
+ robustness1AccessTests->addChild(new Robustness1AccessTest(testCtx, info));
+ }
+
+ return robustness1AccessTests.release();
+}
+
+} // robustness
+} // vkt