From 6566d9075d5fed48af014c93f87c4aed8c4bd21c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antti=20Ker=C3=A4nen?= Date: Sat, 19 Oct 2024 11:04:28 +0300 Subject: [PATCH] Improve Vulkan demo (#717) * demo: fix errors and warnings in GLFW Vulkan demo * demo: improve resize handling in GLFW Vulkan demo On my machine (x86_64 Linux, Wayland) VK_SUBOPTIMAL or VK_OUT_OF_DATE_KHR were never returned so the resizing of the swapchain never happened. Fix by using GLFW's resize callback to detect a window resize and a swapchain recreation. Vulkan tutorial uses the same approach. fb_scale was never used in the Vulkan backend, so it was removed. * demo: fix copy&paste typo in GLFW Vulkan demo * demo: sync nuklear_glfw_vulkan.in.h with nuklear_glfw_vulkan.h Over time people have not been noticing that the expected workflow is to build nuklear_glfw_vulkan.h from nuklear_glfw_vulkan.in.h --- demo/glfw_vulkan/main.c | 47 +++++++++++++++---- demo/glfw_vulkan/nuklear_glfw_vulkan.h | 7 +-- demo/glfw_vulkan/src/nuklear_glfw_vulkan.in.h | 15 +++--- 3 files changed, 50 insertions(+), 19 deletions(-) diff --git a/demo/glfw_vulkan/main.c b/demo/glfw_vulkan/main.c index 7e28eca60..db2f0ab19 100644 --- a/demo/glfw_vulkan/main.c +++ b/demo/glfw_vulkan/main.c @@ -139,8 +139,19 @@ struct vulkan_demo { VkDeviceMemory demo_texture_memory; VkFence render_fence; + + bool framebuffer_resized; }; +static void glfw_framebuffer_resize_callback(GLFWwindow* window, int width, int height) { + struct vulkan_demo* demo; + + (void)width; + (void)height; + demo = glfwGetWindowUserPointer(window); + demo->framebuffer_resized = true; +} + static void glfw_error_callback(int e, const char *d) { fprintf(stderr, "Error %d: %s\n", e, d); } @@ -299,7 +310,7 @@ bool create_instance(struct vulkan_demo *demo) { if (i > 0) { printf(", "); } - printf(enabled_extensions[i]); + printf("%s\n", enabled_extensions[i]); } printf("\n"); for (i = 0; i < enabled_extension_count; i++) { @@ -1227,8 +1238,8 @@ bool create_graphics_pipeline(struct vulkan_demo *demo) { bool ret = false; char *vert_shader_code = NULL; char *frag_shader_code = NULL; - VkShaderModule vert_shader_module; - VkShaderModule frag_shader_module; + VkShaderModule vert_shader_module = VK_NULL_HANDLE; + VkShaderModule frag_shader_module = VK_NULL_HANDLE; FILE *fp; size_t file_len; VkPipelineShaderStageCreateInfo vert_shader_stage_info; @@ -1246,8 +1257,9 @@ bool create_graphics_pipeline(struct vulkan_demo *demo) { VkPipelineLayoutCreateInfo pipeline_layout_info; VkResult result; VkGraphicsPipelineCreateInfo pipeline_info; + size_t read_result; - fp = fopen("shaders/demo.vert.spv", "r"); + fp = fopen("shaders/demo.vert.spv", "rb"); if (!fp) { fprintf(stderr, "Couldn't open shaders/demo.vert.spv\n"); return false; @@ -1256,15 +1268,19 @@ bool create_graphics_pipeline(struct vulkan_demo *demo) { file_len = ftell(fp); vert_shader_code = malloc(file_len); fseek(fp, 0, 0); - fread(vert_shader_code, 1, file_len, fp); + read_result = fread(vert_shader_code, file_len, 1, fp); fclose(fp); + if (read_result != 1) { + fprintf(stderr, "Could not read fragment shader\n"); + goto cleanup; + } if (!create_shader_module(demo->device, vert_shader_code, file_len, &vert_shader_module)) { goto cleanup; } - fp = fopen("shaders/demo.frag.spv", "r"); + fp = fopen("shaders/demo.frag.spv", "rb"); if (!fp) { fprintf(stderr, "Couldn't open shaders/demo.frag.spv\n"); return false; @@ -1273,8 +1289,12 @@ bool create_graphics_pipeline(struct vulkan_demo *demo) { file_len = ftell(fp); frag_shader_code = malloc(file_len); fseek(fp, 0, 0); - fread(frag_shader_code, 1, file_len, fp); + read_result = fread(frag_shader_code, file_len, 1, fp); fclose(fp); + if (read_result != 1) { + fprintf(stderr, "Could not read fragment shader\n"); + goto cleanup; + } if (!create_shader_module(demo->device, frag_shader_code, file_len, &frag_shader_module)) { @@ -1862,6 +1882,8 @@ bool create_vulkan_demo(struct vulkan_demo *demo) { return false; } + demo->framebuffer_resized = false; + return true; } @@ -1876,6 +1898,9 @@ bool recreate_swap_chain(struct vulkan_demo *demo) { update_descriptor_sets(demo); nk_glfw3_resize(demo->swap_chain_image_extent.width, demo->swap_chain_image_extent.height); + + demo->framebuffer_resized = false; + return true; } @@ -1961,7 +1986,7 @@ bool render(struct vulkan_demo *demo, struct nk_colorf *bg, result = vkQueuePresentKHR(demo->present_queue, &present_info); - if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) { + if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || demo->framebuffer_resized) { recreate_swap_chain(demo); } else if (result != VK_SUCCESS) { fprintf(stderr, "vkQueuePresentKHR failed: %d\n", result); @@ -2073,6 +2098,8 @@ int main(void) { memset(&demo, 0, sizeof(struct vulkan_demo)); demo.win = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, "Demo", NULL, NULL); + glfwSetWindowUserPointer(demo.win, &demo); + glfwSetFramebufferSizeCallback(demo.win, glfw_framebuffer_resize_callback); if (!create_vulkan_demo(&demo)) { fprintf(stderr, "failed to create vulkan demo!\n"); @@ -2192,6 +2219,10 @@ int main(void) { demo.image_available, NULL, &image_index); if (result == VK_ERROR_OUT_OF_DATE_KHR) { + recreate_swap_chain(&demo); + + /* If vkAcquireNextImageKHR does not successfully acquire an image, + * semaphore and fence are unaffected. */ continue; } if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) { diff --git a/demo/glfw_vulkan/nuklear_glfw_vulkan.h b/demo/glfw_vulkan/nuklear_glfw_vulkan.h index 8189f8df6..0a7da1d5c 100644 --- a/demo/glfw_vulkan/nuklear_glfw_vulkan.h +++ b/demo/glfw_vulkan/nuklear_glfw_vulkan.h @@ -372,7 +372,6 @@ static struct nk_glfw { struct nk_glfw_device vulkan; struct nk_context ctx; struct nk_font_atlas atlas; - struct nk_vec2 fb_scale; unsigned int text[NK_GLFW_TEXT_MAX]; int text_len; struct nk_vec2 scroll; @@ -1179,8 +1178,6 @@ NK_API void nk_glfw3_resize(uint32_t framebuffer_width, struct nk_glfw_device *dev = &glfw.vulkan; glfwGetWindowSize(glfw.win, &glfw.width, &glfw.height); glfwGetFramebufferSize(glfw.win, &glfw.display_width, &glfw.display_height); - glfw.fb_scale.x = (float)glfw.display_width / (float)glfw.width; - glfw.fb_scale.y = (float)glfw.display_height / (float)glfw.height; nk_glfw3_destroy_render_resources(dev); nk_glfw3_create_render_resources(dev, framebuffer_width, @@ -1264,7 +1261,7 @@ NK_API void nk_glfw3_new_frame(void) { for (i = 0; i < glfw.text_len; ++i) nk_input_unicode(ctx, glfw.text[i]); -#ifdef NK_GLFW_GL4_MOUSE_GRABBING +#ifdef NK_GLFW_VULKAN_MOUSE_GRABBING /* optional grabbing behavior */ if (ctx->input.mouse.grab) glfwSetInputMode(glfw.win, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); @@ -1332,7 +1329,7 @@ NK_API void nk_glfw3_new_frame(void) { glfwGetCursorPos(win, &x, &y); nk_input_motion(ctx, (int)x, (int)y); -#ifdef NK_GLFW_GL4_MOUSE_GRABBING +#ifdef NK_GLFW_VULKAN_MOUSE_GRABBING if (ctx->input.mouse.grabbed) { glfwSetCursorPos(glfw.win, ctx->input.mouse.prev.x, ctx->input.mouse.prev.y); diff --git a/demo/glfw_vulkan/src/nuklear_glfw_vulkan.in.h b/demo/glfw_vulkan/src/nuklear_glfw_vulkan.in.h index ea4957836..37cec69bb 100644 --- a/demo/glfw_vulkan/src/nuklear_glfw_vulkan.in.h +++ b/demo/glfw_vulkan/src/nuklear_glfw_vulkan.in.h @@ -24,6 +24,7 @@ unsigned char nuklearshaders_nuklear_frag_spv[] = {}; unsigned int nuklearshaders_nuklear_frag_spv_len = 0; // NUKLEAR_SHADERS_END +#include #include #include #define GLFW_INCLUDE_VULKAN @@ -71,7 +72,6 @@ NK_API void nk_glfw3_mouse_button_callback(GLFWwindow *win, int button, */ #ifdef NK_GLFW_VULKAN_IMPLEMENTATION #undef NK_GLFW_VULKAN_IMPLEMENTATION -#include #include #ifndef NK_GLFW_TEXT_MAX @@ -149,13 +149,13 @@ static struct nk_glfw { struct nk_glfw_device vulkan; struct nk_context ctx; struct nk_font_atlas atlas; - struct nk_vec2 fb_scale; unsigned int text[NK_GLFW_TEXT_MAX]; int text_len; struct nk_vec2 scroll; double last_button_click; int is_double_click_down; struct nk_vec2 double_click_pos; + float delta_time_seconds_last; } glfw; struct Mat4f { @@ -955,8 +955,6 @@ NK_API void nk_glfw3_resize(uint32_t framebuffer_width, struct nk_glfw_device *dev = &glfw.vulkan; glfwGetWindowSize(glfw.win, &glfw.width, &glfw.height); glfwGetFramebufferSize(glfw.win, &glfw.display_width, &glfw.display_height); - glfw.fb_scale.x = (float)glfw.display_width / (float)glfw.width; - glfw.fb_scale.y = (float)glfw.display_height / (float)glfw.height; nk_glfw3_destroy_render_resources(dev); nk_glfw3_create_render_resources(dev, framebuffer_width, @@ -1031,11 +1029,16 @@ NK_API void nk_glfw3_new_frame(void) { struct nk_context *ctx = &glfw.ctx; struct GLFWwindow *win = glfw.win; + /* update the timer */ + float delta_time_now = (float)glfwGetTime(); + glfw.ctx.delta_time_seconds = delta_time_now - glfw.delta_time_seconds_last; + glfw.delta_time_seconds_last = delta_time_now; + nk_input_begin(ctx); for (i = 0; i < glfw.text_len; ++i) nk_input_unicode(ctx, glfw.text[i]); -#ifdef NK_GLFW_GL4_MOUSE_GRABBING +#ifdef NK_GLFW_VULKAN_MOUSE_GRABBING /* optional grabbing behavior */ if (ctx->input.mouse.grab) glfwSetInputMode(glfw.win, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); @@ -1103,7 +1106,7 @@ NK_API void nk_glfw3_new_frame(void) { glfwGetCursorPos(win, &x, &y); nk_input_motion(ctx, (int)x, (int)y); -#ifdef NK_GLFW_GL4_MOUSE_GRABBING +#ifdef NK_GLFW_VULKAN_MOUSE_GRABBING if (ctx->input.mouse.grabbed) { glfwSetCursorPos(glfw.win, ctx->input.mouse.prev.x, ctx->input.mouse.prev.y);