vktWsiSwapchainTests.cpp revision 74a8338764495b470180da1f4c587c3401373314
1/*------------------------------------------------------------------------- 2 * Vulkan Conformance Tests 3 * ------------------------ 4 * 5 * Copyright (c) 2016 Google Inc. 6 * 7 * Licensed under the Apache License, Version 2.0 (the "License"); 8 * you may not use this file except in compliance with the License. 9 * You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, software 14 * distributed under the License is distributed on an "AS IS" BASIS, 15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 * See the License for the specific language governing permissions and 17 * limitations under the License. 18 * 19 *//*! 20 * \file 21 * \brief VkSwapchain Tests 22 *//*--------------------------------------------------------------------*/ 23 24#include "vktWsiSwapchainTests.hpp" 25 26#include "vktTestCaseUtil.hpp" 27#include "vktTestGroupUtil.hpp" 28 29#include "vkDefs.hpp" 30#include "vkPlatform.hpp" 31#include "vkStrUtil.hpp" 32#include "vkRef.hpp" 33#include "vkRefUtil.hpp" 34#include "vkQueryUtil.hpp" 35#include "vkMemUtil.hpp" 36#include "vkDeviceUtil.hpp" 37#include "vkPrograms.hpp" 38#include "vkTypeUtil.hpp" 39#include "vkWsiPlatform.hpp" 40#include "vkWsiUtil.hpp" 41#include "vkAllocationCallbackUtil.hpp" 42 43#include "tcuTestLog.hpp" 44#include "tcuFormatUtil.hpp" 45#include "tcuPlatform.hpp" 46#include "tcuResultCollector.hpp" 47 48#include "deUniquePtr.hpp" 49#include "deStringUtil.hpp" 50#include "deArrayUtil.hpp" 51#include "deSharedPtr.hpp" 52 53#include <limits> 54 55namespace vkt 56{ 57namespace wsi 58{ 59 60namespace 61{ 62 63using namespace vk; 64using namespace vk::wsi; 65 66using tcu::TestLog; 67using tcu::Maybe; 68using tcu::UVec2; 69 70using de::MovePtr; 71using de::UniquePtr; 72 73using std::string; 74using std::vector; 75 76typedef vector<VkExtensionProperties> Extensions; 77 78void checkAllSupported (const Extensions& supportedExtensions, const vector<string>& requiredExtensions) 79{ 80 for (vector<string>::const_iterator requiredExtName = requiredExtensions.begin(); 81 requiredExtName != requiredExtensions.end(); 82 ++requiredExtName) 83 { 84 if (!isExtensionSupported(supportedExtensions, RequiredExtension(*requiredExtName))) 85 TCU_THROW(NotSupportedError, (*requiredExtName + " is not supported").c_str()); 86 } 87} 88 89Move<VkInstance> createInstanceWithWsi (const PlatformInterface& vkp, 90 const Extensions& supportedExtensions, 91 Type wsiType, 92 const VkAllocationCallbacks* pAllocator = DE_NULL) 93{ 94 vector<string> extensions; 95 96 extensions.push_back("VK_KHR_surface"); 97 extensions.push_back(getExtensionName(wsiType)); 98 99 checkAllSupported(supportedExtensions, extensions); 100 101 return createDefaultInstance(vkp, vector<string>(), extensions, pAllocator); 102} 103 104VkPhysicalDeviceFeatures getDeviceFeaturesForWsi (void) 105{ 106 VkPhysicalDeviceFeatures features; 107 deMemset(&features, 0, sizeof(features)); 108 return features; 109} 110 111Move<VkDevice> createDeviceWithWsi (const InstanceInterface& vki, 112 VkPhysicalDevice physicalDevice, 113 const Extensions& supportedExtensions, 114 const deUint32 queueFamilyIndex, 115 const VkAllocationCallbacks* pAllocator = DE_NULL) 116{ 117 const float queuePriorities[] = { 1.0f }; 118 const VkDeviceQueueCreateInfo queueInfos[] = 119 { 120 { 121 VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, 122 DE_NULL, 123 (VkDeviceQueueCreateFlags)0, 124 queueFamilyIndex, 125 DE_LENGTH_OF_ARRAY(queuePriorities), 126 &queuePriorities[0] 127 } 128 }; 129 const VkPhysicalDeviceFeatures features = getDeviceFeaturesForWsi(); 130 const char* const extensions[] = { "VK_KHR_swapchain" }; 131 const VkDeviceCreateInfo deviceParams = 132 { 133 VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, 134 DE_NULL, 135 (VkDeviceCreateFlags)0, 136 DE_LENGTH_OF_ARRAY(queueInfos), 137 &queueInfos[0], 138 0u, // enabledLayerCount 139 DE_NULL, // ppEnabledLayerNames 140 DE_LENGTH_OF_ARRAY(extensions), // enabledExtensionCount 141 DE_ARRAY_BEGIN(extensions), // ppEnabledExtensionNames 142 &features 143 }; 144 145 for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(extensions); ++ndx) 146 { 147 if (!isExtensionSupported(supportedExtensions, RequiredExtension(extensions[ndx]))) 148 TCU_THROW(NotSupportedError, (string(extensions[ndx]) + " is not supported").c_str()); 149 } 150 151 return createDevice(vki, physicalDevice, &deviceParams, pAllocator); 152} 153 154deUint32 getNumQueueFamilyIndices (const InstanceInterface& vki, VkPhysicalDevice physicalDevice) 155{ 156 deUint32 numFamilies = 0; 157 158 vki.getPhysicalDeviceQueueFamilyProperties(physicalDevice, &numFamilies, DE_NULL); 159 160 return numFamilies; 161} 162 163vector<deUint32> getSupportedQueueFamilyIndices (const InstanceInterface& vki, VkPhysicalDevice physicalDevice, VkSurfaceKHR surface) 164{ 165 const deUint32 numTotalFamilyIndices = getNumQueueFamilyIndices(vki, physicalDevice); 166 vector<deUint32> supportedFamilyIndices; 167 168 for (deUint32 queueFamilyNdx = 0; queueFamilyNdx < numTotalFamilyIndices; ++queueFamilyNdx) 169 { 170 if (getPhysicalDeviceSurfaceSupport(vki, physicalDevice, queueFamilyNdx, surface) != VK_FALSE) 171 supportedFamilyIndices.push_back(queueFamilyNdx); 172 } 173 174 return supportedFamilyIndices; 175} 176 177deUint32 chooseQueueFamilyIndex (const InstanceInterface& vki, VkPhysicalDevice physicalDevice, VkSurfaceKHR surface) 178{ 179 const vector<deUint32> supportedFamilyIndices = getSupportedQueueFamilyIndices(vki, physicalDevice, surface); 180 181 if (supportedFamilyIndices.empty()) 182 TCU_THROW(NotSupportedError, "Device doesn't support presentation"); 183 184 return supportedFamilyIndices[0]; 185} 186 187struct InstanceHelper 188{ 189 const vector<VkExtensionProperties> supportedExtensions; 190 const Unique<VkInstance> instance; 191 const InstanceDriver vki; 192 193 InstanceHelper (Context& context, Type wsiType, const VkAllocationCallbacks* pAllocator = DE_NULL) 194 : supportedExtensions (enumerateInstanceExtensionProperties(context.getPlatformInterface(), 195 DE_NULL)) 196 , instance (createInstanceWithWsi(context.getPlatformInterface(), 197 supportedExtensions, 198 wsiType, 199 pAllocator)) 200 , vki (context.getPlatformInterface(), *instance) 201 {} 202}; 203 204VkQueue getDeviceQueue (const DeviceInterface& vkd, VkDevice device, deUint32 queueFamilyIndex, deUint32 queueIndex) 205{ 206 VkQueue queue = (VkQueue)0; 207 vkd.getDeviceQueue(device, queueFamilyIndex, queueIndex, &queue); 208 return queue; 209} 210 211struct DeviceHelper 212{ 213 const VkPhysicalDevice physicalDevice; 214 const deUint32 queueFamilyIndex; 215 const Unique<VkDevice> device; 216 const DeviceDriver vkd; 217 const VkQueue queue; 218 219 DeviceHelper (Context& context, 220 const InstanceInterface& vki, 221 VkInstance instance, 222 VkSurfaceKHR surface, 223 const VkAllocationCallbacks* pAllocator = DE_NULL) 224 : physicalDevice (chooseDevice(vki, instance, context.getTestContext().getCommandLine())) 225 , queueFamilyIndex (chooseQueueFamilyIndex(vki, physicalDevice, surface)) 226 , device (createDeviceWithWsi(vki, 227 physicalDevice, 228 enumerateDeviceExtensionProperties(vki, physicalDevice, DE_NULL), 229 queueFamilyIndex, 230 pAllocator)) 231 , vkd (vki, *device) 232 , queue (getDeviceQueue(vkd, *device, queueFamilyIndex, 0)) 233 { 234 } 235}; 236 237MovePtr<Display> createDisplay (const vk::Platform& platform, 238 const Extensions& supportedExtensions, 239 Type wsiType) 240{ 241 try 242 { 243 return MovePtr<Display>(platform.createWsiDisplay(wsiType)); 244 } 245 catch (const tcu::NotSupportedError& e) 246 { 247 if (isExtensionSupported(supportedExtensions, RequiredExtension(getExtensionName(wsiType)))) 248 { 249 // If VK_KHR_{platform}_surface was supported, vk::Platform implementation 250 // must support creating native display & window for that WSI type. 251 throw tcu::TestError(e.getMessage()); 252 } 253 else 254 throw; 255 } 256} 257 258MovePtr<Window> createWindow (const Display& display, const Maybe<UVec2>& initialSize) 259{ 260 try 261 { 262 return MovePtr<Window>(display.createWindow(initialSize)); 263 } 264 catch (const tcu::NotSupportedError& e) 265 { 266 // See createDisplay - assuming that wsi::Display was supported platform port 267 // should also support creating a window. 268 throw tcu::TestError(e.getMessage()); 269 } 270} 271 272struct NativeObjects 273{ 274 const UniquePtr<Display> display; 275 const UniquePtr<Window> window; 276 277 NativeObjects (Context& context, 278 const Extensions& supportedExtensions, 279 Type wsiType, 280 const Maybe<UVec2>& initialWindowSize = tcu::nothing<UVec2>()) 281 : display (createDisplay(context.getTestContext().getPlatform().getVulkanPlatform(), supportedExtensions, wsiType)) 282 , window (createWindow(*display, initialWindowSize)) 283 {} 284}; 285 286enum TestDimension 287{ 288 TEST_DIMENSION_MIN_IMAGE_COUNT = 0, //!< Test all supported image counts 289 TEST_DIMENSION_IMAGE_FORMAT, //!< Test all supported formats 290 TEST_DIMENSION_IMAGE_EXTENT, //!< Test various (supported) extents 291 TEST_DIMENSION_IMAGE_ARRAY_LAYERS, 292 TEST_DIMENSION_IMAGE_USAGE, 293 TEST_DIMENSION_IMAGE_SHARING_MODE, 294 TEST_DIMENSION_PRE_TRANSFORM, 295 TEST_DIMENSION_COMPOSITE_ALPHA, 296 TEST_DIMENSION_PRESENT_MODE, 297 TEST_DIMENSION_CLIPPED, 298 299 TEST_DIMENSION_LAST 300}; 301 302const char* getTestDimensionName (TestDimension dimension) 303{ 304 static const char* const s_names[] = 305 { 306 "min_image_count", 307 "image_format", 308 "image_extent", 309 "image_array_layers", 310 "image_usage", 311 "image_sharing_mode", 312 "pre_transform", 313 "composite_alpha", 314 "present_mode", 315 "clipped" 316 }; 317 return de::getSizedArrayElement<TEST_DIMENSION_LAST>(s_names, dimension); 318} 319 320struct TestParameters 321{ 322 Type wsiType; 323 TestDimension dimension; 324 325 TestParameters (Type wsiType_, TestDimension dimension_) 326 : wsiType (wsiType_) 327 , dimension (dimension_) 328 {} 329 330 TestParameters (void) 331 : wsiType (TYPE_LAST) 332 , dimension (TEST_DIMENSION_LAST) 333 {} 334}; 335 336vector<VkSwapchainCreateInfoKHR> generateSwapchainParameterCases (Type wsiType, 337 TestDimension dimension, 338 const VkSurfaceCapabilitiesKHR& capabilities, 339 const vector<VkSurfaceFormatKHR>& formats, 340 const vector<VkPresentModeKHR>& presentModes) 341{ 342 const PlatformProperties& platformProperties = getPlatformProperties(wsiType); 343 vector<VkSwapchainCreateInfoKHR> cases; 344 const VkSurfaceTransformFlagBitsKHR defaultTransform = (capabilities.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) ? VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR : capabilities.currentTransform; 345 const VkSwapchainCreateInfoKHR baseParameters = 346 { 347 VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, 348 DE_NULL, 349 (VkSwapchainCreateFlagsKHR)0, 350 (VkSurfaceKHR)0, 351 capabilities.minImageCount, 352 formats[0].format, 353 formats[0].colorSpace, 354 (platformProperties.swapchainExtent == PlatformProperties::SWAPCHAIN_EXTENT_SETS_WINDOW_SIZE 355 ? capabilities.minImageExtent : capabilities.currentExtent), 356 1u, // imageArrayLayers 357 VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, 358 VK_SHARING_MODE_EXCLUSIVE, 359 0u, 360 (const deUint32*)DE_NULL, 361 defaultTransform, 362 VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, 363 VK_PRESENT_MODE_FIFO_KHR, 364 VK_FALSE, // clipped 365 (VkSwapchainKHR)0 // oldSwapchain 366 }; 367 368 switch (dimension) 369 { 370 case TEST_DIMENSION_MIN_IMAGE_COUNT: 371 { 372 const deUint32 maxImageCountToTest = de::clamp(16u, capabilities.minImageCount, (capabilities.maxImageCount > 0) ? capabilities.maxImageCount : capabilities.minImageCount + 16u); 373 374 for (deUint32 imageCount = capabilities.minImageCount; imageCount <= maxImageCountToTest; ++imageCount) 375 { 376 cases.push_back(baseParameters); 377 cases.back().minImageCount = imageCount; 378 } 379 380 break; 381 } 382 383 case TEST_DIMENSION_IMAGE_FORMAT: 384 { 385 for (vector<VkSurfaceFormatKHR>::const_iterator curFmt = formats.begin(); curFmt != formats.end(); ++curFmt) 386 { 387 cases.push_back(baseParameters); 388 cases.back().imageFormat = curFmt->format; 389 cases.back().imageColorSpace = curFmt->colorSpace; 390 } 391 392 break; 393 } 394 395 case TEST_DIMENSION_IMAGE_EXTENT: 396 { 397 static const VkExtent2D s_testSizes[] = 398 { 399 { 1, 1 }, 400 { 16, 32 }, 401 { 32, 16 }, 402 { 632, 231 }, 403 { 117, 998 }, 404 }; 405 406 if (platformProperties.swapchainExtent == PlatformProperties::SWAPCHAIN_EXTENT_SETS_WINDOW_SIZE || 407 platformProperties.swapchainExtent == PlatformProperties::SWAPCHAIN_EXTENT_SCALED_TO_WINDOW_SIZE) 408 { 409 for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(s_testSizes); ++ndx) 410 { 411 cases.push_back(baseParameters); 412 cases.back().imageExtent.width = de::clamp(s_testSizes[ndx].width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width); 413 cases.back().imageExtent.height = de::clamp(s_testSizes[ndx].height, capabilities.minImageExtent.height, capabilities.maxImageExtent.height); 414 } 415 } 416 417 if (platformProperties.swapchainExtent != PlatformProperties::SWAPCHAIN_EXTENT_SETS_WINDOW_SIZE) 418 { 419 cases.push_back(baseParameters); 420 cases.back().imageExtent = capabilities.currentExtent; 421 } 422 423 if (platformProperties.swapchainExtent != PlatformProperties::SWAPCHAIN_EXTENT_MUST_MATCH_WINDOW_SIZE) 424 { 425 cases.push_back(baseParameters); 426 cases.back().imageExtent = capabilities.minImageExtent; 427 428 cases.push_back(baseParameters); 429 cases.back().imageExtent = capabilities.maxImageExtent; 430 } 431 432 break; 433 } 434 435 case TEST_DIMENSION_IMAGE_ARRAY_LAYERS: 436 { 437 const deUint32 maxLayers = de::min(capabilities.maxImageArrayLayers, 16u); 438 439 for (deUint32 numLayers = 1; numLayers <= maxLayers; ++numLayers) 440 { 441 cases.push_back(baseParameters); 442 cases.back().imageArrayLayers = numLayers; 443 } 444 445 break; 446 } 447 448 case TEST_DIMENSION_IMAGE_USAGE: 449 { 450 for (deUint32 flags = 1u; flags <= capabilities.supportedUsageFlags; ++flags) 451 { 452 if ((flags & ~capabilities.supportedUsageFlags) == 0) 453 { 454 cases.push_back(baseParameters); 455 cases.back().imageUsage = flags; 456 } 457 } 458 459 break; 460 } 461 462 case TEST_DIMENSION_IMAGE_SHARING_MODE: 463 { 464 cases.push_back(baseParameters); 465 cases.back().imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; 466 467 cases.push_back(baseParameters); 468 cases.back().imageSharingMode = VK_SHARING_MODE_CONCURRENT; 469 470 break; 471 } 472 473 case TEST_DIMENSION_PRE_TRANSFORM: 474 { 475 for (deUint32 transform = 1u; 476 transform <= capabilities.supportedTransforms; 477 transform = transform<<1u) 478 { 479 if ((transform & capabilities.supportedTransforms) != 0) 480 { 481 cases.push_back(baseParameters); 482 cases.back().preTransform = (VkSurfaceTransformFlagBitsKHR)transform; 483 } 484 } 485 486 break; 487 } 488 489 case TEST_DIMENSION_COMPOSITE_ALPHA: 490 { 491 for (deUint32 alphaMode = 1u; 492 alphaMode <= capabilities.supportedCompositeAlpha; 493 alphaMode = alphaMode<<1u) 494 { 495 if ((alphaMode & capabilities.supportedCompositeAlpha) != 0) 496 { 497 cases.push_back(baseParameters); 498 cases.back().compositeAlpha = (VkCompositeAlphaFlagBitsKHR)alphaMode; 499 } 500 } 501 502 break; 503 } 504 505 case TEST_DIMENSION_PRESENT_MODE: 506 { 507 for (vector<VkPresentModeKHR>::const_iterator curMode = presentModes.begin(); curMode != presentModes.end(); ++curMode) 508 { 509 cases.push_back(baseParameters); 510 cases.back().presentMode = *curMode; 511 } 512 513 break; 514 } 515 516 case TEST_DIMENSION_CLIPPED: 517 { 518 cases.push_back(baseParameters); 519 cases.back().clipped = VK_FALSE; 520 521 cases.push_back(baseParameters); 522 cases.back().clipped = VK_TRUE; 523 524 break; 525 } 526 527 default: 528 DE_FATAL("Impossible"); 529 } 530 531 DE_ASSERT(!cases.empty()); 532 return cases; 533} 534 535vector<VkSwapchainCreateInfoKHR> generateSwapchainParameterCases (Type wsiType, 536 TestDimension dimension, 537 const InstanceInterface& vki, 538 VkPhysicalDevice physicalDevice, 539 VkSurfaceKHR surface) 540{ 541 const VkSurfaceCapabilitiesKHR capabilities = getPhysicalDeviceSurfaceCapabilities(vki, 542 physicalDevice, 543 surface); 544 const vector<VkSurfaceFormatKHR> formats = getPhysicalDeviceSurfaceFormats(vki, 545 physicalDevice, 546 surface); 547 const vector<VkPresentModeKHR> presentModes = getPhysicalDeviceSurfacePresentModes(vki, 548 physicalDevice, 549 surface); 550 551 return generateSwapchainParameterCases(wsiType, dimension, capabilities, formats, presentModes); 552} 553 554tcu::TestStatus createSwapchainTest (Context& context, TestParameters params) 555{ 556 const InstanceHelper instHelper (context, params.wsiType); 557 const NativeObjects native (context, instHelper.supportedExtensions, params.wsiType); 558 const Unique<VkSurfaceKHR> surface (createSurface(instHelper.vki, *instHelper.instance, params.wsiType, *native.display, *native.window)); 559 const DeviceHelper devHelper (context, instHelper.vki, *instHelper.instance, *surface); 560 const vector<VkSwapchainCreateInfoKHR> cases (generateSwapchainParameterCases(params.wsiType, params.dimension, instHelper.vki, devHelper.physicalDevice, *surface)); 561 562 for (size_t caseNdx = 0; caseNdx < cases.size(); ++caseNdx) 563 { 564 VkSwapchainCreateInfoKHR curParams = cases[caseNdx]; 565 566 curParams.surface = *surface; 567 curParams.queueFamilyIndexCount = 1u; 568 curParams.pQueueFamilyIndices = &devHelper.queueFamilyIndex; 569 570 context.getTestContext().getLog() 571 << TestLog::Message << "Sub-case " << (caseNdx+1) << " / " << cases.size() << ": " << curParams << TestLog::EndMessage; 572 573 { 574 const Unique<VkSwapchainKHR> swapchain (createSwapchainKHR(devHelper.vkd, *devHelper.device, &curParams)); 575 } 576 } 577 578 return tcu::TestStatus::pass("Creating swapchain succeeded"); 579} 580 581class CreateSwapchainSimulateOOMTest : public TestInstance 582{ 583public: 584 CreateSwapchainSimulateOOMTest (Context& context, TestParameters params) 585 : TestInstance (context) 586 , m_params (params) 587 , m_numPassingAllocs (0) 588 { 589 } 590 591 tcu::TestStatus iterate (void); 592 593private: 594 const TestParameters m_params; 595 deUint32 m_numPassingAllocs; 596}; 597 598tcu::TestStatus CreateSwapchainSimulateOOMTest::iterate (void) 599{ 600 tcu::TestLog& log = m_context.getTestContext().getLog(); 601 602 // \note This is a little counter-intuitive order (iterating on callback count until all cases pass) 603 // but since cases depend on what device reports, it is the only easy way. In practice 604 // we should see same number of total callbacks (and executed code) regardless of the 605 // loop order. 606 607 if (m_numPassingAllocs <= 16*1024u) 608 { 609 AllocationCallbackRecorder allocationRecorder (getSystemAllocator()); 610 DeterministicFailAllocator failingAllocator (allocationRecorder.getCallbacks(), m_numPassingAllocs); 611 bool gotOOM = false; 612 613 log << TestLog::Message << "Testing with " << m_numPassingAllocs << " first allocations succeeding" << TestLog::EndMessage; 614 615 try 616 { 617 const InstanceHelper instHelper (m_context, m_params.wsiType, failingAllocator.getCallbacks()); 618 const NativeObjects native (m_context, instHelper.supportedExtensions, m_params.wsiType); 619 const Unique<VkSurfaceKHR> surface (createSurface(instHelper.vki, 620 *instHelper.instance, 621 m_params.wsiType, 622 *native.display, 623 *native.window, 624 failingAllocator.getCallbacks())); 625 const DeviceHelper devHelper (m_context, instHelper.vki, *instHelper.instance, *surface, failingAllocator.getCallbacks()); 626 const vector<VkSwapchainCreateInfoKHR> cases (generateSwapchainParameterCases(m_params.wsiType, m_params.dimension, instHelper.vki, devHelper.physicalDevice, *surface)); 627 628 for (size_t caseNdx = 0; caseNdx < cases.size(); ++caseNdx) 629 { 630 VkSwapchainCreateInfoKHR curParams = cases[caseNdx]; 631 632 curParams.surface = *surface; 633 curParams.queueFamilyIndexCount = 1u; 634 curParams.pQueueFamilyIndices = &devHelper.queueFamilyIndex; 635 636 log 637 << TestLog::Message << "Sub-case " << (caseNdx+1) << " / " << cases.size() << TestLog::EndMessage; 638 639 { 640 const Unique<VkSwapchainKHR> swapchain (createSwapchainKHR(devHelper.vkd, *devHelper.device, &curParams, failingAllocator.getCallbacks())); 641 } 642 } 643 } 644 catch (const OutOfMemoryError& e) 645 { 646 log << TestLog::Message << "Got " << e.getError() << TestLog::EndMessage; 647 gotOOM = true; 648 } 649 650 if (!validateAndLog(log, allocationRecorder, 0u)) 651 return tcu::TestStatus::fail("Detected invalid system allocation callback"); 652 653 if (!gotOOM) 654 { 655 log << TestLog::Message << "Creating surface succeeded!" << TestLog::EndMessage; 656 657 if (m_numPassingAllocs == 0) 658 return tcu::TestStatus(QP_TEST_RESULT_QUALITY_WARNING, "Allocation callbacks were not used"); 659 else 660 return tcu::TestStatus::pass("OOM simulation completed"); 661 } 662 663 m_numPassingAllocs++; 664 return tcu::TestStatus::incomplete(); 665 } 666 else 667 return tcu::TestStatus(QP_TEST_RESULT_QUALITY_WARNING, "Creating swapchain did not succeed, callback limit exceeded"); 668} 669 670struct GroupParameters 671{ 672 typedef FunctionInstance1<TestParameters>::Function Function; 673 674 Type wsiType; 675 Function function; 676 677 GroupParameters (Type wsiType_, Function function_) 678 : wsiType (wsiType_) 679 , function (function_) 680 {} 681 682 GroupParameters (void) 683 : wsiType (TYPE_LAST) 684 , function ((Function)DE_NULL) 685 {} 686}; 687 688void populateSwapchainGroup (tcu::TestCaseGroup* testGroup, GroupParameters params) 689{ 690 for (int dimensionNdx = 0; dimensionNdx < TEST_DIMENSION_LAST; ++dimensionNdx) 691 { 692 const TestDimension testDimension = (TestDimension)dimensionNdx; 693 694 addFunctionCase(testGroup, getTestDimensionName(testDimension), "", params.function, TestParameters(params.wsiType, testDimension)); 695 } 696} 697 698void populateSwapchainOOMGroup (tcu::TestCaseGroup* testGroup, Type wsiType) 699{ 700 for (int dimensionNdx = 0; dimensionNdx < TEST_DIMENSION_LAST; ++dimensionNdx) 701 { 702 const TestDimension testDimension = (TestDimension)dimensionNdx; 703 704 testGroup->addChild(new InstanceFactory1<CreateSwapchainSimulateOOMTest, TestParameters>(testGroup->getTestContext(), tcu::NODETYPE_SELF_VALIDATE, getTestDimensionName(testDimension), "", TestParameters(wsiType, testDimension))); 705 } 706} 707 708VkSwapchainCreateInfoKHR getBasicSwapchainParameters (Type wsiType, 709 const InstanceInterface& vki, 710 VkPhysicalDevice physicalDevice, 711 VkSurfaceKHR surface, 712 const tcu::UVec2& desiredSize, 713 deUint32 desiredImageCount) 714{ 715 const VkSurfaceCapabilitiesKHR capabilities = getPhysicalDeviceSurfaceCapabilities(vki, 716 physicalDevice, 717 surface); 718 const vector<VkSurfaceFormatKHR> formats = getPhysicalDeviceSurfaceFormats(vki, 719 physicalDevice, 720 surface); 721 const PlatformProperties& platformProperties = getPlatformProperties(wsiType); 722 const VkSurfaceTransformFlagBitsKHR transform = (capabilities.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) ? VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR : capabilities.currentTransform; 723 const VkSwapchainCreateInfoKHR parameters = 724 { 725 VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, 726 DE_NULL, 727 (VkSwapchainCreateFlagsKHR)0, 728 surface, 729 de::clamp(desiredImageCount, capabilities.minImageCount, capabilities.maxImageCount > 0 ? capabilities.maxImageCount : capabilities.minImageCount + desiredImageCount), 730 formats[0].format, 731 formats[0].colorSpace, 732 (platformProperties.swapchainExtent == PlatformProperties::SWAPCHAIN_EXTENT_MUST_MATCH_WINDOW_SIZE 733 ? capabilities.currentExtent : vk::makeExtent2D(desiredSize.x(), desiredSize.y())), 734 1u, // imageArrayLayers 735 VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, 736 VK_SHARING_MODE_EXCLUSIVE, 737 0u, 738 (const deUint32*)DE_NULL, 739 transform, 740 VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, 741 VK_PRESENT_MODE_FIFO_KHR, 742 VK_FALSE, // clipped 743 (VkSwapchainKHR)0 // oldSwapchain 744 }; 745 746 return parameters; 747} 748 749typedef de::SharedPtr<Unique<VkImageView> > ImageViewSp; 750typedef de::SharedPtr<Unique<VkFramebuffer> > FramebufferSp; 751 752class TriangleRenderer 753{ 754public: 755 TriangleRenderer (const DeviceInterface& vkd, 756 const VkDevice device, 757 Allocator& allocator, 758 const BinaryCollection& binaryRegistry, 759 const vector<VkImage> swapchainImages, 760 const VkFormat framebufferFormat, 761 const UVec2& renderSize); 762 ~TriangleRenderer (void); 763 764 void recordFrame (VkCommandBuffer cmdBuffer, 765 deUint32 imageNdx, 766 deUint32 frameNdx) const; 767 768 static void getPrograms (SourceCollections& dst); 769 770private: 771 static Move<VkRenderPass> createRenderPass (const DeviceInterface& vkd, 772 const VkDevice device, 773 const VkFormat colorAttachmentFormat); 774 static Move<VkPipelineLayout> createPipelineLayout(const DeviceInterface& vkd, 775 VkDevice device); 776 static Move<VkPipeline> createPipeline (const DeviceInterface& vkd, 777 const VkDevice device, 778 const VkRenderPass renderPass, 779 const VkPipelineLayout pipelineLayout, 780 const BinaryCollection& binaryCollection, 781 const UVec2& renderSize); 782 783 static Move<VkImageView> createAttachmentView(const DeviceInterface& vkd, 784 const VkDevice device, 785 const VkImage image, 786 const VkFormat format); 787 static Move<VkFramebuffer> createFramebuffer (const DeviceInterface& vkd, 788 const VkDevice device, 789 const VkRenderPass renderPass, 790 const VkImageView colorAttachment, 791 const UVec2& renderSize); 792 793 static Move<VkBuffer> createBuffer (const DeviceInterface& vkd, 794 VkDevice device, 795 VkDeviceSize size, 796 VkBufferUsageFlags usage); 797 798 const DeviceInterface& m_vkd; 799 800 const vector<VkImage> m_swapchainImages; 801 const tcu::UVec2 m_renderSize; 802 803 const Unique<VkRenderPass> m_renderPass; 804 const Unique<VkPipelineLayout> m_pipelineLayout; 805 const Unique<VkPipeline> m_pipeline; 806 807 const Unique<VkBuffer> m_vertexBuffer; 808 const UniquePtr<Allocation> m_vertexBufferMemory; 809 810 vector<ImageViewSp> m_attachmentViews; 811 vector<FramebufferSp> m_framebuffers; 812}; 813 814Move<VkRenderPass> TriangleRenderer::createRenderPass (const DeviceInterface& vkd, 815 const VkDevice device, 816 const VkFormat colorAttachmentFormat) 817{ 818 const VkAttachmentDescription colorAttDesc = 819 { 820 (VkAttachmentDescriptionFlags)0, 821 colorAttachmentFormat, 822 VK_SAMPLE_COUNT_1_BIT, 823 VK_ATTACHMENT_LOAD_OP_CLEAR, 824 VK_ATTACHMENT_STORE_OP_STORE, 825 VK_ATTACHMENT_LOAD_OP_DONT_CARE, 826 VK_ATTACHMENT_STORE_OP_DONT_CARE, 827 VK_IMAGE_LAYOUT_UNDEFINED, 828 VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, 829 }; 830 const VkAttachmentReference colorAttRef = 831 { 832 0u, 833 VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, 834 }; 835 const VkSubpassDescription subpassDesc = 836 { 837 (VkSubpassDescriptionFlags)0u, 838 VK_PIPELINE_BIND_POINT_GRAPHICS, 839 0u, // inputAttachmentCount 840 DE_NULL, // pInputAttachments 841 1u, // colorAttachmentCount 842 &colorAttRef, // pColorAttachments 843 DE_NULL, // pResolveAttachments 844 DE_NULL, // depthStencilAttachment 845 0u, // preserveAttachmentCount 846 DE_NULL, // pPreserveAttachments 847 }; 848 const VkSubpassDependency dependencies[] = 849 { 850 { 851 VK_SUBPASS_EXTERNAL, // srcSubpass 852 0u, // dstSubpass 853 VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 854 VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 855 VK_ACCESS_MEMORY_READ_BIT, 856 (VK_ACCESS_COLOR_ATTACHMENT_READ_BIT| 857 VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT), 858 VK_DEPENDENCY_BY_REGION_BIT 859 }, 860 { 861 0u, // srcSubpass 862 VK_SUBPASS_EXTERNAL, // dstSubpass 863 VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 864 VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 865 (VK_ACCESS_COLOR_ATTACHMENT_READ_BIT| 866 VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT), 867 VK_ACCESS_MEMORY_READ_BIT, 868 VK_DEPENDENCY_BY_REGION_BIT 869 }, 870 }; 871 const VkRenderPassCreateInfo renderPassParams = 872 { 873 VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, 874 DE_NULL, 875 (VkRenderPassCreateFlags)0, 876 1u, 877 &colorAttDesc, 878 1u, 879 &subpassDesc, 880 DE_LENGTH_OF_ARRAY(dependencies), 881 dependencies, 882 }; 883 884 return vk::createRenderPass(vkd, device, &renderPassParams); 885} 886 887Move<VkPipelineLayout> TriangleRenderer::createPipelineLayout (const DeviceInterface& vkd, 888 const VkDevice device) 889{ 890 const VkPushConstantRange pushConstantRange = 891 { 892 VK_SHADER_STAGE_VERTEX_BIT, 893 0u, // offset 894 (deUint32)sizeof(deUint32), // size 895 }; 896 const VkPipelineLayoutCreateInfo pipelineLayoutParams = 897 { 898 VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, 899 DE_NULL, 900 (vk::VkPipelineLayoutCreateFlags)0, 901 0u, // setLayoutCount 902 DE_NULL, // pSetLayouts 903 1u, 904 &pushConstantRange, 905 }; 906 907 return vk::createPipelineLayout(vkd, device, &pipelineLayoutParams); 908} 909 910Move<VkPipeline> TriangleRenderer::createPipeline (const DeviceInterface& vkd, 911 const VkDevice device, 912 const VkRenderPass renderPass, 913 const VkPipelineLayout pipelineLayout, 914 const BinaryCollection& binaryCollection, 915 const UVec2& renderSize) 916{ 917 // \note VkShaderModules are fully consumed by vkCreateGraphicsPipelines() 918 // and can be deleted immediately following that call. 919 const Unique<VkShaderModule> vertShaderModule (createShaderModule(vkd, device, binaryCollection.get("tri-vert"), 0)); 920 const Unique<VkShaderModule> fragShaderModule (createShaderModule(vkd, device, binaryCollection.get("tri-frag"), 0)); 921 922 const VkSpecializationInfo emptyShaderSpecParams = 923 { 924 0u, // mapEntryCount 925 DE_NULL, // pMap 926 0, // dataSize 927 DE_NULL, // pData 928 }; 929 const VkPipelineShaderStageCreateInfo shaderStageParams[] = 930 { 931 { 932 VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, 933 DE_NULL, 934 (VkPipelineShaderStageCreateFlags)0, 935 VK_SHADER_STAGE_VERTEX_BIT, 936 *vertShaderModule, 937 "main", 938 &emptyShaderSpecParams, 939 }, 940 { 941 VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, 942 DE_NULL, 943 (VkPipelineShaderStageCreateFlags)0, 944 VK_SHADER_STAGE_FRAGMENT_BIT, 945 *fragShaderModule, 946 "main", 947 &emptyShaderSpecParams, 948 } 949 }; 950 const VkPipelineDepthStencilStateCreateInfo depthStencilParams = 951 { 952 VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, 953 DE_NULL, 954 (VkPipelineDepthStencilStateCreateFlags)0, 955 DE_FALSE, // depthTestEnable 956 DE_FALSE, // depthWriteEnable 957 VK_COMPARE_OP_ALWAYS, // depthCompareOp 958 DE_FALSE, // depthBoundsTestEnable 959 DE_FALSE, // stencilTestEnable 960 { 961 VK_STENCIL_OP_KEEP, // failOp 962 VK_STENCIL_OP_KEEP, // passOp 963 VK_STENCIL_OP_KEEP, // depthFailOp 964 VK_COMPARE_OP_ALWAYS, // compareOp 965 0u, // compareMask 966 0u, // writeMask 967 0u, // reference 968 }, // front 969 { 970 VK_STENCIL_OP_KEEP, // failOp 971 VK_STENCIL_OP_KEEP, // passOp 972 VK_STENCIL_OP_KEEP, // depthFailOp 973 VK_COMPARE_OP_ALWAYS, // compareOp 974 0u, // compareMask 975 0u, // writeMask 976 0u, // reference 977 }, // back 978 -1.0f, // minDepthBounds 979 +1.0f, // maxDepthBounds 980 }; 981 const VkViewport viewport0 = 982 { 983 0.0f, // x 984 0.0f, // y 985 (float)renderSize.x(), // width 986 (float)renderSize.y(), // height 987 0.0f, // minDepth 988 1.0f, // maxDepth 989 }; 990 const VkRect2D scissor0 = 991 { 992 { 0u, 0u, }, // offset 993 { renderSize.x(), renderSize.y() }, // extent 994 }; 995 const VkPipelineViewportStateCreateInfo viewportParams = 996 { 997 VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, 998 DE_NULL, 999 (VkPipelineViewportStateCreateFlags)0, 1000 1u, 1001 &viewport0, 1002 1u, 1003 &scissor0 1004 }; 1005 const VkPipelineMultisampleStateCreateInfo multisampleParams = 1006 { 1007 VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, 1008 DE_NULL, 1009 (VkPipelineMultisampleStateCreateFlags)0, 1010 VK_SAMPLE_COUNT_1_BIT, // rasterizationSamples 1011 VK_FALSE, // sampleShadingEnable 1012 0.0f, // minSampleShading 1013 (const VkSampleMask*)DE_NULL, // sampleMask 1014 VK_FALSE, // alphaToCoverageEnable 1015 VK_FALSE, // alphaToOneEnable 1016 }; 1017 const VkPipelineRasterizationStateCreateInfo rasterParams = 1018 { 1019 VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, 1020 DE_NULL, 1021 (VkPipelineRasterizationStateCreateFlags)0, 1022 VK_FALSE, // depthClampEnable 1023 VK_FALSE, // rasterizerDiscardEnable 1024 VK_POLYGON_MODE_FILL, // polygonMode 1025 VK_CULL_MODE_NONE, // cullMode 1026 VK_FRONT_FACE_COUNTER_CLOCKWISE, // frontFace 1027 VK_FALSE, // depthBiasEnable 1028 0.0f, // depthBiasConstantFactor 1029 0.0f, // depthBiasClamp 1030 0.0f, // depthBiasSlopeFactor 1031 1.0f, // lineWidth 1032 }; 1033 const VkPipelineInputAssemblyStateCreateInfo inputAssemblyParams = 1034 { 1035 VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, 1036 DE_NULL, 1037 (VkPipelineInputAssemblyStateCreateFlags)0, 1038 VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 1039 DE_FALSE, // primitiveRestartEnable 1040 }; 1041 const VkVertexInputBindingDescription vertexBinding0 = 1042 { 1043 0u, // binding 1044 (deUint32)sizeof(tcu::Vec4), // stride 1045 VK_VERTEX_INPUT_RATE_VERTEX, // inputRate 1046 }; 1047 const VkVertexInputAttributeDescription vertexAttrib0 = 1048 { 1049 0u, // location 1050 0u, // binding 1051 VK_FORMAT_R32G32B32A32_SFLOAT, // format 1052 0u, // offset 1053 }; 1054 const VkPipelineVertexInputStateCreateInfo vertexInputStateParams = 1055 { 1056 VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, 1057 DE_NULL, 1058 (VkPipelineVertexInputStateCreateFlags)0, 1059 1u, 1060 &vertexBinding0, 1061 1u, 1062 &vertexAttrib0, 1063 }; 1064 const VkPipelineColorBlendAttachmentState attBlendParams0 = 1065 { 1066 VK_FALSE, // blendEnable 1067 VK_BLEND_FACTOR_ONE, // srcColorBlendFactor 1068 VK_BLEND_FACTOR_ZERO, // dstColorBlendFactor 1069 VK_BLEND_OP_ADD, // colorBlendOp 1070 VK_BLEND_FACTOR_ONE, // srcAlphaBlendFactor 1071 VK_BLEND_FACTOR_ZERO, // dstAlphaBlendFactor 1072 VK_BLEND_OP_ADD, // alphaBlendOp 1073 (VK_COLOR_COMPONENT_R_BIT| 1074 VK_COLOR_COMPONENT_G_BIT| 1075 VK_COLOR_COMPONENT_B_BIT| 1076 VK_COLOR_COMPONENT_A_BIT), // colorWriteMask 1077 }; 1078 const VkPipelineColorBlendStateCreateInfo blendParams = 1079 { 1080 VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, 1081 DE_NULL, 1082 (VkPipelineColorBlendStateCreateFlags)0, 1083 VK_FALSE, // logicOpEnable 1084 VK_LOGIC_OP_COPY, 1085 1u, 1086 &attBlendParams0, 1087 { 0.0f, 0.0f, 0.0f, 0.0f }, // blendConstants[4] 1088 }; 1089 const VkGraphicsPipelineCreateInfo pipelineParams = 1090 { 1091 VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, 1092 DE_NULL, 1093 (VkPipelineCreateFlags)0, 1094 (deUint32)DE_LENGTH_OF_ARRAY(shaderStageParams), 1095 shaderStageParams, 1096 &vertexInputStateParams, 1097 &inputAssemblyParams, 1098 (const VkPipelineTessellationStateCreateInfo*)DE_NULL, 1099 &viewportParams, 1100 &rasterParams, 1101 &multisampleParams, 1102 &depthStencilParams, 1103 &blendParams, 1104 (const VkPipelineDynamicStateCreateInfo*)DE_NULL, 1105 pipelineLayout, 1106 renderPass, 1107 0u, // subpass 1108 DE_NULL, // basePipelineHandle 1109 0u, // basePipelineIndex 1110 }; 1111 1112 return vk::createGraphicsPipeline(vkd, device, (VkPipelineCache)0, &pipelineParams); 1113} 1114 1115Move<VkImageView> TriangleRenderer::createAttachmentView (const DeviceInterface& vkd, 1116 const VkDevice device, 1117 const VkImage image, 1118 const VkFormat format) 1119{ 1120 const VkImageViewCreateInfo viewParams = 1121 { 1122 VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, 1123 DE_NULL, 1124 (VkImageViewCreateFlags)0, 1125 image, 1126 VK_IMAGE_VIEW_TYPE_2D, 1127 format, 1128 vk::makeComponentMappingRGBA(), 1129 { 1130 VK_IMAGE_ASPECT_COLOR_BIT, 1131 0u, // baseMipLevel 1132 1u, // levelCount 1133 0u, // baseArrayLayer 1134 1u, // layerCount 1135 }, 1136 }; 1137 1138 return vk::createImageView(vkd, device, &viewParams); 1139} 1140 1141Move<VkFramebuffer> TriangleRenderer::createFramebuffer (const DeviceInterface& vkd, 1142 const VkDevice device, 1143 const VkRenderPass renderPass, 1144 const VkImageView colorAttachment, 1145 const UVec2& renderSize) 1146{ 1147 const VkFramebufferCreateInfo framebufferParams = 1148 { 1149 VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, 1150 DE_NULL, 1151 (VkFramebufferCreateFlags)0, 1152 renderPass, 1153 1u, 1154 &colorAttachment, 1155 renderSize.x(), 1156 renderSize.y(), 1157 1u, // layers 1158 }; 1159 1160 return vk::createFramebuffer(vkd, device, &framebufferParams); 1161} 1162 1163Move<VkBuffer> TriangleRenderer::createBuffer (const DeviceInterface& vkd, 1164 VkDevice device, 1165 VkDeviceSize size, 1166 VkBufferUsageFlags usage) 1167{ 1168 const VkBufferCreateInfo bufferParams = 1169 { 1170 VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, 1171 DE_NULL, 1172 (VkBufferCreateFlags)0, 1173 size, 1174 usage, 1175 VK_SHARING_MODE_EXCLUSIVE, 1176 0, 1177 DE_NULL 1178 }; 1179 1180 return vk::createBuffer(vkd, device, &bufferParams); 1181} 1182 1183TriangleRenderer::TriangleRenderer (const DeviceInterface& vkd, 1184 const VkDevice device, 1185 Allocator& allocator, 1186 const BinaryCollection& binaryRegistry, 1187 const vector<VkImage> swapchainImages, 1188 const VkFormat framebufferFormat, 1189 const UVec2& renderSize) 1190 : m_vkd (vkd) 1191 , m_swapchainImages (swapchainImages) 1192 , m_renderSize (renderSize) 1193 , m_renderPass (createRenderPass(vkd, device, framebufferFormat)) 1194 , m_pipelineLayout (createPipelineLayout(vkd, device)) 1195 , m_pipeline (createPipeline(vkd, device, *m_renderPass, *m_pipelineLayout, binaryRegistry, renderSize)) 1196 , m_vertexBuffer (createBuffer(vkd, device, (VkDeviceSize)(sizeof(float)*4*3), VK_BUFFER_USAGE_VERTEX_BUFFER_BIT)) 1197 , m_vertexBufferMemory (allocator.allocate(getBufferMemoryRequirements(vkd, device, *m_vertexBuffer), 1198 MemoryRequirement::HostVisible)) 1199{ 1200 m_attachmentViews.resize(swapchainImages.size()); 1201 m_framebuffers.resize(swapchainImages.size()); 1202 1203 for (size_t imageNdx = 0; imageNdx < swapchainImages.size(); ++imageNdx) 1204 { 1205 m_attachmentViews[imageNdx] = ImageViewSp(new Unique<VkImageView>(createAttachmentView(vkd, device, swapchainImages[imageNdx], framebufferFormat))); 1206 m_framebuffers[imageNdx] = FramebufferSp(new Unique<VkFramebuffer>(createFramebuffer(vkd, device, *m_renderPass, **m_attachmentViews[imageNdx], renderSize))); 1207 } 1208 1209 VK_CHECK(vkd.bindBufferMemory(device, *m_vertexBuffer, m_vertexBufferMemory->getMemory(), m_vertexBufferMemory->getOffset())); 1210 1211 { 1212 const VkMappedMemoryRange memRange = 1213 { 1214 VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, 1215 DE_NULL, 1216 m_vertexBufferMemory->getMemory(), 1217 m_vertexBufferMemory->getOffset(), 1218 VK_WHOLE_SIZE 1219 }; 1220 const tcu::Vec4 vertices[] = 1221 { 1222 tcu::Vec4(-0.5f, -0.5f, 0.0f, 1.0f), 1223 tcu::Vec4(+0.5f, -0.5f, 0.0f, 1.0f), 1224 tcu::Vec4( 0.0f, +0.5f, 0.0f, 1.0f) 1225 }; 1226 DE_STATIC_ASSERT(sizeof(vertices) == sizeof(float)*4*3); 1227 1228 deMemcpy(m_vertexBufferMemory->getHostPtr(), &vertices[0], sizeof(vertices)); 1229 VK_CHECK(vkd.flushMappedMemoryRanges(device, 1u, &memRange)); 1230 } 1231} 1232 1233TriangleRenderer::~TriangleRenderer (void) 1234{ 1235} 1236 1237void TriangleRenderer::recordFrame (VkCommandBuffer cmdBuffer, 1238 deUint32 imageNdx, 1239 deUint32 frameNdx) const 1240{ 1241 const VkFramebuffer curFramebuffer = **m_framebuffers[imageNdx]; 1242 1243 { 1244 const VkCommandBufferBeginInfo cmdBufBeginParams = 1245 { 1246 VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, 1247 DE_NULL, 1248 (VkCommandBufferUsageFlags)0, 1249 (const VkCommandBufferInheritanceInfo*)DE_NULL, 1250 }; 1251 VK_CHECK(m_vkd.beginCommandBuffer(cmdBuffer, &cmdBufBeginParams)); 1252 } 1253 1254 { 1255 const VkClearValue clearValue = makeClearValueColorF32(0.125f, 0.25f, 0.75f, 1.0f); 1256 const VkRenderPassBeginInfo passBeginParams = 1257 { 1258 VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, 1259 DE_NULL, 1260 *m_renderPass, 1261 curFramebuffer, 1262 { 1263 { 0, 0 }, 1264 { (deUint32)m_renderSize.x(), (deUint32)m_renderSize.y() } 1265 }, // renderArea 1266 1u, // clearValueCount 1267 &clearValue, // pClearValues 1268 }; 1269 m_vkd.cmdBeginRenderPass(cmdBuffer, &passBeginParams, VK_SUBPASS_CONTENTS_INLINE); 1270 } 1271 1272 m_vkd.cmdBindPipeline(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipeline); 1273 1274 { 1275 const VkDeviceSize bindingOffset = 0; 1276 m_vkd.cmdBindVertexBuffers(cmdBuffer, 0u, 1u, &m_vertexBuffer.get(), &bindingOffset); 1277 } 1278 1279 m_vkd.cmdPushConstants(cmdBuffer, *m_pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0u, (deUint32)sizeof(deUint32), &frameNdx); 1280 m_vkd.cmdDraw(cmdBuffer, 3u, 1u, 0u, 0u); 1281 m_vkd.cmdEndRenderPass(cmdBuffer); 1282 1283 VK_CHECK(m_vkd.endCommandBuffer(cmdBuffer)); 1284} 1285 1286void TriangleRenderer::getPrograms (SourceCollections& dst) 1287{ 1288 dst.glslSources.add("tri-vert") << glu::VertexSource( 1289 "#version 310 es\n" 1290 "layout(location = 0) in highp vec4 a_position;\n" 1291 "layout(push_constant) uniform FrameData\n" 1292 "{\n" 1293 " highp uint frameNdx;\n" 1294 "} frameData;\n" 1295 "void main (void)\n" 1296 "{\n" 1297 " highp float angle = float(frameData.frameNdx) / 100.0;\n" 1298 " highp float c = cos(angle);\n" 1299 " highp float s = sin(angle);\n" 1300 " highp mat4 t = mat4( c, -s, 0, 0,\n" 1301 " s, c, 0, 0,\n" 1302 " 0, 0, 1, 0,\n" 1303 " 0, 0, 0, 1);\n" 1304 " gl_Position = t * a_position;\n" 1305 "}\n"); 1306 dst.glslSources.add("tri-frag") << glu::FragmentSource( 1307 "#version 310 es\n" 1308 "layout(location = 0) out lowp vec4 o_color;\n" 1309 "void main (void) { o_color = vec4(1.0, 0.0, 1.0, 1.0); }\n"); 1310} 1311 1312typedef de::SharedPtr<Unique<VkCommandBuffer> > CommandBufferSp; 1313typedef de::SharedPtr<Unique<VkFence> > FenceSp; 1314typedef de::SharedPtr<Unique<VkSemaphore> > SemaphoreSp; 1315 1316Move<VkFence> createFence (const DeviceInterface& vkd, 1317 const VkDevice device) 1318{ 1319 const VkFenceCreateInfo fenceParams = 1320 { 1321 VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, 1322 DE_NULL, 1323 (VkFenceCreateFlags)0, 1324 }; 1325 return vk::createFence(vkd, device, &fenceParams); 1326} 1327 1328vector<FenceSp> createFences (const DeviceInterface& vkd, 1329 const VkDevice device, 1330 size_t numFences) 1331{ 1332 vector<FenceSp> fences(numFences); 1333 1334 for (size_t ndx = 0; ndx < numFences; ++ndx) 1335 fences[ndx] = FenceSp(new Unique<VkFence>(createFence(vkd, device))); 1336 1337 return fences; 1338} 1339 1340Move<VkSemaphore> createSemaphore (const DeviceInterface& vkd, 1341 const VkDevice device) 1342{ 1343 const VkSemaphoreCreateInfo semaphoreParams = 1344 { 1345 VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, 1346 DE_NULL, 1347 (VkSemaphoreCreateFlags)0, 1348 }; 1349 return vk::createSemaphore(vkd, device, &semaphoreParams); 1350} 1351 1352vector<SemaphoreSp> createSemaphores (const DeviceInterface& vkd, 1353 const VkDevice device, 1354 size_t numSemaphores) 1355{ 1356 vector<SemaphoreSp> semaphores(numSemaphores); 1357 1358 for (size_t ndx = 0; ndx < numSemaphores; ++ndx) 1359 semaphores[ndx] = SemaphoreSp(new Unique<VkSemaphore>(createSemaphore(vkd, device))); 1360 1361 return semaphores; 1362} 1363 1364Move<VkCommandPool> createCommandPool (const DeviceInterface& vkd, 1365 const VkDevice device, 1366 VkCommandPoolCreateFlags flags, 1367 deUint32 queueFamilyIndex) 1368{ 1369 const VkCommandPoolCreateInfo commandPoolParams = 1370 { 1371 VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, 1372 DE_NULL, 1373 flags, 1374 queueFamilyIndex 1375 }; 1376 1377 return createCommandPool(vkd, device, &commandPoolParams); 1378} 1379 1380vector<CommandBufferSp> allocateCommandBuffers (const DeviceInterface& vkd, 1381 const VkDevice device, 1382 const VkCommandPool commandPool, 1383 const VkCommandBufferLevel level, 1384 const size_t numCommandBuffers) 1385{ 1386 const VkCommandBufferAllocateInfo allocInfo = 1387 { 1388 VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, 1389 DE_NULL, 1390 commandPool, 1391 level, 1392 1u, 1393 }; 1394 1395 vector<CommandBufferSp> buffers (numCommandBuffers); 1396 1397 for (size_t ndx = 0; ndx < numCommandBuffers; ++ndx) 1398 buffers[ndx] = CommandBufferSp(new Unique<VkCommandBuffer>(allocateCommandBuffer(vkd, device, &allocInfo))); 1399 1400 return buffers; 1401} 1402 1403tcu::TestStatus basicRenderTest (Context& context, Type wsiType) 1404{ 1405 const tcu::UVec2 desiredSize (256, 256); 1406 const InstanceHelper instHelper (context, wsiType); 1407 const NativeObjects native (context, instHelper.supportedExtensions, wsiType, tcu::just(desiredSize)); 1408 const Unique<VkSurfaceKHR> surface (createSurface(instHelper.vki, *instHelper.instance, wsiType, *native.display, *native.window)); 1409 const DeviceHelper devHelper (context, instHelper.vki, *instHelper.instance, *surface); 1410 const DeviceInterface& vkd = devHelper.vkd; 1411 const VkDevice device = *devHelper.device; 1412 SimpleAllocator allocator (vkd, device, getPhysicalDeviceMemoryProperties(instHelper.vki, devHelper.physicalDevice)); 1413 const VkSwapchainCreateInfoKHR swapchainInfo = getBasicSwapchainParameters(wsiType, instHelper.vki, devHelper.physicalDevice, *surface, desiredSize, 2); 1414 const Unique<VkSwapchainKHR> swapchain (createSwapchainKHR(vkd, device, &swapchainInfo)); 1415 const vector<VkImage> swapchainImages = getSwapchainImages(vkd, device, *swapchain); 1416 1417 const TriangleRenderer renderer (vkd, 1418 device, 1419 allocator, 1420 context.getBinaryCollection(), 1421 swapchainImages, 1422 swapchainInfo.imageFormat, 1423 tcu::UVec2(swapchainInfo.imageExtent.width, swapchainInfo.imageExtent.height)); 1424 1425 const Unique<VkCommandPool> commandPool (createCommandPool(vkd, device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, devHelper.queueFamilyIndex)); 1426 1427 const size_t maxQueuedFrames = swapchainImages.size()*2; 1428 1429 // We need to keep hold of fences from vkAcquireNextImageKHR to actually 1430 // limit number of frames we allow to be queued. 1431 const vector<FenceSp> imageReadyFences (createFences(vkd, device, maxQueuedFrames)); 1432 1433 // We need maxQueuedFrames+1 for imageReadySemaphores pool as we need to pass 1434 // the semaphore in same time as the fence we use to meter rendering. 1435 const vector<SemaphoreSp> imageReadySemaphores (createSemaphores(vkd, device, maxQueuedFrames+1)); 1436 1437 // For rest we simply need maxQueuedFrames as we will wait for image 1438 // from frameNdx-maxQueuedFrames to become available to us, guaranteeing that 1439 // previous uses must have completed. 1440 const vector<SemaphoreSp> renderingCompleteSemaphores (createSemaphores(vkd, device, maxQueuedFrames)); 1441 const vector<CommandBufferSp> commandBuffers (allocateCommandBuffers(vkd, device, *commandPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, maxQueuedFrames)); 1442 1443 try 1444 { 1445 const deUint32 numFramesToRender = 60*10; 1446 1447 for (deUint32 frameNdx = 0; frameNdx < numFramesToRender; ++frameNdx) 1448 { 1449 const VkFence imageReadyFence = **imageReadyFences[frameNdx%imageReadyFences.size()]; 1450 const VkSemaphore imageReadySemaphore = **imageReadySemaphores[frameNdx%imageReadySemaphores.size()]; 1451 deUint32 imageNdx = ~0u; 1452 1453 if (frameNdx >= maxQueuedFrames) 1454 VK_CHECK(vkd.waitForFences(device, 1u, &imageReadyFence, VK_TRUE, std::numeric_limits<deUint64>::max())); 1455 1456 VK_CHECK(vkd.resetFences(device, 1, &imageReadyFence)); 1457 1458 { 1459 const VkResult acquireResult = vkd.acquireNextImageKHR(device, 1460 *swapchain, 1461 std::numeric_limits<deUint64>::max(), 1462 imageReadySemaphore, 1463 imageReadyFence, 1464 &imageNdx); 1465 1466 if (acquireResult == VK_SUBOPTIMAL_KHR) 1467 context.getTestContext().getLog() << TestLog::Message << "Got " << acquireResult << " at frame " << frameNdx << TestLog::EndMessage; 1468 else 1469 VK_CHECK(acquireResult); 1470 } 1471 1472 TCU_CHECK((size_t)imageNdx < swapchainImages.size()); 1473 1474 { 1475 const VkSemaphore renderingCompleteSemaphore = **renderingCompleteSemaphores[frameNdx%renderingCompleteSemaphores.size()]; 1476 const VkCommandBuffer commandBuffer = **commandBuffers[frameNdx%commandBuffers.size()]; 1477 const VkPipelineStageFlags waitDstStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; 1478 const VkSubmitInfo submitInfo = 1479 { 1480 VK_STRUCTURE_TYPE_SUBMIT_INFO, 1481 DE_NULL, 1482 1u, 1483 &imageReadySemaphore, 1484 &waitDstStage, 1485 1u, 1486 &commandBuffer, 1487 1u, 1488 &renderingCompleteSemaphore 1489 }; 1490 const VkPresentInfoKHR presentInfo = 1491 { 1492 VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, 1493 DE_NULL, 1494 1u, 1495 &renderingCompleteSemaphore, 1496 1u, 1497 &*swapchain, 1498 &imageNdx, 1499 (VkResult*)DE_NULL 1500 }; 1501 1502 renderer.recordFrame(commandBuffer, imageNdx, frameNdx); 1503 VK_CHECK(vkd.queueSubmit(devHelper.queue, 1u, &submitInfo, (VkFence)0)); 1504 VK_CHECK(vkd.queuePresentKHR(devHelper.queue, &presentInfo)); 1505 } 1506 } 1507 1508 VK_CHECK(vkd.deviceWaitIdle(device)); 1509 } 1510 catch (...) 1511 { 1512 // Make sure device is idle before destroying resources 1513 vkd.deviceWaitIdle(device); 1514 throw; 1515 } 1516 1517 return tcu::TestStatus::pass("Rendering tests suceeded"); 1518} 1519 1520vector<tcu::UVec2> getSwapchainSizeSequence (const VkSurfaceCapabilitiesKHR& capabilities, const tcu::UVec2& defaultSize) 1521{ 1522 vector<tcu::UVec2> sizes(3); 1523 sizes[0] = defaultSize / 2u; 1524 sizes[1] = defaultSize; 1525 sizes[2] = defaultSize * 2u; 1526 1527 for (deUint32 i = 0; i < sizes.size(); ++i) 1528 { 1529 sizes[i].x() = de::clamp(sizes[i].x(), capabilities.minImageExtent.width, capabilities.maxImageExtent.width); 1530 sizes[i].y() = de::clamp(sizes[i].y(), capabilities.minImageExtent.height, capabilities.maxImageExtent.height); 1531 } 1532 1533 return sizes; 1534} 1535 1536tcu::TestStatus resizeSwapchainTest (Context& context, Type wsiType) 1537{ 1538 const tcu::UVec2 desiredSize (256, 256); 1539 const InstanceHelper instHelper (context, wsiType); 1540 const NativeObjects native (context, instHelper.supportedExtensions, wsiType, tcu::just(desiredSize)); 1541 const Unique<VkSurfaceKHR> surface (createSurface(instHelper.vki, *instHelper.instance, wsiType, *native.display, *native.window)); 1542 const DeviceHelper devHelper (context, instHelper.vki, *instHelper.instance, *surface); 1543 const PlatformProperties& platformProperties = getPlatformProperties(wsiType); 1544 const VkSurfaceCapabilitiesKHR capabilities = getPhysicalDeviceSurfaceCapabilities(instHelper.vki, devHelper.physicalDevice, *surface); 1545 const DeviceInterface& vkd = devHelper.vkd; 1546 const VkDevice device = *devHelper.device; 1547 SimpleAllocator allocator (vkd, device, getPhysicalDeviceMemoryProperties(instHelper.vki, devHelper.physicalDevice)); 1548 vector<tcu::UVec2> sizes = getSwapchainSizeSequence(capabilities, desiredSize); 1549 Move<VkSwapchainKHR> prevSwapchain; 1550 1551 DE_ASSERT(platformProperties.swapchainExtent != PlatformProperties::SWAPCHAIN_EXTENT_MUST_MATCH_WINDOW_SIZE); 1552 1553 for (deUint32 sizeNdx = 0; sizeNdx < sizes.size(); ++sizeNdx) 1554 { 1555 // \todo [2016-05-30 jesse] This test currently waits for idle and 1556 // recreates way more than necessary when recreating the swapchain. Make 1557 // it match expected real app behavior better by smoothly switching from 1558 // old to new swapchain. Once that is done, it will also be possible to 1559 // test creating a new swapchain while images from the previous one are 1560 // still acquired. 1561 1562 VkSwapchainCreateInfoKHR swapchainInfo = getBasicSwapchainParameters(wsiType, instHelper.vki, devHelper.physicalDevice, *surface, sizes[sizeNdx], 2); 1563 swapchainInfo.oldSwapchain = *prevSwapchain; 1564 1565 Move<VkSwapchainKHR> swapchain (createSwapchainKHR(vkd, device, &swapchainInfo)); 1566 const vector<VkImage> swapchainImages = getSwapchainImages(vkd, device, *swapchain); 1567 const TriangleRenderer renderer (vkd, 1568 device, 1569 allocator, 1570 context.getBinaryCollection(), 1571 swapchainImages, 1572 swapchainInfo.imageFormat, 1573 tcu::UVec2(swapchainInfo.imageExtent.width, swapchainInfo.imageExtent.height)); 1574 const Unique<VkCommandPool> commandPool (createCommandPool(vkd, device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, devHelper.queueFamilyIndex)); 1575 const size_t maxQueuedFrames = swapchainImages.size()*2; 1576 1577 // We need to keep hold of fences from vkAcquireNextImageKHR to actually 1578 // limit number of frames we allow to be queued. 1579 const vector<FenceSp> imageReadyFences (createFences(vkd, device, maxQueuedFrames)); 1580 1581 // We need maxQueuedFrames+1 for imageReadySemaphores pool as we need to pass 1582 // the semaphore in same time as the fence we use to meter rendering. 1583 const vector<SemaphoreSp> imageReadySemaphores (createSemaphores(vkd, device, maxQueuedFrames+1)); 1584 1585 // For rest we simply need maxQueuedFrames as we will wait for image 1586 // from frameNdx-maxQueuedFrames to become available to us, guaranteeing that 1587 // previous uses must have completed. 1588 const vector<SemaphoreSp> renderingCompleteSemaphores (createSemaphores(vkd, device, maxQueuedFrames)); 1589 const vector<CommandBufferSp> commandBuffers (allocateCommandBuffers(vkd, device, *commandPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, maxQueuedFrames)); 1590 1591 try 1592 { 1593 const deUint32 numFramesToRender = 60; 1594 1595 for (deUint32 frameNdx = 0; frameNdx < numFramesToRender; ++frameNdx) 1596 { 1597 const VkFence imageReadyFence = **imageReadyFences[frameNdx%imageReadyFences.size()]; 1598 const VkSemaphore imageReadySemaphore = **imageReadySemaphores[frameNdx%imageReadySemaphores.size()]; 1599 deUint32 imageNdx = ~0u; 1600 1601 if (frameNdx >= maxQueuedFrames) 1602 VK_CHECK(vkd.waitForFences(device, 1u, &imageReadyFence, VK_TRUE, std::numeric_limits<deUint64>::max())); 1603 1604 VK_CHECK(vkd.resetFences(device, 1, &imageReadyFence)); 1605 1606 { 1607 const VkResult acquireResult = vkd.acquireNextImageKHR(device, 1608 *swapchain, 1609 std::numeric_limits<deUint64>::max(), 1610 imageReadySemaphore, 1611 imageReadyFence, 1612 &imageNdx); 1613 1614 if (acquireResult == VK_SUBOPTIMAL_KHR) 1615 context.getTestContext().getLog() << TestLog::Message << "Got " << acquireResult << " at frame " << frameNdx << TestLog::EndMessage; 1616 else 1617 VK_CHECK(acquireResult); 1618 } 1619 1620 TCU_CHECK((size_t)imageNdx < swapchainImages.size()); 1621 1622 { 1623 const VkSemaphore renderingCompleteSemaphore = **renderingCompleteSemaphores[frameNdx%renderingCompleteSemaphores.size()]; 1624 const VkCommandBuffer commandBuffer = **commandBuffers[frameNdx%commandBuffers.size()]; 1625 const VkPipelineStageFlags waitDstStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; 1626 const VkSubmitInfo submitInfo = 1627 { 1628 VK_STRUCTURE_TYPE_SUBMIT_INFO, 1629 DE_NULL, 1630 1u, 1631 &imageReadySemaphore, 1632 &waitDstStage, 1633 1u, 1634 &commandBuffer, 1635 1u, 1636 &renderingCompleteSemaphore 1637 }; 1638 const VkPresentInfoKHR presentInfo = 1639 { 1640 VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, 1641 DE_NULL, 1642 1u, 1643 &renderingCompleteSemaphore, 1644 1u, 1645 &*swapchain, 1646 &imageNdx, 1647 (VkResult*)DE_NULL 1648 }; 1649 1650 renderer.recordFrame(commandBuffer, imageNdx, frameNdx); 1651 VK_CHECK(vkd.queueSubmit(devHelper.queue, 1u, &submitInfo, (VkFence)0)); 1652 VK_CHECK(vkd.queuePresentKHR(devHelper.queue, &presentInfo)); 1653 } 1654 } 1655 1656 VK_CHECK(vkd.deviceWaitIdle(device)); 1657 1658 prevSwapchain = swapchain; 1659 } 1660 catch (...) 1661 { 1662 // Make sure device is idle before destroying resources 1663 vkd.deviceWaitIdle(device); 1664 throw; 1665 } 1666 } 1667 1668 return tcu::TestStatus::pass("Resizing tests suceeded"); 1669} 1670 1671void getBasicRenderPrograms (SourceCollections& dst, Type) 1672{ 1673 TriangleRenderer::getPrograms(dst); 1674} 1675 1676void populateRenderGroup (tcu::TestCaseGroup* testGroup, Type wsiType) 1677{ 1678 addFunctionCaseWithPrograms(testGroup, "basic", "Basic Rendering Test", getBasicRenderPrograms, basicRenderTest, wsiType); 1679} 1680 1681void populateModifyGroup (tcu::TestCaseGroup* testGroup, Type wsiType) 1682{ 1683 const PlatformProperties& platformProperties = getPlatformProperties(wsiType); 1684 1685 if (platformProperties.swapchainExtent != PlatformProperties::SWAPCHAIN_EXTENT_MUST_MATCH_WINDOW_SIZE) 1686 { 1687 addFunctionCaseWithPrograms(testGroup, "resize", "Resize Swapchain Test", getBasicRenderPrograms, resizeSwapchainTest, wsiType); 1688 } 1689 1690 // \todo [2016-05-30 jesse] Add tests for modifying preTransform, compositeAlpha, presentMode 1691} 1692 1693} // anonymous 1694 1695void createSwapchainTests (tcu::TestCaseGroup* testGroup, vk::wsi::Type wsiType) 1696{ 1697 addTestGroup(testGroup, "create", "Create VkSwapchain with various parameters", populateSwapchainGroup, GroupParameters(wsiType, createSwapchainTest)); 1698 addTestGroup(testGroup, "simulate_oom", "Simulate OOM using callbacks during swapchain construction", populateSwapchainOOMGroup, wsiType); 1699 addTestGroup(testGroup, "render", "Rendering Tests", populateRenderGroup, wsiType); 1700 addTestGroup(testGroup, "modify", "Modify VkSwapchain", populateModifyGroup, wsiType); 1701} 1702 1703} // wsi 1704} // vkt 1705