blob: 778a1a8bf0bdcdb04fc120a991270df5308f9768 [file] [log] [blame]
/*------------------------------------------------------------------------
* 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 <vector>
#include <string>
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<VkExtensionProperties> Extensions;
CustomInstance createInstance (Context& context)
{
vector<string> 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<VkDevice> 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<VkDisplayKHR> 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<vk::VkDisplayModePropertiesKHR> 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 <VkDisplayPlanePropertiesKHR> 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<VkSemaphore>& semaphores)
{
for (VkSemaphore& semaphore : semaphores)
semaphore = createSemaphore(vkd, device).disown();
}
void deinitSemaphores (const DeviceInterface& vkd,
VkDevice device,
std::vector<VkSemaphore>& 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<VkFence>& fences)
{
for (VkFence& fence : fences)
fence = createFence(vkd, device).disown();
}
void deinitFences (const DeviceInterface& vkd,
VkDevice device,
std::vector<VkFence>& fences)
{
for (VkFence& fence : fences)
{
if (fence == (VkFence)0)
continue;
vkd.destroyFence(device, fence, DE_NULL);
fence = (VkFence)0;
}
fences.clear();
}
Move<VkCommandBuffer> 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<VkCommandBuffer> 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<VkCommandBuffer>& 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<VkCommandPool> 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<VkImageView> imageViews,
deUint32 width,
deUint32 height,
std::vector<VkFramebuffer>& 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<VkFramebuffer>& 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<VkImageView> 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<VkImage>& images,
VkFormat format,
std::vector<VkImageView>& 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<VkImageView>& 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<VkPipeline> 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<VkViewport> viewports (1, makeViewport(tcu::UVec2(width, height)));
const std::vector<VkRect2D> 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<VkViewport>& viewports
scissors, // const std::vector<VkRect2D>& scissors
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, // const VkPrimitiveTopology topology
0u, // const deUint32 subpass
0u, // const deUint32 patchControlPoints
&vertexInputState); // const VkPipelineVertexInputStateCreateInfo* vertexInputStateCreateInfo
}
Move<VkPipelineLayout> 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<VkSurfaceFormatKHR>& formats,
const vector<VkPresentModeKHR>& 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<VkDevice> m_device;
const DeviceDriver m_vkd;
const VkQueue m_queue;
const Unique<VkCommandPool> m_commandPool;
const Unique<VkShaderModule> m_vertexShaderModule;
const Unique<VkShaderModule> m_fragmentShaderModule;
const Unique<VkPipelineLayout> m_pipelineLayout;
const VkSurfaceCapabilities2EXT m_surfaceProperties;
const vector<VkSurfaceFormatKHR> m_surfaceFormats;
const vector<VkPresentModeKHR> m_presentModes;
tcu::ResultCollector m_resultCollector;
Move<VkSwapchainKHR> m_swapchain;
std::vector<VkImage> m_swapchainImages;
Move<VkRenderPass> m_renderPass;
Move<VkPipeline> m_pipeline;
std::vector<VkImageView> m_swapchainImageViews;
std::vector<VkFramebuffer> m_framebuffers;
std::vector<VkCommandBuffer> m_commandBuffers;
std::vector<VkSemaphore> m_acquireSemaphores;
std::vector<VkSemaphore> m_renderSemaphores;
std::vector<VkFence> 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<VkImageView>(swapchainImagesCount, (VkImageView)0);
m_framebuffers = std::vector<VkFramebuffer>(swapchainImagesCount, (VkFramebuffer)0);
m_acquireSemaphores = std::vector<VkSemaphore>(swapchainImagesCount+1, (VkSemaphore)0);
m_renderSemaphores = std::vector<VkSemaphore>(swapchainImagesCount+1, (VkSemaphore)0);
m_fences = std::vector<VkFence>(fenceCount, (VkFence)0);
m_commandBuffers = std::vector<VkCommandBuffer>(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<VkSwapchainKHR>();
m_renderPass = Move<VkRenderPass>();
m_pipeline = Move<VkPipeline>();
}
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,
&currentAcquireSemaphore,
&dstStageMask,
1u,
&commandBuffer,
1u,
&currentRenderSemaphore
};
VK_CHECK(m_vkd.queueSubmit(m_queue, 1u, &submitInfo, fence));
}
VkResult result;
const VkPresentInfoKHR presentInfo =
{
VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
DE_NULL,
1u,
&currentRenderSemaphore,
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<VkDisplayKHR>& 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<VkDisplayPropertiesKHR> 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<VkDisplayKHR> availableDisplays;
getDisplays(context, availableDisplays);
struct PowerStateData
{
VkDisplayPowerStateEXT state;
deUint32 waitMs;
};
vector<PowerStateData> 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<vk::VkDisplayKHR> availableDisplays;
getDisplays(context, availableDisplays);
VkDevice device = context.getDevice();
const DeviceInterface& vkd = context.getDeviceInterface();
std::vector<VkFence> fences = std::vector<VkFence>(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<VkFence> fences = std::vector<VkFence>(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