スワップチェーンの再作成

テクニカル諏訪子 <suwako@076.moe>、2026-07-21 14:47
コミット 63cd403c4b1457ecdf487a10c719563b53fbeb0a ・親 eb2c9ba ・ファイルを見る

2個のファイル、+143 -84

@@ -100,7 +100,7 @@
100100 <ClCompile>
101101 <WarningLevel>Level3</WarningLevel>
102102 <SDLCheck>true</SDLCheck>
103- <PreprocessorDefinitions>_DEBUG;_CONSOLE;USING_GLSL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
103+ <PreprocessorDefinitions>_DEBUG;_CONSOLE;USING_SLANG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
104104 <ConformanceMode>true</ConformanceMode>
105105 <AdditionalIncludeDirectories>$(SolutionDir)dep\include;$(VULKAN_SDK)\Include;$(ProjectDir)</AdditionalIncludeDirectories>
106106 <LanguageStandard>stdcpp20</LanguageStandard>
@@ -19,12 +19,23 @@
1919 constexpr int MAX_FRAMES_IN_FLIGHT = 2;
2020 uint32_t frameIdx = 0;
2121
22+bool framebufferResized = false;
23+
2224 std::vector<vk::Image> swapChainImages;
2325 vk::CommandBuffer cmdBuf;
2426 vk::CommandBufferBeginInfo beginInfo;
2527 std::vector<vk::ImageView> swapChainImageViews;
2628 vk::Extent2D swapChainExtent;
2729 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;
2839
2940 #pragma region debugCallback
3041 static VKAPI_ATTR vk::Bool32 VKAPI_CALL debugCallback(
@@ -77,6 +88,110 @@
7788 }
7889 #pragma endregion
7990
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+
80195 #pragma region recordCmdBuf
81196 void recordCmdBuf(uint32_t imgIdx) {
82197 cmdBuf.reset();
@@ -135,8 +250,10 @@
135250
136251 glfwInit();
137252 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);
140257 #pragma endregion
141258
142259 #pragma region instance
@@ -210,18 +327,18 @@
210327 surfaceCreateInfo.hinstance = GetModuleHandle(nullptr);
211328 surfaceCreateInfo.hwnd = glfwGetWin32Window(window);
212329
213- vk::SurfaceKHR surface = instance.createWin32SurfaceKHR(surfaceCreateInfo);
330+ surface = instance.createWin32SurfaceKHR(surfaceCreateInfo);
214331 #elif defined(__APPLE__)
215332 vk::MetalSurfaceCreateInfoEXT surfaceCreateInfo = {};
216333 surfaceCreateInfo.pLayer = glfwGetCocoaWindow(window);
217334
218- vk::SurfaceKHR surface = instance.createMetalSurfaceEXT(surfaceCreateInfo);
335+ surface = instance.createMetalSurfaceEXT(surfaceCreateInfo);
219336 #else
220337 vk::XcbSurfaceCreateInfoKHR surfaceCreateInfo = {};
221338 surfaceCreateInfo.connection = glfwGetX11Display();
222339 surfaceCreateInfo.window = glfwGetX11Window(window);
223340
224- vk::SurfaceKHR surface = instance.createXcbSurfaceKHR(surfaceCreateInfo);
341+ surface = instance.createXcbSurfaceKHR(surfaceCreateInfo);
225342 #endif
226343 #pragma endregion
227344
@@ -250,7 +367,7 @@
250367 return -1;
251368 }
252369
253- vk::PhysicalDevice physicalDevice = physDevices.rbegin()->second;
370+ physicalDevice = physDevices.rbegin()->second;
254371
255372 auto queueFamilies = physicalDevice.getQueueFamilyProperties();
256373 bool supportsGfx = std::ranges::any_of(queueFamilies, [](auto const &q) {
@@ -333,83 +450,17 @@
333450 devCreateInfo.enabledExtensionCount = static_cast<uint32_t>(reqDevExt.size());
334451 devCreateInfo.ppEnabledExtensionNames = reqDevExt.data();
335452
336- vk::Device device = physicalDevice.createDevice(devCreateInfo);
453+ device = physicalDevice.createDevice(devCreateInfo);
337454 VULKAN_HPP_DEFAULT_DISPATCHER.init(device);
338455 vk::Queue gfxQueue = device.getQueue(gfxIdx, 0);
339456 #pragma endregion
340457
341458 #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();
388460 #pragma endregion
389461
390462 #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();
413464 #pragma endregion
414465
415466 #pragma region gfxPipeline
@@ -582,12 +633,18 @@
582633 device.resetFences(inFlightFences[frameIdx]);
583634 auto [acquireRes, imgIdx] = device.acquireNextImageKHR(swapChain, UINT64_MAX, presentCompleteSemaphores[frameIdx], nullptr);
584635
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) && "次の画像の受け取りに失敗。");
588643 break;
589644 }
590645
646+ device.resetFences(inFlightFences[frameIdx]);
647+
591648 recordCmdBuf(imgIdx);
592649
593650 vk::PipelineStageFlags waitStage = vk::PipelineStageFlagBits::eColorAttachmentOutput;
@@ -619,6 +676,12 @@
619676 presentInfo.pImageIndices = &imgIdx;
620677
621678 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+ }
622685
623686 frameIdx = (frameIdx + 1) % MAX_FRAMES_IN_FLIGHT;
624687 #pragma endregion
@@ -632,11 +695,7 @@
632695 if (gfxPipeline) device.destroyPipeline(gfxPipeline);
633696 if (pipelineLayout) device.destroyPipelineLayout(pipelineLayout);
634697 shader.Finalize();
635-
636- for (auto &view : swapChainImageViews) device.destroyImageView(view);
637- swapChainImageViews.clear();
638-
639- if (swapChain) device.destroySwapchainKHR(swapChain);
698+ cleanupSwapChain();
640699 for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i) device.destroySemaphore(presentCompleteSemaphores[i]);
641700 for (size_t i = 0; i < swapChainImages.size(); ++i) device.destroySemaphore(renderFinishedSemaphores[i]);
642701 for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i) device.destroyFence(inFlightFences[i]);