blob: ab58de1808044f4d5f6c2bd1925113162fc40398 [file] [log] [blame]
/*-------------------------------------------------------------------------
* 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"
#if ( DE_OS == DE_OS_WIN32 )
#define NOMINMAX
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif
namespace vkt
{
namespace wsi
{
namespace
{
using namespace vk;
using namespace vk::wsi;
typedef std::vector<VkExtensionProperties> Extensions;
struct TestParams
{
vk::wsi::Type wsiType;
VkFullScreenExclusiveEXT fseType;
};
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, 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<std::string> 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<VkDevice> 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<const char*> extensions;
if (!isExtensionSupported(supportedExtensions, RequiredExtension("VK_KHR_swapchain")))
TCU_THROW(NotSupportedError, (std::string(extensions[0]) + " is not supported").c_str());
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<VkExtensionProperties> 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<VkDevice> 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<Display> createDisplay (const vk::Platform& platform,
const Extensions& supportedExtensions,
Type wsiType)
{
try
{
return de::MovePtr<Display>(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<Window> createWindow (const Display& display,
const tcu::Maybe<tcu::UVec2>& initialSize)
{
try
{
return de::MovePtr<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<Display> display;
tcu::UVec2 windowSize;
const de::UniquePtr<Window> window;
NativeObjects (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<Unique<VkCommandBuffer> > CommandBufferSp;
typedef de::SharedPtr<Unique<VkFence> > FenceSp;
typedef de::SharedPtr<Unique<VkSemaphore> > SemaphoreSp;
std::vector<FenceSp> createFences (const DeviceInterface& vkd,
const VkDevice device,
size_t numFences)
{
std::vector<FenceSp> fences(numFences);
for (size_t ndx = 0; ndx < numFences; ++ndx)
fences[ndx] = FenceSp(new Unique<VkFence>(createFence(vkd, device)));
return fences;
}
std::vector<SemaphoreSp> createSemaphores (const DeviceInterface& vkd,
const VkDevice device,
size_t numSemaphores)
{
std::vector<SemaphoreSp> semaphores(numSemaphores);
for (size_t ndx = 0; ndx < numSemaphores; ++ndx)
semaphores[ndx] = SemaphoreSp(new Unique<VkSemaphore>(createSemaphore(vkd, device)));
return semaphores;
}
std::vector<CommandBufferSp> allocateCommandBuffers (const DeviceInterface& vkd,
const VkDevice device,
const VkCommandPool commandPool,
const VkCommandBufferLevel level,
const size_t numCommandBuffers)
{
std::vector<CommandBufferSp> buffers (numCommandBuffers);
for (size_t ndx = 0; ndx < numCommandBuffers; ++ndx)
buffers[ndx] = CommandBufferSp(new Unique<VkCommandBuffer>(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 NativeObjects native (context, instHelper.supportedExtensions, testParams.wsiType);
const Unique<VkSurfaceKHR> surface (createSurface(instHelper.vki, instHelper.instance, testParams.wsiType, *native.display, *native.window));
const DeviceHelper devHelper (context, instHelper.vki, instHelper.instance, *surface);
const std::vector<VkExtensionProperties> 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);
// 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<Win32WindowInterface*>(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;
};
#if ( DE_OS == DE_OS_WIN32 )
if (testParams.wsiType == TYPE_WIN32)
{
surfaceInfo.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<VkSurfaceFormatKHR> 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);
// 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;
};
swapchainInfo.pNext = &fseInfo;
#if ( DE_OS == DE_OS_WIN32 )
if (testParams.wsiType == TYPE_WIN32)
{
fseInfo.pNext = &fseWin32Info;
}
#endif
const Unique<VkSwapchainKHR> swapchain (createSwapchainKHR(vkd, device, &swapchainInfo));
const std::vector<VkImage> 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<VkCommandPool> 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<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_COMMAND_BUFFER_LEVEL_PRIMARY, maxQueuedFrames));
bool fullScreenAcquired = (testParams.fseType != VK_FULL_SCREEN_EXCLUSIVE_APPLICATION_CONTROLLED_EXT);
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:
{
if (frameNdx > 3)
throw tcu::TestError("vkAcquireFullScreenExclusiveModeEXT was not able to succeed in first 3 draw frames");
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;
VK_CHECK(acquireResult);
break;
}
default:
{
VK_CHECK(acquireResult);
break;
}
}
}
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 VkResult acquireResult = vkd.acquireNextImageKHR(device,
*swapchain,
std::numeric_limits<deUint64>::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;
VK_CHECK(acquireResult);
}
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;
VK_CHECK_WSI(presentResult);
}
}
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;
VK_CHECK(releaseResult);
}
native.window->setVisible(false);
return tcu::TestStatus::pass("Rendering tests succeeded");
}
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