Commit: dda0049f9455f32e0b48d72c7bc9baa1e84918b3
Parent: 9011f7a3136ffa3d72c60471c500c459a7f2a65f
Author: Randy Palamar
Date:   Thu, 21 Aug 2025 19:49:36 -0600
core: support window resizing
Diffstat:
| M | common.c | | | 59 | +++++++++++++++++++++++++++++++++++++++-------------------- | 
| M | util.h | | | 1 | + | 
| M | vulkan.h | | | 14 | +++++++++++++- | 
3 files changed, 53 insertions(+), 21 deletions(-)
diff --git a/common.c b/common.c
@@ -55,6 +55,14 @@ key_callback(GLFWwindow *window, s32 key, s32 scancode, s32 action, s32 modifier
 		ctx->should_exit = 1;
 }
 
+function void
+fb_callback(GLFWwindow *w, s32 width, s32 height)
+{
+	ViewerContext *v = glfwGetWindowUserPointer(w);
+	v->vulkan.swap_chain.framebuffer_resize = 1;
+	v->window_size = (sv2){{width, height}};
+}
+
 function u32
 vulkan_shader_kind_to_glslang_shader_kind(u32 kind)
 {
@@ -511,6 +519,19 @@ find_best_graphics_device(VulkanContext *vctx, Arena arena)
 	return result;
 }
 
+function VkExtent2D
+swap_chain_extent_for_window(GLFWwindow *w, VkSurfaceCapabilitiesKHR *si)
+{
+	VkExtent2D result = si->currentExtent;
+	if (result.width == U32_MAX)
+		glfwGetFramebufferSize(w, (s32 *)&result.width, (s32 *)&result.height);
+
+	result.width  = CLAMP(result.width,  si->minImageExtent.width,  si->maxImageExtent.width);
+	result.height = CLAMP(result.height, si->minImageExtent.height, si->maxImageExtent.height);
+
+	return result;
+}
+
 function void
 swap_chain_from_existing(VulkanSwapChain *sc, VkDevice device, VkRenderPass render_pass, VkExtent2D extent,
                          VkSurfaceTransformFlagBitsKHR transform, u32 requested_images, Arena *arena)
@@ -545,7 +566,9 @@ swap_chain_from_existing(VulkanSwapChain *sc, VkDevice device, VkRenderPass rend
 	VkSwapchainKHR swap_chain;
 	/* TODO(rnp): custom allocator? */
 	vkCreateSwapchainKHR(device, &create_info, 0, &swap_chain);
-	/* TODO(rnp): does the old one have to be destroyed here? */
+
+	vkDeviceWaitIdle(device);
+	vkDestroySwapchainKHR(device, sc->swap_chain, 0);
 	sc->swap_chain = swap_chain;
 
 	u32 image_count;
@@ -615,7 +638,6 @@ init_viewer(ViewerContext *ctx)
 	if (!glfwInit()) os_fatal(str8("failed to start glfw\n"));
 
 	glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
-	glfwWindowHint(GLFW_RESIZABLE,  GLFW_FALSE);
 
 	ctx->window = glfwCreateWindow(ctx->window_size.w, ctx->window_size.h, "Camera CNN", 0, 0);
 	if (!ctx->window) os_fatal(str8("failed to open window\n"));
@@ -740,20 +762,7 @@ init_viewer(ViewerContext *ctx)
 
 		vkCreateRenderPass(v->device, &render_pass_info, 0, &rp->render.render_pass);
 
-		VkExtent2D extent = surface_info.capabilities.currentExtent;
-		if (extent.width == U32_MAX) {
-			s32 width, height;
-			glfwGetFramebufferSize(ctx->window, &width, &height);
-			extent.width  = (u32)width;
-			extent.height = (u32)height;
-		}
-
-		extent.width  = CLAMP(extent.width,
-		                      surface_info.capabilities.minImageExtent.width,
-		                      surface_info.capabilities.maxImageExtent.width);
-		extent.height = CLAMP(extent.height,
-		                      surface_info.capabilities.minImageExtent.height,
-		                      surface_info.capabilities.maxImageExtent.height);
+		VkExtent2D extent = swap_chain_extent_for_window(ctx->window, &surface_info.capabilities);
 
 		sc->queue_index     = 0;
 		sc->exclusive_queue = 1;
@@ -839,10 +848,10 @@ init_viewer(ViewerContext *ctx)
 
 	glfwSetWindowUserPointer(ctx->window, ctx);
 	glfwSetKeyCallback(ctx->window, key_callback);
+	glfwSetFramebufferSizeCallback(ctx->window, fb_callback);
 
 	#if 0
 	glfwSetScrollCallback(ctx->window, scroll_callback);
-	glfwSetFramebufferSizeCallback(ctx->window, fb_callback);
 	#endif
 }
 
