| /*------------------------------------------------------------------------- |
| * Vulkan Conformance Tests |
| * ------------------------ |
| * |
| * Copyright (c) 2016 Google 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 VkSwapchain Tests |
| *//*--------------------------------------------------------------------*/ |
| |
| #include "vktWsiSwapchainTests.hpp" |
| |
| #include "vktTestCaseUtil.hpp" |
| #include "vktTestGroupUtil.hpp" |
| |
| #include "vkDefs.hpp" |
| #include "vkPlatform.hpp" |
| #include "vkStrUtil.hpp" |
| #include "vkRef.hpp" |
| #include "vkRefUtil.hpp" |
| #include "vkQueryUtil.hpp" |
| #include "vkMemUtil.hpp" |
| #include "vkDeviceUtil.hpp" |
| #include "vkPrograms.hpp" |
| #include "vkTypeUtil.hpp" |
| #include "vkWsiPlatform.hpp" |
| #include "vkWsiUtil.hpp" |
| #include "vkAllocationCallbackUtil.hpp" |
| |
| #include "tcuTestLog.hpp" |
| #include "tcuFormatUtil.hpp" |
| #include "tcuPlatform.hpp" |
| #include "tcuResultCollector.hpp" |
| |
| #include "deUniquePtr.hpp" |
| #include "deStringUtil.hpp" |
| #include "deArrayUtil.hpp" |
| #include "deSharedPtr.hpp" |
| |
| #include <limits> |
| |
| namespace vkt |
| { |
| namespace wsi |
| { |
| |
| namespace |
| { |
| |
| using namespace vk; |
| using namespace vk::wsi; |
| |
| using tcu::TestLog; |
| using tcu::Maybe; |
| using tcu::UVec2; |
| |
| using de::MovePtr; |
| using de::UniquePtr; |
| |
| using std::string; |
| using std::vector; |
| |
| typedef vector<VkExtensionProperties> Extensions; |
| |
| void checkAllSupported (const Extensions& supportedExtensions, const vector<string>& requiredExtensions) |
| { |
| for (vector<string>::const_iterator requiredExtName = requiredExtensions.begin(); |
| requiredExtName != requiredExtensions.end(); |
| ++requiredExtName) |
| { |
| if (!isExtensionSupported(supportedExtensions, RequiredExtension(*requiredExtName))) |
| TCU_THROW(NotSupportedError, (*requiredExtName + " is not supported").c_str()); |
| } |
| } |
| |
| Move<VkInstance> createInstanceWithWsi (const PlatformInterface& vkp, |
| const Extensions& supportedExtensions, |
| Type wsiType, |
| const VkAllocationCallbacks* pAllocator = DE_NULL) |
| { |
| vector<string> extensions; |
| |
| extensions.push_back("VK_KHR_surface"); |
| extensions.push_back(getExtensionName(wsiType)); |
| |
| // VK_EXT_swapchain_colorspace adds new surface formats. Driver can enumerate |
| // the formats regardless of whether VK_EXT_swapchain_colorspace was enabled, |
| // but using them without enabling the extension is not allowed. Thus we have |
| // two options: |
| // |
| // 1) Filter out non-core formats to stay within valid usage. |
| // |
| // 2) Enable VK_EXT_swapchain colorspace if advertised by the driver. |
| // |
| // We opt for (2) as it provides basic coverage for the extension as a bonus. |
| if (isExtensionSupported(supportedExtensions, RequiredExtension("VK_EXT_swapchain_colorspace"))) |
| extensions.push_back("VK_EXT_swapchain_colorspace"); |
| |
| checkAllSupported(supportedExtensions, extensions); |
| |
| return createDefaultInstance(vkp, vector<string>(), extensions, pAllocator); |
| } |
| |
| VkPhysicalDeviceFeatures getDeviceFeaturesForWsi (void) |
| { |
| VkPhysicalDeviceFeatures features; |
| deMemset(&features, 0, sizeof(features)); |
| return features; |
| } |
| |
| Move<VkDevice> createDeviceWithWsi (const InstanceInterface& vki, |
| VkPhysicalDevice physicalDevice, |
| const Extensions& supportedExtensions, |
| const deUint32 queueFamilyIndex, |
| const VkAllocationCallbacks* pAllocator = DE_NULL) |
| { |
| 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(); |
| const char* const extensions[] = { "VK_KHR_swapchain" }; |
| const VkDeviceCreateInfo deviceParams = |
| { |
| VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, |
| DE_NULL, |
| (VkDeviceCreateFlags)0, |
| DE_LENGTH_OF_ARRAY(queueInfos), |
| &queueInfos[0], |
| 0u, // enabledLayerCount |
| DE_NULL, // ppEnabledLayerNames |
| DE_LENGTH_OF_ARRAY(extensions), // enabledExtensionCount |
| DE_ARRAY_BEGIN(extensions), // ppEnabledExtensionNames |
| &features |
| }; |
| |
| for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(extensions); ++ndx) |
| { |
| if (!isExtensionSupported(supportedExtensions, RequiredExtension(extensions[ndx]))) |
| TCU_THROW(NotSupportedError, (string(extensions[ndx]) + " is not supported").c_str()); |
| } |
| |
| return createDevice(vki, physicalDevice, &deviceParams, pAllocator); |
| } |
| |
| deUint32 getNumQueueFamilyIndices (const InstanceInterface& vki, VkPhysicalDevice physicalDevice) |
| { |
| deUint32 numFamilies = 0; |
| |
| vki.getPhysicalDeviceQueueFamilyProperties(physicalDevice, &numFamilies, DE_NULL); |
| |
| return numFamilies; |
| } |
| |
| vector<deUint32> getSupportedQueueFamilyIndices (const InstanceInterface& vki, VkPhysicalDevice physicalDevice, VkSurfaceKHR surface) |
| { |
| const deUint32 numTotalFamilyIndices = getNumQueueFamilyIndices(vki, physicalDevice); |
| vector<deUint32> supportedFamilyIndices; |
| |
| for (deUint32 queueFamilyNdx = 0; queueFamilyNdx < numTotalFamilyIndices; ++queueFamilyNdx) |
| { |
| if (getPhysicalDeviceSurfaceSupport(vki, physicalDevice, queueFamilyNdx, surface) != VK_FALSE) |
| supportedFamilyIndices.push_back(queueFamilyNdx); |
| } |
| |
| return supportedFamilyIndices; |
| } |
| |
| deUint32 chooseQueueFamilyIndex (const InstanceInterface& vki, VkPhysicalDevice physicalDevice, VkSurfaceKHR surface) |
| { |
| const vector<deUint32> supportedFamilyIndices = getSupportedQueueFamilyIndices(vki, physicalDevice, surface); |
| |
| if (supportedFamilyIndices.empty()) |
| TCU_THROW(NotSupportedError, "Device doesn't support presentation"); |
| |
| return supportedFamilyIndices[0]; |
| } |
| |
| struct InstanceHelper |
| { |
| const vector<VkExtensionProperties> supportedExtensions; |
| const Unique<VkInstance> instance; |
| const InstanceDriver vki; |
| |
| InstanceHelper (Context& context, Type wsiType, const VkAllocationCallbacks* pAllocator = DE_NULL) |
| : supportedExtensions (enumerateInstanceExtensionProperties(context.getPlatformInterface(), |
| DE_NULL)) |
| , instance (createInstanceWithWsi(context.getPlatformInterface(), |
| supportedExtensions, |
| wsiType, |
| pAllocator)) |
| , vki (context.getPlatformInterface(), *instance) |
| {} |
| }; |
| |
| 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(vki, |
| physicalDevice, |
| enumerateDeviceExtensionProperties(vki, physicalDevice, DE_NULL), |
| queueFamilyIndex, |
| pAllocator)) |
| , vkd (vki, *device) |
| , queue (getDeviceQueue(vkd, *device, queueFamilyIndex, 0)) |
| { |
| } |
| }; |
| |
| MovePtr<Display> createDisplay (const vk::Platform& platform, |
| const Extensions& supportedExtensions, |
| Type wsiType) |
| { |
| try |
| { |
| return MovePtr<Display>(platform.createWsiDisplay(wsiType)); |
| } |
| catch (const tcu::NotSupportedError& e) |
| { |
| if (isExtensionSupported(supportedExtensions, RequiredExtension(getExtensionName(wsiType)))) |
| { |
| // If VK_KHR_{platform}_surface was supported, vk::Platform implementation |
| // must support creating native display & window for that WSI type. |
| throw tcu::TestError(e.getMessage()); |
| } |
| else |
| throw; |
| } |
| } |
| |
| MovePtr<Window> createWindow (const Display& display, const Maybe<UVec2>& initialSize) |
| { |
| try |
| { |
| return 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 UniquePtr<Display> display; |
| const UniquePtr<Window> window; |
| |
| NativeObjects (Context& context, |
| const Extensions& supportedExtensions, |
| Type wsiType, |
| const Maybe<UVec2>& initialWindowSize = tcu::nothing<UVec2>()) |
| : display (createDisplay(context.getTestContext().getPlatform().getVulkanPlatform(), supportedExtensions, wsiType)) |
| , window (createWindow(*display, initialWindowSize)) |
| {} |
| }; |
| |
| enum TestDimension |
| { |
| TEST_DIMENSION_MIN_IMAGE_COUNT = 0, //!< Test all supported image counts |
| TEST_DIMENSION_IMAGE_FORMAT, //!< Test all supported formats |
| TEST_DIMENSION_IMAGE_EXTENT, //!< Test various (supported) extents |
| TEST_DIMENSION_IMAGE_ARRAY_LAYERS, |
| TEST_DIMENSION_IMAGE_USAGE, |
| TEST_DIMENSION_IMAGE_SHARING_MODE, |
| TEST_DIMENSION_PRE_TRANSFORM, |
| TEST_DIMENSION_COMPOSITE_ALPHA, |
| TEST_DIMENSION_PRESENT_MODE, |
| TEST_DIMENSION_CLIPPED, |
| |
| TEST_DIMENSION_LAST |
| }; |
| |
| const char* getTestDimensionName (TestDimension dimension) |
| { |
| static const char* const s_names[] = |
| { |
| "min_image_count", |
| "image_format", |
| "image_extent", |
| "image_array_layers", |
| "image_usage", |
| "image_sharing_mode", |
| "pre_transform", |
| "composite_alpha", |
| "present_mode", |
| "clipped" |
| }; |
| return de::getSizedArrayElement<TEST_DIMENSION_LAST>(s_names, dimension); |
| } |
| |
| struct TestParameters |
| { |
| Type wsiType; |
| TestDimension dimension; |
| |
| TestParameters (Type wsiType_, TestDimension dimension_) |
| : wsiType (wsiType_) |
| , dimension (dimension_) |
| {} |
| |
| TestParameters (void) |
| : wsiType (TYPE_LAST) |
| , dimension (TEST_DIMENSION_LAST) |
| {} |
| }; |
| |
| vector<VkSwapchainCreateInfoKHR> generateSwapchainParameterCases (Type wsiType, |
| TestDimension dimension, |
| const VkSurfaceCapabilitiesKHR& capabilities, |
| const vector<VkSurfaceFormatKHR>& formats, |
| const vector<VkPresentModeKHR>& presentModes) |
| { |
| const PlatformProperties& platformProperties = getPlatformProperties(wsiType); |
| vector<VkSwapchainCreateInfoKHR> cases; |
| const VkSurfaceTransformFlagBitsKHR defaultTransform = (capabilities.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) ? VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR : capabilities.currentTransform; |
| const VkSwapchainCreateInfoKHR baseParameters = |
| { |
| VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, |
| DE_NULL, |
| (VkSwapchainCreateFlagsKHR)0, |
| (VkSurfaceKHR)0, |
| capabilities.minImageCount, |
| formats[0].format, |
| formats[0].colorSpace, |
| (platformProperties.swapchainExtent == PlatformProperties::SWAPCHAIN_EXTENT_SETS_WINDOW_SIZE |
| ? capabilities.minImageExtent : capabilities.currentExtent), |
| 1u, // imageArrayLayers |
| VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, |
| VK_SHARING_MODE_EXCLUSIVE, |
| 0u, |
| (const deUint32*)DE_NULL, |
| defaultTransform, |
| VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, |
| VK_PRESENT_MODE_FIFO_KHR, |
| VK_FALSE, // clipped |
| (VkSwapchainKHR)0 // oldSwapchain |
| }; |
| |
| switch (dimension) |
| { |
| case TEST_DIMENSION_MIN_IMAGE_COUNT: |
| { |
| const deUint32 maxImageCountToTest = de::clamp(16u, capabilities.minImageCount, (capabilities.maxImageCount > 0) ? capabilities.maxImageCount : capabilities.minImageCount + 16u); |
| |
| for (deUint32 imageCount = capabilities.minImageCount; imageCount <= maxImageCountToTest; ++imageCount) |
| { |
| cases.push_back(baseParameters); |
| cases.back().minImageCount = imageCount; |
| } |
| |
| break; |
| } |
| |
| case TEST_DIMENSION_IMAGE_FORMAT: |
| { |
| for (vector<VkSurfaceFormatKHR>::const_iterator curFmt = formats.begin(); curFmt != formats.end(); ++curFmt) |
| { |
| cases.push_back(baseParameters); |
| cases.back().imageFormat = curFmt->format; |
| cases.back().imageColorSpace = curFmt->colorSpace; |
| } |
| |
| break; |
| } |
| |
| case TEST_DIMENSION_IMAGE_EXTENT: |
| { |
| static const VkExtent2D s_testSizes[] = |
| { |
| { 1, 1 }, |
| { 16, 32 }, |
| { 32, 16 }, |
| { 632, 231 }, |
| { 117, 998 }, |
| }; |
| |
| if (platformProperties.swapchainExtent == PlatformProperties::SWAPCHAIN_EXTENT_SETS_WINDOW_SIZE || |
| platformProperties.swapchainExtent == PlatformProperties::SWAPCHAIN_EXTENT_SCALED_TO_WINDOW_SIZE) |
| { |
| for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(s_testSizes); ++ndx) |
| { |
| cases.push_back(baseParameters); |
| cases.back().imageExtent.width = de::clamp(s_testSizes[ndx].width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width); |
| cases.back().imageExtent.height = de::clamp(s_testSizes[ndx].height, capabilities.minImageExtent.height, capabilities.maxImageExtent.height); |
| } |
| } |
| |
| if (platformProperties.swapchainExtent != PlatformProperties::SWAPCHAIN_EXTENT_SETS_WINDOW_SIZE) |
| { |
| cases.push_back(baseParameters); |
| cases.back().imageExtent = capabilities.currentExtent; |
| } |
| |
| if (platformProperties.swapchainExtent != PlatformProperties::SWAPCHAIN_EXTENT_MUST_MATCH_WINDOW_SIZE) |
| { |
| cases.push_back(baseParameters); |
| cases.back().imageExtent = capabilities.minImageExtent; |
| |
| cases.push_back(baseParameters); |
| cases.back().imageExtent = capabilities.maxImageExtent; |
| } |
| |
| break; |
| } |
| |
| case TEST_DIMENSION_IMAGE_ARRAY_LAYERS: |
| { |
| const deUint32 maxLayers = de::min(capabilities.maxImageArrayLayers, 16u); |
| |
| for (deUint32 numLayers = 1; numLayers <= maxLayers; ++numLayers) |
| { |
| cases.push_back(baseParameters); |
| cases.back().imageArrayLayers = numLayers; |
| } |
| |
| break; |
| } |
| |
| case TEST_DIMENSION_IMAGE_USAGE: |
| { |
| for (deUint32 flags = 1u; flags <= capabilities.supportedUsageFlags; ++flags) |
| { |
| if ((flags & ~capabilities.supportedUsageFlags) == 0) |
| { |
| cases.push_back(baseParameters); |
| cases.back().imageUsage = flags; |
| } |
| } |
| |
| break; |
| } |
| |
| case TEST_DIMENSION_IMAGE_SHARING_MODE: |
| { |
| cases.push_back(baseParameters); |
| cases.back().imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; |
| |
| cases.push_back(baseParameters); |
| cases.back().imageSharingMode = VK_SHARING_MODE_CONCURRENT; |
| |
| break; |
| } |
| |
| case TEST_DIMENSION_PRE_TRANSFORM: |
| { |
| for (deUint32 transform = 1u; |
| transform <= capabilities.supportedTransforms; |
| transform = transform<<1u) |
| { |
| if ((transform & capabilities.supportedTransforms) != 0) |
| { |
| cases.push_back(baseParameters); |
| cases.back().preTransform = (VkSurfaceTransformFlagBitsKHR)transform; |
| } |
| } |
| |
| break; |
| } |
| |
| case TEST_DIMENSION_COMPOSITE_ALPHA: |
| { |
| for (deUint32 alphaMode = 1u; |
| alphaMode <= capabilities.supportedCompositeAlpha; |
| alphaMode = alphaMode<<1u) |
| { |
| if ((alphaMode & capabilities.supportedCompositeAlpha) != 0) |
| { |
| cases.push_back(baseParameters); |
| cases.back().compositeAlpha = (VkCompositeAlphaFlagBitsKHR)alphaMode; |
| } |
| } |
| |
| break; |
| } |
| |
| case TEST_DIMENSION_PRESENT_MODE: |
| { |
| for (vector<VkPresentModeKHR>::const_iterator curMode = presentModes.begin(); curMode != presentModes.end(); ++curMode) |
| { |
| cases.push_back(baseParameters); |
| cases.back().presentMode = *curMode; |
| } |
| |
| break; |
| } |
| |
| case TEST_DIMENSION_CLIPPED: |
| { |
| cases.push_back(baseParameters); |
| cases.back().clipped = VK_FALSE; |
| |
| cases.push_back(baseParameters); |
| cases.back().clipped = VK_TRUE; |
| |
| break; |
| } |
| |
| default: |
| DE_FATAL("Impossible"); |
| } |
| |
| DE_ASSERT(!cases.empty()); |
| return cases; |
| } |
| |
| vector<VkSwapchainCreateInfoKHR> generateSwapchainParameterCases (Type wsiType, |
| TestDimension dimension, |
| const InstanceInterface& vki, |
| VkPhysicalDevice physicalDevice, |
| VkSurfaceKHR surface) |
| { |
| const VkSurfaceCapabilitiesKHR capabilities = getPhysicalDeviceSurfaceCapabilities(vki, |
| physicalDevice, |
| surface); |
| const vector<VkSurfaceFormatKHR> formats = getPhysicalDeviceSurfaceFormats(vki, |
| physicalDevice, |
| surface); |
| const vector<VkPresentModeKHR> presentModes = getPhysicalDeviceSurfacePresentModes(vki, |
| physicalDevice, |
| surface); |
| |
| return generateSwapchainParameterCases(wsiType, dimension, capabilities, formats, presentModes); |
| } |
| |
| tcu::TestStatus createSwapchainTest (Context& context, TestParameters params) |
| { |
| const InstanceHelper instHelper (context, params.wsiType); |
| const NativeObjects native (context, instHelper.supportedExtensions, params.wsiType); |
| const Unique<VkSurfaceKHR> surface (createSurface(instHelper.vki, *instHelper.instance, params.wsiType, *native.display, *native.window)); |
| const DeviceHelper devHelper (context, instHelper.vki, *instHelper.instance, *surface); |
| const vector<VkSwapchainCreateInfoKHR> cases (generateSwapchainParameterCases(params.wsiType, params.dimension, instHelper.vki, devHelper.physicalDevice, *surface)); |
| |
| for (size_t caseNdx = 0; caseNdx < cases.size(); ++caseNdx) |
| { |
| VkSwapchainCreateInfoKHR curParams = cases[caseNdx]; |
| |
| curParams.surface = *surface; |
| curParams.queueFamilyIndexCount = 1u; |
| curParams.pQueueFamilyIndices = &devHelper.queueFamilyIndex; |
| |
| context.getTestContext().getLog() |
| << TestLog::Message << "Sub-case " << (caseNdx+1) << " / " << cases.size() << ": " << curParams << TestLog::EndMessage; |
| |
| { |
| const Unique<VkSwapchainKHR> swapchain (createSwapchainKHR(devHelper.vkd, *devHelper.device, &curParams)); |
| } |
| } |
| |
| return tcu::TestStatus::pass("Creating swapchain succeeded"); |
| } |
| |
| tcu::TestStatus createSwapchainSimulateOOMTest (Context& context, TestParameters params) |
| { |
| const size_t maxCases = 300u; |
| const deUint32 maxAllocs = 1024u; |
| |
| tcu::TestLog& log = context.getTestContext().getLog(); |
| tcu::ResultCollector results (log); |
| |
| AllocationCallbackRecorder allocationRecorder (getSystemAllocator()); |
| DeterministicFailAllocator failingAllocator (allocationRecorder.getCallbacks(), |
| DeterministicFailAllocator::MODE_DO_NOT_COUNT, |
| 0); |
| { |
| const InstanceHelper instHelper (context, params.wsiType, failingAllocator.getCallbacks()); |
| const NativeObjects native (context, instHelper.supportedExtensions, params.wsiType); |
| const Unique<VkSurfaceKHR> surface (createSurface(instHelper.vki, |
| *instHelper.instance, |
| params.wsiType, |
| *native.display, |
| *native.window, |
| failingAllocator.getCallbacks())); |
| const DeviceHelper devHelper (context, instHelper.vki, *instHelper.instance, *surface, failingAllocator.getCallbacks()); |
| const vector<VkSwapchainCreateInfoKHR> allCases (generateSwapchainParameterCases(params.wsiType, params.dimension, instHelper.vki, devHelper.physicalDevice, *surface)); |
| |
| if (maxCases < allCases.size()) |
| log << TestLog::Message << "Note: Will only test first " << maxCases << " cases out of total of " << allCases.size() << " parameter combinations" << TestLog::EndMessage; |
| |
| for (size_t caseNdx = 0; caseNdx < de::min(maxCases, allCases.size()); ++caseNdx) |
| { |
| log << TestLog::Message << "Testing parameter case " << caseNdx << ": " << allCases[caseNdx] << TestLog::EndMessage; |
| |
| for (deUint32 numPassingAllocs = 0; numPassingAllocs <= maxAllocs; ++numPassingAllocs) |
| { |
| bool gotOOM = false; |
| |
| failingAllocator.reset(DeterministicFailAllocator::MODE_COUNT_AND_FAIL, numPassingAllocs); |
| |
| log << TestLog::Message << "Testing with " << numPassingAllocs << " first allocations succeeding" << TestLog::EndMessage; |
| |
| try |
| { |
| VkSwapchainCreateInfoKHR curParams = allCases[caseNdx]; |
| |
| curParams.surface = *surface; |
| curParams.queueFamilyIndexCount = 1u; |
| curParams.pQueueFamilyIndices = &devHelper.queueFamilyIndex; |
| |
| { |
| const Unique<VkSwapchainKHR> swapchain (createSwapchainKHR(devHelper.vkd, *devHelper.device, &curParams, failingAllocator.getCallbacks())); |
| } |
| } |
| catch (const OutOfMemoryError& e) |
| { |
| log << TestLog::Message << "Got " << e.getError() << TestLog::EndMessage; |
| gotOOM = true; |
| } |
| |
| if (!gotOOM) |
| { |
| log << TestLog::Message << "Creating swapchain succeeded!" << TestLog::EndMessage; |
| |
| if (numPassingAllocs == 0) |
| results.addResult(QP_TEST_RESULT_QUALITY_WARNING, "Allocation callbacks were not used"); |
| |
| break; |
| } |
| else if (numPassingAllocs == maxAllocs) |
| results.addResult(QP_TEST_RESULT_QUALITY_WARNING, "Creating swapchain did not succeed, callback limit exceeded"); |
| } |
| } |
| |
| context.getTestContext().touchWatchdog(); |
| } |
| |
| if (!validateAndLog(log, allocationRecorder, 0u)) |
| results.fail("Detected invalid system allocation callback"); |
| |
| return tcu::TestStatus(results.getResult(), results.getMessage()); |
| } |
| |
| struct GroupParameters |
| { |
| typedef FunctionInstance1<TestParameters>::Function Function; |
| |
| Type wsiType; |
| Function function; |
| |
| GroupParameters (Type wsiType_, Function function_) |
| : wsiType (wsiType_) |
| , function (function_) |
| {} |
| |
| GroupParameters (void) |
| : wsiType (TYPE_LAST) |
| , function ((Function)DE_NULL) |
| {} |
| }; |
| |
| void populateSwapchainGroup (tcu::TestCaseGroup* testGroup, GroupParameters params) |
| { |
| for (int dimensionNdx = 0; dimensionNdx < TEST_DIMENSION_LAST; ++dimensionNdx) |
| { |
| const TestDimension testDimension = (TestDimension)dimensionNdx; |
| |
| addFunctionCase(testGroup, getTestDimensionName(testDimension), "", params.function, TestParameters(params.wsiType, testDimension)); |
| } |
| } |
| |
| VkSwapchainCreateInfoKHR getBasicSwapchainParameters (Type wsiType, |
| const InstanceInterface& vki, |
| VkPhysicalDevice physicalDevice, |
| VkSurfaceKHR surface, |
| const tcu::UVec2& desiredSize, |
| deUint32 desiredImageCount) |
| { |
| const VkSurfaceCapabilitiesKHR capabilities = getPhysicalDeviceSurfaceCapabilities(vki, |
| physicalDevice, |
| surface); |
| const vector<VkSurfaceFormatKHR> formats = getPhysicalDeviceSurfaceFormats(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), |
| formats[0].format, |
| formats[0].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<VkImageView> > ImageViewSp; |
| typedef de::SharedPtr<Unique<VkFramebuffer> > FramebufferSp; |
| |
| class TriangleRenderer |
| { |
| public: |
| TriangleRenderer (const DeviceInterface& vkd, |
| const VkDevice device, |
| Allocator& allocator, |
| const BinaryCollection& binaryRegistry, |
| const vector<VkImage> swapchainImages, |
| const VkFormat framebufferFormat, |
| const UVec2& renderSize); |
| ~TriangleRenderer (void); |
| |
| void recordFrame (VkCommandBuffer cmdBuffer, |
| deUint32 imageNdx, |
| deUint32 frameNdx) const; |
| |
| static void getPrograms (SourceCollections& dst); |
| |
| private: |
| static Move<VkRenderPass> createRenderPass (const DeviceInterface& vkd, |
| const VkDevice device, |
| const VkFormat colorAttachmentFormat); |
| static Move<VkPipelineLayout> createPipelineLayout(const DeviceInterface& vkd, |
| VkDevice device); |
| static Move<VkPipeline> createPipeline (const DeviceInterface& vkd, |
| const VkDevice device, |
| const VkRenderPass renderPass, |
| const VkPipelineLayout pipelineLayout, |
| const BinaryCollection& binaryCollection, |
| const UVec2& renderSize); |
| |
| static Move<VkImageView> createAttachmentView(const DeviceInterface& vkd, |
| const VkDevice device, |
| const VkImage image, |
| const VkFormat format); |
| static Move<VkFramebuffer> createFramebuffer (const DeviceInterface& vkd, |
| const VkDevice device, |
| const VkRenderPass renderPass, |
| const VkImageView colorAttachment, |
| const UVec2& renderSize); |
| |
| static Move<VkBuffer> createBuffer (const DeviceInterface& vkd, |
| VkDevice device, |
| VkDeviceSize size, |
| VkBufferUsageFlags usage); |
| |
| const DeviceInterface& m_vkd; |
| |
| const vector<VkImage> m_swapchainImages; |
| const tcu::UVec2 m_renderSize; |
| |
| const Unique<VkRenderPass> m_renderPass; |
| const Unique<VkPipelineLayout> m_pipelineLayout; |
| const Unique<VkPipeline> m_pipeline; |
| |
| const Unique<VkBuffer> m_vertexBuffer; |
| const UniquePtr<Allocation> m_vertexBufferMemory; |
| |
| vector<ImageViewSp> m_attachmentViews; |
| vector<FramebufferSp> m_framebuffers; |
| }; |
| |
| Move<VkRenderPass> TriangleRenderer::createRenderPass (const DeviceInterface& vkd, |
| const VkDevice device, |
| const VkFormat colorAttachmentFormat) |
| { |
| const VkAttachmentDescription colorAttDesc = |
| { |
| (VkAttachmentDescriptionFlags)0, |
| colorAttachmentFormat, |
| VK_SAMPLE_COUNT_1_BIT, |
| VK_ATTACHMENT_LOAD_OP_CLEAR, |
| VK_ATTACHMENT_STORE_OP_STORE, |
| VK_ATTACHMENT_LOAD_OP_DONT_CARE, |
| VK_ATTACHMENT_STORE_OP_DONT_CARE, |
| VK_IMAGE_LAYOUT_UNDEFINED, |
| VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, |
| }; |
| const VkAttachmentReference colorAttRef = |
| { |
| 0u, |
| VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, |
| }; |
| const VkSubpassDescription subpassDesc = |
| { |
| (VkSubpassDescriptionFlags)0u, |
| VK_PIPELINE_BIND_POINT_GRAPHICS, |
| 0u, // inputAttachmentCount |
| DE_NULL, // pInputAttachments |
| 1u, // colorAttachmentCount |
| &colorAttRef, // pColorAttachments |
| DE_NULL, // pResolveAttachments |
| DE_NULL, // depthStencilAttachment |
| 0u, // preserveAttachmentCount |
| DE_NULL, // pPreserveAttachments |
| }; |
| const VkSubpassDependency dependencies[] = |
| { |
| { |
| VK_SUBPASS_EXTERNAL, // srcSubpass |
| 0u, // dstSubpass |
| VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, |
| VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, |
| VK_ACCESS_MEMORY_READ_BIT, |
| (VK_ACCESS_COLOR_ATTACHMENT_READ_BIT| |
| VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT), |
| VK_DEPENDENCY_BY_REGION_BIT |
| }, |
| { |
| 0u, // srcSubpass |
| VK_SUBPASS_EXTERNAL, // dstSubpass |
| VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, |
| VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, |
| (VK_ACCESS_COLOR_ATTACHMENT_READ_BIT| |
| VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT), |
| VK_ACCESS_MEMORY_READ_BIT, |
| VK_DEPENDENCY_BY_REGION_BIT |
| }, |
| }; |
| const VkRenderPassCreateInfo renderPassParams = |
| { |
| VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, |
| DE_NULL, |
| (VkRenderPassCreateFlags)0, |
| 1u, |
| &colorAttDesc, |
| 1u, |
| &subpassDesc, |
| DE_LENGTH_OF_ARRAY(dependencies), |
| dependencies, |
| }; |
| |
| return vk::createRenderPass(vkd, device, &renderPassParams); |
| } |
| |
| Move<VkPipelineLayout> TriangleRenderer::createPipelineLayout (const DeviceInterface& vkd, |
| const VkDevice device) |
| { |
| const VkPushConstantRange pushConstantRange = |
| { |
| VK_SHADER_STAGE_VERTEX_BIT, |
| 0u, // offset |
| (deUint32)sizeof(deUint32), // size |
| }; |
| const VkPipelineLayoutCreateInfo pipelineLayoutParams = |
| { |
| VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, |
| DE_NULL, |
| (vk::VkPipelineLayoutCreateFlags)0, |
| 0u, // setLayoutCount |
| DE_NULL, // pSetLayouts |
| 1u, |
| &pushConstantRange, |
| }; |
| |
| return vk::createPipelineLayout(vkd, device, &pipelineLayoutParams); |
| } |
| |
| Move<VkPipeline> TriangleRenderer::createPipeline (const DeviceInterface& vkd, |
| const VkDevice device, |
| const VkRenderPass renderPass, |
| const VkPipelineLayout pipelineLayout, |
| const BinaryCollection& binaryCollection, |
| const UVec2& renderSize) |
| { |
| // \note VkShaderModules are fully consumed by vkCreateGraphicsPipelines() |
| // and can be deleted immediately following that call. |
| const Unique<VkShaderModule> vertShaderModule (createShaderModule(vkd, device, binaryCollection.get("tri-vert"), 0)); |
| const Unique<VkShaderModule> fragShaderModule (createShaderModule(vkd, device, binaryCollection.get("tri-frag"), 0)); |
| |
| const VkSpecializationInfo emptyShaderSpecParams = |
| { |
| 0u, // mapEntryCount |
| DE_NULL, // pMap |
| 0, // dataSize |
| DE_NULL, // pData |
| }; |
| const VkPipelineShaderStageCreateInfo shaderStageParams[] = |
| { |
| { |
| VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, |
| DE_NULL, |
| (VkPipelineShaderStageCreateFlags)0, |
| VK_SHADER_STAGE_VERTEX_BIT, |
| *vertShaderModule, |
| "main", |
| &emptyShaderSpecParams, |
| }, |
| { |
| VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, |
| DE_NULL, |
| (VkPipelineShaderStageCreateFlags)0, |
| VK_SHADER_STAGE_FRAGMENT_BIT, |
| *fragShaderModule, |
| "main", |
| &emptyShaderSpecParams, |
| } |
| }; |
| const VkPipelineDepthStencilStateCreateInfo depthStencilParams = |
| { |
| VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, |
| DE_NULL, |
| (VkPipelineDepthStencilStateCreateFlags)0, |
| DE_FALSE, // depthTestEnable |
| DE_FALSE, // depthWriteEnable |
| VK_COMPARE_OP_ALWAYS, // depthCompareOp |
| DE_FALSE, // depthBoundsTestEnable |
| DE_FALSE, // stencilTestEnable |
| { |
| VK_STENCIL_OP_KEEP, // failOp |
| VK_STENCIL_OP_KEEP, // passOp |
| VK_STENCIL_OP_KEEP, // depthFailOp |
| VK_COMPARE_OP_ALWAYS, // compareOp |
| 0u, // compareMask |
| 0u, // writeMask |
| 0u, // reference |
| }, // front |
| { |
| VK_STENCIL_OP_KEEP, // failOp |
| VK_STENCIL_OP_KEEP, // passOp |
| VK_STENCIL_OP_KEEP, // depthFailOp |
| VK_COMPARE_OP_ALWAYS, // compareOp |
| 0u, // compareMask |
| 0u, // writeMask |
| 0u, // reference |
| }, // back |
| -1.0f, // minDepthBounds |
| +1.0f, // maxDepthBounds |
| }; |
| const VkViewport viewport0 = |
| { |
| 0.0f, // x |
| 0.0f, // y |
| (float)renderSize.x(), // width |
| (float)renderSize.y(), // height |
| 0.0f, // minDepth |
| 1.0f, // maxDepth |
| }; |
| const VkRect2D scissor0 = |
| { |
| { 0u, 0u, }, // offset |
| { renderSize.x(), renderSize.y() }, // extent |
| }; |
| const VkPipelineViewportStateCreateInfo viewportParams = |
| { |
| VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, |
| DE_NULL, |
| (VkPipelineViewportStateCreateFlags)0, |
| 1u, |
| &viewport0, |
| 1u, |
| &scissor0 |
| }; |
| const VkPipelineMultisampleStateCreateInfo multisampleParams = |
| { |
| VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, |
| DE_NULL, |
| (VkPipelineMultisampleStateCreateFlags)0, |
| VK_SAMPLE_COUNT_1_BIT, // rasterizationSamples |
| VK_FALSE, // sampleShadingEnable |
| 0.0f, // minSampleShading |
| (const VkSampleMask*)DE_NULL, // sampleMask |
| VK_FALSE, // alphaToCoverageEnable |
| VK_FALSE, // alphaToOneEnable |
| }; |
| const VkPipelineRasterizationStateCreateInfo rasterParams = |
| { |
| VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, |
| DE_NULL, |
| (VkPipelineRasterizationStateCreateFlags)0, |
| VK_FALSE, // depthClampEnable |
| VK_FALSE, // rasterizerDiscardEnable |
| VK_POLYGON_MODE_FILL, // polygonMode |
| VK_CULL_MODE_NONE, // cullMode |
| VK_FRONT_FACE_COUNTER_CLOCKWISE, // frontFace |
| VK_FALSE, // depthBiasEnable |
| 0.0f, // depthBiasConstantFactor |
| 0.0f, // depthBiasClamp |
| 0.0f, // depthBiasSlopeFactor |
| 1.0f, // lineWidth |
| }; |
| const VkPipelineInputAssemblyStateCreateInfo inputAssemblyParams = |
| { |
| VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, |
| DE_NULL, |
| (VkPipelineInputAssemblyStateCreateFlags)0, |
| VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, |
| DE_FALSE, // primitiveRestartEnable |
| }; |
| const VkVertexInputBindingDescription vertexBinding0 = |
| { |
| 0u, // binding |
| (deUint32)sizeof(tcu::Vec4), // stride |
| VK_VERTEX_INPUT_RATE_VERTEX, // inputRate |
| }; |
| const VkVertexInputAttributeDescription vertexAttrib0 = |
| { |
| 0u, // location |
| 0u, // binding |
| VK_FORMAT_R32G32B32A32_SFLOAT, // format |
| 0u, // offset |
| }; |
| const VkPipelineVertexInputStateCreateInfo vertexInputStateParams = |
| { |
| VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, |
| DE_NULL, |
| (VkPipelineVertexInputStateCreateFlags)0, |
| 1u, |
| &vertexBinding0, |
| 1u, |
| &vertexAttrib0, |
| }; |
| const VkPipelineColorBlendAttachmentState attBlendParams0 = |
| { |
| VK_FALSE, // blendEnable |
| VK_BLEND_FACTOR_ONE, // srcColorBlendFactor |
| VK_BLEND_FACTOR_ZERO, // dstColorBlendFactor |
| VK_BLEND_OP_ADD, // colorBlendOp |
| VK_BLEND_FACTOR_ONE, // srcAlphaBlendFactor |
| VK_BLEND_FACTOR_ZERO, // dstAlphaBlendFactor |
| VK_BLEND_OP_ADD, // alphaBlendOp |
| (VK_COLOR_COMPONENT_R_BIT| |
| VK_COLOR_COMPONENT_G_BIT| |
| VK_COLOR_COMPONENT_B_BIT| |
| VK_COLOR_COMPONENT_A_BIT), // colorWriteMask |
| }; |
| const VkPipelineColorBlendStateCreateInfo blendParams = |
| { |
| VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, |
| DE_NULL, |
| (VkPipelineColorBlendStateCreateFlags)0, |
| VK_FALSE, // logicOpEnable |
| VK_LOGIC_OP_COPY, |
| 1u, |
| &attBlendParams0, |
| { 0.0f, 0.0f, 0.0f, 0.0f }, // blendConstants[4] |
| }; |
| const VkGraphicsPipelineCreateInfo pipelineParams = |
| { |
| VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, |
| DE_NULL, |
| (VkPipelineCreateFlags)0, |
| (deUint32)DE_LENGTH_OF_ARRAY(shaderStageParams), |
| shaderStageParams, |
| &vertexInputStateParams, |
| &inputAssemblyParams, |
| (const VkPipelineTessellationStateCreateInfo*)DE_NULL, |
| &viewportParams, |
| &rasterParams, |
| &multisampleParams, |
| &depthStencilParams, |
| &blendParams, |
| (const VkPipelineDynamicStateCreateInfo*)DE_NULL, |
| pipelineLayout, |
| renderPass, |
| 0u, // subpass |
| DE_NULL, // basePipelineHandle |
| 0u, // basePipelineIndex |
| }; |
| |
| return vk::createGraphicsPipeline(vkd, device, (VkPipelineCache)0, &pipelineParams); |
| } |
| |
| Move<VkImageView> TriangleRenderer::createAttachmentView (const DeviceInterface& vkd, |
| const VkDevice device, |
| const VkImage image, |
| const VkFormat format) |
| { |
| const VkImageViewCreateInfo viewParams = |
| { |
| VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, |
| DE_NULL, |
| (VkImageViewCreateFlags)0, |
| image, |
| VK_IMAGE_VIEW_TYPE_2D, |
| format, |
| vk::makeComponentMappingRGBA(), |
| { |
| VK_IMAGE_ASPECT_COLOR_BIT, |
| 0u, // baseMipLevel |
| 1u, // levelCount |
| 0u, // baseArrayLayer |
| 1u, // layerCount |
| }, |
| }; |
| |
| return vk::createImageView(vkd, device, &viewParams); |
| } |
| |
| Move<VkFramebuffer> TriangleRenderer::createFramebuffer (const DeviceInterface& vkd, |
| const VkDevice device, |
| const VkRenderPass renderPass, |
| const VkImageView colorAttachment, |
| const UVec2& renderSize) |
| { |
| const VkFramebufferCreateInfo framebufferParams = |
| { |
| VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, |
| DE_NULL, |
| (VkFramebufferCreateFlags)0, |
| renderPass, |
| 1u, |
| &colorAttachment, |
| renderSize.x(), |
| renderSize.y(), |
| 1u, // layers |
| }; |
| |
| return vk::createFramebuffer(vkd, device, &framebufferParams); |
| } |
| |
| Move<VkBuffer> TriangleRenderer::createBuffer (const DeviceInterface& vkd, |
| VkDevice device, |
| VkDeviceSize size, |
| VkBufferUsageFlags usage) |
| { |
| const VkBufferCreateInfo bufferParams = |
| { |
| VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, |
| DE_NULL, |
| (VkBufferCreateFlags)0, |
| size, |
| usage, |
| VK_SHARING_MODE_EXCLUSIVE, |
| 0, |
| DE_NULL |
| }; |
| |
| return vk::createBuffer(vkd, device, &bufferParams); |
| } |
| |
| TriangleRenderer::TriangleRenderer (const DeviceInterface& vkd, |
| const VkDevice device, |
| Allocator& allocator, |
| const BinaryCollection& binaryRegistry, |
| const vector<VkImage> swapchainImages, |
| const VkFormat framebufferFormat, |
| const UVec2& renderSize) |
| : m_vkd (vkd) |
| , m_swapchainImages (swapchainImages) |
| , m_renderSize (renderSize) |
| , m_renderPass (createRenderPass(vkd, device, framebufferFormat)) |
| , m_pipelineLayout (createPipelineLayout(vkd, device)) |
| , m_pipeline (createPipeline(vkd, device, *m_renderPass, *m_pipelineLayout, binaryRegistry, renderSize)) |
| , m_vertexBuffer (createBuffer(vkd, device, (VkDeviceSize)(sizeof(float)*4*3), VK_BUFFER_USAGE_VERTEX_BUFFER_BIT)) |
| , m_vertexBufferMemory (allocator.allocate(getBufferMemoryRequirements(vkd, device, *m_vertexBuffer), |
| MemoryRequirement::HostVisible)) |
| { |
| m_attachmentViews.resize(swapchainImages.size()); |
| m_framebuffers.resize(swapchainImages.size()); |
| |
| for (size_t imageNdx = 0; imageNdx < swapchainImages.size(); ++imageNdx) |
| { |
| m_attachmentViews[imageNdx] = ImageViewSp(new Unique<VkImageView>(createAttachmentView(vkd, device, swapchainImages[imageNdx], framebufferFormat))); |
| m_framebuffers[imageNdx] = FramebufferSp(new Unique<VkFramebuffer>(createFramebuffer(vkd, device, *m_renderPass, **m_attachmentViews[imageNdx], renderSize))); |
| } |
| |
| VK_CHECK(vkd.bindBufferMemory(device, *m_vertexBuffer, m_vertexBufferMemory->getMemory(), m_vertexBufferMemory->getOffset())); |
| |
| { |
| const VkMappedMemoryRange memRange = |
| { |
| VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, |
| DE_NULL, |
| m_vertexBufferMemory->getMemory(), |
| m_vertexBufferMemory->getOffset(), |
| VK_WHOLE_SIZE |
| }; |
| const tcu::Vec4 vertices[] = |
| { |
| tcu::Vec4(-0.5f, -0.5f, 0.0f, 1.0f), |
| tcu::Vec4(+0.5f, -0.5f, 0.0f, 1.0f), |
| tcu::Vec4( 0.0f, +0.5f, 0.0f, 1.0f) |
| }; |
| DE_STATIC_ASSERT(sizeof(vertices) == sizeof(float)*4*3); |
| |
| deMemcpy(m_vertexBufferMemory->getHostPtr(), &vertices[0], sizeof(vertices)); |
| VK_CHECK(vkd.flushMappedMemoryRanges(device, 1u, &memRange)); |
| } |
| } |
| |
| TriangleRenderer::~TriangleRenderer (void) |
| { |
| } |
| |
| void TriangleRenderer::recordFrame (VkCommandBuffer cmdBuffer, |
| deUint32 imageNdx, |
| deUint32 frameNdx) const |
| { |
| const VkFramebuffer curFramebuffer = **m_framebuffers[imageNdx]; |
| |
| { |
| const VkCommandBufferBeginInfo cmdBufBeginParams = |
| { |
| VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, |
| DE_NULL, |
| (VkCommandBufferUsageFlags)0, |
| (const VkCommandBufferInheritanceInfo*)DE_NULL, |
| }; |
| VK_CHECK(m_vkd.beginCommandBuffer(cmdBuffer, &cmdBufBeginParams)); |
| } |
| |
| { |
| const VkClearValue clearValue = makeClearValueColorF32(0.125f, 0.25f, 0.75f, 1.0f); |
| const VkRenderPassBeginInfo passBeginParams = |
| { |
| VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, |
| DE_NULL, |
| *m_renderPass, |
| curFramebuffer, |
| { |
| { 0, 0 }, |
| { (deUint32)m_renderSize.x(), (deUint32)m_renderSize.y() } |
| }, // renderArea |
| 1u, // clearValueCount |
| &clearValue, // pClearValues |
| }; |
| m_vkd.cmdBeginRenderPass(cmdBuffer, &passBeginParams, VK_SUBPASS_CONTENTS_INLINE); |
| } |
| |
| m_vkd.cmdBindPipeline(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipeline); |
| |
| { |
| const VkDeviceSize bindingOffset = 0; |
| m_vkd.cmdBindVertexBuffers(cmdBuffer, 0u, 1u, &m_vertexBuffer.get(), &bindingOffset); |
| } |
| |
| m_vkd.cmdPushConstants(cmdBuffer, *m_pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0u, (deUint32)sizeof(deUint32), &frameNdx); |
| m_vkd.cmdDraw(cmdBuffer, 3u, 1u, 0u, 0u); |
| m_vkd.cmdEndRenderPass(cmdBuffer); |
| |
| VK_CHECK(m_vkd.endCommandBuffer(cmdBuffer)); |
| } |
| |
| void TriangleRenderer::getPrograms (SourceCollections& dst) |
| { |
| dst.glslSources.add("tri-vert") << glu::VertexSource( |
| "#version 310 es\n" |
| "layout(location = 0) in highp vec4 a_position;\n" |
| "layout(push_constant) uniform FrameData\n" |
| "{\n" |
| " highp uint frameNdx;\n" |
| "} frameData;\n" |
| "void main (void)\n" |
| "{\n" |
| " highp float angle = float(frameData.frameNdx) / 100.0;\n" |
| " highp float c = cos(angle);\n" |
| " highp float s = sin(angle);\n" |
| " highp mat4 t = mat4( c, -s, 0, 0,\n" |
| " s, c, 0, 0,\n" |
| " 0, 0, 1, 0,\n" |
| " 0, 0, 0, 1);\n" |
| " gl_Position = t * a_position;\n" |
| "}\n"); |
| dst.glslSources.add("tri-frag") << glu::FragmentSource( |
| "#version 310 es\n" |
| "layout(location = 0) out lowp vec4 o_color;\n" |
| "void main (void) { o_color = vec4(1.0, 0.0, 1.0, 1.0); }\n"); |
| } |
| |
| typedef de::SharedPtr<Unique<VkCommandBuffer> > CommandBufferSp; |
| typedef de::SharedPtr<Unique<VkFence> > FenceSp; |
| typedef de::SharedPtr<Unique<VkSemaphore> > SemaphoreSp; |
| |
| vector<FenceSp> createFences (const DeviceInterface& vkd, |
| const VkDevice device, |
| size_t numFences) |
| { |
| vector<FenceSp> fences(numFences); |
| |
| for (size_t ndx = 0; ndx < numFences; ++ndx) |
| fences[ndx] = FenceSp(new Unique<VkFence>(createFence(vkd, device))); |
| |
| return fences; |
| } |
| |
| vector<SemaphoreSp> createSemaphores (const DeviceInterface& vkd, |
| const VkDevice device, |
| size_t numSemaphores) |
| { |
| vector<SemaphoreSp> semaphores(numSemaphores); |
| |
| for (size_t ndx = 0; ndx < numSemaphores; ++ndx) |
| semaphores[ndx] = SemaphoreSp(new Unique<VkSemaphore>(createSemaphore(vkd, device))); |
| |
| return semaphores; |
| } |
| |
| vector<CommandBufferSp> allocateCommandBuffers (const DeviceInterface& vkd, |
| const VkDevice device, |
| const VkCommandPool commandPool, |
| const VkCommandBufferLevel level, |
| const size_t numCommandBuffers) |
| { |
| 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 basicRenderTest (Context& context, Type wsiType) |
| { |
| const tcu::UVec2 desiredSize (256, 256); |
| const InstanceHelper instHelper (context, wsiType); |
| const NativeObjects native (context, instHelper.supportedExtensions, wsiType, tcu::just(desiredSize)); |
| const Unique<VkSurfaceKHR> surface (createSurface(instHelper.vki, *instHelper.instance, wsiType, *native.display, *native.window)); |
| const DeviceHelper devHelper (context, instHelper.vki, *instHelper.instance, *surface); |
| const DeviceInterface& vkd = devHelper.vkd; |
| const VkDevice device = *devHelper.device; |
| SimpleAllocator allocator (vkd, device, getPhysicalDeviceMemoryProperties(instHelper.vki, devHelper.physicalDevice)); |
| const VkSwapchainCreateInfoKHR swapchainInfo = getBasicSwapchainParameters(wsiType, instHelper.vki, devHelper.physicalDevice, *surface, desiredSize, 2); |
| const Unique<VkSwapchainKHR> swapchain (createSwapchainKHR(vkd, device, &swapchainInfo)); |
| const vector<VkImage> swapchainImages = getSwapchainImages(vkd, device, *swapchain); |
| |
| const TriangleRenderer renderer (vkd, |
| device, |
| allocator, |
| context.getBinaryCollection(), |
| 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 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 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 vector<SemaphoreSp> renderingCompleteSemaphores (createSemaphores(vkd, device, maxQueuedFrames)); |
| const vector<CommandBufferSp> commandBuffers (allocateCommandBuffers(vkd, device, *commandPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, maxQueuedFrames)); |
| |
| try |
| { |
| const deUint32 numFramesToRender = 60*10; |
| |
| 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 (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, |
| imageReadyFence, |
| &imageNdx); |
| |
| if (acquireResult == VK_SUBOPTIMAL_KHR) |
| context.getTestContext().getLog() << TestLog::Message << "Got " << acquireResult << " at frame " << frameNdx << TestLog::EndMessage; |
| else |
| 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, (VkFence)0)); |
| VK_CHECK(vkd.queuePresentKHR(devHelper.queue, &presentInfo)); |
| } |
| } |
| |
| VK_CHECK(vkd.deviceWaitIdle(device)); |
| } |
| catch (...) |
| { |
| // Make sure device is idle before destroying resources |
| vkd.deviceWaitIdle(device); |
| throw; |
| } |
| |
| return tcu::TestStatus::pass("Rendering tests succeeded"); |
| } |
| |
| vector<tcu::UVec2> getSwapchainSizeSequence (const VkSurfaceCapabilitiesKHR& capabilities, const tcu::UVec2& defaultSize) |
| { |
| vector<tcu::UVec2> sizes(3); |
| sizes[0] = defaultSize / 2u; |
| sizes[1] = defaultSize; |
| sizes[2] = defaultSize * 2u; |
| |
| for (deUint32 i = 0; i < sizes.size(); ++i) |
| { |
| sizes[i].x() = de::clamp(sizes[i].x(), capabilities.minImageExtent.width, capabilities.maxImageExtent.width); |
| sizes[i].y() = de::clamp(sizes[i].y(), capabilities.minImageExtent.height, capabilities.maxImageExtent.height); |
| } |
| |
| return sizes; |
| } |
| |
| tcu::TestStatus resizeSwapchainTest (Context& context, Type wsiType) |
| { |
| const tcu::UVec2 desiredSize (256, 256); |
| const InstanceHelper instHelper (context, wsiType); |
| const NativeObjects native (context, instHelper.supportedExtensions, wsiType, tcu::just(desiredSize)); |
| const Unique<VkSurfaceKHR> surface (createSurface(instHelper.vki, *instHelper.instance, wsiType, *native.display, *native.window)); |
| const DeviceHelper devHelper (context, instHelper.vki, *instHelper.instance, *surface); |
| const PlatformProperties& platformProperties = getPlatformProperties(wsiType); |
| const VkSurfaceCapabilitiesKHR capabilities = getPhysicalDeviceSurfaceCapabilities(instHelper.vki, devHelper.physicalDevice, *surface); |
| const DeviceInterface& vkd = devHelper.vkd; |
| const VkDevice device = *devHelper.device; |
| SimpleAllocator allocator (vkd, device, getPhysicalDeviceMemoryProperties(instHelper.vki, devHelper.physicalDevice)); |
| vector<tcu::UVec2> sizes = getSwapchainSizeSequence(capabilities, desiredSize); |
| Move<VkSwapchainKHR> prevSwapchain; |
| |
| DE_ASSERT(platformProperties.swapchainExtent != PlatformProperties::SWAPCHAIN_EXTENT_MUST_MATCH_WINDOW_SIZE); |
| DE_UNREF(platformProperties); |
| |
| for (deUint32 sizeNdx = 0; sizeNdx < sizes.size(); ++sizeNdx) |
| { |
| // \todo [2016-05-30 jesse] This test currently waits for idle and |
| // recreates way more than necessary when recreating the swapchain. Make |
| // it match expected real app behavior better by smoothly switching from |
| // old to new swapchain. Once that is done, it will also be possible to |
| // test creating a new swapchain while images from the previous one are |
| // still acquired. |
| |
| VkSwapchainCreateInfoKHR swapchainInfo = getBasicSwapchainParameters(wsiType, instHelper.vki, devHelper.physicalDevice, *surface, sizes[sizeNdx], 2); |
| swapchainInfo.oldSwapchain = *prevSwapchain; |
| |
| Move<VkSwapchainKHR> swapchain (createSwapchainKHR(vkd, device, &swapchainInfo)); |
| const vector<VkImage> swapchainImages = getSwapchainImages(vkd, device, *swapchain); |
| const TriangleRenderer renderer (vkd, |
| device, |
| allocator, |
| context.getBinaryCollection(), |
| 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 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 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 vector<SemaphoreSp> renderingCompleteSemaphores (createSemaphores(vkd, device, maxQueuedFrames)); |
| const vector<CommandBufferSp> commandBuffers (allocateCommandBuffers(vkd, device, *commandPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, maxQueuedFrames)); |
| |
| 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 (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, |
| imageReadyFence, |
| &imageNdx); |
| |
| if (acquireResult == VK_SUBOPTIMAL_KHR) |
| context.getTestContext().getLog() << TestLog::Message << "Got " << acquireResult << " at frame " << frameNdx << TestLog::EndMessage; |
| else |
| 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, (VkFence)0)); |
| VK_CHECK(vkd.queuePresentKHR(devHelper.queue, &presentInfo)); |
| } |
| } |
| |
| VK_CHECK(vkd.deviceWaitIdle(device)); |
| |
| prevSwapchain = swapchain; |
| } |
| catch (...) |
| { |
| // Make sure device is idle before destroying resources |
| vkd.deviceWaitIdle(device); |
| throw; |
| } |
| } |
| |
| return tcu::TestStatus::pass("Resizing tests succeeded"); |
| } |
| |
| tcu::TestStatus getImagesIncompleteResultTest (Context& context, Type wsiType) |
| { |
| const tcu::UVec2 desiredSize (256, 256); |
| const InstanceHelper instHelper (context, wsiType); |
| const NativeObjects native (context, instHelper.supportedExtensions, wsiType, tcu::just(desiredSize)); |
| const Unique<VkSurfaceKHR> surface (createSurface(instHelper.vki, *instHelper.instance, wsiType, *native.display, *native.window)); |
| const DeviceHelper devHelper (context, instHelper.vki, *instHelper.instance, *surface); |
| const VkSwapchainCreateInfoKHR swapchainInfo = getBasicSwapchainParameters(wsiType, instHelper.vki, devHelper.physicalDevice, *surface, desiredSize, 2); |
| const Unique<VkSwapchainKHR> swapchain (createSwapchainKHR(devHelper.vkd, *devHelper.device, &swapchainInfo)); |
| |
| vector<VkImage> swapchainImages = getSwapchainImages(devHelper.vkd, *devHelper.device, *swapchain); |
| |
| ValidateQueryBits::fillBits(swapchainImages.begin(), swapchainImages.end()); |
| |
| const deUint32 usedCount = static_cast<deUint32>(swapchainImages.size() / 2); |
| deUint32 count = usedCount; |
| const VkResult result = devHelper.vkd.getSwapchainImagesKHR(*devHelper.device, *swapchain, &count, &swapchainImages[0]); |
| |
| if (count != usedCount || result != VK_INCOMPLETE || !ValidateQueryBits::checkBits(swapchainImages.begin() + count, swapchainImages.end())) |
| return tcu::TestStatus::fail("Get swapchain images didn't return VK_INCOMPLETE"); |
| else |
| return tcu::TestStatus::pass("Get swapchain images tests succeeded"); |
| } |
| |
| tcu::TestStatus destroyNullHandleSwapchainTest (Context& context, Type wsiType) |
| { |
| const InstanceHelper instHelper (context, wsiType); |
| const NativeObjects native (context, instHelper.supportedExtensions, wsiType); |
| const Unique<VkSurfaceKHR> surface (createSurface(instHelper.vki, *instHelper.instance, wsiType, *native.display, *native.window)); |
| const DeviceHelper devHelper (context, instHelper.vki, *instHelper.instance, *surface); |
| const VkSwapchainKHR nullHandle = DE_NULL; |
| |
| // Default allocator |
| devHelper.vkd.destroySwapchainKHR(*devHelper.device, nullHandle, DE_NULL); |
| |
| // Custom allocator |
| { |
| AllocationCallbackRecorder recordingAllocator (getSystemAllocator(), 1u); |
| |
| devHelper.vkd.destroySwapchainKHR(*devHelper.device, nullHandle, recordingAllocator.getCallbacks()); |
| |
| if (recordingAllocator.getNumRecords() != 0u) |
| return tcu::TestStatus::fail("Implementation allocated/freed the memory"); |
| } |
| |
| return tcu::TestStatus::pass("Destroying a VK_NULL_HANDLE surface has no effect"); |
| } |
| |
| void getBasicRenderPrograms (SourceCollections& dst, Type) |
| { |
| TriangleRenderer::getPrograms(dst); |
| } |
| |
| void populateRenderGroup (tcu::TestCaseGroup* testGroup, Type wsiType) |
| { |
| addFunctionCaseWithPrograms(testGroup, "basic", "Basic Rendering Test", getBasicRenderPrograms, basicRenderTest, wsiType); |
| } |
| |
| void populateGetImagesGroup (tcu::TestCaseGroup* testGroup, Type wsiType) |
| { |
| addFunctionCase(testGroup, "incomplete", "Test VK_INCOMPLETE return code", getImagesIncompleteResultTest, wsiType); |
| } |
| |
| void populateModifyGroup (tcu::TestCaseGroup* testGroup, Type wsiType) |
| { |
| const PlatformProperties& platformProperties = getPlatformProperties(wsiType); |
| |
| if (platformProperties.swapchainExtent != PlatformProperties::SWAPCHAIN_EXTENT_MUST_MATCH_WINDOW_SIZE) |
| { |
| addFunctionCaseWithPrograms(testGroup, "resize", "Resize Swapchain Test", getBasicRenderPrograms, resizeSwapchainTest, wsiType); |
| } |
| |
| // \todo [2016-05-30 jesse] Add tests for modifying preTransform, compositeAlpha, presentMode |
| } |
| |
| void populateDestroyGroup (tcu::TestCaseGroup* testGroup, Type wsiType) |
| { |
| addFunctionCase(testGroup, "null_handle", "Destroying a VK_NULL_HANDLE swapchain", destroyNullHandleSwapchainTest, wsiType); |
| } |
| |
| } // anonymous |
| |
| void createSwapchainTests (tcu::TestCaseGroup* testGroup, vk::wsi::Type wsiType) |
| { |
| addTestGroup(testGroup, "create", "Create VkSwapchain with various parameters", populateSwapchainGroup, GroupParameters(wsiType, createSwapchainTest)); |
| addTestGroup(testGroup, "simulate_oom", "Simulate OOM using callbacks during swapchain construction", populateSwapchainGroup, GroupParameters(wsiType, createSwapchainSimulateOOMTest)); |
| addTestGroup(testGroup, "render", "Rendering Tests", populateRenderGroup, wsiType); |
| addTestGroup(testGroup, "modify", "Modify VkSwapchain", populateModifyGroup, wsiType); |
| addTestGroup(testGroup, "destroy", "Destroy VkSwapchain", populateDestroyGroup, wsiType); |
| addTestGroup(testGroup, "get_images", "Get swapchain images", populateGetImagesGroup, wsiType); |
| } |
| |
| } // wsi |
| } // vkt |