From 3279fabb1bf18aac699b8ea0810e71539523807c Mon Sep 17 00:00:00 2001 From: Matthew Date: Sun, 2 Mar 2025 17:09:19 +1100 Subject: [PATCH] hello triangle completed --- src/game.c | 71 +++++++++++++++ src/game.h | 6 ++ src/main.c | 39 +-------- src/main.h | 1 + src/platform.c | 5 ++ src/platform.h | 3 + src/platform_linux.c | 105 +++++++++++++++++------ src/platform_linux.h | 34 ++++++++ src/render.c | 5 ++ src/render.h | 2 +- src/render_vulkan.c | 200 +++++++++++++++++++++++++++++++------------ src/render_vulkan.h | 31 +++++-- 12 files changed, 381 insertions(+), 121 deletions(-) create mode 100644 src/game.c create mode 100644 src/game.h diff --git a/src/game.c b/src/game.c new file mode 100644 index 0000000..63885f5 --- /dev/null +++ b/src/game.c @@ -0,0 +1,71 @@ +static void Run() +{ + InitPlatform(); + + u8 *mem = (u8 *)MemAllocZeroed(MB(512)); + Arena *arena = CreateArenaDebug(mem, MB(512), __LINE__); + + isize renderer_mem_size = MB(8); + + rawptr renderer_mem = ArenaAlloc(arena, renderer_mem_size); + Arena *renderer_arena = CreateArenaDebug(renderer_mem, MB(8), __LINE__); + + Assert(CreateWindow(), "Failed to initialize the window"); + Assert(InitRenderer(renderer_arena), "Failed to initialize the renderer"); + + b32 quit = false; + while (!quit) + { + if (HandleEvents()) break; + + BeginFrame(); + DrawTriangle(); + FinishFrame(); + } + + DestroyRenderer(); +} + +static b32 HandleEvents() +{ + b32 quit = false; + + WindowEvent e; + while (GetWindowEvent(&e)) + { + quit = ProcessEvent(&e); + } + + return quit; +} + +static b32 WaitForAndHandleEvent() +{ + WindowEvent e; + WaitForWindowEvent(&e); + return ProcessEvent(&e); +} + +static b32 ProcessEvent(WindowEvent *e) +{ + b32 quit = false; + + switch (e->type) + { + case EVENT_QUIT: + quit = true; + break; + case EVENT_RESIZE: + SetRenderResolution(e->resize.w, e->resize.h); + break; + case EVENT_MINIMIZE: + break; + case EVENT_SHOW: + break; + default: + break; + } + + return quit; +} + diff --git a/src/game.h b/src/game.h new file mode 100644 index 0000000..39005fd --- /dev/null +++ b/src/game.h @@ -0,0 +1,6 @@ +#pragma once + +static void Run(); +static b32 HandleEvents(); +static b32 WaitForAndHandleEvent(); +static b32 ProcessEvent(WindowEvent *e); diff --git a/src/main.c b/src/main.c index 9b8ef56..4802c68 100644 --- a/src/main.c +++ b/src/main.c @@ -4,44 +4,9 @@ #include "util.c" #include "arena.c" #include "render.c" +#include "game.c" int main(int argc, char **argv) { - InitPlatform(); - - u8 *mem = (u8 *)MemAllocZeroed(MB(512)); - Arena *arena = CreateArenaDebug(mem, MB(512), __LINE__); - - isize renderer_mem_size = MB(8); - - rawptr renderer_mem = ArenaAlloc(arena, renderer_mem_size); - Arena *renderer_arena = CreateArenaDebug(renderer_mem, MB(8), __LINE__); - - Assert(CreateWindow(), "Failed to initialize the window"); - Assert(InitRenderer(renderer_arena), "Failed to initialize the renderer"); - - b32 quit = false; - WindowEvent e; - while (!quit) - { - while (GetWindowEvent(&e)) - { - switch (e.type) - { - case EVENT_QUIT: - quit = true; - break; - default: - break; - } - } - - BeginFrame(); - DrawTriangle(); - FinishFrame(); - } - - DestroyRenderer(); - - return 0; + Run(); } diff --git a/src/main.h b/src/main.h index d00e82f..3dfdb7c 100644 --- a/src/main.h +++ b/src/main.h @@ -12,5 +12,6 @@ #include "util.h" #include "arena.h" #include "render.h" +#include "game.h" int main(int argc, char **argv); diff --git a/src/platform.c b/src/platform.c index 08f7ca8..39e4679 100644 --- a/src/platform.c +++ b/src/platform.c @@ -97,6 +97,11 @@ b32 GetWindowEvent(WindowEvent *event) return _GetWindowEvent(event); } +void WaitForWindowEvent(WindowEvent *event) +{ + _WaitForWindowEvent(event); +} + WindowSize GetWindowSize() { return _GetWindowSize(); diff --git a/src/platform.h b/src/platform.h index ed39ce4..5d70a86 100644 --- a/src/platform.h +++ b/src/platform.h @@ -25,6 +25,8 @@ enum Event_e EVENT_NONE = 0, EVENT_QUIT, EVENT_RESIZE, + EVENT_MINIMIZE, + EVENT_SHOW, }; struct WindowSize @@ -62,6 +64,7 @@ i32 EPrintf(const char *fmt, ...); b32 CreateWindow(); Window *GetWindow(); b32 GetWindowEvent(WindowEvent *event); +void WaitForWindowEvent(WindowEvent *event); WindowSize GetWindowSize(); // Directory Functions diff --git a/src/platform_linux.c b/src/platform_linux.c index e137a8f..8be3c4b 100644 --- a/src/platform_linux.c +++ b/src/platform_linux.c @@ -140,7 +140,8 @@ b32 _CreateWindow() { Window *window = &linux_window; - window->connection = xcb_connect(NULL, NULL); + int screen_index; + window->connection = xcb_connect(NULL, &screen_index); if (!window->connection) { return false; @@ -159,23 +160,23 @@ b32 _CreateWindow() XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_POINTER_MOTION | - XCB_EVENT_MASK_BUTTON_MOTION | - XCB_EVENT_MASK_STRUCTURE_NOTIFY| - XCB_EVENT_MASK_RESIZE_REDIRECT; + XCB_EVENT_MASK_STRUCTURE_NOTIFY; - const int val_win[] = {event_mask, 0}; - const int val_mask = XCB_CW_EVENT_MASK; + const int val_win[] = {screen->black_pixel, event_mask}; + const int val_mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK; window->window = xcb_generate_id(window->connection); - cookie = xcb_create_window_checked( + cookie = xcb_create_window( window->connection, XCB_COPY_FROM_PARENT, window->window, screen->root, - 0, 0, // x/y pos - window->w, window->h, - 10, + 0, // x pos + 0, // y pos + window->w, // width + window->h, // height + 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, screen->root_visual, val_mask, @@ -206,6 +207,10 @@ b32 _CreateWindow() xcb_intern_atom_reply_t *r_close = xcb_intern_atom_reply(window->connection, c_close, &error); XCB_CHECK_CURRENT_ERROR(window, error, "Failed to get WM_DELETE_WINDOW."); + xcb_intern_atom_cookie_t c_minimize = xcb_intern_atom(window->connection, 0, 20, "_NET_WM_STATE_HIDDEN"); + xcb_intern_atom_reply_t *r_minimize = xcb_intern_atom_reply(window->connection, c_minimize, &error); + XCB_CHECK_CURRENT_ERROR(window, error, "Failed to get _NET_WM_STATE_HIDDEN"); + cookie = xcb_change_property_checked( window->connection, XCB_PROP_MODE_REPLACE, @@ -219,6 +224,20 @@ b32 _CreateWindow() XCB_CHECK_ERROR(window, cookie, error, "Failed to set window close event."); window->close_event = r_close->atom; + window->minimize_event = r_minimize->atom; + + free(r_proto); + free(r_close); + free(r_minimize); + + xcb_map_window(window->connection, window->window); + + i32 stream_result = xcb_flush(window->connection); + if (stream_result <= 0) + { + Printfln("Error flushing the stream: %d", stream_result); + return false; + } return true; } @@ -236,6 +255,16 @@ WindowSize _GetWindowSize() } b32 _GetWindowEvent(WindowEvent *event) +{ + return HandleWindowEvent(event, false); +} + +void _WaitForWindowEvent(WindowEvent *event) +{ + HandleWindowEvent(event, true); +} + +b32 HandleWindowEvent(WindowEvent *event, b32 wait_for_event) { Assert(event != NULL, "GetWindowEvent received a null pointer"); @@ -243,8 +272,13 @@ b32 _GetWindowEvent(WindowEvent *event) b32 no_event = false; do { - xcb_generic_event_t *e = xcb_poll_for_event(linux_window.connection); + xcb_generic_event_t *e; + if (wait_for_event) + e = xcb_wait_for_event(linux_window.connection); + else + e = xcb_poll_for_event(linux_window.connection); + // XCB_UNMAP_NOTIFY if (e != NULL) { switch (e->response_type & ~0x80) @@ -256,34 +290,55 @@ b32 _GetWindowEvent(WindowEvent *event) event->type = EVENT_QUIT; valid_event = true; } - break; - case XCB_RESIZE_REQUEST: - event->type = EVENT_RESIZE; - valid_event = true; - - xcb_resize_request_event_t *resize = (xcb_resize_request_event_t *)e; - - if (resize->width > 0) + if (msg->data.data32[0] == linux_window.minimize_event) { - linux_window.w = resize->width; - event->resize.w = resize->width; + event->type = EVENT_MINIMIZE; + valid_event = true; + linux_window.w = 0; + linux_window.h = 0; } - if (resize->height > 0) + break; + case XCB_UNMAP_NOTIFY: + break; + case XCB_EXPOSE: + event->type = EVENT_SHOW; + + xcb_expose_event_t *expose_e = (xcb_expose_event_t *)e; + linux_window.w = expose_e->width; + linux_window.h = expose_e->height; + break; + case XCB_CONFIGURE_NOTIFY: + Printfln("configure notify"); + xcb_configure_notify_event_t *configure_event = (xcb_configure_notify_event_t *)e; + + if (linux_window.w != configure_event->width || linux_window.h != configure_event->height) { - linux_window.h = resize->height; - event->resize.h = resize->height; + + Printfln("inner w %d h %d", configure_event->width, configure_event->height); + event->type = EVENT_RESIZE; + valid_event = true; + + event->resize.w = configure_event->width; + event->resize.h = configure_event->height; + + linux_window.w = configure_event->width; + linux_window.h = configure_event->height; } break; default: break; } + + free(e); } else { break; } - } while(!valid_event); + + + } while(!valid_event && !wait_for_event); return valid_event; } diff --git a/src/platform_linux.h b/src/platform_linux.h index 0eebe82..fd0fc0e 100644 --- a/src/platform_linux.h +++ b/src/platform_linux.h @@ -62,6 +62,7 @@ typedef size_t usize; typedef float f32; typedef double f64; +typedef uint8_t b8; typedef uint32_t b32; typedef void * rawptr; @@ -70,6 +71,7 @@ typedef struct { xcb_connection_t *connection; xcb_window_t window; xcb_atom_t close_event; + xcb_atom_t minimize_event; u16 w, h; } Window; @@ -81,6 +83,36 @@ typedef struct { void *fn; } Function; +// X11 + +typedef struct +{ + +} XConnectRequest; + +typedef struct { + u8 opcode; + u16 len; // length in units of 4 bytes + u8 data; + // Extra data +} XRequest; + +typedef struct { + u32 len; +} XReply; + +typedef struct { + +} XError; + +typedef struct { + +} XEvent; + +typedef u32 XWindow; +typedef u32 XAtom; +typedef u32 XVisID; + // Platform API // Init Functions @@ -103,7 +135,9 @@ i32 _EPrintf(const char *fmt, va_list arg); b32 _CreateWindow(); Window *_GetWindow(); b32 _GetWindowEvent(WindowEvent *event); +void _WaitForWindowEvent(WindowEvent *event); WindowSize _GetWindowSize(); +b32 HandleWindowEvent(WindowEvent *event, b32 wait_for_event); // Platform API END diff --git a/src/render.c b/src/render.c index f86348b..3f80f57 100644 --- a/src/render.c +++ b/src/render.c @@ -36,3 +36,8 @@ b32 FinishFrame() { return _FinishFrame(); } + +void SetRenderResolution(u32 x, u32 y) +{ + _SetRenderResolution(x, y); +} diff --git a/src/render.h b/src/render.h index 2cb773f..3007614 100644 --- a/src/render.h +++ b/src/render.h @@ -23,4 +23,4 @@ void DestroyRenderer(); static b32 DrawFrame(); static void DrawTriangle(); static b32 FinishFrame(); - +static void SetRenderResolution(u32 x, u32 y); diff --git a/src/render_vulkan.c b/src/render_vulkan.c index 786b764..030f721 100644 --- a/src/render_vulkan.c +++ b/src/render_vulkan.c @@ -15,22 +15,45 @@ void _DestroyRenderer() DestroyVulkan(); } +static u32 GetFrameIndex() +{ + return renderer.frame_state.frame_cnt % renderer.vk.sc.img_count; +} + +static VkCommandBuffer GetFrameCmdBuf() +{ + return renderer.vk.frame.buffers[GetFrameIndex()]; +} + +static VkFence *GetFrameRenderFence() +{ + return &renderer.vk.frame.render_fences[GetFrameIndex()]; +} + +static VkSemaphore GetFrameRenderSem() +{ + return renderer.vk.frame.render_sems[GetFrameIndex()]; +} + +static VkSemaphore GetFrameSwapSem() +{ + return renderer.vk.frame.swapchain_sems[GetFrameIndex()]; +} + b32 _BeginFrame() { b32 success = true; VkResult result; - u8 f_ix = renderer.frame_state.frame_cnt % FRAME_OVERLAP; - VkDevice device = renderer.vk.device; - VkCommandBuffer cmd = renderer.vk.frame.buffers[f_ix]; - VkFence fence = renderer.vk.frame.render_fences[f_ix]; - VkSemaphore sc_sem = renderer.vk.frame.swapchain_sems[f_ix]; - VkSemaphore rndr_sem = renderer.vk.frame.render_sems[f_ix]; VkSwapchainKHR swapchain = renderer.vk.swapchain; + VkCommandBuffer cmd = GetFrameCmdBuf(); + VkFence *fence = GetFrameRenderFence(); + VkSemaphore sc_sem = GetFrameSwapSem(); + VkSemaphore rndr_sem = GetFrameRenderSem(); // TODO(MA): make this work with VK_PRESENT_MODE_MAILBOX_KHR and remove assignment of present mode to FIFO - result = vkWaitForFences(device, 1, &fence, VK_TRUE, 1000000000); + result = vkWaitForFences(device, 1, fence, VK_TRUE, 1000000000); if (result != VK_SUCCESS) { Printf("vkWaitForFences failure: %s", VkResultStr(result)); @@ -39,20 +62,22 @@ b32 _BeginFrame() if (success) { - result = vkAcquireNextImageKHR(device, swapchain, 1000000000, sc_sem, 0, &renderer.frame_state.img_ix); + result = vkResetFences(device, 1, fence); if (result != VK_SUCCESS) { - Printf("vkAcquireNextImageKHR failure: %s", VkResultStr(result)); + Printf("vkResetFences failure: %s", VkResultStr(result)); success = false; } } if (success) { - result = vkResetFences(device, 1, &fence); - if (result != VK_SUCCESS) + result = vkAcquireNextImageKHR(device, swapchain, 1000000000, sc_sem, 0, &renderer.frame_state.img_ix); + if (result == VK_ERROR_OUT_OF_DATE_KHR) + ResizeSwapchain(); + else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) { - Printf("vkResetFences failure: %s", VkResultStr(result)); + Printf("vkAcquireNextImageKHR failure: %s", VkResultStr(result)); success = false; } } @@ -67,6 +92,7 @@ b32 _BeginFrame() } } + if (success) { VkCommandBufferBeginInfo cmd_info = { @@ -85,9 +111,14 @@ b32 _BeginFrame() return success; } +void _ClearScreen() +{ + +} + void _DrawTriangle() { - u8 f_ix = renderer.frame_state.frame_cnt % FRAME_OVERLAP; + u32 f_ix = renderer.frame_state.frame_cnt % renderer.vk.sc.img_count; VkCommandBuffer cmd = renderer.vk.frame.buffers[f_ix]; @@ -106,7 +137,7 @@ void _DrawTriangle() .sType = STYPE(RENDERING_ATTACHMENT_INFO), .imageView = renderer.vk.sc.depth_img.view, .imageLayout = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL, - .loadOp = VK_ATTACHMENT_LOAD_OP_LOAD, + .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, .storeOp = VK_ATTACHMENT_STORE_OP_STORE, }; @@ -146,7 +177,7 @@ void _DrawTriangle() vkCmdSetScissor(cmd, 0, 1, &scissor); - vkCmdDrawIndexed(cmd, 3, 1, 0, 0, 0); + vkCmdDraw(cmd, 3, 1, 0, 0); // TODO(MA): maybe store current image layout between functions } @@ -156,26 +187,27 @@ b32 _FinishFrame() b32 success = true; VkResult result; - u32 f_ix = renderer.frame_state.frame_cnt % FRAME_OVERLAP; + u32 f_ix = renderer.frame_state.frame_cnt % renderer.vk.sc.img_count; VkCommandBuffer cmd = renderer.vk.frame.buffers[f_ix]; VkSemaphore sc_sem = renderer.vk.frame.swapchain_sems[f_ix]; VkSemaphore rndr_sem = renderer.vk.frame.render_sems[f_ix]; VkFence fence = renderer.vk.frame.render_fences[f_ix]; + VkImage curr_img = renderer.vk.sc.imgs[renderer.frame_state.img_ix]; vkCmdEndRendering(cmd); TransitionImage(cmd, renderer.vk.sc.draw_img.img, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); - TransitionImage(cmd, renderer.vk.sc.imgs[f_ix], VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + TransitionImage(cmd, curr_img, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); VkExtent2D extent = { .width = renderer.vk.sc.extent.width, .height = renderer.vk.sc.extent.height, }; - CopyImageToImage(cmd, renderer.vk.sc.draw_img.img, renderer.vk.sc.imgs[f_ix], extent, extent); + CopyImageToImage(cmd, renderer.vk.sc.draw_img.img, curr_img, extent, extent); - TransitionImage(cmd, renderer.vk.sc.imgs[f_ix], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); + TransitionImage(cmd, curr_img, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); result = vkEndCommandBuffer(cmd); if (result != VK_SUCCESS) @@ -231,20 +263,34 @@ b32 _FinishFrame() .swapchainCount = 1, .pWaitSemaphores = &rndr_sem, .waitSemaphoreCount = 1, - .pImageIndices = &f_ix, + .pImageIndices = &renderer.frame_state.img_ix, }; result = vkQueuePresentKHR(renderer.vk.queues.graphics_queue, &present_info); - if (result != VK_SUCCESS) + if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || renderer.resize_requested) + { + renderer.resize_requested = false; + ResizeSwapchain(); + } + else if (result != VK_SUCCESS) { Printf("vkQueuePresentKHR failure: %s", VkResultStr(result)); success = false; } } + renderer.frame_state.frame_cnt++; + return success; } +static void _SetRenderResolution(u32 x, u32 y) +{ + renderer.vk.sc.extent.width = x; + renderer.vk.sc.extent.height = y; + renderer.resize_requested = true; +} + /** * BACK END API END @@ -254,6 +300,21 @@ b32 _FinishFrame() * INTERNAL API */ +// Swapchain +static void ResizeSwapchain() +{ + vkDeviceWaitIdle(renderer.vk.device); + + DestroySwapchain(); + + DestroyDrawImages(); + + Assert(CreateSwapchain(), "Unable to recreate swapchain"); + Assert(CreateDrawImages(), "Unable to recreate draw images"); + + renderer.resize_requested = false; +} + // UTIL static void CopyImageToImage(VkCommandBuffer cmd, VkImage src, VkImage dst, VkExtent2D src_ext, VkExtent2D dst_ext) @@ -285,7 +346,7 @@ static void CopyImageToImage(VkCommandBuffer cmd, VkImage src, VkImage dst, VkEx VkBlitImageInfo2 blit_info = { .sType = STYPE(BLIT_IMAGE_INFO_2), .srcImage = src, - .srcImageLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + .srcImageLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, .dstImage = dst, .dstImageLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, .filter = VK_FILTER_LINEAR, @@ -350,9 +411,10 @@ static b32 InitVulkan(Arena *arena) Assert(CreateDevice(), "Unable to create device"); Assert(CreateVmaAllocator(), "Unable to create VMA allocator"); + Assert(CreateSwapchain(), "Unable to initialize swapchain and draw images"); + Assert(CreateDrawImages(), "Unable to create draw images"); Assert(CreateFrameStructures(), "Unable to create frame structures"); Assert(CreateImmediateStructures(), "Unable to create immediate structures"); - Assert(CreateSwapchain(), "Unable to initialize swapchain and draw images"); Assert(CreateDescriptors(), "Unable to initialize descriptors."); Assert(CreatePipelines(), "Unable to initialize pipelines."); @@ -661,6 +723,8 @@ static b32 InitVkDeviceFunctions() { INIT_DEV_FN(vkDestroyDescriptorSetLayout); INIT_DEV_FN(vkDestroyShaderModule); INIT_DEV_FN(vkQueuePresentKHR); + INIT_DEV_FN(vkCmdDraw); + INIT_DEV_FN(vkDeviceWaitIdle); return true; } @@ -727,9 +791,17 @@ static b32 CreateFrameStructures() { b32 success = true; FrameStructures *data = &renderer.vk.frame; + u32 img_count = renderer.vk.sc.img_count; + pool_create_info.queueFamilyIndex = renderer.vk.queues.graphics; - for (u32 i = 0; i < FRAME_OVERLAP; i++) + renderer.vk.frame.pools = ArenaAlloc(renderer.perm_arena, sizeof(VkCommandPool) * img_count); + renderer.vk.frame.buffers = ArenaAlloc(renderer.perm_arena, sizeof(VkCommandBuffer) * img_count); + renderer.vk.frame.swapchain_sems = ArenaAlloc(renderer.perm_arena, sizeof(VkSemaphore) * img_count); + renderer.vk.frame.render_sems = ArenaAlloc(renderer.perm_arena, sizeof(VkSemaphore) * img_count); + renderer.vk.frame.render_fences = ArenaAlloc(renderer.perm_arena, sizeof(VkFence) * img_count); + + for (u32 i = 0; i < renderer.vk.sc.img_count; i++) { VkResult result; VkDevice device = renderer.vk.device; @@ -795,11 +867,14 @@ static b32 CreateSwapchain() VkSurfaceCapabilitiesKHR capabilities; vkGetPhysicalDeviceSurfaceCapabilitiesKHR(phys_device, surface, &capabilities); + // Maybe reconsider handling window sizing within here and only handle it from events themselves + // causes issues when the window size is out of sync with the current swapchain VkExtent2D extent; - WindowSize win_size = GetWindowSize(); + u32 width = renderer.vk.sc.extent.width; + u32 height = renderer.vk.sc.extent.height; - extent.width = u32Clamp((u32)win_size.w, capabilities.minImageExtent.width, capabilities.maxImageExtent.width); - extent.height = u32Clamp((u32)win_size.h, capabilities.minImageExtent.height, capabilities.maxImageExtent.height); + extent.width = u32Clamp((u32)width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width); + extent.height = u32Clamp((u32)height, capabilities.minImageExtent.height, capabilities.maxImageExtent.height); u32 format_count; vkGetPhysicalDeviceSurfaceFormatsKHR(phys_device, surface, &format_count, NULL); @@ -827,11 +902,6 @@ static b32 CreateSwapchain() if (present_mode != VK_PRESENT_MODE_MAILBOX_KHR) present_mode = VK_PRESENT_MODE_FIFO_KHR; - - // - // TODO(MA): REMOVE THIS LATER - present_mode = VK_PRESENT_MODE_FIFO_KHR; - swapchain_create_info.minImageCount = capabilities.minImageCount + 1; swapchain_create_info.surface = surface; swapchain_create_info.imageFormat = renderer.vk.sc.format; @@ -848,7 +918,7 @@ static b32 CreateSwapchain() u32 image_count; vkGetSwapchainImagesKHR(device, renderer.vk.swapchain, &image_count, NULL); VkImage *sc_images = ArenaAlloc(renderer.perm_arena, sizeof(VkImage) * image_count); - VkImageView *sc_views = ArenaAlloc(renderer.perm_arena, sizeof(VkImage) * image_count); + VkImageView *sc_views = ArenaAlloc(renderer.perm_arena, sizeof(VkImageView) * image_count); vkGetSwapchainImagesKHR(device, renderer.vk.swapchain, &image_count, sc_images); for (u32 i = 0; i < image_count; i++) @@ -864,13 +934,21 @@ static b32 CreateSwapchain() renderer.vk.sc.imgs = sc_images; renderer.vk.sc.views = sc_views; renderer.vk.sc.img_count = image_count; + renderer.vk.sc.extent.width = extent.width; + renderer.vk.sc.extent.height = extent.height; + renderer.vk.sc.extent.depth = 1; + + return success; +} + +static b32 CreateDrawImages() +{ + b32 success = true; + VkResult result; VkFormat image_format = GetImageFormat(); - VkExtent3D extent_3d = { - .width = extent.width, - .height = extent.height, - .depth = 1, - }; + VkExtent3D extent = renderer.vk.sc.extent; + VkDevice device = renderer.vk.device; VmaAllocationCreateInfo alloc_create_info = { .usage = VMA_MEMORY_USAGE_GPU_ONLY, @@ -879,7 +957,7 @@ static b32 CreateSwapchain() // Draw Image draw_image_create_info.format = image_format; - draw_image_create_info.extent = extent_3d; + draw_image_create_info.extent = extent; result = vmaCreateImage(renderer.vk.alloc, &draw_image_create_info, &alloc_create_info, &renderer.vk.sc.draw_img.img, &renderer.vk.sc.draw_img.alloc, NULL); @@ -895,7 +973,7 @@ static b32 CreateSwapchain() success = false; // Depth Image - depth_image_create_info.extent = extent_3d; + depth_image_create_info.extent = extent; result = vmaCreateImage(renderer.vk.alloc, &depth_image_create_info, &alloc_create_info, &renderer.vk.sc.depth_img.img, &renderer.vk.sc.depth_img.alloc, NULL); @@ -909,7 +987,7 @@ static b32 CreateSwapchain() success = false; // Setting values - renderer.vk.sc.extent = extent_3d; + renderer.vk.sc.extent = extent; renderer.vk.sc.depth_img.fmt = depth_image_create_info.format; renderer.vk.sc.draw_img.fmt = draw_image_create_info.format; @@ -1058,6 +1136,29 @@ static b32 CreateShaderModule(u8 *bytes, u32 len, VkShaderModule *module) return success; } +static void DestroySwapchain() +{ + for (u32 i = 0; i < renderer.vk.sc.img_count; i++) + { + vkDestroyImageView(renderer.vk.device, renderer.vk.sc.views[i], NULL); + } + + vkDestroySwapchainKHR(renderer.vk.device, renderer.vk.swapchain, NULL); +} + +static void DestroyDrawImages() +{ + SwapchainStructures *sc = &renderer.vk.sc; + VkDevice device = renderer.vk.device; + VmaAllocator vma_alloc = renderer.vk.alloc; + + vkDestroyImageView(device, sc->draw_img.view, NULL); + vmaDestroyImage(vma_alloc, sc->draw_img.img, sc->draw_img.alloc); + + vkDestroyImageView(device, sc->depth_img.view, NULL); + vmaDestroyImage(vma_alloc, sc->depth_img.img, sc->depth_img.alloc); +} + static void DestroyVulkan() { VkDevice device = renderer.vk.device; @@ -1069,6 +1170,8 @@ static void DestroyVulkan() VkSwapchainKHR swapchain = renderer.vk.swapchain; PipelineStructures pipe = renderer.vk.pipe; + vkDeviceWaitIdle(device); + for (u32 i = PIPELINE_CUBE; i < PIPELINE_MAX; i++) vkDestroyPipeline(device, pipe.pipelines[i], NULL); @@ -1079,24 +1182,15 @@ static void DestroyVulkan() vkDestroyDescriptorPool(device, pipe.pool, NULL); - vkDestroyImageView(device, sc.draw_img.view, NULL); - vmaDestroyImage(vma_alloc, sc.draw_img.img, sc.draw_img.alloc); + DestroyDrawImages(); - vkDestroyImageView(device, sc.depth_img.view, NULL); - vmaDestroyImage(vma_alloc, sc.depth_img.img, sc.depth_img.alloc); - - for (u32 i = 0; i < sc.img_count; i++) - { - vkDestroyImageView(device, sc.views[i], NULL); - } - - vkDestroySwapchainKHR(device, swapchain, NULL); + DestroySwapchain(); vkDestroyFence(device, imm.fence, NULL); vkFreeCommandBuffers(device, imm.pool, 1, &imm.buffer); vkDestroyCommandPool(device, imm.pool, NULL); - for (u32 i = 0; i < FRAME_OVERLAP; i++) + for (u32 i = 0; i < sc.img_count; i++) { vkDestroySemaphore(device, data.render_sems[i], NULL); vkDestroySemaphore(device, data.swapchain_sems[i], NULL); diff --git a/src/render_vulkan.h b/src/render_vulkan.h index 7b5260e..a176cb4 100644 --- a/src/render_vulkan.h +++ b/src/render_vulkan.h @@ -118,6 +118,8 @@ VK_DECLARE(vkFreeCommandBuffers); VK_DECLARE(vkDestroyDescriptorSetLayout); VK_DECLARE(vkDestroyShaderModule); VK_DECLARE(vkQueuePresentKHR); +VK_DECLARE(vkCmdDraw); +VK_DECLARE(vkDeviceWaitIdle); // Vulkan Functions END @@ -170,11 +172,11 @@ typedef struct typedef struct { - VkCommandPool pools[FRAME_OVERLAP]; - VkCommandBuffer buffers[FRAME_OVERLAP]; - VkSemaphore swapchain_sems[FRAME_OVERLAP]; - VkSemaphore render_sems[FRAME_OVERLAP]; - VkFence render_fences[FRAME_OVERLAP]; + VkCommandPool *pools; + VkCommandBuffer *buffers; + VkSemaphore *swapchain_sems; + VkSemaphore *render_sems; + VkFence *render_fences; } FrameStructures; typedef struct @@ -210,6 +212,8 @@ typedef struct { typedef struct { u32 img_ix; u64 frame_cnt; + b8 comp_started; + b8 gfx_started; } FrameState; typedef struct { @@ -233,6 +237,7 @@ typedef struct { typedef struct { Vulkan_t vk; FrameState frame_state; + b32 resize_requested; Arena *arena; Arena *perm_arena; } Renderer_t; @@ -265,18 +270,33 @@ static b32 CreateVmaAllocator(); static b32 CreateFrameStructures(); static b32 CreateImmediateStructures(); static b32 CreateSwapchain(); +static b32 CreateDrawImages(); static VkFormat GetImageFormat(); static b32 CreateDescriptors(); static b32 CreatePipelines(); static b32 CreateShaderModule(u8 *bytes, u32 len, VkShaderModule *module); +// Util +static VkCommandBuffer GetFrameCmdBuf(); +static VkFence *GetFrameRenderFence(); +static VkSemaphore GetFrameRenderSem(); +static VkSemaphore GetFrameSwapSem(); +static u32 GetFrameIndex(); + // Destroy static void DestroyVulkan(); +static void DestroySwapchain(); +static void DestroyDrawImages(); // Util static void TransitionImage(VkCommandBuffer cmd, VkImage img, VkImageLayout curr, VkImageLayout new); static void CopyImageToImage(VkCommandBuffer cmd, VkImage src, VkImage dst, VkExtent2D src_ext, VkExtent2D dst_ext); +// Swapchain +static VkExtent2D SelectSwapchainExtent(VkSurfaceCapabilitiesKHR *capabilities); +static VkSurfaceFormatKHR SelectSwapchainFormat(VkSurfaceFormatKHR *formats); +static void ResizeSwapchain(); + // Renderer Functions Declarations END #include "vulkan_config.c" @@ -322,3 +342,4 @@ void _DestroyRenderer(); static b32 _BeginFrame(); static void _DrawTriangle(); static b32 _FinishFrame(); +static void _SetRenderSize(u32 x, u32 y);