@@ -897,7 +906,7 @@ begin_frame(VulkanContext *v)
 }
 
 function void
-end_frame(VulkanContext *v)
+end_frame(VulkanContext *v, GLFWwindow *window, Arena arena)
 {
 	VulkanSwapChain *sc = &v->swap_chain;
 	VulkanPipeline  *rp = &v->render_pipeline;
@@ -927,7 +936,17 @@ end_frame(VulkanContext *v)
 	vkCmdEndRenderPass(rp->render.command_buffers[index]);
 	vkEndCommandBuffer(rp->render.command_buffers[index]);
 	vkQueueSubmit(v->queue, 1, &submit_info, rp->render.command_buffer_fences[index]);
-	vkQueuePresentKHR(v->queue, &present_info);
+	VkResult pres = vkQueuePresentKHR(v->queue, &present_info);
+	if (pres == VK_ERROR_OUT_OF_DATE_KHR || pres == VK_SUBOPTIMAL_KHR || sc->framebuffer_resize) {
+		sc->framebuffer_resize = 0;
+		struct VulkanSurfaceInfo surface_info;
+		fill_vulkan_surface_info(v->physical_device, sc->surface, &surface_info, &arena);
+		/* TODO(rnp): handle surface format change */
+
+		VkExtent2D extent = swap_chain_extent_for_window(window, &surface_info.capabilities);
+		swap_chain_from_existing(&v->swap_chain, v->device, rp->render.render_pass, extent,
+		                         surface_info.capabilities.currentTransform, sc->image_count, &arena);
+	}
 
 	sc->current_frame_index = (sc->current_frame_index + 1) % MAX_RENDER_FRAMES_IN_FLIGHT;
 }
@@ -941,5 +960,5 @@ viewer_frame_step(ViewerContext *ctx, f32 dt)
 	VulkanPipeline *rp = &ctx->vulkan.render_pipeline;
 	begin_frame(&ctx->vulkan);
 	vkCmdDraw(rp->render.command_buffers[ctx->vulkan.swap_chain.current_frame_index], 3, 1, 0, 0);
-	end_frame(&ctx->vulkan);
+	end_frame(&ctx->vulkan, ctx->window, ctx->arena);
 }
diff --git a/util.h b/util.h
@@ -254,6 +254,7 @@ typedef struct {
 	u32                 image_count;
 	u32                 framebuffer_index;
 	u32                 current_frame_index;
+	b32                 framebuffer_resize;
 } VulkanSwapChain;
 
 typedef enum {
diff --git a/vulkan.h b/vulkan.h
@@ -18,7 +18,6 @@
 
 typedef uint32_t VkBool32;
 typedef uint32_t VkFlags;
-typedef uint32_t VkResult;
 typedef uint32_t VkSampleMask;
 typedef uint64_t VkDeviceSize;
 typedef void *   VkCommandBuffer;
@@ -42,6 +41,13 @@ typedef void *   VkSurfaceKHR;
 typedef void *   VkSwapchainKHR;
 
 typedef enum {
+	VK_SUCCESS               = 0,
+	VK_SUBOPTIMAL_KHR        = 1000001003,
+	VK_ERROR_OUT_OF_DATE_KHR = -1000001004,
+	VK_RESULT_MAX_ENUM       = 0x7FFFFFFF
+} VkResult;
+
+typedef enum {
 	VK_STRUCTURE_TYPE_APPLICATION_INFO                          = 0,
 	VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO                      = 1,
 	VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO                  = 2,
@@ -1969,11 +1975,17 @@ VkResult vkQueueSubmit(VkQueue             queue,
                        const VkSubmitInfo *pSubmits,
                        VkFence             fence);
 
+VkResult vkDeviceWaitIdle(VkDevice device);
+
 VkResult vkCreateSwapchainKHR(VkDevice                        device,
                               const VkSwapchainCreateInfoKHR *pCreateInfo,
                               const VkAllocationCallbacks    *pAllocator,
                               VkSwapchainKHR                 *pSwapchain);
 
+void vkDestroySwapchainKHR(VkDevice                     device,
+                           VkSwapchainKHR               swapchain,
+                           const VkAllocationCallbacks *pAllocator);
+
 VkResult vkGetSwapchainImagesKHR(VkDevice        device,
                                  VkSwapchainKHR  swapchain,
                                  uint32_t       *pSwapchainImageCount,