/*------------------------------------------------------------------------- * Vulkan Conformance Tests * ------------------------ * * Copyright (c) 2017 Google Inc. * Copyright (c) 2020 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. * *//*! * \file * \brief VK_EXT_full_screen_exclusive extension Tests *//*--------------------------------------------------------------------*/ #include "vktWsiFullScreenExclusiveTests.hpp" #include "vktTestCaseUtil.hpp" #include "vktCustomInstancesDevices.hpp" #include "vkRefUtil.hpp" #include "vkQueryUtil.hpp" #include "vkDeviceUtil.hpp" #include "vkTypeUtil.hpp" #include "vkCmdUtil.hpp" #include "vkWsiPlatform.hpp" #include "vkWsiUtil.hpp" #include "tcuTestLog.hpp" #include "tcuPlatform.hpp" #include "tcuCommandLine.hpp" #include #if ( DE_OS == DE_OS_WIN32 ) #define NOMINMAX #define WIN32_LEAN_AND_MEAN #include #endif namespace vkt { namespace wsi { namespace { using namespace vk; using namespace vk::wsi; typedef std::vector Extensions; struct TestParams { vk::wsi::Type wsiType; VkFullScreenExclusiveEXT fseType; }; void checkAllSupported (const Extensions& supportedExtensions, const std::vector& requiredExtensions) { for (std::vector::const_iterator requiredExtName = requiredExtensions.begin(); requiredExtName != requiredExtensions.end(); ++requiredExtName) { if (!isExtensionSupported(supportedExtensions, RequiredExtension(*requiredExtName))) TCU_THROW(NotSupportedError, (*requiredExtName + " is not supported").c_str()); } } CustomInstance createInstanceWithWsi (Context& context, const Extensions& supportedExtensions, Type wsiType, const VkAllocationCallbacks* pAllocator = DE_NULL) { std::vector extensions; extensions.push_back("VK_KHR_surface"); extensions.push_back(getExtensionName(wsiType)); if (isExtensionSupported(supportedExtensions, RequiredExtension("VK_KHR_get_surface_capabilities2"))) extensions.push_back("VK_KHR_get_surface_capabilities2"); checkAllSupported(supportedExtensions, extensions); return createCustomInstanceWithExtensions(context, extensions, pAllocator); } VkPhysicalDeviceFeatures getDeviceFeaturesForWsi (void) { VkPhysicalDeviceFeatures features; deMemset(&features, 0, sizeof(features)); return features; } Move createDeviceWithWsi (const vk::PlatformInterface& vkp, vk::VkInstance instance, const InstanceInterface& vki, VkPhysicalDevice physicalDevice, const Extensions& supportedExtensions, const deUint32 queueFamilyIndex, const VkAllocationCallbacks* pAllocator, bool validationEnabled) { const float queuePriorities[] = { 1.0f }; const VkDeviceQueueCreateInfo queueInfos[] = { { VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, DE_NULL, (VkDeviceQueueCreateFlags)0, queueFamilyIndex, DE_LENGTH_OF_ARRAY(queuePriorities), &queuePriorities[0] } }; const VkPhysicalDeviceFeatures features = getDeviceFeaturesForWsi(); std::vector extensions; if (!isExtensionSupported(supportedExtensions, RequiredExtension("VK_KHR_swapchain"))) TCU_THROW(NotSupportedError, "VK_KHR_swapchain is not supported"); extensions.push_back("VK_KHR_swapchain"); if (isExtensionSupported(supportedExtensions, RequiredExtension("VK_EXT_full_screen_exclusive"))) { extensions.push_back("VK_EXT_full_screen_exclusive"); } VkDeviceCreateInfo deviceParams = { VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, DE_NULL, (VkDeviceCreateFlags)0, DE_LENGTH_OF_ARRAY(queueInfos), &queueInfos[0], 0u, // enabledLayerCount DE_NULL, // ppEnabledLayerNames (deUint32)extensions.size(), extensions.empty() ? DE_NULL : &extensions[0], &features }; return createCustomDevice(validationEnabled, vkp, instance, vki, physicalDevice, &deviceParams, pAllocator); } struct InstanceHelper { const std::vector supportedExtensions; const CustomInstance instance; const InstanceDriver& vki; InstanceHelper (Context& context, Type wsiType, const VkAllocationCallbacks* pAllocator = DE_NULL) : supportedExtensions (enumerateInstanceExtensionProperties(context.getPlatformInterface(), DE_NULL)) , instance (createInstanceWithWsi(context, supportedExtensions, wsiType, pAllocator)) , vki (instance.getDriver()) {} }; struct DeviceHelper { const VkPhysicalDevice physicalDevice; const deUint32 queueFamilyIndex; const Unique device; const DeviceDriver vkd; const VkQueue queue; DeviceHelper (Context& context, const InstanceInterface& vki, VkInstance instance, VkSurfaceKHR surface, const VkAllocationCallbacks* pAllocator = DE_NULL) : physicalDevice (chooseDevice(vki, instance, context.getTestContext().getCommandLine())) , queueFamilyIndex (chooseQueueFamilyIndex(vki, physicalDevice, surface)) , device (createDeviceWithWsi(context.getPlatformInterface(), instance, vki, physicalDevice, enumerateDeviceExtensionProperties(vki, physicalDevice, DE_NULL), queueFamilyIndex, pAllocator, context.getTestContext().getCommandLine().isValidationEnabled())) , vkd (context.getPlatformInterface(), instance, *device) , queue (getDeviceQueue(vkd, *device, queueFamilyIndex, 0)) { } }; de::MovePtr createDisplay (const vk::Platform& platform, const Extensions& supportedExtensions, Type wsiType) { try { return de::MovePtr(platform.createWsiDisplay(wsiType)); } catch (const tcu::NotSupportedError& e) { if (isExtensionSupported(supportedExtensions, RequiredExtension(getExtensionName(wsiType))) && platform.hasDisplay(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 createWindow (const Display& display, const tcu::Maybe& initialSize) { try { return de::MovePtr(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 NativeObjectsFS { const de::UniquePtr display; tcu::UVec2 windowSize; const de::UniquePtr window; NativeObjectsFS (Context& context, const Extensions& supportedExtensions, Type wsiType) : display (createDisplay(context.getTestContext().getPlatform().getVulkanPlatform(), supportedExtensions, wsiType)) , windowSize (getFullScreenSize(wsiType, *display.get(), tcu::UVec2(256U, 256U))) , window (createWindow(*display, windowSize)) {} }; VkSwapchainCreateInfoKHR getBasicSwapchainParameters (Type wsiType, const InstanceInterface& vki, VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, VkSurfaceFormatKHR surfaceFormat, const tcu::UVec2& desiredSize, deUint32 desiredImageCount) { const VkSurfaceCapabilitiesKHR capabilities = getPhysicalDeviceSurfaceCapabilities(vki, physicalDevice, surface); const PlatformProperties& platformProperties = getPlatformProperties(wsiType); const VkSurfaceTransformFlagBitsKHR transform = (capabilities.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) ? VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR : capabilities.currentTransform; const VkSwapchainCreateInfoKHR parameters = { VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, DE_NULL, (VkSwapchainCreateFlagsKHR)0, surface, de::clamp(desiredImageCount, capabilities.minImageCount, capabilities.maxImageCount > 0 ? capabilities.maxImageCount : capabilities.minImageCount + desiredImageCount), surfaceFormat.format, surfaceFormat.colorSpace, (platformProperties.swapchainExtent == PlatformProperties::SWAPCHAIN_EXTENT_MUST_MATCH_WINDOW_SIZE ? capabilities.currentExtent : vk::makeExtent2D(desiredSize.x(), desiredSize.y())), 1u, // imageArrayLayers VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, VK_SHARING_MODE_EXCLUSIVE, 0u, (const deUint32*)DE_NULL, transform, VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, VK_PRESENT_MODE_FIFO_KHR, VK_FALSE, // clipped (VkSwapchainKHR)0 // oldSwapchain }; return parameters; } typedef de::SharedPtr > CommandBufferSp; typedef de::SharedPtr > FenceSp; typedef de::SharedPtr > SemaphoreSp; std::vector createFences (const DeviceInterface& vkd, const VkDevice device, size_t numFences) { std::vector fences(numFences); for (size_t ndx = 0; ndx < numFences; ++ndx) fences[ndx] = FenceSp(new Unique(createFence(vkd, device))); return fences; } std::vector createSemaphores (const DeviceInterface& vkd, const VkDevice device, size_t numSemaphores) { std::vector semaphores(numSemaphores); for (size_t ndx = 0; ndx < numSemaphores; ++ndx) semaphores[ndx] = SemaphoreSp(new Unique(createSemaphore(vkd, device))); return semaphores; } std::vector allocateCommandBuffers (const DeviceInterface& vkd, const VkDevice device, const VkCommandPool commandPool, const VkCommandBufferLevel level, const size_t numCommandBuffers) { std::vector buffers (numCommandBuffers); for (size_t ndx = 0; ndx < numCommandBuffers; ++ndx) buffers[ndx] = CommandBufferSp(new Unique(allocateCommandBuffer(vkd, device, commandPool, level))); return buffers; } tcu::TestStatus fullScreenExclusiveTest(Context& context, TestParams testParams) { if (!de::contains(context.getDeviceExtensions().begin(), context.getDeviceExtensions().end(), "VK_EXT_full_screen_exclusive")) TCU_THROW(NotSupportedError, "Extension VK_EXT_full_screen_exclusive not supported"); const InstanceHelper instHelper(context, testParams.wsiType); const NativeObjectsFS native(context, instHelper.supportedExtensions, testParams.wsiType); const Unique surface(createSurface(instHelper.vki, instHelper.instance, testParams.wsiType, *native.display, *native.window)); const DeviceHelper devHelper(context, instHelper.vki, instHelper.instance, *surface); const std::vector deviceExtensions(enumerateDeviceExtensionProperties(instHelper.vki, devHelper.physicalDevice, DE_NULL)); if (!isExtensionSupported(deviceExtensions, RequiredExtension("VK_EXT_full_screen_exclusive"))) TCU_THROW(NotSupportedError, "Extension VK_EXT_full_screen_exclusive not supported"); native.window->setVisible(true); if (testParams.wsiType == TYPE_WIN32) { native.window->setForeground(); } // add information about full screen exclusive to VkSwapchainCreateInfoKHR VkSurfaceFullScreenExclusiveInfoEXT fseInfo = { VK_STRUCTURE_TYPE_SURFACE_FULL_SCREEN_EXCLUSIVE_INFO_EXT, // VkStructureType sType; DE_NULL, // void* pNext; testParams.fseType // VkFullScreenExclusiveEXT fullScreenExclusive; }; // for Win32 - create structure containing HMONITOR value #if ( DE_OS == DE_OS_WIN32 ) VkSurfaceFullScreenExclusiveWin32InfoEXT fseWin32Info = { VK_STRUCTURE_TYPE_SURFACE_FULL_SCREEN_EXCLUSIVE_WIN32_INFO_EXT, // VkStructureType sType; DE_NULL, // const void* pNext; pt::Win32MonitorHandle(0) // HMONITOR hmonitor; }; if (testParams.wsiType == TYPE_WIN32) { Win32WindowInterface* windowInterface = dynamic_cast(native.window.get()); fseWin32Info.hmonitor = (pt::Win32MonitorHandle)MonitorFromWindow((HWND)windowInterface->getNative().internal, MONITOR_DEFAULTTONEAREST); } #endif // check surface capabilities VkSurfaceCapabilitiesFullScreenExclusiveEXT surfaceCapabilitiesFSE = { VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_FULL_SCREEN_EXCLUSIVE_EXT, // VkStructureType sType; DE_NULL, // void* pNext; DE_FALSE // VkBool32 fullScreenExclusiveSupported; }; VkSurfaceCapabilities2KHR surfaceCapabilities2 = { VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR, // VkStructureType sType; &surfaceCapabilitiesFSE, // void* pNext; VkSurfaceCapabilitiesKHR {} // VkSurfaceCapabilitiesKHR surfaceCapabilities; }; VkPhysicalDeviceSurfaceInfo2KHR surfaceInfo = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR, // VkStructureType sType; DE_NULL, // const void* pNext; *surface // VkSurfaceKHR surface; }; surfaceInfo.pNext = &fseInfo; #if ( DE_OS == DE_OS_WIN32 ) if (testParams.wsiType == TYPE_WIN32) { fseInfo.pNext = &fseWin32Info; } #endif instHelper.vki.getPhysicalDeviceSurfaceCapabilities2KHR(devHelper.physicalDevice, &surfaceInfo, &surfaceCapabilities2); if (surfaceCapabilitiesFSE.fullScreenExclusiveSupported == DE_FALSE) TCU_THROW(NotSupportedError, "VkSurfaceCapabilitiesFullScreenExclusiveEXT::fullScreenExclusiveSupported is set to false"); const DeviceInterface& vkd = devHelper.vkd; const VkDevice device = *devHelper.device; SimpleAllocator allocator (vkd, device, getPhysicalDeviceMemoryProperties(instHelper.vki, devHelper.physicalDevice)); std::vector surfaceFormats = vk::wsi::getPhysicalDeviceSurfaceFormats(instHelper.vki, devHelper.physicalDevice, *surface); if(surfaceFormats.empty()) return tcu::TestStatus::fail("No VkSurfaceFormatKHR defined"); VkSwapchainCreateInfoKHR swapchainInfo = getBasicSwapchainParameters(testParams.wsiType, instHelper.vki, devHelper.physicalDevice, *surface, surfaceFormats[0], native.windowSize, 2); swapchainInfo.pNext = &fseInfo; #if ( DE_OS == DE_OS_WIN32 ) if (testParams.wsiType == TYPE_WIN32) { fseInfo.pNext = &fseWin32Info; } #endif Move swapchain; { VkSwapchainKHR object = 0; VkResult result = vkd.createSwapchainKHR(device, &swapchainInfo, DE_NULL, &object); if (result == VK_ERROR_INITIALIZATION_FAILED && testParams.fseType == VK_FULL_SCREEN_EXCLUSIVE_APPLICATION_CONTROLLED_EXT) { // In some cases, swapchain creation may fail if exclusive full-screen mode is requested for application control, // but for some implementation-specific reason exclusive full-screen access is unavailable for the particular combination // of parameters provided. If this occurs, VK_ERROR_INITIALIZATION_FAILED will be returned. return tcu::TestStatus(QP_TEST_RESULT_QUALITY_WARNING, "Failed to create swapchain with exclusive full-screen mode for application control."); } else { VK_CHECK(result); } swapchain = Move(check(object), Deleter(vkd, device, DE_NULL)); } const std::vector swapchainImages = getSwapchainImages(vkd, device, *swapchain); const WsiTriangleRenderer renderer (vkd, device, allocator, context.getBinaryCollection(), true, swapchainImages, swapchainImages, swapchainInfo.imageFormat, tcu::UVec2(swapchainInfo.imageExtent.width, swapchainInfo.imageExtent.height)); const Unique commandPool (createCommandPool(vkd, device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, devHelper.queueFamilyIndex)); 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 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 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 renderingCompleteSemaphores (createSemaphores(vkd, device, maxQueuedFrames)); const std::vector commandBuffers (allocateCommandBuffers(vkd, device, *commandPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, maxQueuedFrames)); bool fullScreenAcquired = (testParams.fseType != VK_FULL_SCREEN_EXCLUSIVE_APPLICATION_CONTROLLED_EXT); bool fullScreenLost = false; try { const deUint32 numFramesToRender = 60; for (deUint32 frameNdx = 0; frameNdx < numFramesToRender; ++frameNdx) { const VkFence imageReadyFence = **imageReadyFences[frameNdx%imageReadyFences.size()]; const VkSemaphore imageReadySemaphore = **imageReadySemaphores[frameNdx%imageReadySemaphores.size()]; deUint32 imageNdx = ~0u; if (!fullScreenAcquired) { const VkResult acquireResult = vkd.acquireFullScreenExclusiveModeEXT(device, *swapchain); switch (acquireResult) { case VK_SUCCESS: { fullScreenAcquired = true; break; } case VK_ERROR_INITIALIZATION_FAILED: { break; } case VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT: { context.getTestContext().getLog() << tcu::TestLog::Message << "Got VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT at vkAcquireFullScreenExclusiveModeEXT. Frame " << frameNdx << tcu::TestLog::EndMessage; break; } default: { VK_CHECK(acquireResult); break; } } } if (frameNdx >= maxQueuedFrames) VK_CHECK(vkd.waitForFences(device, 1u, &imageReadyFence, VK_TRUE, std::numeric_limits::max())); VK_CHECK(vkd.resetFences(device, 1, &imageReadyFence)); VkResult acquireResult; { acquireResult = vkd.acquireNextImageKHR(device, *swapchain, std::numeric_limits::max(), imageReadySemaphore, (vk::VkFence)0, &imageNdx); if (acquireResult == VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT) { context.getTestContext().getLog() << tcu::TestLog::Message << "Got VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT at vkAcquireNextImageKHR" << tcu::TestLog::EndMessage; fullScreenLost = true; } VK_CHECK_WSI(acquireResult); } if (acquireResult != VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT) { TCU_CHECK((size_t)imageNdx < swapchainImages.size()); const VkSemaphore renderingCompleteSemaphore = **renderingCompleteSemaphores[frameNdx%renderingCompleteSemaphores.size()]; const VkCommandBuffer commandBuffer = **commandBuffers[frameNdx%commandBuffers.size()]; const VkPipelineStageFlags waitDstStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; const VkSubmitInfo submitInfo = { VK_STRUCTURE_TYPE_SUBMIT_INFO, DE_NULL, 1u, &imageReadySemaphore, &waitDstStage, 1u, &commandBuffer, 1u, &renderingCompleteSemaphore }; const VkPresentInfoKHR presentInfo = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, DE_NULL, 1u, &renderingCompleteSemaphore, 1u, &*swapchain, &imageNdx, (VkResult*)DE_NULL }; renderer.recordFrame(commandBuffer, imageNdx, frameNdx); VK_CHECK(vkd.queueSubmit(devHelper.queue, 1u, &submitInfo, imageReadyFence)); const VkResult presentResult = vkd.queuePresentKHR(devHelper.queue, &presentInfo); if (presentResult == VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT) { context.getTestContext().getLog() << tcu::TestLog::Message << "Got VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT at vkQueuePresentKHR" << tcu::TestLog::EndMessage; fullScreenLost = true; } VK_CHECK_WSI(presentResult); } else { // image was not acquired, just roll the synchronization VK_CHECK(vkd.queueSubmit(devHelper.queue, 0u, DE_NULL, imageReadyFence)); } } VK_CHECK(vkd.deviceWaitIdle(device)); } catch (...) { // Make sure device is idle before destroying resources vkd.deviceWaitIdle(device); throw; } if (fullScreenAcquired && testParams.fseType == VK_FULL_SCREEN_EXCLUSIVE_APPLICATION_CONTROLLED_EXT) { const VkResult releaseResult = vkd.releaseFullScreenExclusiveModeEXT(device, *swapchain); if (releaseResult == VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT) { context.getTestContext().getLog() << tcu::TestLog::Message << "Got VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT at vkReleaseFullScreenExclusiveModeEXT" << tcu::TestLog::EndMessage; fullScreenLost = true; } VK_CHECK_WSI(releaseResult); } native.window->setVisible(false); if (fullScreenAcquired && !fullScreenLost) { return tcu::TestStatus::pass("Rendering tests succeeded"); } else { if (fullScreenLost) { return tcu::TestStatus(QP_TEST_RESULT_QUALITY_WARNING, "Full screen exclusive was lost during test, but did not end with an error."); } else { return tcu::TestStatus(QP_TEST_RESULT_QUALITY_WARNING, "Failed to acquire full screen exclusive, but did not end with an error."); } } } void getBasicRenderPrograms (SourceCollections& dst, TestParams) { WsiTriangleRenderer::getPrograms(dst); } } // anonymous void createFullScreenExclusiveTests (tcu::TestCaseGroup* testGroup, vk::wsi::Type wsiType) { struct { VkFullScreenExclusiveEXT testType; const char* name; } fullScreenTestTypes[] = { { VK_FULL_SCREEN_EXCLUSIVE_DEFAULT_EXT, "default" }, { VK_FULL_SCREEN_EXCLUSIVE_ALLOWED_EXT, "allowed" }, { VK_FULL_SCREEN_EXCLUSIVE_DISALLOWED_EXT, "disallowed" }, { VK_FULL_SCREEN_EXCLUSIVE_APPLICATION_CONTROLLED_EXT, "application_controlled" }, }; for (size_t fseNdx = 0; fseNdx < DE_LENGTH_OF_ARRAY(fullScreenTestTypes); ++fseNdx) { TestParams testParams { wsiType, fullScreenTestTypes[fseNdx].testType }; addFunctionCaseWithPrograms(testGroup, fullScreenTestTypes[fseNdx].name, "", getBasicRenderPrograms, fullScreenExclusiveTest, testParams); } } } // wsi } // vkt