スワップチェーンの再作成
テクニカル諏訪子 <suwako@076.moe>、2026-07-21 14:47
コミット 63cd403c4b1457ecdf487a10c719563b53fbeb0a
・親 eb2c9ba
・ファイルを見る
2個のファイル、+143 -84
| @@ -100,7 +100,7 @@ | ||
| 100 | 100 | <ClCompile> |
| 101 | 101 | <WarningLevel>Level3</WarningLevel> |
| 102 | 102 | <SDLCheck>true</SDLCheck> |
| 103 | - <PreprocessorDefinitions>_DEBUG;_CONSOLE;USING_GLSL;%(PreprocessorDefinitions)</PreprocessorDefinitions> | |
| 103 | + <PreprocessorDefinitions>_DEBUG;_CONSOLE;USING_SLANG;%(PreprocessorDefinitions)</PreprocessorDefinitions> | |
| 104 | 104 | <ConformanceMode>true</ConformanceMode> |
| 105 | 105 | <AdditionalIncludeDirectories>$(SolutionDir)dep\include;$(VULKAN_SDK)\Include;$(ProjectDir)</AdditionalIncludeDirectories> |
| 106 | 106 | <LanguageStandard>stdcpp20</LanguageStandard> |
| @@ -19,12 +19,23 @@ | ||
| 19 | 19 | constexpr int MAX_FRAMES_IN_FLIGHT = 2; |
| 20 | 20 | uint32_t frameIdx = 0; |
| 21 | 21 | |
| 22 | +bool framebufferResized = false; | |
| 23 | + | |
| 22 | 24 | std::vector<vk::Image> swapChainImages; |
| 23 | 25 | vk::CommandBuffer cmdBuf; |
| 24 | 26 | vk::CommandBufferBeginInfo beginInfo; |
| 25 | 27 | std::vector<vk::ImageView> swapChainImageViews; |
| 26 | 28 | vk::Extent2D swapChainExtent; |
| 27 | 29 | vk::Pipeline gfxPipeline; |
| 30 | +vk::PhysicalDevice physicalDevice; | |
| 31 | +vk::Device device; | |
| 32 | +vk::SurfaceKHR surface; | |
| 33 | +vk::SwapchainKHR swapChain; | |
| 34 | +vk::SurfaceFormatKHR swapChainFormat; | |
| 35 | +vk::SurfaceCapabilitiesKHR surfaceCapabilities; | |
| 36 | +std::vector<vk::SurfaceFormatKHR> availFormats; | |
| 37 | +std::vector<vk::PresentModeKHR> availPresentModes; | |
| 38 | +GLFWwindow *window; | |
| 28 | 39 | |
| 29 | 40 | #pragma region debugCallback |
| 30 | 41 | static VKAPI_ATTR vk::Bool32 VKAPI_CALL debugCallback( |
| @@ -77,6 +88,110 @@ | ||
| 77 | 88 | } |
| 78 | 89 | #pragma endregion |
| 79 | 90 | |
| 91 | +#pragma region framebufferResizeCallback | |
| 92 | +static void framebufferResizeCallback(GLFWwindow *window, int width, int height) { | |
| 93 | + framebufferResized = true; | |
| 94 | +} | |
| 95 | +#pragma endregion | |
| 96 | + | |
| 97 | +#pragma region swapchain | |
| 98 | +void createSwapChain() { | |
| 99 | + surfaceCapabilities = physicalDevice.getSurfaceCapabilitiesKHR(surface); | |
| 100 | + availFormats = physicalDevice.getSurfaceFormatsKHR(surface); | |
| 101 | + availPresentModes = physicalDevice.getSurfacePresentModesKHR(surface); | |
| 102 | + assert(!availFormats.empty()); | |
| 103 | + | |
| 104 | + const auto formatIt = std::ranges::find_if(availFormats, [](const auto &format) { | |
| 105 | + return format.format == vk::Format::eB8G8R8A8Srgb && format.colorSpace == vk::ColorSpaceKHR::eSrgbNonlinear; | |
| 106 | + }); | |
| 107 | + | |
| 108 | + swapChainFormat = formatIt != availFormats.end() ? *formatIt : availFormats[0]; | |
| 109 | + | |
| 110 | + assert(std::ranges::any_of(availPresentModes, [](auto present) { return present == vk::PresentModeKHR::eFifo; })); | |
| 111 | + vk::PresentModeKHR presentMode = std::ranges::any_of(availPresentModes, [](const vk::PresentModeKHR val) { | |
| 112 | + return vk::PresentModeKHR::eMailbox == val; | |
| 113 | + }) ? vk::PresentModeKHR::eMailbox : vk::PresentModeKHR::eFifo; | |
| 114 | + | |
| 115 | + if (surfaceCapabilities.currentExtent.width != std::numeric_limits<uint32_t>::max()) { | |
| 116 | + swapChainExtent = surfaceCapabilities.currentExtent; | |
| 117 | + } else { | |
| 118 | + int width, height; | |
| 119 | + glfwGetFramebufferSize(window, &width, &height); | |
| 120 | + swapChainExtent.width = std::clamp<uint32_t>(width, surfaceCapabilities.minImageExtent.width, surfaceCapabilities.maxImageExtent.width); | |
| 121 | + swapChainExtent.height = std::clamp<uint32_t>(height, surfaceCapabilities.minImageExtent.height, surfaceCapabilities.maxImageExtent.height); | |
| 122 | + } | |
| 123 | + | |
| 124 | + uint32_t minImageCount = std::max(3u, surfaceCapabilities.minImageCount); | |
| 125 | + if ((0 < surfaceCapabilities.maxImageCount) && (surfaceCapabilities.maxImageCount < minImageCount)) { | |
| 126 | + minImageCount = surfaceCapabilities.maxImageCount; | |
| 127 | + } | |
| 128 | + | |
| 129 | + vk::SwapchainCreateInfoKHR swapChainCreateInfo = {}; | |
| 130 | + swapChainCreateInfo.surface = surface; | |
| 131 | + swapChainCreateInfo.minImageCount = minImageCount; | |
| 132 | + swapChainCreateInfo.imageFormat = swapChainFormat.format; | |
| 133 | + swapChainCreateInfo.imageColorSpace = swapChainFormat.colorSpace; | |
| 134 | + swapChainCreateInfo.imageExtent = swapChainExtent; | |
| 135 | + swapChainCreateInfo.imageArrayLayers = 1; | |
| 136 | + swapChainCreateInfo.imageUsage = vk::ImageUsageFlagBits::eColorAttachment; | |
| 137 | + swapChainCreateInfo.imageSharingMode = vk::SharingMode::eExclusive; | |
| 138 | + swapChainCreateInfo.preTransform = surfaceCapabilities.currentTransform; | |
| 139 | + swapChainCreateInfo.compositeAlpha = vk::CompositeAlphaFlagBitsKHR::eOpaque; | |
| 140 | + swapChainCreateInfo.presentMode = presentMode; | |
| 141 | + swapChainCreateInfo.clipped = true; | |
| 142 | + | |
| 143 | + swapChain = device.createSwapchainKHR(swapChainCreateInfo); | |
| 144 | + swapChainImages = device.getSwapchainImagesKHR(swapChain); | |
| 145 | +} | |
| 146 | + | |
| 147 | +void createImageViews() { | |
| 148 | + assert(swapChainImageViews.empty()); | |
| 149 | + | |
| 150 | + vk::ImageSubresourceRange subrange = {}; | |
| 151 | + subrange.aspectMask = vk::ImageAspectFlagBits::eColor; | |
| 152 | + subrange.levelCount = 1; | |
| 153 | + subrange.layerCount = 1; | |
| 154 | + | |
| 155 | + vk::ImageViewCreateInfo imgViewCreateInfo = {}; | |
| 156 | + imgViewCreateInfo.viewType = vk::ImageViewType::e2D; | |
| 157 | + imgViewCreateInfo.format = swapChainFormat.format; | |
| 158 | + imgViewCreateInfo.subresourceRange = subrange; | |
| 159 | + imgViewCreateInfo.components = { | |
| 160 | + vk::ComponentSwizzle::eIdentity, | |
| 161 | + vk::ComponentSwizzle::eIdentity, | |
| 162 | + vk::ComponentSwizzle::eIdentity, | |
| 163 | + vk::ComponentSwizzle::eIdentity, | |
| 164 | + }; | |
| 165 | + | |
| 166 | + for (auto &img : swapChainImages) { | |
| 167 | + imgViewCreateInfo.image = img; | |
| 168 | + swapChainImageViews.push_back(device.createImageView(imgViewCreateInfo)); | |
| 169 | + } | |
| 170 | +} | |
| 171 | + | |
| 172 | +void cleanupSwapChain() { | |
| 173 | + for (auto &view : swapChainImageViews) device.destroyImageView(view); | |
| 174 | + swapChainImageViews.clear(); | |
| 175 | + | |
| 176 | + if (swapChain) device.destroySwapchainKHR(swapChain); | |
| 177 | + swapChain = nullptr; | |
| 178 | +} | |
| 179 | + | |
| 180 | +void recreateSwapChain() { | |
| 181 | + int width = 0, height = 0; | |
| 182 | + glfwGetFramebufferSize(window, &width, &height); | |
| 183 | + while (width == 0 || height == 0) { | |
| 184 | + glfwGetFramebufferSize(window, &width, &height); | |
| 185 | + glfwWaitEvents(); | |
| 186 | + } | |
| 187 | + | |
| 188 | + device.waitIdle(); | |
| 189 | + cleanupSwapChain(); | |
| 190 | + createSwapChain(); | |
| 191 | + createImageViews(); | |
| 192 | +} | |
| 193 | +#pragma endregion | |
| 194 | + | |
| 80 | 195 | #pragma region recordCmdBuf |
| 81 | 196 | void recordCmdBuf(uint32_t imgIdx) { |
| 82 | 197 | cmdBuf.reset(); |
| @@ -135,8 +250,10 @@ | ||
| 135 | 250 | |
| 136 | 251 | glfwInit(); |
| 137 | 252 | glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); |
| 138 | - glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); | |
| 139 | - GLFWwindow *window = glfwCreateWindow(winWidth, winHeight, winName.c_str(), nullptr, nullptr); | |
| 253 | + //glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); | |
| 254 | + window = glfwCreateWindow(winWidth, winHeight, winName.c_str(), nullptr, nullptr); | |
| 255 | + //glfwSetWindowUserPointer(window, this); | |
| 256 | + glfwSetFramebufferSizeCallback(window, framebufferResizeCallback); | |
| 140 | 257 | #pragma endregion |
| 141 | 258 | |
| 142 | 259 | #pragma region instance |
| @@ -210,18 +327,18 @@ | ||
| 210 | 327 | surfaceCreateInfo.hinstance = GetModuleHandle(nullptr); |
| 211 | 328 | surfaceCreateInfo.hwnd = glfwGetWin32Window(window); |
| 212 | 329 | |
| 213 | - vk::SurfaceKHR surface = instance.createWin32SurfaceKHR(surfaceCreateInfo); | |
| 330 | + surface = instance.createWin32SurfaceKHR(surfaceCreateInfo); | |
| 214 | 331 | #elif defined(__APPLE__) |
| 215 | 332 | vk::MetalSurfaceCreateInfoEXT surfaceCreateInfo = {}; |
| 216 | 333 | surfaceCreateInfo.pLayer = glfwGetCocoaWindow(window); |
| 217 | 334 | |
| 218 | - vk::SurfaceKHR surface = instance.createMetalSurfaceEXT(surfaceCreateInfo); | |
| 335 | + surface = instance.createMetalSurfaceEXT(surfaceCreateInfo); | |
| 219 | 336 | #else |
| 220 | 337 | vk::XcbSurfaceCreateInfoKHR surfaceCreateInfo = {}; |
| 221 | 338 | surfaceCreateInfo.connection = glfwGetX11Display(); |
| 222 | 339 | surfaceCreateInfo.window = glfwGetX11Window(window); |
| 223 | 340 | |
| 224 | - vk::SurfaceKHR surface = instance.createXcbSurfaceKHR(surfaceCreateInfo); | |
| 341 | + surface = instance.createXcbSurfaceKHR(surfaceCreateInfo); | |
| 225 | 342 | #endif |
| 226 | 343 | #pragma endregion |
| 227 | 344 | |
| @@ -250,7 +367,7 @@ | ||
| 250 | 367 | return -1; |
| 251 | 368 | } |
| 252 | 369 | |
| 253 | - vk::PhysicalDevice physicalDevice = physDevices.rbegin()->second; | |
| 370 | + physicalDevice = physDevices.rbegin()->second; | |
| 254 | 371 | |
| 255 | 372 | auto queueFamilies = physicalDevice.getQueueFamilyProperties(); |
| 256 | 373 | bool supportsGfx = std::ranges::any_of(queueFamilies, [](auto const &q) { |
| @@ -333,83 +450,17 @@ | ||
| 333 | 450 | devCreateInfo.enabledExtensionCount = static_cast<uint32_t>(reqDevExt.size()); |
| 334 | 451 | devCreateInfo.ppEnabledExtensionNames = reqDevExt.data(); |
| 335 | 452 | |
| 336 | - vk::Device device = physicalDevice.createDevice(devCreateInfo); | |
| 453 | + device = physicalDevice.createDevice(devCreateInfo); | |
| 337 | 454 | VULKAN_HPP_DEFAULT_DISPATCHER.init(device); |
| 338 | 455 | vk::Queue gfxQueue = device.getQueue(gfxIdx, 0); |
| 339 | 456 | #pragma endregion |
| 340 | 457 | |
| 341 | 458 | #pragma region swapchain |
| 342 | - auto surfaceCapabilities = physicalDevice.getSurfaceCapabilitiesKHR(surface); | |
| 343 | - std::vector<vk::SurfaceFormatKHR> availFormats = physicalDevice.getSurfaceFormatsKHR(surface); | |
| 344 | - std::vector<vk::PresentModeKHR> availPresentModes = physicalDevice.getSurfacePresentModesKHR(surface); | |
| 345 | - assert(!availFormats.empty()); | |
| 346 | - | |
| 347 | - const auto formatIt = std::ranges::find_if(availFormats, [](const auto &format) { | |
| 348 | - return format.format == vk::Format::eB8G8R8A8Srgb && format.colorSpace == vk::ColorSpaceKHR::eSrgbNonlinear; | |
| 349 | - }); | |
| 350 | - | |
| 351 | - vk::SurfaceFormatKHR swapChainFormat = formatIt != availFormats.end() ? *formatIt : availFormats[0]; | |
| 352 | - | |
| 353 | - assert(std::ranges::any_of(availPresentModes, [](auto present) { return present == vk::PresentModeKHR::eFifo; })); | |
| 354 | - vk::PresentModeKHR presentMode = std::ranges::any_of(availPresentModes, [](const vk::PresentModeKHR val) { | |
| 355 | - return vk::PresentModeKHR::eMailbox == val; | |
| 356 | - }) ? vk::PresentModeKHR::eMailbox : vk::PresentModeKHR::eFifo; | |
| 357 | - | |
| 358 | - if (surfaceCapabilities.currentExtent.width != std::numeric_limits<uint32_t>::max()) { | |
| 359 | - swapChainExtent = surfaceCapabilities.currentExtent; | |
| 360 | - } else { | |
| 361 | - int width, height; | |
| 362 | - glfwGetFramebufferSize(window, &width, &height); | |
| 363 | - swapChainExtent.width = std::clamp<uint32_t>(width, surfaceCapabilities.minImageExtent.width, surfaceCapabilities.maxImageExtent.width); | |
| 364 | - swapChainExtent.height = std::clamp<uint32_t>(height, surfaceCapabilities.minImageExtent.height, surfaceCapabilities.maxImageExtent.height); | |
| 365 | - } | |
| 366 | - | |
| 367 | - uint32_t minImageCount = std::max(3u, surfaceCapabilities.minImageCount); | |
| 368 | - if ((0 < surfaceCapabilities.maxImageCount) && (surfaceCapabilities.maxImageCount < minImageCount)) { | |
| 369 | - minImageCount = surfaceCapabilities.maxImageCount; | |
| 370 | - } | |
| 371 | - | |
| 372 | - vk::SwapchainCreateInfoKHR swapChainCreateInfo = {}; | |
| 373 | - swapChainCreateInfo.surface = surface; | |
| 374 | - swapChainCreateInfo.minImageCount = minImageCount; | |
| 375 | - swapChainCreateInfo.imageFormat = swapChainFormat.format; | |
| 376 | - swapChainCreateInfo.imageColorSpace = swapChainFormat.colorSpace; | |
| 377 | - swapChainCreateInfo.imageExtent = swapChainExtent; | |
| 378 | - swapChainCreateInfo.imageArrayLayers = 1; | |
| 379 | - swapChainCreateInfo.imageUsage = vk::ImageUsageFlagBits::eColorAttachment; | |
| 380 | - swapChainCreateInfo.imageSharingMode = vk::SharingMode::eExclusive; | |
| 381 | - swapChainCreateInfo.preTransform = surfaceCapabilities.currentTransform; | |
| 382 | - swapChainCreateInfo.compositeAlpha = vk::CompositeAlphaFlagBitsKHR::eOpaque; | |
| 383 | - swapChainCreateInfo.presentMode = presentMode; | |
| 384 | - swapChainCreateInfo.clipped = true; | |
| 385 | - | |
| 386 | - vk::SwapchainKHR swapChain = device.createSwapchainKHR(swapChainCreateInfo); | |
| 387 | - swapChainImages = device.getSwapchainImagesKHR(swapChain); | |
| 459 | + createSwapChain(); | |
| 388 | 460 | #pragma endregion |
| 389 | 461 | |
| 390 | 462 | #pragma region imageviews |
| 391 | - assert(swapChainImageViews.empty()); | |
| 392 | - | |
| 393 | - vk::ImageSubresourceRange subrange = {}; | |
| 394 | - subrange.aspectMask = vk::ImageAspectFlagBits::eColor; | |
| 395 | - subrange.levelCount = 1; | |
| 396 | - subrange.layerCount = 1; | |
| 397 | - | |
| 398 | - vk::ImageViewCreateInfo imgViewCreateInfo = {}; | |
| 399 | - imgViewCreateInfo.viewType = vk::ImageViewType::e2D; | |
| 400 | - imgViewCreateInfo.format = swapChainFormat.format; | |
| 401 | - imgViewCreateInfo.subresourceRange = subrange; | |
| 402 | - imgViewCreateInfo.components = { | |
| 403 | - vk::ComponentSwizzle::eIdentity, | |
| 404 | - vk::ComponentSwizzle::eIdentity, | |
| 405 | - vk::ComponentSwizzle::eIdentity, | |
| 406 | - vk::ComponentSwizzle::eIdentity, | |
| 407 | - }; | |
| 408 | - | |
| 409 | - for (auto &img : swapChainImages) { | |
| 410 | - imgViewCreateInfo.image = img; | |
| 411 | - swapChainImageViews.push_back(device.createImageView(imgViewCreateInfo)); | |
| 412 | - } | |
| 463 | + createImageViews(); | |
| 413 | 464 | #pragma endregion |
| 414 | 465 | |
| 415 | 466 | #pragma region gfxPipeline |
| @@ -582,12 +633,18 @@ | ||
| 582 | 633 | device.resetFences(inFlightFences[frameIdx]); |
| 583 | 634 | auto [acquireRes, imgIdx] = device.acquireNextImageKHR(swapChain, UINT64_MAX, presentCompleteSemaphores[frameIdx], nullptr); |
| 584 | 635 | |
| 585 | - if (acquireRes == vk::Result::eErrorOutOfDateKHR || acquireRes == vk::Result::eSuboptimalKHR) continue; | |
| 586 | - if (acquireRes != vk::Result::eSuccess) { | |
| 587 | - assert(false && "次の画像の受け取りに失敗。"); | |
| 636 | + if (acquireRes == vk::Result::eErrorOutOfDateKHR) { | |
| 637 | + recreateSwapChain(); | |
| 638 | + framebufferResized = false; | |
| 639 | + continue; | |
| 640 | + } | |
| 641 | + if (acquireRes != vk::Result::eSuccess && acquireRes != vk::Result::eSuboptimalKHR) { | |
| 642 | + assert((acquireRes == vk::Result::eTimeout || acquireRes == vk::Result::eNotReady) && "次の画像の受け取りに失敗。"); | |
| 588 | 643 | break; |
| 589 | 644 | } |
| 590 | 645 | |
| 646 | + device.resetFences(inFlightFences[frameIdx]); | |
| 647 | + | |
| 591 | 648 | recordCmdBuf(imgIdx); |
| 592 | 649 | |
| 593 | 650 | vk::PipelineStageFlags waitStage = vk::PipelineStageFlagBits::eColorAttachmentOutput; |
| @@ -619,6 +676,12 @@ | ||
| 619 | 676 | presentInfo.pImageIndices = &imgIdx; |
| 620 | 677 | |
| 621 | 678 | vk::Result presentRes = gfxQueue.presentKHR(presentInfo); |
| 679 | + if (presentRes == vk::Result::eSuboptimalKHR || presentRes == vk::Result::eErrorOutOfDateKHR || framebufferResized) { | |
| 680 | + recreateSwapChain(); | |
| 681 | + framebufferResized = false; | |
| 682 | + } else { | |
| 683 | + assert(presentRes == vk::Result::eSuccess); | |
| 684 | + } | |
| 622 | 685 | |
| 623 | 686 | frameIdx = (frameIdx + 1) % MAX_FRAMES_IN_FLIGHT; |
| 624 | 687 | #pragma endregion |
| @@ -632,11 +695,7 @@ | ||
| 632 | 695 | if (gfxPipeline) device.destroyPipeline(gfxPipeline); |
| 633 | 696 | if (pipelineLayout) device.destroyPipelineLayout(pipelineLayout); |
| 634 | 697 | shader.Finalize(); |
| 635 | - | |
| 636 | - for (auto &view : swapChainImageViews) device.destroyImageView(view); | |
| 637 | - swapChainImageViews.clear(); | |
| 638 | - | |
| 639 | - if (swapChain) device.destroySwapchainKHR(swapChain); | |
| 698 | + cleanupSwapChain(); | |
| 640 | 699 | for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i) device.destroySemaphore(presentCompleteSemaphores[i]); |
| 641 | 700 | for (size_t i = 0; i < swapChainImages.size(); ++i) device.destroySemaphore(renderFinishedSemaphores[i]); |
| 642 | 701 | for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i) device.destroyFence(inFlightFences[i]); |