/*------------------------------------------------------------------------ * Vulkan Conformance Tests * ------------------------ * * Copyright (c) 2019 The Khronos Group 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 VK_EXT_display_control tests *//*--------------------------------------------------------------------*/ #include "vkRefUtil.hpp" #include "vkWsiPlatform.hpp" #include "vkWsiUtil.hpp" #include "vkQueryUtil.hpp" #include "vkDeviceUtil.hpp" #include "vkPlatform.hpp" #include "vkTypeUtil.hpp" #include "vkPrograms.hpp" #include "vkCmdUtil.hpp" #include "vkWsiUtil.hpp" #include "vkCmdUtil.hpp" #include "vkObjUtil.hpp" #include "vktWsiDisplayControlTests.hpp" #include "vktTestCaseUtil.hpp" #include "vktTestGroupUtil.hpp" #include "vktCustomInstancesDevices.hpp" #include "tcuPlatform.hpp" #include "tcuResultCollector.hpp" #include "tcuTestLog.hpp" #include "tcuCommandLine.hpp" #include "deClock.h" #include #include using std::vector; using std::string; using tcu::Maybe; using tcu::UVec2; using tcu::TestLog; namespace vkt { namespace wsi { namespace { using namespace vk; using namespace vk::wsi; typedef vector Extensions; CustomInstance createInstance (Context& context) { vector extensions = { "VK_KHR_surface", "VK_KHR_display", "VK_EXT_display_surface_counter", }; return vkt::createCustomInstanceWithExtensions(context, extensions); } deUint32 chooseQueueFamilyIndex (const InstanceInterface& vki, VkPhysicalDevice physicalDevice, VkSurfaceKHR surface) { deUint32 numTotalFamilyIndices; vki.getPhysicalDeviceQueueFamilyProperties(physicalDevice, &numTotalFamilyIndices, DE_NULL); for (deUint32 queueFamilyNdx = 0; queueFamilyNdx < numTotalFamilyIndices; ++queueFamilyNdx) { if (wsi::getPhysicalDeviceSurfaceSupport(vki, physicalDevice, queueFamilyNdx, surface) == VK_TRUE) return queueFamilyNdx; } TCU_THROW(NotSupportedError, "Device doesn't support presentation"); return 0; } Move createDevice (const vk::Platform& platform, const PlatformInterface& vkp, const VkInstance instance, const InstanceInterface& vki, VkPhysicalDevice physicalDevice, const Extensions& supportedExtensions, const deUint32 queueFamilyIndex, bool validationEnabled, const VkAllocationCallbacks* pAllocator = DE_NULL) { const float queuePriorities[] = { 1.0f }; bool displayAvailable = true; const VkDeviceQueueCreateInfo queueInfos[] = { { VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, DE_NULL, (VkDeviceQueueCreateFlags)0, queueFamilyIndex, DE_LENGTH_OF_ARRAY(queuePriorities), &queuePriorities[0] } }; VkPhysicalDeviceFeatures features; deMemset(&features, 0, sizeof(features)); const char* extensions[] = { "VK_KHR_swapchain", "VK_EXT_display_control" }; const VkDeviceCreateInfo deviceParams = { VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, DE_NULL, (VkDeviceCreateFlags)0, DE_LENGTH_OF_ARRAY(queueInfos), &queueInfos[0], 0u, DE_NULL, DE_LENGTH_OF_ARRAY(extensions), &extensions[0], &features }; for (auto ext: extensions) { if (!isExtensionSupported(supportedExtensions, RequiredExtension(ext))) TCU_THROW(NotSupportedError, (string(ext) + " is not supported").c_str()); } for (int typeNdx = 0; typeNdx < vk::wsi::TYPE_LAST; ++typeNdx) { vk::wsi::Type wsiType = (vk::wsi::Type)typeNdx; if (platform.hasDisplay(wsiType)) { displayAvailable = false; break; } } if (!displayAvailable) TCU_THROW(NotSupportedError, "Display is unavailable as windowing system has access"); return createCustomDevice(validationEnabled, vkp, instance, vki, physicalDevice, &deviceParams, pAllocator); } VkDisplayKHR getDisplayAndDisplayPlane(const InstanceInterface& vki, VkPhysicalDevice physicalDevice, deUint32 *pPlaneIndex) { deUint32 countDisplays = 0; VkResult result = vki.getPhysicalDeviceDisplayPropertiesKHR(physicalDevice, &countDisplays, DE_NULL); if (result != VK_SUCCESS) TCU_THROW(NotSupportedError, "vkGetPhysicalDeviceDisplayPropertiesKHR failed"); if (countDisplays == 0) TCU_THROW(NotSupportedError, "No displays available"); deUint32 countDisplayPlanes = 0; result = vki.getPhysicalDeviceDisplayPlanePropertiesKHR(physicalDevice, &countDisplayPlanes, DE_NULL); if (result != VK_SUCCESS || !countDisplayPlanes) TCU_FAIL("GetPhysicalDeviceDisplayPlanePropertiesKHR failed"); for (deUint32 p = 0; p < countDisplayPlanes; p++) { deUint32 count = 0u; result = vki.getDisplayPlaneSupportedDisplaysKHR(physicalDevice, p, &count, DE_NULL); if (result != VK_SUCCESS) TCU_FAIL("GetDisplayPlaneSupportedDisplaysKHR failed"); // No displays that can make use of this plane are available. if (!count) continue; std::vector displays(count); result = vki.getDisplayPlaneSupportedDisplaysKHR(physicalDevice, p, &count, &displays[0]); if (result != VK_SUCCESS) TCU_FAIL("GetDisplayPlaneSupportedDisplaysKHR failed"); // return first plane with an available display *pPlaneIndex = p; return displays[0]; } TCU_FAIL("No intersection between displays and display planes"); // Unreachable. return DE_NULL; } VkSurfaceKHR createSurface(const InstanceInterface& vki, VkInstance instance, VkPhysicalDevice physicalDevice, VkDisplayKHR display, deUint32 planeIndex) { // get number of display modes for this display deUint32 displayModesCount = 0; VkResult result = vki.getDisplayModePropertiesKHR(physicalDevice, display, &displayModesCount, DE_NULL); if (result != VK_SUCCESS) TCU_FAIL("GetDisplayModePropertiesKHR failed"); // get first display mode of this display std::vector modeProperties(displayModesCount); result = vki.getDisplayModePropertiesKHR(physicalDevice, display, &displayModesCount, &modeProperties[0]); if (result != VK_SUCCESS) TCU_FAIL("GetDisplayModePropertiesKHR failed"); VkDisplayModeKHR displayMode = modeProperties[0].displayMode; // get capabielieties for first plane of this display VkDisplayPlaneCapabilitiesKHR planeCapabilities; result = vki.getDisplayPlaneCapabilitiesKHR(physicalDevice, displayMode, planeIndex, &planeCapabilities); if (result != VK_SUCCESS) TCU_FAIL("GetDisplayPlaneCapabilitiesKHR failed"); // get plane properties count deUint32 planePropertiesCount = 0; result = vki.getPhysicalDeviceDisplayPlanePropertiesKHR(physicalDevice, &planePropertiesCount, DE_NULL); if (result != VK_SUCCESS || !planePropertiesCount) TCU_FAIL("GetPhysicalDeviceDisplayPlanePropertiesKHR failed"); // get plane properties std::vector planeProperties(planePropertiesCount); result = vki.getPhysicalDeviceDisplayPlanePropertiesKHR(physicalDevice, &planePropertiesCount, &planeProperties[0]); if (result != VK_SUCCESS) TCU_FAIL("GetPhysicalDeviceDisplayPlanePropertiesKHR failed"); // define surface create info const VkDisplaySurfaceCreateInfoKHR createInfo = { VK_STRUCTURE_TYPE_DISPLAY_SURFACE_CREATE_INFO_KHR, // VkStructureType sType DE_NULL, // const void* pNext 0, // VkDisplaySurfaceCreateFlagsKHR flags displayMode, // VkDisplayModeKHR displayMode planeIndex, // uint32_t planeIndex planeProperties[planeIndex].currentStackIndex, // uint32_t planeStackIndex VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR, // VkSurfaceTransformFlagBitsKHR transform 1.0f, // float globalAlpha VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR, // VkDisplayPlaneAlphaFlagBitsKHR alphaMode { // VkExtent2D imageExtent planeCapabilities.minDstExtent.width, planeCapabilities.minDstExtent.height } }; VkSurfaceKHR surface = DE_NULL; result = vki.createDisplayPlaneSurfaceKHR(instance, &createInfo, DE_NULL, &surface); if (result != VK_SUCCESS) TCU_FAIL("CreateDisplayPlaneSurfaceKHR failed"); if (surface == DE_NULL) TCU_FAIL("Invalid surface handle returned"); return surface; } void initSemaphores (const DeviceInterface& vkd, VkDevice device, std::vector& semaphores) { for (VkSemaphore& semaphore : semaphores) semaphore = createSemaphore(vkd, device).disown(); } void deinitSemaphores (const DeviceInterface& vkd, VkDevice device, std::vector& semaphores) { for (VkSemaphore& semaphore : semaphores) { if (semaphore == (VkSemaphore)0) continue; vkd.destroySemaphore(device, semaphore, DE_NULL); semaphore = (VkSemaphore)0; } semaphores.clear(); } void initFences (const DeviceInterface& vkd, VkDevice device, std::vector& fences) { for (VkFence& fence : fences) fence = createFence(vkd, device).disown(); } void deinitFences (const DeviceInterface& vkd, VkDevice device, std::vector& fences) { for (VkFence& fence : fences) { if (fence == (VkFence)0) continue; vkd.destroyFence(device, fence, DE_NULL); fence = (VkFence)0; } fences.clear(); } Move createCommandBuffer (const DeviceInterface& vkd, VkDevice device, VkCommandPool commandPool, VkRenderPass renderPass, VkImage image, VkFramebuffer framebuffer, VkPipeline pipeline, deUint32 imageWidth, deUint32 imageHeight) { const VkCommandBufferAllocateInfo allocateInfo = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, DE_NULL, commandPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, 1 }; VkImageMemoryBarrier imageBarrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType; DE_NULL, // const void* pNext; 0u, // VkAccessFlags srcAccessMask; VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, // VkAccessFlags dstAccessMask; VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout oldLayout; VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // VkImageLayout newLayout; VK_QUEUE_FAMILY_IGNORED, // deUint32 srcQueueFamilyIndex; VK_QUEUE_FAMILY_IGNORED, // deUint32 dstQueueFamilyIndex; image, // VkImage image; { // VkImageSubresourceRange subresourceRange; VK_IMAGE_ASPECT_COLOR_BIT, // VkImageAspectFlags aspectMask; 0u, // deUint32 baseMipLevel; 1u, // deUint32 mipLevels; 0u, // deUint32 baseArraySlice; 1u // deUint32 arraySize; } }; Move commandBuffer (allocateCommandBuffer(vkd, device, &allocateInfo)); beginCommandBuffer(vkd, *commandBuffer, 0u); vkd.cmdPipelineBarrier(*commandBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, (VkDependencyFlags)0, 0, (const VkMemoryBarrier*)DE_NULL, 0, (const VkBufferMemoryBarrier*)DE_NULL, 1, &imageBarrier); beginRenderPass(vkd, *commandBuffer, renderPass, framebuffer, makeRect2D(0, 0, imageWidth, imageHeight), tcu::Vec4(0.25f, 0.5f, 0.75f, 1.0f)); vkd.cmdBindPipeline(*commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); vkd.cmdDraw(*commandBuffer, 6u, 1u, 0u, 0u); endRenderPass(vkd, *commandBuffer); endCommandBuffer(vkd, *commandBuffer); return commandBuffer; } void deinitCommandBuffers (const DeviceInterface& vkd, VkDevice device, VkCommandPool commandPool, std::vector& commandBuffers) { for (size_t ndx = 0; ndx < commandBuffers.size(); ndx++) { if (commandBuffers[ndx] != (VkCommandBuffer)0) vkd.freeCommandBuffers(device, commandPool, 1u, &commandBuffers[ndx]); commandBuffers[ndx] = (VkCommandBuffer)0; } commandBuffers.clear(); } Move createCommandPool (const DeviceInterface& vkd, VkDevice device, deUint32 queueFamilyIndex) { const VkCommandPoolCreateInfo createInfo = { VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, DE_NULL, 0u, queueFamilyIndex }; return createCommandPool(vkd, device, &createInfo); } void initFramebuffers (const DeviceInterface& vkd, VkDevice device, VkRenderPass renderPass, std::vector imageViews, deUint32 width, deUint32 height, std::vector& framebuffers) { DE_ASSERT(framebuffers.size() == imageViews.size()); for (size_t ndx = 0; ndx < framebuffers.size(); ndx++) { const VkFramebufferCreateInfo createInfo = { VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, DE_NULL, 0u, renderPass, 1u, &imageViews[ndx], width, height, 1u }; framebuffers[ndx] = createFramebuffer(vkd, device, &createInfo).disown(); } } void deinitFramebuffers (const DeviceInterface& vkd, VkDevice device, std::vector& framebuffers) { for (size_t ndx = 0; ndx < framebuffers.size(); ndx++) { if (framebuffers[ndx] != (VkFramebuffer)0) vkd.destroyFramebuffer(device, framebuffers[ndx], DE_NULL); framebuffers[ndx] = (VkFramebuffer)0; } framebuffers.clear(); } Move createImageView (const DeviceInterface& vkd, VkDevice device, VkImage image, VkFormat format) { const VkImageViewCreateInfo createInfo = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, DE_NULL, 0u, image, VK_IMAGE_VIEW_TYPE_2D, format, makeComponentMappingRGBA(), { VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u } }; return createImageView(vkd, device, &createInfo, DE_NULL); } void initImageViews (const DeviceInterface& vkd, VkDevice device, const std::vector& images, VkFormat format, std::vector& imageViews) { DE_ASSERT(images.size() == imageViews.size()); for (size_t ndx = 0; ndx < imageViews.size(); ndx++) imageViews[ndx] = createImageView(vkd, device, images[ndx], format).disown(); } void deinitImageViews (const DeviceInterface& vkd, VkDevice device, std::vector& imageViews) { for (size_t ndx = 0; ndx < imageViews.size(); ndx++) { if (imageViews[ndx] != (VkImageView)0) vkd.destroyImageView(device, imageViews[ndx], DE_NULL); imageViews[ndx] = (VkImageView)0; } imageViews.clear(); } Move createPipeline (const DeviceInterface& vkd, VkDevice device, VkRenderPass renderPass, VkPipelineLayout layout, VkShaderModule vertexShaderModule, VkShaderModule fragmentShaderModule, deUint32 width, deUint32 height) { const VkPipelineVertexInputStateCreateInfo vertexInputState = { VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, DE_NULL, 0u, 0u, DE_NULL, 0u, DE_NULL }; const std::vector viewports (1, makeViewport(tcu::UVec2(width, height))); const std::vector scissors (1, makeRect2D(tcu::UVec2(width, height))); return makeGraphicsPipeline(vkd, // const DeviceInterface& vk device, // const VkDevice device layout, // 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 renderPass, // const VkRenderPass renderPass viewports, // const std::vector& viewports scissors, // const std::vector& scissors VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, // const VkPrimitiveTopology topology 0u, // const deUint32 subpass 0u, // const deUint32 patchControlPoints &vertexInputState); // const VkPipelineVertexInputStateCreateInfo* vertexInputStateCreateInfo } Move createPipelineLayout (const DeviceInterface& vkd, VkDevice device) { const VkPipelineLayoutCreateInfo createInfo = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, DE_NULL, 0u, 0u, DE_NULL, 0u, DE_NULL, }; return createPipelineLayout(vkd, device, &createInfo); } VkSwapchainCounterCreateInfoEXT createSwapchainCounterConfig() { const VkSwapchainCounterCreateInfoEXT swapchainCounterConfig = { VK_STRUCTURE_TYPE_SWAPCHAIN_COUNTER_CREATE_INFO_EXT, DE_NULL, VK_SURFACE_COUNTER_VBLANK_EXT }; return swapchainCounterConfig; } VkSwapchainCreateInfoKHR createSwapchainConfig (VkSurfaceKHR surface, deUint32 queueFamilyIndex, const VkSurfaceCapabilities2EXT& properties, const vector& formats, const vector& presentModes, VkPresentModeKHR presentMode, VkSwapchainCounterCreateInfoEXT *swapchainCounterInfo) { if ((properties.supportedSurfaceCounters & VK_SURFACE_COUNTER_VBLANK_EXT) == 0) TCU_THROW(NotSupportedError, "vblank counter not supported"); const deUint32 imageLayers = 1u; const VkImageUsageFlags imageUsage = properties.supportedUsageFlags; const VkBool32 clipped = VK_FALSE; const deUint32 imageWidth = (properties.currentExtent.width != 0xFFFFFFFFu) ? properties.currentExtent.width : de::min(1024u, properties.minImageExtent.width + ((properties.maxImageExtent.width - properties.minImageExtent.width) / 2)); const deUint32 imageHeight = (properties.currentExtent.height != 0xFFFFFFFFu) ? properties.currentExtent.height : de::min(1024u, properties.minImageExtent.height + ((properties.maxImageExtent.height - properties.minImageExtent.height) / 2)); const VkExtent2D imageSize = { imageWidth, imageHeight }; if (std::find(presentModes.begin(), presentModes.end(), presentMode) == presentModes.end()) TCU_THROW(NotSupportedError, "Present mode not supported"); // Pick the first supported transform, alpha, and format: VkSurfaceTransformFlagsKHR transform; for (transform = 1u; transform <= properties.supportedTransforms; transform = transform << 1u) { if ((properties.supportedTransforms & transform) != 0) break; } VkCompositeAlphaFlagsKHR alpha; for (alpha = 1u; alpha <= properties.supportedCompositeAlpha; alpha = alpha << 1u) { if ((alpha & properties.supportedCompositeAlpha) != 0) break; } { const VkSurfaceTransformFlagBitsKHR preTransform = (VkSurfaceTransformFlagBitsKHR)transform; const VkCompositeAlphaFlagBitsKHR compositeAlpha = (VkCompositeAlphaFlagBitsKHR)alpha; const VkFormat imageFormat = formats[0].format; const VkColorSpaceKHR imageColorSpace = formats[0].colorSpace; const VkSwapchainCreateInfoKHR createInfo = { VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, swapchainCounterInfo, 0u, surface, properties.minImageCount, imageFormat, imageColorSpace, imageSize, imageLayers, imageUsage, VK_SHARING_MODE_EXCLUSIVE, 1u, &queueFamilyIndex, preTransform, compositeAlpha, presentMode, clipped, (VkSwapchainKHR)0 }; return createInfo; } } class SwapchainCounterTestInstance : public TestInstance { public: SwapchainCounterTestInstance (Context& context); ~SwapchainCounterTestInstance (void); tcu::TestStatus iterate (void); private: void initSwapchainResources (void); void deinitSwapchainResources (void); void render (void); private: const PlatformInterface& m_vkp; const CustomInstance m_instance; const InstanceDriver& m_vki; const VkPhysicalDevice m_physicalDevice; deUint32 m_planeIndex; const VkDisplayKHR m_display; const VkSurfaceKHR m_surface; const deUint32 m_queueFamilyIndex; const Extensions m_deviceExtensions; const Unique m_device; const DeviceDriver m_vkd; const VkQueue m_queue; const Unique m_commandPool; const Unique m_vertexShaderModule; const Unique m_fragmentShaderModule; const Unique m_pipelineLayout; const VkSurfaceCapabilities2EXT m_surfaceProperties; const vector m_surfaceFormats; const vector m_presentModes; tcu::ResultCollector m_resultCollector; Move m_swapchain; std::vector m_swapchainImages; Move m_renderPass; Move m_pipeline; std::vector m_swapchainImageViews; std::vector m_framebuffers; std::vector m_commandBuffers; std::vector m_acquireSemaphores; std::vector m_renderSemaphores; std::vector m_fences; VkSwapchainCounterCreateInfoEXT m_swapchainCounterConfig; VkSwapchainCreateInfoKHR m_swapchainConfig; const size_t m_frameCount; size_t m_frameNdx; const size_t m_maxOutOfDateCount; size_t m_outOfDateCount; }; SwapchainCounterTestInstance::SwapchainCounterTestInstance (Context& context) : TestInstance (context) , m_vkp (context.getPlatformInterface()) , m_instance (createInstance(context)) , m_vki (m_instance.getDriver()) , m_physicalDevice (chooseDevice(m_vki, m_instance, context.getTestContext().getCommandLine())) , m_planeIndex (0) , m_display (getDisplayAndDisplayPlane(m_vki, m_physicalDevice, &m_planeIndex)) , m_surface (createSurface(m_vki, m_instance, m_physicalDevice, m_display, m_planeIndex)) , m_queueFamilyIndex (chooseQueueFamilyIndex(m_vki, m_physicalDevice, m_surface)) , m_deviceExtensions (enumerateDeviceExtensionProperties(m_vki, m_physicalDevice, DE_NULL)) , m_device (createDevice(context.getTestContext().getPlatform().getVulkanPlatform(), m_vkp, m_instance, m_vki, m_physicalDevice, m_deviceExtensions, m_queueFamilyIndex, context.getTestContext().getCommandLine().isValidationEnabled())) , m_vkd (m_vkp, m_instance, *m_device) , m_queue (getDeviceQueue(m_vkd, *m_device, m_queueFamilyIndex, 0u)) , m_commandPool (createCommandPool(m_vkd, *m_device, m_queueFamilyIndex)) , m_vertexShaderModule (createShaderModule(m_vkd, *m_device, context.getBinaryCollection().get("quad-vert"), 0u)) , m_fragmentShaderModule (createShaderModule(m_vkd, *m_device, context.getBinaryCollection().get("quad-frag"), 0u)) , m_pipelineLayout (createPipelineLayout(m_vkd, *m_device)) , m_surfaceProperties (wsi::getPhysicalDeviceSurfaceCapabilities2EXT(m_vki, m_physicalDevice, m_surface)) , m_surfaceFormats (wsi::getPhysicalDeviceSurfaceFormats(m_vki, m_physicalDevice, m_surface)) , m_presentModes (wsi::getPhysicalDeviceSurfacePresentModes(m_vki, m_physicalDevice, m_surface)) , m_swapchainCounterConfig (createSwapchainCounterConfig()) , m_swapchainConfig (createSwapchainConfig(m_surface, m_queueFamilyIndex, m_surfaceProperties, m_surfaceFormats, m_presentModes, VK_PRESENT_MODE_FIFO_KHR, &m_swapchainCounterConfig)) , m_frameCount (20u) , m_frameNdx (0u) , m_maxOutOfDateCount (10u) , m_outOfDateCount (0u) { } SwapchainCounterTestInstance::~SwapchainCounterTestInstance (void) { deinitSwapchainResources(); m_vki.destroySurfaceKHR(m_instance, m_surface, DE_NULL); } void SwapchainCounterTestInstance::initSwapchainResources (void) { const deUint32 imageWidth = m_swapchainConfig.imageExtent.width; const deUint32 imageHeight = m_swapchainConfig.imageExtent.height; const VkFormat imageFormat = m_swapchainConfig.imageFormat; m_swapchain = createSwapchainKHR(m_vkd, *m_device, &m_swapchainConfig); m_swapchainImages = wsi::getSwapchainImages(m_vkd, *m_device, *m_swapchain); m_renderPass = makeRenderPass(m_vkd, *m_device, imageFormat, VK_FORMAT_UNDEFINED, VK_ATTACHMENT_LOAD_OP_LOAD, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); m_pipeline = createPipeline(m_vkd, *m_device, *m_renderPass, *m_pipelineLayout, *m_vertexShaderModule, *m_fragmentShaderModule, imageWidth, imageHeight); const size_t swapchainImagesCount = m_swapchainImages.size(); const size_t fenceCount = swapchainImagesCount * 2; m_swapchainImageViews = std::vector(swapchainImagesCount, (VkImageView)0); m_framebuffers = std::vector(swapchainImagesCount, (VkFramebuffer)0); m_acquireSemaphores = std::vector(swapchainImagesCount+1, (VkSemaphore)0); m_renderSemaphores = std::vector(swapchainImagesCount+1, (VkSemaphore)0); m_fences = std::vector(fenceCount, (VkFence)0); m_commandBuffers = std::vector(fenceCount, (VkCommandBuffer)0); initImageViews(m_vkd, *m_device, m_swapchainImages, imageFormat, m_swapchainImageViews); initFramebuffers(m_vkd, *m_device, *m_renderPass, m_swapchainImageViews, imageWidth, imageHeight, m_framebuffers); initSemaphores(m_vkd, *m_device, m_acquireSemaphores); initSemaphores(m_vkd, *m_device, m_renderSemaphores); initFences(m_vkd, *m_device, m_fences); } void SwapchainCounterTestInstance::deinitSwapchainResources (void) { VK_CHECK(m_vkd.queueWaitIdle(m_queue)); deinitSemaphores(m_vkd, *m_device, m_acquireSemaphores); deinitSemaphores(m_vkd, *m_device, m_renderSemaphores); deinitFences(m_vkd, *m_device, m_fences); deinitCommandBuffers(m_vkd, *m_device, *m_commandPool, m_commandBuffers); deinitFramebuffers(m_vkd, *m_device, m_framebuffers); deinitImageViews(m_vkd, *m_device, m_swapchainImageViews); m_swapchainImages.clear(); m_swapchain = Move(); m_renderPass = Move(); m_pipeline = Move(); } void SwapchainCounterTestInstance::render (void) { const deUint64 foreverNs = ~0x0ull; VkCommandBuffer& commandBuffer = m_commandBuffers[m_frameNdx % m_commandBuffers.size()]; const VkFence fence = m_fences[m_frameNdx % m_fences.size()]; const deUint32 width = m_swapchainConfig.imageExtent.width; const deUint32 height = m_swapchainConfig.imageExtent.height; if (m_frameNdx >= m_fences.size()) VK_CHECK(m_vkd.waitForFences(*m_device, 1u, &fence, VK_TRUE, foreverNs)); VK_CHECK(m_vkd.resetFences(*m_device, 1u, &fence)); VkSemaphore currentAcquireSemaphore = m_acquireSemaphores[m_frameNdx % m_acquireSemaphores.size()]; VkSemaphore currentRenderSemaphore = m_renderSemaphores[m_frameNdx % m_renderSemaphores.size()]; // Acquire next image deUint32 imageIndex; VK_CHECK(m_vkd.acquireNextImageKHR(*m_device, *m_swapchain, foreverNs, currentAcquireSemaphore, (VkFence)0, &imageIndex)); // Create command buffer commandBuffer = createCommandBuffer(m_vkd, *m_device, *m_commandPool, *m_renderPass, m_swapchainImages[imageIndex], m_framebuffers[imageIndex], *m_pipeline, width, height).disown(); // Submit command buffer { const VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; const VkSubmitInfo submitInfo = { VK_STRUCTURE_TYPE_SUBMIT_INFO, DE_NULL, 1u, ¤tAcquireSemaphore, &dstStageMask, 1u, &commandBuffer, 1u, ¤tRenderSemaphore }; VK_CHECK(m_vkd.queueSubmit(m_queue, 1u, &submitInfo, fence)); } VkResult result; const VkPresentInfoKHR presentInfo = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, DE_NULL, 1u, ¤tRenderSemaphore, 1u, &*m_swapchain, &imageIndex, &result }; VK_CHECK_WSI(m_vkd.queuePresentKHR(m_queue, &presentInfo)); VK_CHECK_WSI(result); // verify counter on last frame - we know that we must have presented as meny frames // as we rendered minus the number of images in swapchain - that may not have been presented yet if (m_frameNdx >= m_frameCount) { deUint64 counter = 0; m_vkd.getSwapchainCounterEXT(*m_device, *m_swapchain, VK_SURFACE_COUNTER_VBLANK_EXT, &counter); if ((counter < (m_frameCount - m_swapchainImages.size())) || (counter > m_frameCount)) { deinitSwapchainResources(); m_resultCollector.fail("Invalid surface counter value"); } } } tcu::TestStatus SwapchainCounterTestInstance::iterate (void) { try { // Initialize swapchain specific resources if (m_frameNdx == 0) initSwapchainResources(); // Render frame render(); } catch (const Error& error) { if (error.getError() == VK_ERROR_OUT_OF_DATE_KHR) { if (m_outOfDateCount < m_maxOutOfDateCount) { m_context.getTestContext().getLog() << TestLog::Message << "Frame " << m_frameNdx << ": Swapchain out of date. Recreating resources." << TestLog::EndMessage; deinitSwapchainResources(); m_outOfDateCount++; m_frameNdx = 0; return tcu::TestStatus::incomplete(); } m_context.getTestContext().getLog() << TestLog::Message << "Frame " << m_frameNdx << ": Swapchain out of date." << TestLog::EndMessage; return tcu::TestStatus::fail("Received too many VK_ERROR_OUT_OF_DATE_KHR errors."); } deinitSwapchainResources(); return tcu::TestStatus::fail(error.what()); } m_frameNdx++; if (m_frameNdx < m_frameCount) return tcu::TestStatus::incomplete(); deinitSwapchainResources(); return tcu::TestStatus(m_resultCollector.getResult(), m_resultCollector.getMessage()); } class SwapchainCounterTestCase : public TestCase { public: SwapchainCounterTestCase(tcu::TestContext& context, const char* name); ~SwapchainCounterTestCase() = default; void initPrograms(SourceCollections& programCollection) const; virtual TestInstance* createInstance(Context& context) const; virtual void checkSupport(Context& context) const; }; SwapchainCounterTestCase::SwapchainCounterTestCase(tcu::TestContext& context, const char* name) : vkt::TestCase(context, name, name) { } void SwapchainCounterTestCase::initPrograms(SourceCollections& dst) const { dst.glslSources.add("quad-vert") << glu::VertexSource( "#version 450\n" "out gl_PerVertex {\n" " vec4 gl_Position;\n" "};\n" "highp float;\n" "void main (void) {\n" " gl_Position = vec4(((gl_VertexIndex + 2) / 3) % 2 == 0 ? -1.0 : 1.0,\n" " ((gl_VertexIndex + 1) / 3) % 2 == 0 ? -1.0 : 1.0, 0.0, 1.0);\n" "}\n"); dst.glslSources.add("quad-frag") << glu::FragmentSource( "#version 450\n" "layout(location = 0) out highp vec4 o_color;\n" "void main (void)\n" "{\n" " o_color = vec4(1.0, 0.5, 0.0, 1.0);\n" "}\n"); } TestInstance* SwapchainCounterTestCase::createInstance(Context& context) const { return new SwapchainCounterTestInstance(context); } void SwapchainCounterTestCase::checkSupport(Context& context) const { context.requireInstanceFunctionality("VK_KHR_display"); context.requireDeviceFunctionality("VK_EXT_display_control"); } void getDisplays(Context& context, std::vector& availableDisplays) { // get number of displays deUint32 countReported = 0u; VkPhysicalDevice physicalDevice = context.getPhysicalDevice(); const InstanceInterface& vki = context.getInstanceInterface(); VkResult result = vki.getPhysicalDeviceDisplayPropertiesKHR(physicalDevice, &countReported, DE_NULL); if (result != VK_SUCCESS) TCU_THROW(NotSupportedError, "vkGetPhysicalDeviceDisplayPropertiesKHR failed"); if (countReported == 0) TCU_THROW(NotSupportedError, "No displays available"); // get display properties std::vector displaysProperties(countReported); result = vki.getPhysicalDeviceDisplayPropertiesKHR(physicalDevice, &countReported, &displaysProperties[0]); if (result != VK_SUCCESS) TCU_THROW(NotSupportedError, "vkGetPhysicalDeviceDisplayPropertiesKHR failed"); availableDisplays.clear(); for (const auto& dp : displaysProperties) availableDisplays.push_back(dp.display); } tcu::TestStatus testDisplayPowerControl(Context& context) { // make sure VK_EXT_display_control is available context.requireDeviceFunctionality("VK_EXT_display_control"); // get all connected displays std::vector availableDisplays; getDisplays(context, availableDisplays); struct PowerStateData { VkDisplayPowerStateEXT state; deUint32 waitMs; }; vector powerStateDataVect = { { VK_DISPLAY_POWER_STATE_ON_EXT, 1000 }, { VK_DISPLAY_POWER_STATE_SUSPEND_EXT, 1000 }, { VK_DISPLAY_POWER_STATE_OFF_EXT, 1000 }, { VK_DISPLAY_POWER_STATE_ON_EXT, 1000 }, }; // iterate over all displays VkDevice device = context.getDevice(); const vk::DeviceInterface& vkd = context.getDeviceInterface(); for (const auto& display : availableDisplays) { // iterate over tested sequence of power states for (const auto& psd : powerStateDataVect) { VkDisplayPowerInfoEXT displayPowerInfo = { VK_STRUCTURE_TYPE_DISPLAY_POWER_INFO_EXT, DE_NULL, psd.state }; VkResult result = vkd.displayPowerControlEXT(device, display, &displayPowerInfo); if (result != VK_SUCCESS) tcu::TestStatus::fail(std::string("vkDisplayPowerControlEXT returned invalid result for ") + de::toString(psd.state)); deSleep(psd.waitMs); } } return tcu::TestStatus::pass("pass"); } tcu::TestStatus testDisplayEvent(Context& context) { // make sure VK_EXT_display_control is available context.requireDeviceFunctionality("VK_EXT_display_control"); // get all connected displays std::vector availableDisplays; getDisplays(context, availableDisplays); VkDevice device = context.getDevice(); const DeviceInterface& vkd = context.getDeviceInterface(); std::vector fences = std::vector(availableDisplays.size(), (VkFence)0); // iterate over all displays for (size_t i = 0 ; i < availableDisplays.size() ; ++i) { VkDisplayEventInfoEXT displayEventInfo = { VK_STRUCTURE_TYPE_DISPLAY_EVENT_INFO_EXT, DE_NULL, VK_DISPLAY_EVENT_TYPE_FIRST_PIXEL_OUT_EXT }; VkFence& fence = fences[i]; VkDisplayKHR& display = availableDisplays[i]; VkResult result = vkd.registerDisplayEventEXT(device, display, &displayEventInfo, DE_NULL, &fence); if (result != VK_SUCCESS) tcu::TestStatus::fail(std::string("vkRegisterDisplayEventEXT returned invalid result")); } // deinit fence deinitFences (vkd, device, fences); return tcu::TestStatus::pass("pass"); } tcu::TestStatus testDeviceEvent(Context& context) { // make sure VK_EXT_display_control is available context.requireDeviceFunctionality("VK_EXT_display_control"); VkDevice device = context.getDevice(); const DeviceInterface& vkd = context.getDeviceInterface(); std::vector fences = std::vector(1, (VkFence)0); vk::VkDeviceEventInfoEXT deviceEventInfo = { VK_STRUCTURE_TYPE_DEVICE_EVENT_INFO_EXT, DE_NULL, VK_DEVICE_EVENT_TYPE_DISPLAY_HOTPLUG_EXT }; VkResult result = vkd.registerDeviceEventEXT(device, &deviceEventInfo, DE_NULL, &fences[0]); if (result != VK_SUCCESS) tcu::TestStatus::fail(std::string("vkRegisterDeviceEventEXT returned invalid result")); // deinit fence deinitFences(vkd, device, fences); return tcu::TestStatus::pass("pass"); } } // anonymous void createDisplayControlTests (tcu::TestCaseGroup* testGroup) { testGroup->addChild(new SwapchainCounterTestCase(testGroup->getTestContext(), "swapchain_counter")); addFunctionCase(testGroup, "display_power_control", "Test display power control", testDisplayPowerControl); addFunctionCase(testGroup, "register_display_event", "Test register display event", testDisplayEvent); addFunctionCase(testGroup, "register_device_event", "Test register device event", testDeviceEvent); } } // wsi } // vkt