--- /dev/null
+/*-------------------------------------------------------------------------
+ * 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 interaction with VkSwapchain Tests
+ *//*--------------------------------------------------------------------*/
+
+#include "vktProtectedMemWsiSwapchainTests.hpp"
+
+#include "vktTestCaseUtil.hpp"
+#include "vktTestGroupUtil.hpp"
+
+#include "vkDefs.hpp"
+#include "vkPlatform.hpp"
+#include "vkStrUtil.hpp"
+#include "vkRef.hpp"
+#include "vkRefUtil.hpp"
+#include "vkQueryUtil.hpp"
+#include "vkMemUtil.hpp"
+#include "vkDeviceUtil.hpp"
+#include "vkPrograms.hpp"
+#include "vkTypeUtil.hpp"
+#include "vkWsiPlatform.hpp"
+#include "vkWsiUtil.hpp"
+#include "vkAllocationCallbackUtil.hpp"
+
+#include "tcuTestLog.hpp"
+#include "tcuFormatUtil.hpp"
+#include "tcuPlatform.hpp"
+#include "tcuResultCollector.hpp"
+
+#include "deUniquePtr.hpp"
+#include "deStringUtil.hpp"
+#include "deArrayUtil.hpp"
+#include "deSharedPtr.hpp"
+
+#include <limits>
+
+#include "vktProtectedMemContext.hpp"
+#include "vktProtectedMemUtils.hpp"
+
+namespace vkt
+{
+namespace ProtectedMem
+{
+
+namespace
+{
+
+typedef std::vector<vk::VkExtensionProperties> Extensions;
+
+void checkAllSupported (const Extensions& supportedExtensions, const std::vector<std::string>& requiredExtensions)
+{
+ for (std::vector<std::string>::const_iterator requiredExtName = requiredExtensions.begin();
+ requiredExtName != requiredExtensions.end();
+ ++requiredExtName)
+ {
+ if (!isExtensionSupported(supportedExtensions, vk::RequiredExtension(*requiredExtName)))
+ TCU_THROW(NotSupportedError, (*requiredExtName + " is not supported").c_str());
+ }
+}
+
+std::vector<std::string> getRequiredWsiExtensions (const Extensions& supportedExtensions,
+ vk::wsi::Type wsiType)
+{
+ std::vector<std::string> extensions;
+
+ extensions.push_back("VK_KHR_surface");
+ extensions.push_back(getExtensionName(wsiType));
+
+ // VK_EXT_swapchain_colorspace adds new surface formats. Driver can enumerate
+ // the formats regardless of whether VK_EXT_swapchain_colorspace was enabled,
+ // but using them without enabling the extension is not allowed. Thus we have
+ // two options:
+ //
+ // 1) Filter out non-core formats to stay within valid usage.
+ //
+ // 2) Enable VK_EXT_swapchain colorspace if advertised by the driver.
+ //
+ // We opt for (2) as it provides basic coverage for the extension as a bonus.
+ if (isExtensionSupported(supportedExtensions, vk::RequiredExtension("VK_EXT_swapchain_colorspace")))
+ extensions.push_back("VK_EXT_swapchain_colorspace");
+
+ checkAllSupported(supportedExtensions, extensions);
+
+ return extensions;
+}
+
+de::MovePtr<vk::wsi::Display> createDisplay (const vk::Platform& platform,
+ const Extensions& supportedExtensions,
+ vk::wsi::Type wsiType)
+{
+ try
+ {
+ return de::MovePtr<vk::wsi::Display>(platform.createWsiDisplay(wsiType));
+ }
+ catch (const tcu::NotSupportedError& e)
+ {
+ if (isExtensionSupported(supportedExtensions, vk::RequiredExtension(getExtensionName(wsiType))))
+ {
+ // If VK_KHR_{platform}_surface was supported, vk::Platform implementation
+ // must support creating native display & window for that WSI type.
+ throw tcu::TestError(e.getMessage());
+ }
+ else
+ throw;
+ }
+}
+
+de::MovePtr<vk::wsi::Window> createWindow (const vk::wsi::Display& display, const tcu::Maybe<tcu::UVec2>& initialSize)
+{
+ try
+ {
+ return de::MovePtr<vk::wsi::Window>(display.createWindow(initialSize));
+ }
+ catch (const tcu::NotSupportedError& e)
+ {
+ // See createDisplay - assuming that wsi::Display was supported platform port
+ // should also support creating a window.
+ throw tcu::TestError(e.getMessage());
+ }
+}
+
+struct NativeObjects
+{
+ const de::UniquePtr<vk::wsi::Display> display;
+ const de::UniquePtr<vk::wsi::Window> window;
+
+ NativeObjects (Context& context,
+ const Extensions& supportedExtensions,
+ vk::wsi::Type wsiType,
+ const tcu::Maybe<tcu::UVec2>& initialWindowSize = tcu::nothing<tcu::UVec2>())
+ : display (createDisplay(context.getTestContext().getPlatform().getVulkanPlatform(), supportedExtensions, wsiType))
+ , window (createWindow(*display, initialWindowSize))
+ {}
+};
+
+enum TestDimension
+{
+ TEST_DIMENSION_MIN_IMAGE_COUNT = 0, //!< Test all supported image counts
+ TEST_DIMENSION_IMAGE_FORMAT, //!< Test all supported formats
+ TEST_DIMENSION_IMAGE_EXTENT, //!< Test various (supported) extents
+ TEST_DIMENSION_IMAGE_ARRAY_LAYERS,
+ TEST_DIMENSION_IMAGE_USAGE,
+ TEST_DIMENSION_IMAGE_SHARING_MODE,
+ TEST_DIMENSION_PRE_TRANSFORM,
+ TEST_DIMENSION_COMPOSITE_ALPHA,
+ TEST_DIMENSION_PRESENT_MODE,
+ TEST_DIMENSION_CLIPPED,
+
+ TEST_DIMENSION_LAST
+};
+
+const char* getTestDimensionName (TestDimension dimension)
+{
+ static const char* const s_names[] =
+ {
+ "min_image_count",
+ "image_format",
+ "image_extent",
+ "image_array_layers",
+ "image_usage",
+ "image_sharing_mode",
+ "pre_transform",
+ "composite_alpha",
+ "present_mode",
+ "clipped"
+ };
+ return de::getSizedArrayElement<TEST_DIMENSION_LAST>(s_names, dimension);
+}
+
+struct TestParameters
+{
+ vk::wsi::Type wsiType;
+ TestDimension dimension;
+
+ TestParameters (vk::wsi::Type wsiType_, TestDimension dimension_)
+ : wsiType (wsiType_)
+ , dimension (dimension_)
+ {}
+
+ TestParameters (void)
+ : wsiType (vk::wsi::TYPE_LAST)
+ , dimension (TEST_DIMENSION_LAST)
+ {}
+};
+
+std::vector<vk::VkSwapchainCreateInfoKHR> generateSwapchainParameterCases (vk::wsi::Type wsiType,
+ TestDimension dimension,
+ const vk::VkSurfaceCapabilitiesKHR& capabilities,
+ const std::vector<vk::VkSurfaceFormatKHR>& formats,
+ const std::vector<vk::VkPresentModeKHR>& presentModes)
+{
+ std::vector<vk::VkSwapchainCreateInfoKHR> cases;
+ const vk::wsi::PlatformProperties& platformProperties = getPlatformProperties(wsiType);
+ const vk::VkSurfaceTransformFlagBitsKHR defaultTransform = (capabilities.supportedTransforms & vk::VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR)
+ ? vk::VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR : capabilities.currentTransform;
+ const vk::VkSwapchainCreateInfoKHR baseParameters =
+ {
+ vk::VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
+ DE_NULL,
+ vk::VK_SWAPCHAIN_CREATE_PROTECTED_BIT_KHR,
+ (vk::VkSurfaceKHR)0,
+ capabilities.minImageCount,
+ formats[0].format,
+ formats[0].colorSpace,
+ (platformProperties.swapchainExtent == vk::wsi::PlatformProperties::SWAPCHAIN_EXTENT_SETS_WINDOW_SIZE
+ ? capabilities.minImageExtent : capabilities.currentExtent),
+ 1u, // imageArrayLayers
+ vk::VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
+ vk::VK_SHARING_MODE_EXCLUSIVE,
+ 0u,
+ (const deUint32*)DE_NULL,
+ defaultTransform,
+ vk::VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
+ vk::VK_PRESENT_MODE_FIFO_KHR,
+ VK_FALSE, // clipped
+ (vk::VkSwapchainKHR)0 // oldSwapchain
+ };
+
+ switch (dimension)
+ {
+ case TEST_DIMENSION_MIN_IMAGE_COUNT:
+ {
+ const deUint32 maxImageCountToTest = de::clamp(16u, capabilities.minImageCount, (capabilities.maxImageCount > 0) ? capabilities.maxImageCount : capabilities.minImageCount + 16u);
+
+ for (deUint32 imageCount = capabilities.minImageCount; imageCount <= maxImageCountToTest; ++imageCount)
+ {
+ cases.push_back(baseParameters);
+ cases.back().minImageCount = imageCount;
+ }
+
+ break;
+ }
+
+ case TEST_DIMENSION_IMAGE_FORMAT:
+ {
+ for (std::vector<vk::VkSurfaceFormatKHR>::const_iterator curFmt = formats.begin(); curFmt != formats.end(); ++curFmt)
+ {
+ cases.push_back(baseParameters);
+ cases.back().imageFormat = curFmt->format;
+ cases.back().imageColorSpace = curFmt->colorSpace;
+ }
+
+ break;
+ }
+
+ case TEST_DIMENSION_IMAGE_EXTENT:
+ {
+ static const vk::VkExtent2D s_testSizes[] =
+ {
+ { 1, 1 },
+ { 16, 32 },
+ { 32, 16 },
+ { 632, 231 },
+ { 117, 998 },
+ };
+
+ if (platformProperties.swapchainExtent == vk::wsi::PlatformProperties::SWAPCHAIN_EXTENT_SETS_WINDOW_SIZE ||
+ platformProperties.swapchainExtent == vk::wsi::PlatformProperties::SWAPCHAIN_EXTENT_SCALED_TO_WINDOW_SIZE)
+ {
+ for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(s_testSizes); ++ndx)
+ {
+ cases.push_back(baseParameters);
+ cases.back().imageExtent.width = de::clamp(s_testSizes[ndx].width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width);
+ cases.back().imageExtent.height = de::clamp(s_testSizes[ndx].height, capabilities.minImageExtent.height, capabilities.maxImageExtent.height);
+ }
+ }
+
+ if (platformProperties.swapchainExtent != vk::wsi::PlatformProperties::SWAPCHAIN_EXTENT_SETS_WINDOW_SIZE)
+ {
+ cases.push_back(baseParameters);
+ cases.back().imageExtent = capabilities.currentExtent;
+ }
+
+ if (platformProperties.swapchainExtent != vk::wsi::PlatformProperties::SWAPCHAIN_EXTENT_MUST_MATCH_WINDOW_SIZE)
+ {
+ cases.push_back(baseParameters);
+ cases.back().imageExtent = capabilities.minImageExtent;
+
+ cases.push_back(baseParameters);
+ cases.back().imageExtent = capabilities.maxImageExtent;
+ }
+
+ break;
+ }
+
+ case TEST_DIMENSION_IMAGE_ARRAY_LAYERS:
+ {
+ const deUint32 maxLayers = de::min(capabilities.maxImageArrayLayers, 16u);
+
+ for (deUint32 numLayers = 1; numLayers <= maxLayers; ++numLayers)
+ {
+ cases.push_back(baseParameters);
+ cases.back().imageArrayLayers = numLayers;
+ }
+
+ break;
+ }
+
+ case TEST_DIMENSION_IMAGE_USAGE:
+ {
+ for (deUint32 flags = 1u; flags <= capabilities.supportedUsageFlags; ++flags)
+ {
+ if ((flags & ~capabilities.supportedUsageFlags) == 0)
+ {
+ cases.push_back(baseParameters);
+ cases.back().imageUsage = flags;
+ }
+ }
+
+ break;
+ }
+
+ case TEST_DIMENSION_IMAGE_SHARING_MODE:
+ {
+ cases.push_back(baseParameters);
+ cases.back().imageSharingMode = vk::VK_SHARING_MODE_EXCLUSIVE;
+
+ cases.push_back(baseParameters);
+ cases.back().imageSharingMode = vk::VK_SHARING_MODE_CONCURRENT;
+
+ break;
+ }
+
+ case TEST_DIMENSION_PRE_TRANSFORM:
+ {
+ for (deUint32 transform = 1u;
+ transform <= capabilities.supportedTransforms;
+ transform = transform<<1u)
+ {
+ if ((transform & capabilities.supportedTransforms) != 0)
+ {
+ cases.push_back(baseParameters);
+ cases.back().preTransform = (vk::VkSurfaceTransformFlagBitsKHR)transform;
+ }
+ }
+
+ break;
+ }
+
+ case TEST_DIMENSION_COMPOSITE_ALPHA:
+ {
+ for (deUint32 alphaMode = 1u;
+ alphaMode <= capabilities.supportedCompositeAlpha;
+ alphaMode = alphaMode<<1u)
+ {
+ if ((alphaMode & capabilities.supportedCompositeAlpha) != 0)
+ {
+ cases.push_back(baseParameters);
+ cases.back().compositeAlpha = (vk::VkCompositeAlphaFlagBitsKHR)alphaMode;
+ }
+ }
+
+ break;
+ }
+
+ case TEST_DIMENSION_PRESENT_MODE:
+ {
+ for (std::vector<vk::VkPresentModeKHR>::const_iterator curMode = presentModes.begin(); curMode != presentModes.end(); ++curMode)
+ {
+ cases.push_back(baseParameters);
+ cases.back().presentMode = *curMode;
+ }
+
+ break;
+ }
+
+ case TEST_DIMENSION_CLIPPED:
+ {
+ cases.push_back(baseParameters);
+ cases.back().clipped = VK_FALSE;
+
+ cases.push_back(baseParameters);
+ cases.back().clipped = VK_TRUE;
+
+ break;
+ }
+
+ default:
+ DE_FATAL("Impossible");
+ }
+
+ DE_ASSERT(!cases.empty());
+ return cases;
+}
+
+std::vector<vk::VkSwapchainCreateInfoKHR> generateSwapchainParameterCases (vk::wsi::Type wsiType,
+ TestDimension dimension,
+ const vk::InstanceInterface& vki,
+ vk::VkPhysicalDevice physicalDevice,
+ vk::VkSurfaceKHR surface)
+{
+ const vk::VkSurfaceCapabilitiesKHR capabilities = vk::wsi::getPhysicalDeviceSurfaceCapabilities(vki,
+ physicalDevice,
+ surface);
+ const std::vector<vk::VkSurfaceFormatKHR> formats = vk::wsi::getPhysicalDeviceSurfaceFormats(vki,
+ physicalDevice,
+ surface);
+ const std::vector<vk::VkPresentModeKHR> presentModes = vk::wsi::getPhysicalDeviceSurfacePresentModes(vki,
+ physicalDevice,
+ surface);
+
+ return generateSwapchainParameterCases(wsiType, dimension, capabilities, formats, presentModes);
+}
+
+tcu::TestStatus createSwapchainTest (Context& baseCtx, TestParameters params)
+{
+ std::vector<vk::VkExtensionProperties> supportedExtensions (enumerateInstanceExtensionProperties(baseCtx.getPlatformInterface(), DE_NULL));
+ std::vector<std::string> instExts = getRequiredWsiExtensions(supportedExtensions, params.wsiType);
+ std::vector<std::string> devExts;
+ devExts.push_back("VK_KHR_swapchain");
+
+ const NativeObjects native (baseCtx, supportedExtensions, params.wsiType);
+ ProtectedContext context (baseCtx, params.wsiType, *native.display, *native.window, instExts, devExts);
+ vk::VkSurfaceKHR surface = context.getSurface();
+ const std::vector<vk::VkSwapchainCreateInfoKHR> cases (generateSwapchainParameterCases(params.wsiType,
+ params.dimension,
+ context.getInstanceDriver(),
+ context.getPhysicalDevice(),
+ surface));
+ deUint32 queueIdx = context.getQueueFamilyIndex();
+ for (size_t caseNdx = 0; caseNdx < cases.size(); ++caseNdx)
+ {
+ vk::VkSwapchainCreateInfoKHR curParams = cases[caseNdx];
+
+ curParams.surface = surface;
+ curParams.queueFamilyIndexCount = 1u;
+ curParams.pQueueFamilyIndices = &queueIdx;
+
+ context.getTestContext().getLog()
+ << tcu::TestLog::Message << "Sub-case " << (caseNdx+1) << " / " << cases.size() << ": " << curParams << tcu::TestLog::EndMessage;
+
+ {
+ const vk::Unique<vk::VkSwapchainKHR> swapchain (createSwapchainKHR(context.getDeviceDriver(), context.getDevice(), &curParams));
+ }
+ }
+
+ return tcu::TestStatus::pass("Creating swapchain succeeded");
+}
+
+struct GroupParameters
+{
+ typedef FunctionInstance1<TestParameters>::Function Function;
+
+ vk::wsi::Type wsiType;
+ Function function;
+
+ GroupParameters (vk::wsi::Type wsiType_, Function function_)
+ : wsiType (wsiType_)
+ , function (function_)
+ {}
+
+ GroupParameters (void)
+ : wsiType (vk::wsi::TYPE_LAST)
+ , function ((Function)DE_NULL)
+ {}
+};
+
+void populateSwapchainGroup (tcu::TestCaseGroup* testGroup, GroupParameters params)
+{
+ for (int dimensionNdx = 0; dimensionNdx < TEST_DIMENSION_LAST; ++dimensionNdx)
+ {
+ const TestDimension testDimension = (TestDimension)dimensionNdx;
+
+ addFunctionCase(testGroup, getTestDimensionName(testDimension), "", params.function, TestParameters(params.wsiType, testDimension));
+ }
+}
+
+vk::VkSwapchainCreateInfoKHR getBasicSwapchainParameters (vk::wsi::Type wsiType,
+ const vk::InstanceInterface& vki,
+ vk::VkPhysicalDevice physicalDevice,
+ vk::VkSurfaceKHR surface,
+ const tcu::UVec2& desiredSize,
+ deUint32 desiredImageCount)
+{
+ const vk::VkSurfaceCapabilitiesKHR capabilities = vk::wsi::getPhysicalDeviceSurfaceCapabilities(vki,
+ physicalDevice,
+ surface);
+ const std::vector<vk::VkSurfaceFormatKHR> formats = vk::wsi::getPhysicalDeviceSurfaceFormats(vki,
+ physicalDevice,
+ surface);
+ const vk::wsi::PlatformProperties& platformProperties = vk::wsi::getPlatformProperties(wsiType);
+ const vk::VkSurfaceTransformFlagBitsKHR transform = (capabilities.supportedTransforms & vk::VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR)
+ ? vk::VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR : capabilities.currentTransform;
+ const vk::VkSwapchainCreateInfoKHR parameters =
+ {
+ vk::VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
+ DE_NULL,
+ vk::VK_SWAPCHAIN_CREATE_PROTECTED_BIT_KHR,
+ surface,
+ de::clamp(desiredImageCount, capabilities.minImageCount, capabilities.maxImageCount > 0 ? capabilities.maxImageCount : capabilities.minImageCount + desiredImageCount),
+ formats[0].format,
+ formats[0].colorSpace,
+ (platformProperties.swapchainExtent == vk::wsi::PlatformProperties::SWAPCHAIN_EXTENT_MUST_MATCH_WINDOW_SIZE
+ ? capabilities.currentExtent : vk::makeExtent2D(desiredSize.x(), desiredSize.y())),
+ 1u, // imageArrayLayers
+ vk::VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
+ vk::VK_SHARING_MODE_EXCLUSIVE,
+ 0u,
+ (const deUint32*)DE_NULL,
+ transform,
+ vk::VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
+ vk::VK_PRESENT_MODE_FIFO_KHR,
+ VK_FALSE, // clipped
+ (vk::VkSwapchainKHR)0 // oldSwapchain
+ };
+
+ return parameters;
+}
+
+typedef de::SharedPtr<vk::Unique<vk::VkImageView> > ImageViewSp;
+typedef de::SharedPtr<vk::Unique<vk::VkFramebuffer> > FramebufferSp;
+
+class TriangleRenderer
+{
+public:
+ TriangleRenderer (ProtectedContext& context,
+ const vk::BinaryCollection& binaryRegistry,
+ const std::vector<vk::VkImage> swapchainImages,
+ const vk::VkFormat framebufferFormat,
+ const tcu::UVec2& renderSize);
+ ~TriangleRenderer (void);
+
+ void recordFrame (vk::VkCommandBuffer cmdBuffer,
+ deUint32 imageNdx,
+ deUint32 frameNdx) const;
+
+ static void getPrograms (vk::SourceCollections& dst);
+
+private:
+ static vk::Move<vk::VkRenderPass> createRenderPass (const vk::DeviceInterface& vkd,
+ const vk::VkDevice device,
+ const vk::VkFormat colorAttachmentFormat);
+ static vk::Move<vk::VkPipelineLayout> createPipelineLayout(const vk::DeviceInterface& vkd,
+ vk::VkDevice device);
+ static vk::Move<vk::VkPipeline> createPipeline (const vk::DeviceInterface& vkd,
+ const vk::VkDevice device,
+ const vk::VkRenderPass renderPass,
+ const vk::VkPipelineLayout pipelineLayout,
+ const vk::BinaryCollection& binaryCollection,
+ const tcu::UVec2& renderSize);
+
+ const vk::DeviceInterface& m_vkd;
+
+ const std::vector<vk::VkImage> m_swapchainImages;
+ const tcu::UVec2 m_renderSize;
+
+ const vk::Unique<vk::VkRenderPass> m_renderPass;
+ const vk::Unique<vk::VkPipelineLayout> m_pipelineLayout;
+ const vk::Unique<vk::VkPipeline> m_pipeline;
+
+ const de::UniquePtr<vk::BufferWithMemory> m_vertexBuffer;
+
+ std::vector<ImageViewSp> m_attachmentViews;
+ std::vector<FramebufferSp> m_framebuffers;
+};
+
+vk::Move<vk::VkRenderPass> TriangleRenderer::createRenderPass (const vk::DeviceInterface& vkd,
+ const vk::VkDevice device,
+ const vk::VkFormat colorAttachmentFormat)
+{
+ const vk::VkAttachmentDescription colorAttDesc =
+ {
+ (vk::VkAttachmentDescriptionFlags)0,
+ colorAttachmentFormat,
+ vk::VK_SAMPLE_COUNT_1_BIT,
+ vk::VK_ATTACHMENT_LOAD_OP_CLEAR,
+ vk::VK_ATTACHMENT_STORE_OP_STORE,
+ vk::VK_ATTACHMENT_LOAD_OP_DONT_CARE,
+ vk::VK_ATTACHMENT_STORE_OP_DONT_CARE,
+ vk::VK_IMAGE_LAYOUT_UNDEFINED,
+ vk::VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
+ };
+ const vk::VkAttachmentReference colorAttRef =
+ {
+ 0u,
+ vk::VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
+ };
+ const vk::VkSubpassDescription subpassDesc =
+ {
+ (vk::VkSubpassDescriptionFlags)0u,
+ vk::VK_PIPELINE_BIND_POINT_GRAPHICS,
+ 0u, // inputAttachmentCount
+ DE_NULL, // pInputAttachments
+ 1u, // colorAttachmentCount
+ &colorAttRef, // pColorAttachments
+ DE_NULL, // pResolveAttachments
+ DE_NULL, // depthStencilAttachment
+ 0u, // preserveAttachmentCount
+ DE_NULL, // pPreserveAttachments
+ };
+ const vk::VkSubpassDependency dependencies[] =
+ {
+ {
+ VK_SUBPASS_EXTERNAL, // srcSubpass
+ 0u, // dstSubpass
+ vk::VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
+ vk::VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
+ vk::VK_ACCESS_MEMORY_READ_BIT,
+ (vk::VK_ACCESS_COLOR_ATTACHMENT_READ_BIT|
+ vk::VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT),
+ vk::VK_DEPENDENCY_BY_REGION_BIT
+ },
+ {
+ 0u, // srcSubpass
+ VK_SUBPASS_EXTERNAL, // dstSubpass
+ vk::VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
+ vk::VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
+ (vk::VK_ACCESS_COLOR_ATTACHMENT_READ_BIT|
+ vk::VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT),
+ vk::VK_ACCESS_MEMORY_READ_BIT,
+ vk::VK_DEPENDENCY_BY_REGION_BIT
+ },
+ };
+ const vk::VkRenderPassCreateInfo renderPassParams =
+ {
+ vk::VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
+ DE_NULL,
+ (vk::VkRenderPassCreateFlags)0,
+ 1u,
+ &colorAttDesc,
+ 1u,
+ &subpassDesc,
+ DE_LENGTH_OF_ARRAY(dependencies),
+ dependencies,
+ };
+
+ return vk::createRenderPass(vkd, device, &renderPassParams);
+}
+
+vk::Move<vk::VkPipelineLayout> TriangleRenderer::createPipelineLayout (const vk::DeviceInterface& vkd,
+ const vk::VkDevice device)
+{
+ const vk::VkPushConstantRange pushConstantRange =
+ {
+ vk::VK_SHADER_STAGE_VERTEX_BIT,
+ 0u, // offset
+ (deUint32)sizeof(deUint32), // size
+ };
+ const vk::VkPipelineLayoutCreateInfo pipelineLayoutParams =
+ {
+ vk::VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
+ DE_NULL,
+ (vk::VkPipelineLayoutCreateFlags)0,
+ 0u, // setLayoutCount
+ DE_NULL, // pSetLayouts
+ 1u,
+ &pushConstantRange,
+ };
+
+ return vk::createPipelineLayout(vkd, device, &pipelineLayoutParams);
+}
+
+vk::Move<vk::VkPipeline> TriangleRenderer::createPipeline (const vk::DeviceInterface& vkd,
+ const vk::VkDevice device,
+ const vk::VkRenderPass renderPass,
+ const vk::VkPipelineLayout pipelineLayout,
+ const vk::BinaryCollection& binaryCollection,
+ const tcu::UVec2& renderSize)
+{
+ // \note VkShaderModules are fully consumed by vkCreateGraphicsPipelines()
+ // and can be deleted immediately following that call.
+ const vk::Unique<vk::VkShaderModule> vertShaderModule (createShaderModule(vkd, device, binaryCollection.get("tri-vert"), 0));
+ const vk::Unique<vk::VkShaderModule> fragShaderModule (createShaderModule(vkd, device, binaryCollection.get("tri-frag"), 0));
+
+ const vk::VkPipelineShaderStageCreateInfo shaderStageParams[] =
+ {
+ {
+ vk::VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
+ DE_NULL,
+ (vk::VkPipelineShaderStageCreateFlags)0,
+ vk::VK_SHADER_STAGE_VERTEX_BIT,
+ *vertShaderModule,
+ "main",
+ DE_NULL
+ },
+ {
+ vk::VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
+ DE_NULL,
+ (vk::VkPipelineShaderStageCreateFlags)0,
+ vk::VK_SHADER_STAGE_FRAGMENT_BIT,
+ *fragShaderModule,
+ "main",
+ DE_NULL
+ }
+ };
+ const vk::VkPipelineDepthStencilStateCreateInfo depthStencilParams =
+ {
+ vk::VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
+ DE_NULL,
+ (vk::VkPipelineDepthStencilStateCreateFlags)0,
+ DE_FALSE, // depthTestEnable
+ DE_FALSE, // depthWriteEnable
+ vk::VK_COMPARE_OP_ALWAYS, // depthCompareOp
+ DE_FALSE, // depthBoundsTestEnable
+ DE_FALSE, // stencilTestEnable
+ {
+ vk::VK_STENCIL_OP_KEEP, // failOp
+ vk::VK_STENCIL_OP_KEEP, // passOp
+ vk::VK_STENCIL_OP_KEEP, // depthFailOp
+ vk::VK_COMPARE_OP_ALWAYS, // compareOp
+ 0u, // compareMask
+ 0u, // writeMask
+ 0u, // reference
+ }, // front
+ {
+ vk::VK_STENCIL_OP_KEEP, // failOp
+ vk::VK_STENCIL_OP_KEEP, // passOp
+ vk::VK_STENCIL_OP_KEEP, // depthFailOp
+ vk::VK_COMPARE_OP_ALWAYS, // compareOp
+ 0u, // compareMask
+ 0u, // writeMask
+ 0u, // reference
+ }, // back
+ -1.0f, // minDepthBounds
+ +1.0f, // maxDepthBounds
+ };
+ const vk::VkViewport viewport0 =
+ {
+ 0.0f, // x
+ 0.0f, // y
+ (float)renderSize.x(), // width
+ (float)renderSize.y(), // height
+ 0.0f, // minDepth
+ 1.0f, // maxDepth
+ };
+ const vk::VkRect2D scissor0 =
+ {
+ { 0u, 0u, }, // offset
+ { renderSize.x(), renderSize.y() }, // extent
+ };
+ const vk::VkPipelineViewportStateCreateInfo viewportParams =
+ {
+ vk::VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
+ DE_NULL,
+ (vk::VkPipelineViewportStateCreateFlags)0,
+ 1u,
+ &viewport0,
+ 1u,
+ &scissor0
+ };
+ const vk::VkPipelineMultisampleStateCreateInfo multisampleParams =
+ {
+ vk::VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
+ DE_NULL,
+ (vk::VkPipelineMultisampleStateCreateFlags)0,
+ vk::VK_SAMPLE_COUNT_1_BIT, // rasterizationSamples
+ VK_FALSE, // sampleShadingEnable
+ 0.0f, // minSampleShading
+ (const vk::VkSampleMask*)DE_NULL, // sampleMask
+ VK_FALSE, // alphaToCoverageEnable
+ VK_FALSE, // alphaToOneEnable
+ };
+ const vk::VkPipelineRasterizationStateCreateInfo rasterParams =
+ {
+ vk::VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
+ DE_NULL,
+ (vk::VkPipelineRasterizationStateCreateFlags)0,
+ VK_FALSE, // depthClampEnable
+ VK_FALSE, // rasterizerDiscardEnable
+ vk::VK_POLYGON_MODE_FILL, // polygonMode
+ vk::VK_CULL_MODE_NONE, // cullMode
+ vk::VK_FRONT_FACE_COUNTER_CLOCKWISE, // frontFace
+ VK_FALSE, // depthBiasEnable
+ 0.0f, // depthBiasConstantFactor
+ 0.0f, // depthBiasClamp
+ 0.0f, // depthBiasSlopeFactor
+ 1.0f, // lineWidth
+ };
+ const vk::VkPipelineInputAssemblyStateCreateInfo inputAssemblyParams =
+ {
+ vk::VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
+ DE_NULL,
+ (vk::VkPipelineInputAssemblyStateCreateFlags)0,
+ vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
+ DE_FALSE, // primitiveRestartEnable
+ };
+ const vk::VkVertexInputBindingDescription vertexBinding0 =
+ {
+ 0u, // binding
+ (deUint32)sizeof(tcu::Vec4), // stride
+ vk::VK_VERTEX_INPUT_RATE_VERTEX, // inputRate
+ };
+ const vk::VkVertexInputAttributeDescription vertexAttrib0 =
+ {
+ 0u, // location
+ 0u, // binding
+ vk::VK_FORMAT_R32G32B32A32_SFLOAT, // format
+ 0u, // offset
+ };
+ const vk::VkPipelineVertexInputStateCreateInfo vertexInputStateParams =
+ {
+ vk::VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
+ DE_NULL,
+ (vk::VkPipelineVertexInputStateCreateFlags)0,
+ 1u,
+ &vertexBinding0,
+ 1u,
+ &vertexAttrib0,
+ };
+ const vk::VkPipelineColorBlendAttachmentState attBlendParams0 =
+ {
+ VK_FALSE, // blendEnable
+ vk::VK_BLEND_FACTOR_ONE, // srcColorBlendFactor
+ vk::VK_BLEND_FACTOR_ZERO, // dstColorBlendFactor
+ vk::VK_BLEND_OP_ADD, // colorBlendOp
+ vk::VK_BLEND_FACTOR_ONE, // srcAlphaBlendFactor
+ vk::VK_BLEND_FACTOR_ZERO, // dstAlphaBlendFactor
+ vk::VK_BLEND_OP_ADD, // alphaBlendOp
+ (vk::VK_COLOR_COMPONENT_R_BIT|
+ vk::VK_COLOR_COMPONENT_G_BIT|
+ vk::VK_COLOR_COMPONENT_B_BIT|
+ vk::VK_COLOR_COMPONENT_A_BIT), // colorWriteMask
+ };
+ const vk::VkPipelineColorBlendStateCreateInfo blendParams =
+ {
+ vk::VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
+ DE_NULL,
+ (vk::VkPipelineColorBlendStateCreateFlags)0,
+ VK_FALSE, // logicOpEnable
+ vk::VK_LOGIC_OP_COPY,
+ 1u,
+ &attBlendParams0,
+ { 0.0f, 0.0f, 0.0f, 0.0f }, // blendConstants[4]
+ };
+ const vk::VkGraphicsPipelineCreateInfo pipelineParams =
+ {
+ vk::VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
+ DE_NULL,
+ (vk::VkPipelineCreateFlags)0,
+ (deUint32)DE_LENGTH_OF_ARRAY(shaderStageParams),
+ shaderStageParams,
+ &vertexInputStateParams,
+ &inputAssemblyParams,
+ (const vk::VkPipelineTessellationStateCreateInfo*)DE_NULL,
+ &viewportParams,
+ &rasterParams,
+ &multisampleParams,
+ &depthStencilParams,
+ &blendParams,
+ (const vk::VkPipelineDynamicStateCreateInfo*)DE_NULL,
+ pipelineLayout,
+ renderPass,
+ 0u, // subpass
+ DE_NULL, // basePipelineHandle
+ 0u, // basePipelineIndex
+ };
+
+ return vk::createGraphicsPipeline(vkd, device, (vk::VkPipelineCache)0, &pipelineParams);
+}
+
+TriangleRenderer::TriangleRenderer (ProtectedContext& context,
+ const vk::BinaryCollection& binaryRegistry,
+ const std::vector<vk::VkImage> swapchainImages,
+ const vk::VkFormat framebufferFormat,
+ const tcu::UVec2& renderSize)
+ : m_vkd (context.getDeviceInterface())
+ , m_swapchainImages (swapchainImages)
+ , m_renderSize (renderSize)
+ , m_renderPass (createRenderPass(m_vkd, context.getDevice(), framebufferFormat))
+ , m_pipelineLayout (createPipelineLayout(m_vkd, context.getDevice()))
+ , m_pipeline (createPipeline(m_vkd, context.getDevice(), *m_renderPass, *m_pipelineLayout, binaryRegistry, renderSize))
+ , m_vertexBuffer (makeBuffer(context,
+ PROTECTION_DISABLED,
+ context.getQueueFamilyIndex(),
+ (deUint32)(sizeof(float)*4*3),
+ vk::VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
+ vk::MemoryRequirement::HostVisible))
+{
+ m_attachmentViews.resize(swapchainImages.size());
+ m_framebuffers.resize(swapchainImages.size());
+
+ for (size_t imageNdx = 0; imageNdx < swapchainImages.size(); ++imageNdx)
+ {
+ m_attachmentViews[imageNdx] = ImageViewSp(new vk::Unique<vk::VkImageView>(createImageView(context, swapchainImages[imageNdx], framebufferFormat)));
+ m_framebuffers[imageNdx] = FramebufferSp(new vk::Unique<vk::VkFramebuffer>(createFramebuffer(context,
+ renderSize.x(),
+ renderSize.y(),
+ *m_renderPass,
+ **m_attachmentViews[imageNdx])));
+ }
+
+ // Upload vertex data
+ {
+ const tcu::Vec4 vertices[] =
+ {
+ tcu::Vec4(-0.5f, -0.5f, 0.0f, 1.0f),
+ tcu::Vec4(+0.5f, -0.5f, 0.0f, 1.0f),
+ tcu::Vec4( 0.0f, +0.5f, 0.0f, 1.0f)
+ };
+ DE_STATIC_ASSERT(sizeof(vertices) == sizeof(float)*4*3);
+
+ deMemcpy(m_vertexBuffer->getAllocation().getHostPtr(), &vertices[0], sizeof(vertices));
+ vk::flushMappedMemoryRange(m_vkd, context.getDevice(), m_vertexBuffer->getAllocation().getMemory(), m_vertexBuffer->getAllocation().getOffset(), sizeof(vertices));
+ }
+}
+
+TriangleRenderer::~TriangleRenderer (void)
+{
+}
+
+void TriangleRenderer::recordFrame (vk::VkCommandBuffer cmdBuffer,
+ deUint32 imageNdx,
+ deUint32 frameNdx) const
+{
+ const vk::VkFramebuffer curFramebuffer = **m_framebuffers[imageNdx];
+
+ beginCommandBuffer(m_vkd, cmdBuffer);
+
+ {
+ const vk::VkClearValue clearValue = vk::makeClearValueColorF32(0.125f, 0.25f, 0.75f, 1.0f);
+ const vk::VkRenderPassBeginInfo passBeginParams =
+ {
+ vk::VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
+ DE_NULL,
+ *m_renderPass,
+ curFramebuffer,
+ {
+ { 0, 0 },
+ { (deUint32)m_renderSize.x(), (deUint32)m_renderSize.y() }
+ }, // renderArea
+ 1u, // clearValueCount
+ &clearValue, // pClearValues
+ };
+ m_vkd.cmdBeginRenderPass(cmdBuffer, &passBeginParams, vk::VK_SUBPASS_CONTENTS_INLINE);
+ }
+
+ m_vkd.cmdBindPipeline(cmdBuffer, vk::VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipeline);
+
+ {
+ const vk::VkDeviceSize bindingOffset = 0;
+ m_vkd.cmdBindVertexBuffers(cmdBuffer, 0u, 1u, &m_vertexBuffer->get(), &bindingOffset);
+ }
+
+ m_vkd.cmdPushConstants(cmdBuffer, *m_pipelineLayout, vk::VK_SHADER_STAGE_VERTEX_BIT, 0u, (deUint32)sizeof(deUint32), &frameNdx);
+ m_vkd.cmdDraw(cmdBuffer, 3u, 1u, 0u, 0u);
+ m_vkd.cmdEndRenderPass(cmdBuffer);
+
+ VK_CHECK(m_vkd.endCommandBuffer(cmdBuffer));
+}
+
+void TriangleRenderer::getPrograms (vk::SourceCollections& dst)
+{
+ dst.glslSources.add("tri-vert") << glu::VertexSource(
+ "#version 310 es\n"
+ "layout(location = 0) in highp vec4 a_position;\n"
+ "layout(push_constant) uniform FrameData\n"
+ "{\n"
+ " highp uint frameNdx;\n"
+ "} frameData;\n"
+ "void main (void)\n"
+ "{\n"
+ " highp float angle = float(frameData.frameNdx) / 100.0;\n"
+ " highp float c = cos(angle);\n"
+ " highp float s = sin(angle);\n"
+ " highp mat4 t = mat4( c, -s, 0, 0,\n"
+ " s, c, 0, 0,\n"
+ " 0, 0, 1, 0,\n"
+ " 0, 0, 0, 1);\n"
+ " gl_Position = t * a_position;\n"
+ "}\n");
+ dst.glslSources.add("tri-frag") << glu::FragmentSource(
+ "#version 310 es\n"
+ "layout(location = 0) out lowp vec4 o_color;\n"
+ "void main (void) { o_color = vec4(1.0, 0.0, 1.0, 1.0); }\n");
+}
+
+typedef de::SharedPtr<vk::Unique<vk::VkCommandBuffer> > CommandBufferSp;
+typedef de::SharedPtr<vk::Unique<vk::VkFence> > FenceSp;
+typedef de::SharedPtr<vk::Unique<vk::VkSemaphore> > SemaphoreSp;
+
+std::vector<FenceSp> createFences (const vk::DeviceInterface& vkd,
+ const vk::VkDevice device,
+ size_t numFences)
+{
+ std::vector<FenceSp> fences(numFences);
+
+ for (size_t ndx = 0; ndx < numFences; ++ndx)
+ fences[ndx] = FenceSp(new vk::Unique<vk::VkFence>(createFence(vkd, device)));
+
+ return fences;
+}
+
+std::vector<SemaphoreSp> createSemaphores (const vk::DeviceInterface& vkd,
+ const vk::VkDevice device,
+ size_t numSemaphores)
+{
+ std::vector<SemaphoreSp> semaphores(numSemaphores);
+
+ for (size_t ndx = 0; ndx < numSemaphores; ++ndx)
+ semaphores[ndx] = SemaphoreSp(new vk::Unique<vk::VkSemaphore>(createSemaphore(vkd, device)));
+
+ return semaphores;
+}
+
+std::vector<CommandBufferSp> allocateCommandBuffers (const vk::DeviceInterface& vkd,
+ const vk::VkDevice device,
+ const vk::VkCommandPool commandPool,
+ const vk::VkCommandBufferLevel level,
+ const size_t numCommandBuffers)
+{
+ std::vector<CommandBufferSp> buffers (numCommandBuffers);
+
+ for (size_t ndx = 0; ndx < numCommandBuffers; ++ndx)
+ buffers[ndx] = CommandBufferSp(new vk::Unique<vk::VkCommandBuffer>(allocateCommandBuffer(vkd, device, commandPool, level)));
+
+ return buffers;
+}
+
+tcu::TestStatus basicRenderTest (Context& baseCtx, vk::wsi::Type wsiType)
+{
+ std::vector<vk::VkExtensionProperties> supportedExtensions (enumerateInstanceExtensionProperties(baseCtx.getPlatformInterface(), DE_NULL));
+ std::vector<std::string> instExts = getRequiredWsiExtensions(supportedExtensions, wsiType);
+ std::vector<std::string> devExts;
+ devExts.push_back("VK_KHR_swapchain");
+
+ const tcu::UVec2 desiredSize (256, 256);
+ const NativeObjects native (baseCtx, supportedExtensions, wsiType, tcu::just(desiredSize));
+ ProtectedContext context (baseCtx, wsiType, *native.display, *native.window, instExts, devExts);
+ vk::VkSurfaceKHR surface = context.getSurface();
+ const vk::DeviceInterface& vkd = context.getDeviceInterface();
+ const vk::VkDevice device = context.getDevice();
+ const vk::VkSwapchainCreateInfoKHR swapchainInfo = getBasicSwapchainParameters(wsiType,
+ context.getInstanceDriver(),
+ context.getPhysicalDevice(),
+ surface,
+ desiredSize,
+ 2);
+ const vk::Unique<vk::VkSwapchainKHR> swapchain (createSwapchainKHR(vkd, device, &swapchainInfo));
+ const std::vector<vk::VkImage> swapchainImages = vk::wsi::getSwapchainImages(vkd, device, *swapchain);
+
+ const TriangleRenderer renderer (context,
+ context.getBinaryCollection(),
+ swapchainImages,
+ swapchainInfo.imageFormat,
+ tcu::UVec2(swapchainInfo.imageExtent.width, swapchainInfo.imageExtent.height));
+
+ const vk::Unique<vk::VkCommandPool> commandPool (makeCommandPool(vkd, device, PROTECTION_ENABLED,
+ context.getQueueFamilyIndex()));
+
+ const size_t maxQueuedFrames = swapchainImages.size()*2;
+
+ // We need to keep hold of fences from vkAcquireNextImageKHR to actually
+ // limit number of frames we allow to be queued.
+ const std::vector<FenceSp> imageReadyFences (createFences(vkd, device, maxQueuedFrames));
+
+ // We need maxQueuedFrames+1 for imageReadySemaphores pool as we need to pass
+ // the semaphore in same time as the fence we use to meter rendering.
+ const std::vector<SemaphoreSp> imageReadySemaphores (createSemaphores(vkd, device, maxQueuedFrames+1));
+
+ // For rest we simply need maxQueuedFrames as we will wait for image
+ // from frameNdx-maxQueuedFrames to become available to us, guaranteeing that
+ // previous uses must have completed.
+ const std::vector<SemaphoreSp> renderingCompleteSemaphores (createSemaphores(vkd, device, maxQueuedFrames));
+ const std::vector<CommandBufferSp> commandBuffers (allocateCommandBuffers(vkd,
+ device,
+ *commandPool,
+ vk::VK_COMMAND_BUFFER_LEVEL_PRIMARY,
+ maxQueuedFrames));
+
+ try
+ {
+ const deUint32 numFramesToRender = 60*10;
+
+ for (deUint32 frameNdx = 0; frameNdx < numFramesToRender; ++frameNdx)
+ {
+ const vk::VkFence imageReadyFence = **imageReadyFences[frameNdx%imageReadyFences.size()];
+ const vk::VkSemaphore imageReadySemaphore = **imageReadySemaphores[frameNdx%imageReadySemaphores.size()];
+ deUint32 imageNdx = ~0u;
+
+ if (frameNdx >= maxQueuedFrames)
+ VK_CHECK(vkd.waitForFences(device, 1u, &imageReadyFence, VK_TRUE, std::numeric_limits<deUint64>::max()));
+
+ VK_CHECK(vkd.resetFences(device, 1, &imageReadyFence));
+
+ {
+ const vk::VkResult acquireResult = vkd.acquireNextImageKHR(device,
+ *swapchain,
+ std::numeric_limits<deUint64>::max(),
+ imageReadySemaphore,
+ imageReadyFence,
+ &imageNdx);
+
+ if (acquireResult == vk::VK_SUBOPTIMAL_KHR)
+ context.getTestContext().getLog() << tcu::TestLog::Message << "Got " << acquireResult << " at frame " << frameNdx << tcu::TestLog::EndMessage;
+ else
+ VK_CHECK(acquireResult);
+ }
+
+ TCU_CHECK((size_t)imageNdx < swapchainImages.size());
+
+ {
+ const vk::VkSemaphore renderingCompleteSemaphore = **renderingCompleteSemaphores[frameNdx%renderingCompleteSemaphores.size()];
+ const vk::VkCommandBuffer commandBuffer = **commandBuffers[frameNdx%commandBuffers.size()];
+ const vk::VkPipelineStageFlags waitDstStage = vk::VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+ vk::VkSubmitInfo submitInfo =
+ {
+ vk::VK_STRUCTURE_TYPE_SUBMIT_INFO,
+ DE_NULL,
+ 1u,
+ &imageReadySemaphore,
+ &waitDstStage,
+ 1u,
+ &commandBuffer,
+ 1u,
+ &renderingCompleteSemaphore
+ };
+
+ const vk::VkProtectedSubmitInfoKHR protectedInfo =
+ {
+ vk::VK_STRUCTURE_TYPE_PROTECTED_SUBMIT_INFO_KHR, // sType
+ DE_NULL, // pNext
+ VK_TRUE, // protectedSubmit
+ };
+ submitInfo.pNext = &protectedInfo;
+
+ const vk::VkPresentInfoKHR presentInfo =
+ {
+ vk::VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
+ DE_NULL,
+ 1u,
+ &renderingCompleteSemaphore,
+ 1u,
+ &*swapchain,
+ &imageNdx,
+ (vk::VkResult*)DE_NULL
+ };
+
+ renderer.recordFrame(commandBuffer, imageNdx, frameNdx);
+ VK_CHECK(vkd.queueSubmit(context.getQueue(), 1u, &submitInfo, (vk::VkFence)0));
+ VK_CHECK(vkd.queuePresentKHR(context.getQueue(), &presentInfo));
+ }
+ }
+
+ VK_CHECK(vkd.deviceWaitIdle(device));
+ }
+ catch (...)
+ {
+ // Make sure device is idle before destroying resources
+ vkd.deviceWaitIdle(device);
+ throw;
+ }
+
+ return tcu::TestStatus::pass("Rendering tests succeeded");
+}
+
+void getBasicRenderPrograms (vk::SourceCollections& dst, vk::wsi::Type)
+{
+ TriangleRenderer::getPrograms(dst);
+}
+
+void populateRenderGroup (tcu::TestCaseGroup* testGroup, vk::wsi::Type wsiType)
+{
+ addFunctionCaseWithPrograms(testGroup, "basic", "Basic Rendering Test", getBasicRenderPrograms, basicRenderTest, wsiType);
+}
+
+void createSwapchainTests (tcu::TestCaseGroup* testGroup, vk::wsi::Type wsiType)
+{
+ addTestGroup(testGroup, "create", "Create VkSwapchain with various parameters", populateSwapchainGroup, GroupParameters(wsiType, createSwapchainTest));
+ addTestGroup(testGroup, "render", "Rendering Tests", populateRenderGroup, wsiType);
+}
+
+void createTypeSpecificTests (tcu::TestCaseGroup* testGroup, vk::wsi::Type wsiType)
+{
+ addTestGroup(testGroup, "swapchain", "VkSwapchain Tests", createSwapchainTests, wsiType);
+}
+
+} // anonymous
+
+tcu::TestCaseGroup* createSwapchainTests (tcu::TestContext& testCtx)
+{
+ de::MovePtr<tcu::TestCaseGroup> wsiTestGroup (new tcu::TestCaseGroup(testCtx, "wsi", "WSI Tests"));
+
+ for (int typeNdx = 0; typeNdx < vk::wsi::TYPE_LAST; ++typeNdx)
+ {
+ const vk::wsi::Type wsiType = (vk::wsi::Type)typeNdx;
+
+ addTestGroup(&*wsiTestGroup, getName(wsiType), "", createTypeSpecificTests, wsiType);
+ }
+
+ return wsiTestGroup.release();
+}
+
+} // wsi
+} // vkt