Created
December 9, 2025 16:36
-
-
Save ClearlyKyle/515d741a0f12284c1334fb23361a5991 to your computer and use it in GitHub Desktop.
microui demo using vulkan and RGFW
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| static char micro_demo_logbuf[64000] = {0}; | |
| static int micro_demo_logbuf_updated = 0; | |
| static float micro_demo_bg[3] = {90, 95, 100}; | |
| static void _micro_write_log(const char *text) | |
| { | |
| if (micro_demo_logbuf[0]) { strcat(micro_demo_logbuf, "\n"); } | |
| strcat(micro_demo_logbuf, text); | |
| micro_demo_logbuf_updated = 1; | |
| } | |
| static void micro_demo_test_window(mu_Context *ctx) | |
| { | |
| /* do window */ | |
| if (mu_begin_window(ctx, "Demo Window", mu_rect(40, 40, 300, 450))) | |
| { | |
| mu_Container *win = mu_get_current_container(ctx); | |
| win->rect.w = mu_max(win->rect.w, 240); | |
| win->rect.h = mu_max(win->rect.h, 300); | |
| /* window info */ | |
| if (mu_header(ctx, "Window Info")) | |
| { | |
| win = mu_get_current_container(ctx); | |
| char buf[64]; | |
| mu_layout_row(ctx, 2, (int[]){54, -1}, 0); | |
| mu_label(ctx, "Position:"); | |
| sprintf(buf, "%d, %d", win->rect.x, win->rect.y); | |
| mu_label(ctx, buf); | |
| mu_label(ctx, "Size:"); | |
| sprintf(buf, "%d, %d", win->rect.w, win->rect.h); | |
| mu_label(ctx, buf); | |
| } | |
| /* labels + buttons */ | |
| if (mu_header_ex(ctx, "Test Buttons", MU_OPT_EXPANDED)) | |
| { | |
| mu_layout_row(ctx, 3, (int[]){86, -110, -1}, 0); | |
| mu_label(ctx, "Test buttons 1:"); | |
| if (mu_button(ctx, "Button 1")) { _micro_write_log("Pressed button 1"); } | |
| if (mu_button(ctx, "Button 2")) { _micro_write_log("Pressed button 2"); } | |
| mu_label(ctx, "Test buttons 2:"); | |
| if (mu_button(ctx, "Button 3")) { _micro_write_log("Pressed button 3"); } | |
| if (mu_button(ctx, "Popup")) { mu_open_popup(ctx, "Test Popup"); } | |
| if (mu_begin_popup(ctx, "Test Popup")) | |
| { | |
| mu_button(ctx, "Hello"); | |
| mu_button(ctx, "World"); | |
| mu_end_popup(ctx); | |
| } | |
| } | |
| /* tree */ | |
| if (mu_header_ex(ctx, "Tree and Text", MU_OPT_EXPANDED)) | |
| { | |
| mu_layout_row(ctx, 2, (int[]){140, -1}, 0); | |
| mu_layout_begin_column(ctx); | |
| if (mu_begin_treenode(ctx, "Test 1")) | |
| { | |
| if (mu_begin_treenode(ctx, "Test 1a")) | |
| { | |
| mu_label(ctx, "Hello"); | |
| mu_label(ctx, "world"); | |
| mu_end_treenode(ctx); | |
| } | |
| if (mu_begin_treenode(ctx, "Test 1b")) | |
| { | |
| if (mu_button(ctx, "Button 1")) { _micro_write_log("Pressed button 1"); } | |
| if (mu_button(ctx, "Button 2")) { _micro_write_log("Pressed button 2"); } | |
| mu_end_treenode(ctx); | |
| } | |
| mu_end_treenode(ctx); | |
| } | |
| if (mu_begin_treenode(ctx, "Test 2")) | |
| { | |
| mu_layout_row(ctx, 2, (int[]){54, 54}, 0); | |
| if (mu_button(ctx, "Button 3")) { _micro_write_log("Pressed button 3"); } | |
| if (mu_button(ctx, "Button 4")) { _micro_write_log("Pressed button 4"); } | |
| if (mu_button(ctx, "Button 5")) { _micro_write_log("Pressed button 5"); } | |
| if (mu_button(ctx, "Button 6")) { _micro_write_log("Pressed button 6"); } | |
| mu_end_treenode(ctx); | |
| } | |
| if (mu_begin_treenode(ctx, "Test 3")) | |
| { | |
| static int checks[3] = {1, 0, 1}; | |
| mu_checkbox(ctx, "Checkbox 1", &checks[0]); | |
| mu_checkbox(ctx, "Checkbox 2", &checks[1]); | |
| mu_checkbox(ctx, "Checkbox 3", &checks[2]); | |
| mu_end_treenode(ctx); | |
| } | |
| mu_layout_end_column(ctx); | |
| mu_layout_begin_column(ctx); | |
| mu_layout_row(ctx, 1, (int[]){-1}, 0); | |
| mu_text(ctx, "Lorem ipsum dolor sit amet, consectetur adipiscing " | |
| "elit. Maecenas lacinia, sem eu lacinia molestie, mi risus faucibus " | |
| "ipsum, eu varius magna felis a nulla."); | |
| mu_layout_end_column(ctx); | |
| } | |
| /* background color sliders */ | |
| if (mu_header_ex(ctx, "Background Color", MU_OPT_EXPANDED)) | |
| { | |
| mu_layout_row(ctx, 2, (int[]){-78, -1}, 74); | |
| /* sliders */ | |
| mu_layout_begin_column(ctx); | |
| mu_layout_row(ctx, 2, (int[]){46, -1}, 0); | |
| mu_label(ctx, "Red:"); | |
| mu_slider(ctx, µ_demo_bg[0], 0, 255); | |
| mu_label(ctx, "Green:"); | |
| mu_slider(ctx, µ_demo_bg[1], 0, 255); | |
| mu_label(ctx, "Blue:"); | |
| mu_slider(ctx, µ_demo_bg[2], 0, 255); | |
| mu_layout_end_column(ctx); | |
| /* color preview */ | |
| mu_Rect r = mu_layout_next(ctx); | |
| mu_draw_rect(ctx, r, mu_color((int)micro_demo_bg[0], (int)micro_demo_bg[1], (int)micro_demo_bg[2], 255)); | |
| char buf[32]; | |
| sprintf(buf, "#%02X%02X%02X", (int)micro_demo_bg[0], (int)micro_demo_bg[1], (int)micro_demo_bg[2]); | |
| mu_draw_control_text(ctx, buf, r, MU_COLOR_TEXT, MU_OPT_ALIGNCENTER); | |
| } | |
| mu_end_window(ctx); | |
| } | |
| } | |
| static void micro_demo_log_window(mu_Context *ctx) | |
| { | |
| if (mu_begin_window(ctx, "Log Window", mu_rect(350, 40, 300, 200))) | |
| { | |
| /* output text panel */ | |
| mu_layout_row(ctx, 1, (int[]){-1}, -25); | |
| mu_begin_panel(ctx, "Log Output"); | |
| mu_Container *panel = mu_get_current_container(ctx); | |
| mu_layout_row(ctx, 1, (int[]){-1}, -1); | |
| mu_text(ctx, micro_demo_logbuf); | |
| mu_end_panel(ctx); | |
| if (micro_demo_logbuf_updated) | |
| { | |
| panel->scroll.y = panel->content_size.y; | |
| micro_demo_logbuf_updated = 0; | |
| } | |
| /* input textbox + submit button */ | |
| static char buf[128]; | |
| int submitted = 0; | |
| mu_layout_row(ctx, 2, (int[]){-70, -1}, 0); | |
| if (mu_textbox(ctx, buf, sizeof(buf)) & MU_RES_SUBMIT) | |
| { | |
| mu_set_focus(ctx, ctx->last_id); | |
| submitted = 1; | |
| } | |
| if (mu_button(ctx, "Submit")) { submitted = 1; } | |
| if (submitted) | |
| { | |
| _micro_write_log(buf); | |
| buf[0] = '\0'; | |
| } | |
| mu_end_window(ctx); | |
| } | |
| } | |
| static int uint8_slider(mu_Context *ctx, unsigned char *value, int low, int high) | |
| { | |
| static float tmp; | |
| mu_push_id(ctx, &value, sizeof(value)); | |
| tmp = *value; | |
| int res = mu_slider_ex(ctx, &tmp, (mu_Real)low, (mu_Real)high, 0, "%.0f", MU_OPT_ALIGNCENTER); | |
| *value = (unsigned char)tmp; | |
| mu_pop_id(ctx); | |
| return res; | |
| } | |
| static void micro_demo_style_window(mu_Context *ctx) | |
| { | |
| static struct | |
| { | |
| const char *label; | |
| int idx; | |
| } colors[] = { | |
| {"text:", MU_COLOR_TEXT}, | |
| {"border:", MU_COLOR_BORDER}, | |
| {"windowbg:", MU_COLOR_WINDOWBG}, | |
| {"titlebg:", MU_COLOR_TITLEBG}, | |
| {"titletext:", MU_COLOR_TITLETEXT}, | |
| {"panelbg:", MU_COLOR_PANELBG}, | |
| {"button:", MU_COLOR_BUTTON}, | |
| {"buttonhover:", MU_COLOR_BUTTONHOVER}, | |
| {"buttonfocus:", MU_COLOR_BUTTONFOCUS}, | |
| {"base:", MU_COLOR_BASE}, | |
| {"basehover:", MU_COLOR_BASEHOVER}, | |
| {"basefocus:", MU_COLOR_BASEFOCUS}, | |
| {"scrollbase:", MU_COLOR_SCROLLBASE}, | |
| {"scrollthumb:", MU_COLOR_SCROLLTHUMB}, | |
| {NULL}}; | |
| if (mu_begin_window(ctx, "Style Editor", mu_rect(350, 250, 300, 240))) | |
| { | |
| int sw = (int)(mu_get_current_container(ctx)->body.w * 0.14); | |
| mu_layout_row(ctx, 6, (int[]){80, sw, sw, sw, sw, -1}, 0); | |
| for (int i = 0; colors[i].label; i++) | |
| { | |
| mu_label(ctx, colors[i].label); | |
| uint8_slider(ctx, &ctx->style->colors[i].r, 0, 255); | |
| uint8_slider(ctx, &ctx->style->colors[i].g, 0, 255); | |
| uint8_slider(ctx, &ctx->style->colors[i].b, 0, 255); | |
| uint8_slider(ctx, &ctx->style->colors[i].a, 0, 255); | |
| mu_draw_rect(ctx, mu_layout_next(ctx), ctx->style->colors[i]); | |
| } | |
| mu_end_window(ctx); | |
| } | |
| } | |
| static void micro_run_demo(mu_Context *ctx) | |
| { | |
| mu_begin(ctx); | |
| micro_demo_style_window(ctx); | |
| micro_demo_log_window(ctx); | |
| micro_demo_test_window(ctx); | |
| mu_end(ctx); | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #define _CRT_SECURE_NO_WARNINGS | |
| #include <assert.h> | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <stdbool.h> | |
| #include <string.h> | |
| #pragma warning(push) | |
| #pragma warning(disable : 6385 6011 6031 6340 6387 28183) | |
| // #define RGFW_DEBUG | |
| #define RGFW_VULKAN | |
| #define RGFW_IMPLEMENTATION | |
| #include "RGFW.h" | |
| #pragma warning(pop) | |
| #pragma warning(push) | |
| #pragma warning(disable : 4244 4267 4456 6246 4996) | |
| #include "microui/microui.c" | |
| #include "microui/atlas.inl" | |
| #pragma warning(pop) | |
| #include <vulkan/vulkan.h> | |
| #define DEBUG_MODE | |
| #define MAX_FRAMES_IN_FLIGHT (2) | |
| #define WINDOW_WIDTH (1280) | |
| #define WINDOW_HEIGHT (720) | |
| // | |
| // UTILS | |
| // | |
| #define KUNUSED(VAR) ((void)(VAR)) | |
| #define KASSERT(EXPR) assert(EXPR) | |
| #define KARRAYSIZE(ARR) ((int)(sizeof(ARR) / sizeof(*(ARR)))) | |
| #define KCLAMP(value, min_val, max_val) \ | |
| ((value) < (min_val) ? (min_val) : ((value) > (max_val) ? (max_val) : (value))) | |
| // | |
| // SHADERS | |
| // | |
| // C:\VulkanSDK\1.3.280.0\Bin\glslangValidator -V v.vert -o vert.h --vn triangle_vert_spv | |
| // C:\VulkanSDK\1.3.280.0\Bin\glslangValidator -V f.frag -o frag.h --vn triangle_frag_spv | |
| /* | |
| #version 450 | |
| layout(location = 0) out vec3 frag_color; | |
| vec2 positions[3] = vec2[](vec2(-0.6, -0.75), vec2(0.6, -0.75), vec2(0, 0.75)); | |
| vec3 colours[3] = vec3[](vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0), vec3(0.0, 0.0, 1.0)); | |
| void main() | |
| { | |
| gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0); | |
| frag_color = colours[gl_VertexIndex]; | |
| } | |
| */ | |
| const uint32_t triangle_vert_spv[] = | |
| { | |
| 0x07230203, 0x00010000, 0x0008000b, 0x00000038, 0x00000000, 0x00020011, 0x00000001, 0x0006000b, | |
| 0x00000001, 0x4c534c47, 0x6474732e, 0x3035342e, 0x00000000, 0x0003000e, 0x00000000, 0x00000001, | |
| 0x0008000f, 0x00000000, 0x00000004, 0x6e69616d, 0x00000000, 0x00000024, 0x00000028, 0x00000033, | |
| 0x00030003, 0x00000002, 0x000001c2, 0x00040005, 0x00000004, 0x6e69616d, 0x00000000, 0x00050005, | |
| 0x0000000c, 0x69736f70, 0x6e6f6974, 0x00000073, 0x00040005, 0x00000019, 0x6f6c6f63, 0x00007372, | |
| 0x00060005, 0x00000022, 0x505f6c67, 0x65567265, 0x78657472, 0x00000000, 0x00060006, 0x00000022, | |
| 0x00000000, 0x505f6c67, 0x7469736f, 0x006e6f69, 0x00070006, 0x00000022, 0x00000001, 0x505f6c67, | |
| 0x746e696f, 0x657a6953, 0x00000000, 0x00070006, 0x00000022, 0x00000002, 0x435f6c67, 0x4470696c, | |
| 0x61747369, 0x0065636e, 0x00070006, 0x00000022, 0x00000003, 0x435f6c67, 0x446c6c75, 0x61747369, | |
| 0x0065636e, 0x00030005, 0x00000024, 0x00000000, 0x00060005, 0x00000028, 0x565f6c67, 0x65747265, | |
| 0x646e4978, 0x00007865, 0x00050005, 0x00000033, 0x67617266, 0x6f6c6f43, 0x00000072, 0x00050048, | |
| 0x00000022, 0x00000000, 0x0000000b, 0x00000000, 0x00050048, 0x00000022, 0x00000001, 0x0000000b, | |
| 0x00000001, 0x00050048, 0x00000022, 0x00000002, 0x0000000b, 0x00000003, 0x00050048, 0x00000022, | |
| 0x00000003, 0x0000000b, 0x00000004, 0x00030047, 0x00000022, 0x00000002, 0x00040047, 0x00000028, | |
| 0x0000000b, 0x0000002a, 0x00040047, 0x00000033, 0x0000001e, 0x00000000, 0x00020013, 0x00000002, | |
| 0x00030021, 0x00000003, 0x00000002, 0x00030016, 0x00000006, 0x00000020, 0x00040017, 0x00000007, | |
| 0x00000006, 0x00000002, 0x00040015, 0x00000008, 0x00000020, 0x00000000, 0x0004002b, 0x00000008, | |
| 0x00000009, 0x00000003, 0x0004001c, 0x0000000a, 0x00000007, 0x00000009, 0x00040020, 0x0000000b, | |
| 0x00000006, 0x0000000a, 0x0004003b, 0x0000000b, 0x0000000c, 0x00000006, 0x0004002b, 0x00000006, | |
| 0x0000000d, 0xbf19999a, 0x0004002b, 0x00000006, 0x0000000e, 0xbf400000, 0x0005002c, 0x00000007, | |
| 0x0000000f, 0x0000000d, 0x0000000e, 0x0004002b, 0x00000006, 0x00000010, 0x3f19999a, 0x0005002c, | |
| 0x00000007, 0x00000011, 0x00000010, 0x0000000e, 0x0004002b, 0x00000006, 0x00000012, 0x00000000, | |
| 0x0004002b, 0x00000006, 0x00000013, 0x3f400000, 0x0005002c, 0x00000007, 0x00000014, 0x00000012, | |
| 0x00000013, 0x0006002c, 0x0000000a, 0x00000015, 0x0000000f, 0x00000011, 0x00000014, 0x00040017, | |
| 0x00000016, 0x00000006, 0x00000003, 0x0004001c, 0x00000017, 0x00000016, 0x00000009, 0x00040020, | |
| 0x00000018, 0x00000006, 0x00000017, 0x0004003b, 0x00000018, 0x00000019, 0x00000006, 0x0004002b, | |
| 0x00000006, 0x0000001a, 0x3f800000, 0x0006002c, 0x00000016, 0x0000001b, 0x0000001a, 0x00000012, | |
| 0x00000012, 0x0006002c, 0x00000016, 0x0000001c, 0x00000012, 0x0000001a, 0x00000012, 0x0006002c, | |
| 0x00000016, 0x0000001d, 0x00000012, 0x00000012, 0x0000001a, 0x0006002c, 0x00000017, 0x0000001e, | |
| 0x0000001b, 0x0000001c, 0x0000001d, 0x00040017, 0x0000001f, 0x00000006, 0x00000004, 0x0004002b, | |
| 0x00000008, 0x00000020, 0x00000001, 0x0004001c, 0x00000021, 0x00000006, 0x00000020, 0x0006001e, | |
| 0x00000022, 0x0000001f, 0x00000006, 0x00000021, 0x00000021, 0x00040020, 0x00000023, 0x00000003, | |
| 0x00000022, 0x0004003b, 0x00000023, 0x00000024, 0x00000003, 0x00040015, 0x00000025, 0x00000020, | |
| 0x00000001, 0x0004002b, 0x00000025, 0x00000026, 0x00000000, 0x00040020, 0x00000027, 0x00000001, | |
| 0x00000025, 0x0004003b, 0x00000027, 0x00000028, 0x00000001, 0x00040020, 0x0000002a, 0x00000006, | |
| 0x00000007, 0x00040020, 0x00000030, 0x00000003, 0x0000001f, 0x00040020, 0x00000032, 0x00000003, | |
| 0x00000016, 0x0004003b, 0x00000032, 0x00000033, 0x00000003, 0x00040020, 0x00000035, 0x00000006, | |
| 0x00000016, 0x00050036, 0x00000002, 0x00000004, 0x00000000, 0x00000003, 0x000200f8, 0x00000005, | |
| 0x0003003e, 0x0000000c, 0x00000015, 0x0003003e, 0x00000019, 0x0000001e, 0x0004003d, 0x00000025, | |
| 0x00000029, 0x00000028, 0x00050041, 0x0000002a, 0x0000002b, 0x0000000c, 0x00000029, 0x0004003d, | |
| 0x00000007, 0x0000002c, 0x0000002b, 0x00050051, 0x00000006, 0x0000002d, 0x0000002c, 0x00000000, | |
| 0x00050051, 0x00000006, 0x0000002e, 0x0000002c, 0x00000001, 0x00070050, 0x0000001f, 0x0000002f, | |
| 0x0000002d, 0x0000002e, 0x00000012, 0x0000001a, 0x00050041, 0x00000030, 0x00000031, 0x00000024, | |
| 0x00000026, 0x0003003e, 0x00000031, 0x0000002f, 0x0004003d, 0x00000025, 0x00000034, 0x00000028, | |
| 0x00050041, 0x00000035, 0x00000036, 0x00000019, 0x00000034, 0x0004003d, 0x00000016, 0x00000037, | |
| 0x00000036, 0x0003003e, 0x00000033, 0x00000037, 0x000100fd, 0x00010038}; | |
| /* | |
| #version 450 | |
| layout(location = 0) in vec3 frag_colour; | |
| layout(location = 0) out vec4 out_colour; | |
| void main() | |
| { | |
| out_colour = vec4(frag_colour, 1.0); | |
| } | |
| */ | |
| const uint32_t triangle_frag_spv[] = | |
| { | |
| 0x07230203, 0x00010000, 0x0008000b, 0x00000013, 0x00000000, 0x00020011, 0x00000001, 0x0006000b, | |
| 0x00000001, 0x4c534c47, 0x6474732e, 0x3035342e, 0x00000000, 0x0003000e, 0x00000000, 0x00000001, | |
| 0x0007000f, 0x00000004, 0x00000004, 0x6e69616d, 0x00000000, 0x00000009, 0x0000000c, 0x00030010, | |
| 0x00000004, 0x00000007, 0x00030003, 0x00000002, 0x000001c2, 0x00040005, 0x00000004, 0x6e69616d, | |
| 0x00000000, 0x00050005, 0x00000009, 0x4374756f, 0x726f6c6f, 0x00000000, 0x00050005, 0x0000000c, | |
| 0x67617266, 0x6f6c6f43, 0x00000072, 0x00040047, 0x00000009, 0x0000001e, 0x00000000, 0x00040047, | |
| 0x0000000c, 0x0000001e, 0x00000000, 0x00020013, 0x00000002, 0x00030021, 0x00000003, 0x00000002, | |
| 0x00030016, 0x00000006, 0x00000020, 0x00040017, 0x00000007, 0x00000006, 0x00000004, 0x00040020, | |
| 0x00000008, 0x00000003, 0x00000007, 0x0004003b, 0x00000008, 0x00000009, 0x00000003, 0x00040017, | |
| 0x0000000a, 0x00000006, 0x00000003, 0x00040020, 0x0000000b, 0x00000001, 0x0000000a, 0x0004003b, | |
| 0x0000000b, 0x0000000c, 0x00000001, 0x0004002b, 0x00000006, 0x0000000e, 0x3f800000, 0x00050036, | |
| 0x00000002, 0x00000004, 0x00000000, 0x00000003, 0x000200f8, 0x00000005, 0x0004003d, 0x0000000a, | |
| 0x0000000d, 0x0000000c, 0x00050051, 0x00000006, 0x0000000f, 0x0000000d, 0x00000000, 0x00050051, | |
| 0x00000006, 0x00000010, 0x0000000d, 0x00000001, 0x00050051, 0x00000006, 0x00000011, 0x0000000d, | |
| 0x00000002, 0x00070050, 0x00000007, 0x00000012, 0x0000000f, 0x00000010, 0x00000011, 0x0000000e, | |
| 0x0003003e, 0x00000009, 0x00000012, 0x000100fd, 0x00010038}; | |
| // | |
| // VULKAN STATE | |
| // | |
| struct vulkan_context | |
| { | |
| VkSurfaceKHR surface; | |
| uint32_t width, height; | |
| VkSwapchainKHR swapchain; | |
| uint32_t image_count; | |
| VkImage *swapchain_images; | |
| VkImageView *swapchain_image_views; | |
| }; | |
| struct vulkan_info | |
| { | |
| VkInstance instance; | |
| VkPhysicalDevice physical_device; | |
| VkDevice device; | |
| VkDescriptorPool pool; | |
| VkDebugUtilsMessengerEXT debugMessenger; | |
| uint32_t graphics_family_index; | |
| VkQueue graphics_queue; | |
| uint32_t present_family_index; | |
| VkQueue present_queue; | |
| VkFramebuffer *framebuffers; | |
| VkRenderPass render_pass; | |
| VkPipelineLayout pipeline_layout; | |
| VkPipeline graphics_pipeline; | |
| VkCommandPool command_pool; | |
| VkCommandBuffer *command_buffers; | |
| VkSemaphore *available_semaphores; | |
| VkSemaphore *finished_semaphores; | |
| VkFence *in_flight_fences; | |
| VkFence *image_in_flight; | |
| size_t current_frame; | |
| }; | |
| struct vulkan_info vk_state = {0}; | |
| // | |
| // VULKAN DEBUG | |
| // | |
| #ifdef DEBUG_MODE | |
| static inline void _check_vk_result(VkResult res, char *file, int line, char *func) | |
| { | |
| if (res != VK_SUCCESS) | |
| { | |
| fprintf(stderr, "Error! %s:%d - %s - res(%d)\n", file, line, func, res); | |
| exit(1); | |
| } | |
| } | |
| #define CHECK_VK_RESULT(res) _check_vk_result((res), __FILE__, __LINE__, __FUNCTION__) | |
| static const char *get_debug_severity_str[] = { | |
| [VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT] = "Verbose", | |
| [VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT] = "Info", | |
| [VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT] = "Warning", | |
| [VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT] = "Error", | |
| NULL}; | |
| static const char *get_debug_type[] = { | |
| [VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT] = "General", | |
| [VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT] = "Validation", | |
| [VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT] = "Performance", | |
| [VK_DEBUG_UTILS_MESSAGE_TYPE_DEVICE_ADDRESS_BINDING_BIT_EXT] = "Device address binding", | |
| NULL}; | |
| static VKAPI_ATTR VkBool32 VKAPI_CALL debug_user_callback( | |
| VkDebugUtilsMessageSeverityFlagBitsEXT Severity, | |
| VkDebugUtilsMessageTypeFlagsEXT Type, | |
| const VkDebugUtilsMessengerCallbackDataEXT *pCallbackData, | |
| void *pUserData) | |
| { | |
| RGFW_UNUSED(pUserData); | |
| printf("\nDebug callback: %s\n", pCallbackData->pMessage); | |
| printf(" Severity: %s\n", get_debug_severity_str[Severity]); | |
| printf(" Type: %s\n", get_debug_type[Type]); | |
| printf(" Objects "); | |
| for (uint32_t i = 0; i < pCallbackData->objectCount; i++) | |
| { | |
| printf("%llx ", pCallbackData->pObjects[i].objectHandle); | |
| } | |
| return 1; | |
| } | |
| static void vulkan_create_debug_callback(void) | |
| { | |
| KASSERT(vk_state.instance); | |
| VkDebugUtilsMessengerCreateInfoEXT messenger_create_info = {0}; | |
| messenger_create_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; | |
| messenger_create_info.pNext = NULL; | |
| messenger_create_info.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | | |
| // VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT | | |
| VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | | |
| VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; | |
| messenger_create_info.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | | |
| VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | | |
| VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; | |
| messenger_create_info.pfnUserCallback = debug_user_callback; | |
| messenger_create_info.pUserData = NULL; | |
| PFN_vkCreateDebugUtilsMessengerEXT vkCreateDebugUtilsMessenger = VK_NULL_HANDLE; | |
| vkCreateDebugUtilsMessenger = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(vk_state.instance, "vkCreateDebugUtilsMessengerEXT"); | |
| if (vkCreateDebugUtilsMessenger) | |
| CHECK_VK_RESULT(vkCreateDebugUtilsMessenger(vk_state.instance, &messenger_create_info, NULL, &vk_state.debugMessenger)); | |
| } | |
| #else // DEBUG_MODE | |
| #define CHECK_VK_RESULT(res) | |
| #endif | |
| static bool vulkan_init_device(RGFW_window *win, struct vulkan_context *ctx) | |
| { | |
| KASSERT(win); | |
| KASSERT(win->w > 0 && win->h > 0); | |
| KASSERT(ctx); | |
| #ifdef DEBUG_MODE | |
| memset(ctx, 0xCD, sizeof(*ctx)); | |
| #endif | |
| ctx->swapchain = VK_NULL_HANDLE; | |
| ctx->image_count = 0; | |
| ctx->width = win->w; | |
| ctx->height = win->h; | |
| vk_state.current_frame = 0; | |
| VkApplicationInfo app_info = {0}; | |
| app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; | |
| app_info.pApplicationName = "rgfw_microui"; | |
| app_info.apiVersion = VK_MAKE_VERSION(1, 0, 0); | |
| #ifdef DEBUG_MODE | |
| const char *enabled_extensions[] = {VK_KHR_SURFACE_EXTENSION_NAME, RGFW_VK_SURFACE, VK_EXT_DEBUG_UTILS_EXTENSION_NAME}; | |
| #else | |
| const char *enabled_extensions[] = {VK_KHR_SURFACE_EXTENSION_NAME, RGFW_VK_SURFACE}; | |
| #endif | |
| uint32_t enabled_extension_count = sizeof(enabled_extensions) / sizeof(enabled_extensions[0]); | |
| VkInstanceCreateInfo instance_create_info = {0}; | |
| instance_create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; | |
| instance_create_info.pApplicationInfo = &app_info; | |
| instance_create_info.enabledExtensionCount = enabled_extension_count; | |
| instance_create_info.ppEnabledExtensionNames = enabled_extensions; | |
| CHECK_VK_RESULT(vkCreateInstance(&instance_create_info, NULL, &vk_state.instance)); | |
| #ifdef DEBUG_MODE | |
| vk_state.debugMessenger = VK_NULL_HANDLE; | |
| vulkan_create_debug_callback(); | |
| #endif | |
| RGFW_window_createSurface_Vulkan(win, vk_state.instance, &ctx->surface); | |
| uint32_t device_count = 0; | |
| CHECK_VK_RESULT(vkEnumeratePhysicalDevices(vk_state.instance, &device_count, NULL)); | |
| VkPhysicalDevice *devices = (VkPhysicalDevice *)malloc(sizeof(VkPhysicalDevice) * device_count); | |
| KASSERT(devices); | |
| CHECK_VK_RESULT(vkEnumeratePhysicalDevices(vk_state.instance, &device_count, devices)); | |
| // NOTE : here we are just choosing the first device we get back!! | |
| // should add a search here for VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU | |
| vk_state.physical_device = devices[0]; | |
| if (devices) free(devices); | |
| uint32_t graphics_queue_family = 0; | |
| if (vk_state.physical_device != NULL && device_count) | |
| { | |
| uint32_t queue_family_count = 0; | |
| VkQueueFamilyProperties *queue_families; | |
| vkGetPhysicalDeviceQueueFamilyProperties(vk_state.physical_device, &queue_family_count, NULL); | |
| queue_families = (VkQueueFamilyProperties *)malloc(sizeof(VkQueueFamilyProperties) * queue_family_count); | |
| KASSERT(queue_families); | |
| vkGetPhysicalDeviceQueueFamilyProperties(vk_state.physical_device, &queue_family_count, queue_families); | |
| for (uint32_t i = 0; i < queue_family_count; i++) | |
| { | |
| if (queue_families[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) | |
| { | |
| graphics_queue_family = i; | |
| break; | |
| } | |
| } | |
| if (queue_families) free(queue_families); | |
| } | |
| const float queue_priority = 1.0f; | |
| VkDeviceQueueCreateInfo queue_create_info = {0}; | |
| queue_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; | |
| queue_create_info.queueCount = 1; | |
| queue_create_info.pQueuePriorities = &queue_priority; | |
| queue_create_info.queueFamilyIndex = graphics_queue_family; | |
| queue_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; | |
| VkPhysicalDeviceFeatures device_features = {0}; | |
| VkDeviceCreateInfo device_create_info = {0}; | |
| device_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; | |
| device_create_info.pQueueCreateInfos = &queue_create_info; | |
| device_create_info.queueCreateInfoCount = 1; | |
| device_create_info.pEnabledFeatures = &device_features; | |
| const char *device_extensions[] = {VK_KHR_SWAPCHAIN_EXTENSION_NAME}; | |
| device_create_info.ppEnabledExtensionNames = device_extensions; | |
| device_create_info.enabledExtensionCount = 1; | |
| CHECK_VK_RESULT(vkCreateDevice(vk_state.physical_device, &device_create_info, NULL, &vk_state.device)); | |
| return true; | |
| } | |
| static uint32_t vulkan_get_memory_type(VkPhysicalDevice physical_device, VkMemoryPropertyFlags properties, uint32_t type_bits) | |
| { | |
| VkPhysicalDeviceMemoryProperties prop = {0}; | |
| vkGetPhysicalDeviceMemoryProperties(physical_device, &prop); | |
| for (uint32_t i = 0; i < prop.memoryTypeCount; i++) | |
| if ((prop.memoryTypes[i].propertyFlags & properties) == properties && type_bits & (1 << i)) | |
| return i; | |
| return 0xFFFFFFFF; // Unable to find memoryType | |
| } | |
| static inline VkDeviceSize vulkan_align_buffer_size(VkDeviceSize size, VkDeviceSize alignment) | |
| { | |
| return (size + alignment - 1) & ~(alignment - 1); | |
| } | |
| static void vulkan_create_or_resize_buffer(VkDevice device, VkPhysicalDevice physical_device, const VkAllocationCallbacks *allocator, VkBuffer *buffer, VkDeviceMemory *buffer_memory, VkDeviceSize buffer_size_aligned, VkBufferUsageFlagBits usage) | |
| { | |
| KASSERT(device); | |
| KASSERT(physical_device); | |
| if ((*buffer) != VK_NULL_HANDLE) vkDestroyBuffer(device, (*buffer), allocator); | |
| if ((*buffer_memory) != VK_NULL_HANDLE) vkFreeMemory(device, (*buffer_memory), allocator); | |
| VkBufferCreateInfo buffer_info = {0}; | |
| buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; | |
| buffer_info.size = buffer_size_aligned; | |
| buffer_info.usage = usage; | |
| buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; | |
| CHECK_VK_RESULT(vkCreateBuffer(device, &buffer_info, allocator, buffer)); | |
| VkMemoryRequirements req = {0}; | |
| vkGetBufferMemoryRequirements(device, (*buffer), &req); | |
| uint32_t memory_type_index = vulkan_get_memory_type(physical_device, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, req.memoryTypeBits); | |
| VkMemoryAllocateInfo alloc_info = {0}; | |
| alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; | |
| alloc_info.allocationSize = req.size; | |
| alloc_info.memoryTypeIndex = memory_type_index; | |
| CHECK_VK_RESULT(vkAllocateMemory(device, &alloc_info, allocator, buffer_memory)); | |
| CHECK_VK_RESULT(vkBindBufferMemory(device, (*buffer), (*buffer_memory), 0)); | |
| } | |
| static bool vulkan_create_swapchain(struct vulkan_context *ctx) | |
| { | |
| KASSERT(ctx); | |
| KASSERT(vk_state.device); | |
| // TODO : implement proper device checking for desired surface and present values | |
| VkSurfaceFormatKHR surface_format = {VK_FORMAT_B8G8R8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}; | |
| VkPresentModeKHR present_mode = VK_PRESENT_MODE_FIFO_KHR; | |
| VkSurfaceCapabilitiesKHR capabilities = {0}; | |
| CHECK_VK_RESULT(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(vk_state.physical_device, ctx->surface, &capabilities)); | |
| ctx->image_count = capabilities.minImageCount + 1; | |
| if ((capabilities.maxImageCount > 0) && (ctx->image_count > capabilities.maxImageCount)) | |
| { | |
| ctx->image_count = capabilities.maxImageCount; | |
| } | |
| VkSwapchainCreateInfoKHR swapchain_create_info = {0}; | |
| swapchain_create_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; | |
| swapchain_create_info.surface = ctx->surface; | |
| swapchain_create_info.minImageCount = ctx->image_count; | |
| swapchain_create_info.imageFormat = surface_format.format; | |
| swapchain_create_info.imageColorSpace = surface_format.colorSpace; | |
| swapchain_create_info.imageExtent = (VkExtent2D){ctx->width, ctx->height}; | |
| swapchain_create_info.imageArrayLayers = 1; | |
| swapchain_create_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; | |
| swapchain_create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; | |
| swapchain_create_info.queueFamilyIndexCount = 2; | |
| swapchain_create_info.preTransform = capabilities.currentTransform; | |
| swapchain_create_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; | |
| swapchain_create_info.presentMode = present_mode; | |
| swapchain_create_info.clipped = VK_TRUE; | |
| swapchain_create_info.oldSwapchain = VK_NULL_HANDLE; | |
| CHECK_VK_RESULT(vkCreateSwapchainKHR(vk_state.device, &swapchain_create_info, NULL, &ctx->swapchain)); | |
| uint32_t image_count; | |
| CHECK_VK_RESULT(vkGetSwapchainImagesKHR(vk_state.device, ctx->swapchain, &image_count, NULL)); | |
| ctx->swapchain_images = (VkImage *)malloc(sizeof(VkImage) * image_count); | |
| KASSERT(ctx->swapchain_images); | |
| CHECK_VK_RESULT(vkGetSwapchainImagesKHR(vk_state.device, ctx->swapchain, &image_count, ctx->swapchain_images)); | |
| ctx->swapchain_image_views = (VkImageView *)malloc(sizeof(VkImageView) * image_count); | |
| KASSERT(ctx->swapchain_image_views); | |
| for (uint32_t i = 0; i < image_count; i++) | |
| { | |
| VkImageViewCreateInfo image_view_create_infos = {0}; | |
| image_view_create_infos.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; | |
| image_view_create_infos.image = ctx->swapchain_images[i]; | |
| image_view_create_infos.viewType = VK_IMAGE_VIEW_TYPE_2D; | |
| image_view_create_infos.format = VK_FORMAT_B8G8R8A8_UNORM; | |
| image_view_create_infos.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; | |
| image_view_create_infos.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; | |
| image_view_create_infos.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; | |
| image_view_create_infos.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; | |
| image_view_create_infos.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; | |
| image_view_create_infos.subresourceRange.baseMipLevel = 0; | |
| image_view_create_infos.subresourceRange.levelCount = 1; | |
| image_view_create_infos.subresourceRange.baseArrayLayer = 0; | |
| image_view_create_infos.subresourceRange.layerCount = 1; | |
| CHECK_VK_RESULT(vkCreateImageView(vk_state.device, &image_view_create_infos, NULL, &ctx->swapchain_image_views[i])); | |
| } | |
| return true; | |
| } | |
| static bool vulkan_create_render_pass(void) | |
| { | |
| KASSERT(vk_state.device); | |
| // TODO : proper surface format selecting | |
| VkAttachmentDescription colour_attachment = {0}; | |
| colour_attachment.format = VK_FORMAT_B8G8R8A8_UNORM; | |
| colour_attachment.samples = VK_SAMPLE_COUNT_1_BIT; | |
| colour_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; | |
| colour_attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; | |
| colour_attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; | |
| colour_attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; | |
| colour_attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; | |
| colour_attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; | |
| VkAttachmentReference colour_attachment_ref = {0}; | |
| colour_attachment_ref.attachment = 0; | |
| colour_attachment_ref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; | |
| VkSubpassDescription subpass = {0}; | |
| subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; | |
| subpass.colorAttachmentCount = 1; | |
| subpass.pColorAttachments = &colour_attachment_ref; | |
| VkSubpassDependency dependency = {0}; | |
| dependency.srcSubpass = VK_SUBPASS_EXTERNAL; | |
| dependency.dstSubpass = 0; | |
| dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; | |
| dependency.srcAccessMask = 0; | |
| dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; | |
| dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; | |
| VkRenderPassCreateInfo render_pass_info = {0}; | |
| render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; | |
| render_pass_info.attachmentCount = 1; | |
| render_pass_info.pAttachments = &colour_attachment; | |
| render_pass_info.subpassCount = 1; | |
| render_pass_info.pSubpasses = &subpass; | |
| render_pass_info.dependencyCount = 1; | |
| render_pass_info.pDependencies = &dependency; | |
| CHECK_VK_RESULT(vkCreateRenderPass(vk_state.device, &render_pass_info, NULL, &vk_state.render_pass)); | |
| return true; | |
| } | |
| static bool vulkan_create_framebuffers(struct vulkan_context *ctx) | |
| { | |
| KASSERT(ctx); | |
| KASSERT(ctx->image_count > 0); | |
| KASSERT(vk_state.device); | |
| vk_state.framebuffers = (VkFramebuffer *)malloc(sizeof(VkFramebuffer) * ctx->image_count); | |
| KASSERT(vk_state.framebuffers); | |
| for (size_t i = 0; i < ctx->image_count; i++) | |
| { | |
| VkImageView attachments[] = {ctx->swapchain_image_views[i]}; | |
| VkFramebufferCreateInfo framebuffer_info = {0}; | |
| framebuffer_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; | |
| framebuffer_info.renderPass = vk_state.render_pass; | |
| framebuffer_info.pAttachments = attachments; | |
| framebuffer_info.attachmentCount = KARRAYSIZE(attachments); | |
| framebuffer_info.width = ctx->width; | |
| framebuffer_info.height = ctx->height; | |
| framebuffer_info.layers = 1; | |
| CHECK_VK_RESULT(vkCreateFramebuffer(vk_state.device, &framebuffer_info, NULL, &vk_state.framebuffers[i])); | |
| } | |
| return true; | |
| } | |
| static bool vulkan_create_command_pool(void) | |
| { | |
| KASSERT(vk_state.device); | |
| VkCommandPoolCreateInfo pool_info = {0}; | |
| pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; | |
| pool_info.queueFamilyIndex = 0; | |
| CHECK_VK_RESULT(vkCreateCommandPool(vk_state.device, &pool_info, NULL, &vk_state.command_pool)); | |
| return true; | |
| } | |
| static bool vulkan_create_command_buffers(struct vulkan_context *ctx) | |
| { | |
| KASSERT(ctx); | |
| KASSERT(ctx->image_count > 0); | |
| KASSERT(vk_state.device); | |
| KASSERT(vk_state.command_pool); | |
| vk_state.command_buffers = (VkCommandBuffer *)malloc(sizeof(VkCommandBuffer) * ctx->image_count); | |
| KASSERT(vk_state.command_buffers); | |
| VkCommandBufferAllocateInfo alloc_info = {0}; | |
| alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; | |
| alloc_info.commandPool = vk_state.command_pool; | |
| alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; | |
| alloc_info.commandBufferCount = (uint32_t)ctx->image_count; | |
| CHECK_VK_RESULT(vkAllocateCommandBuffers(vk_state.device, &alloc_info, vk_state.command_buffers)); | |
| return true; | |
| } | |
| static bool vulkan_create_sync_objects(struct vulkan_context *ctx) | |
| { | |
| KASSERT(ctx); | |
| KASSERT(ctx->image_count > 0); | |
| KASSERT(vk_state.device); | |
| vk_state.available_semaphores = (VkSemaphore *)malloc(sizeof(VkSemaphore) * MAX_FRAMES_IN_FLIGHT); | |
| vk_state.finished_semaphores = (VkSemaphore *)malloc(sizeof(VkSemaphore) * MAX_FRAMES_IN_FLIGHT); | |
| vk_state.in_flight_fences = (VkFence *)malloc(sizeof(VkFence) * MAX_FRAMES_IN_FLIGHT); | |
| vk_state.image_in_flight = (VkFence *)malloc(sizeof(VkFence) * ctx->image_count); | |
| KASSERT(vk_state.available_semaphores); | |
| KASSERT(vk_state.finished_semaphores); | |
| KASSERT(vk_state.in_flight_fences); | |
| KASSERT(vk_state.image_in_flight); | |
| VkSemaphoreCreateInfo semaphore_info = {0}; | |
| semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; | |
| semaphore_info.pNext = NULL; | |
| semaphore_info.flags = 0; | |
| VkFenceCreateInfo fence_info = {0}; | |
| fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; | |
| semaphore_info.pNext = NULL; | |
| fence_info.flags = VK_FENCE_CREATE_SIGNALED_BIT; | |
| for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) | |
| { | |
| CHECK_VK_RESULT(vkCreateSemaphore(vk_state.device, &semaphore_info, NULL, &vk_state.available_semaphores[i])); | |
| CHECK_VK_RESULT(vkCreateSemaphore(vk_state.device, &semaphore_info, NULL, &vk_state.finished_semaphores[i])); | |
| CHECK_VK_RESULT(vkCreateFence(vk_state.device, &fence_info, NULL, &vk_state.in_flight_fences[i])); | |
| } | |
| for (size_t i = 0; i < ctx->image_count; i++) | |
| { | |
| vk_state.image_in_flight[i] = VK_NULL_HANDLE; | |
| } | |
| return true; | |
| } | |
| static bool vulkan_init(struct vulkan_context *ctx) | |
| { | |
| if (!vulkan_create_swapchain(ctx)) return (printf("vulkan_create_swapchain failed\n"), false); | |
| // TODO : get queue index from vkGetPhysicalDeviceQueueFamilyProperties | |
| uint32_t graphics_family_index = 0; | |
| uint32_t present_family_index = 0; | |
| vkGetDeviceQueue(vk_state.device, graphics_family_index, 0, &vk_state.graphics_queue); | |
| vkGetDeviceQueue(vk_state.device, present_family_index, 0, &vk_state.present_queue); | |
| if (!vulkan_create_render_pass()) return (printf("vulkan_create_render_pass failed\n"), false); | |
| if (!vulkan_create_framebuffers(ctx)) return (printf("vulkan_create_framebuffers failed\n"), false); | |
| if (!vulkan_create_command_pool()) return (printf("vulkan_create_command_pool failed\n"), false); | |
| if (!vulkan_create_command_buffers(ctx)) return (printf("vulkan_create_command_buffers failed\n"), false); | |
| if (!vulkan_create_sync_objects(ctx)) return (printf("vulkan_create_sync_objects failed\n"), false); | |
| return true; | |
| } | |
| static void vulkan_destroy(struct vulkan_context *ctx) | |
| { | |
| for (uint32_t i = 0; i < ctx->image_count; i++) | |
| { | |
| vkDestroyImageView(vk_state.device, ctx->swapchain_image_views[i], NULL); | |
| } | |
| for (uint32_t i = 0; i < ctx->image_count; i++) | |
| { | |
| vkDestroyFramebuffer(vk_state.device, vk_state.framebuffers[i], NULL); | |
| } | |
| vkDestroySwapchainKHR(vk_state.device, ctx->swapchain, NULL); | |
| vkDestroySurfaceKHR(vk_state.instance, ctx->surface, NULL); | |
| if (ctx->swapchain_image_views) free(ctx->swapchain_image_views); | |
| if (ctx->swapchain_images) free(ctx->swapchain_images); | |
| vkDeviceWaitIdle(vk_state.device); | |
| for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) | |
| { | |
| vkDestroySemaphore(vk_state.device, vk_state.finished_semaphores[i], NULL); | |
| vkDestroySemaphore(vk_state.device, vk_state.available_semaphores[i], NULL); | |
| vkDestroyFence(vk_state.device, vk_state.in_flight_fences[i], NULL); | |
| } | |
| vkDestroyCommandPool(vk_state.device, vk_state.command_pool, NULL); | |
| vkDestroyPipeline(vk_state.device, vk_state.graphics_pipeline, NULL); | |
| vkDestroyPipelineLayout(vk_state.device, vk_state.pipeline_layout, NULL); | |
| vkDestroyRenderPass(vk_state.device, vk_state.render_pass, NULL); | |
| vkDestroyDescriptorPool(vk_state.device, vk_state.pool, NULL); | |
| #ifdef DEBUG_MODE | |
| PFN_vkDestroyDebugUtilsMessengerEXT vkDestroyDebugUtilsMessengerEXT = (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(vk_state.instance, "vkDestroyDebugUtilsMessengerEXT"); | |
| if (vkDestroyDebugUtilsMessengerEXT) | |
| vkDestroyDebugUtilsMessengerEXT(vk_state.instance, vk_state.debugMessenger, NULL); | |
| #endif | |
| vkDestroyDevice(vk_state.device, NULL); | |
| vkDestroyInstance(vk_state.instance, NULL); | |
| if (vk_state.framebuffers) free(vk_state.framebuffers); | |
| if (vk_state.command_buffers) free(vk_state.command_buffers); | |
| if (vk_state.available_semaphores) free(vk_state.available_semaphores); | |
| if (vk_state.finished_semaphores) free(vk_state.finished_semaphores); | |
| if (vk_state.in_flight_fences) free(vk_state.in_flight_fences); | |
| if (vk_state.image_in_flight) free(vk_state.image_in_flight); | |
| } | |
| static VkShaderModule vulkan_create_shader(const uint32_t *code, size_t code_size) | |
| { | |
| KASSERT(code && code_size > 0); | |
| KASSERT(vk_state.device); | |
| VkShaderModuleCreateInfo create_info = {0}; | |
| create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; | |
| create_info.codeSize = code_size; | |
| create_info.pCode = code; | |
| VkShaderModule shader_module; | |
| CHECK_VK_RESULT(vkCreateShaderModule(vk_state.device, &create_info, NULL, &shader_module)); | |
| return shader_module; | |
| } | |
| // | |
| // MICRO UI | |
| // | |
| #define MICRO_MAX_VERTICES (8000) | |
| #define MICRO_MAX_RENDER_INFO (64) | |
| struct vec2 | |
| { | |
| float x, y; | |
| }; | |
| struct micro_vertex | |
| { | |
| struct vec2 pos; | |
| struct vec2 uv; // For textured quads (text/icons) | |
| uint32_t col; // RGBA | |
| }; | |
| struct micro_setup | |
| { | |
| VkInstance instance; | |
| VkPhysicalDevice physical_device; | |
| VkDevice device; | |
| VkQueue queue; | |
| uint32_t queue_family; | |
| VkDescriptorPool descriptor_pool; // needs VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER | |
| VkRenderPass render_pass; | |
| uint32_t min_image_count; // >= 2 | |
| uint32_t image_count; // >= min_image_count | |
| VkSampleCountFlagBits MSAA_samples; | |
| uint32_t subpass; | |
| VkAllocationCallbacks *allocator; | |
| }; | |
| struct micro_render_block | |
| { | |
| uint32_t count; // vertex count for this block | |
| int sx, sy, sw, sh; // scissor | |
| }; | |
| struct micro_renderer | |
| { | |
| mu_Context *ctx; | |
| struct micro_vertex *vertices; | |
| uint32_t vertex_count; | |
| VkBuffer vertex_buffer[3]; // TODO : allocate these values? | |
| VkDeviceMemory vertex_memory[3]; // TODO : allocate these values? | |
| VkDeviceSize vertex_size; // NOTE : same for all buffers | |
| VkDeviceSize vertex_allocation_size; | |
| uint32_t render_block_index; | |
| struct micro_render_block render_block[MICRO_MAX_RENDER_INFO]; | |
| // info needed from the main app | |
| struct micro_setup setup; | |
| // micro pipeline | |
| VkDescriptorSetLayout descriptor_set_layout; | |
| VkDescriptorSet descriptor_set; | |
| VkPipelineCreateFlags pipeline_create_flags; | |
| VkPipelineLayout pipeline_layout; | |
| VkPipeline pipeline; | |
| VkPipelineCache pipeline_cache; | |
| VkShaderModule vert; | |
| VkShaderModule frag; | |
| // font data | |
| VkSampler tex_sampler; | |
| VkCommandPool tex_command_pool; | |
| VkCommandBuffer tex_command_buffer; | |
| VkDeviceMemory tex_memory; | |
| VkImage tex_image; | |
| VkImageView tex_image_view; | |
| }; | |
| // compiled with: | |
| // C:\VulkanSDK\1.3.280.0\Bin\glslangValidator -V mui.vert -o mui_vert.h --vn microui_vert_spv | |
| /* | |
| #version 450 core | |
| layout(location = 0) in vec2 aPos; | |
| layout(location = 1) in vec2 aUV; | |
| layout(location = 2) in vec4 aColor; | |
| layout(push_constant) uniform uPushConstant { vec2 uScale; vec2 uTranslate; } pc; | |
| out gl_PerVertex { vec4 gl_Position; }; | |
| layout(location = 0) out struct { vec4 Color; vec2 UV; } Out; | |
| void main() | |
| { | |
| Out.Color = aColor; | |
| Out.UV = aUV; | |
| gl_Position = vec4(aPos * pc.uScale + pc.uTranslate, 0, 1); | |
| } | |
| */ | |
| static uint32_t microui_vert_spv[] = | |
| { | |
| 0x07230203, 0x00010000, 0x00080001, 0x0000002e, 0x00000000, 0x00020011, 0x00000001, 0x0006000b, | |
| 0x00000001, 0x4c534c47, 0x6474732e, 0x3035342e, 0x00000000, 0x0003000e, 0x00000000, 0x00000001, | |
| 0x000a000f, 0x00000000, 0x00000004, 0x6e69616d, 0x00000000, 0x0000000b, 0x0000000f, 0x00000015, | |
| 0x0000001b, 0x0000001c, 0x00030003, 0x00000002, 0x000001c2, 0x00040005, 0x00000004, 0x6e69616d, | |
| 0x00000000, 0x00030005, 0x00000009, 0x00000000, 0x00050006, 0x00000009, 0x00000000, 0x6f6c6f43, | |
| 0x00000072, 0x00040006, 0x00000009, 0x00000001, 0x00005655, 0x00030005, 0x0000000b, 0x0074754f, | |
| 0x00040005, 0x0000000f, 0x6c6f4361, 0x0000726f, 0x00030005, 0x00000015, 0x00565561, 0x00060005, | |
| 0x00000019, 0x505f6c67, 0x65567265, 0x78657472, 0x00000000, 0x00060006, 0x00000019, 0x00000000, | |
| 0x505f6c67, 0x7469736f, 0x006e6f69, 0x00030005, 0x0000001b, 0x00000000, 0x00040005, 0x0000001c, | |
| 0x736f5061, 0x00000000, 0x00060005, 0x0000001e, 0x73755075, 0x6e6f4368, 0x6e617473, 0x00000074, | |
| 0x00050006, 0x0000001e, 0x00000000, 0x61635375, 0x0000656c, 0x00060006, 0x0000001e, 0x00000001, | |
| 0x61725475, 0x616c736e, 0x00006574, 0x00030005, 0x00000020, 0x00006370, 0x00040047, 0x0000000b, | |
| 0x0000001e, 0x00000000, 0x00040047, 0x0000000f, 0x0000001e, 0x00000002, 0x00040047, 0x00000015, | |
| 0x0000001e, 0x00000001, 0x00050048, 0x00000019, 0x00000000, 0x0000000b, 0x00000000, 0x00030047, | |
| 0x00000019, 0x00000002, 0x00040047, 0x0000001c, 0x0000001e, 0x00000000, 0x00050048, 0x0000001e, | |
| 0x00000000, 0x00000023, 0x00000000, 0x00050048, 0x0000001e, 0x00000001, 0x00000023, 0x00000008, | |
| 0x00030047, 0x0000001e, 0x00000002, 0x00020013, 0x00000002, 0x00030021, 0x00000003, 0x00000002, | |
| 0x00030016, 0x00000006, 0x00000020, 0x00040017, 0x00000007, 0x00000006, 0x00000004, 0x00040017, | |
| 0x00000008, 0x00000006, 0x00000002, 0x0004001e, 0x00000009, 0x00000007, 0x00000008, 0x00040020, | |
| 0x0000000a, 0x00000003, 0x00000009, 0x0004003b, 0x0000000a, 0x0000000b, 0x00000003, 0x00040015, | |
| 0x0000000c, 0x00000020, 0x00000001, 0x0004002b, 0x0000000c, 0x0000000d, 0x00000000, 0x00040020, | |
| 0x0000000e, 0x00000001, 0x00000007, 0x0004003b, 0x0000000e, 0x0000000f, 0x00000001, 0x00040020, | |
| 0x00000011, 0x00000003, 0x00000007, 0x0004002b, 0x0000000c, 0x00000013, 0x00000001, 0x00040020, | |
| 0x00000014, 0x00000001, 0x00000008, 0x0004003b, 0x00000014, 0x00000015, 0x00000001, 0x00040020, | |
| 0x00000017, 0x00000003, 0x00000008, 0x0003001e, 0x00000019, 0x00000007, 0x00040020, 0x0000001a, | |
| 0x00000003, 0x00000019, 0x0004003b, 0x0000001a, 0x0000001b, 0x00000003, 0x0004003b, 0x00000014, | |
| 0x0000001c, 0x00000001, 0x0004001e, 0x0000001e, 0x00000008, 0x00000008, 0x00040020, 0x0000001f, | |
| 0x00000009, 0x0000001e, 0x0004003b, 0x0000001f, 0x00000020, 0x00000009, 0x00040020, 0x00000021, | |
| 0x00000009, 0x00000008, 0x0004002b, 0x00000006, 0x00000028, 0x00000000, 0x0004002b, 0x00000006, | |
| 0x00000029, 0x3f800000, 0x00050036, 0x00000002, 0x00000004, 0x00000000, 0x00000003, 0x000200f8, | |
| 0x00000005, 0x0004003d, 0x00000007, 0x00000010, 0x0000000f, 0x00050041, 0x00000011, 0x00000012, | |
| 0x0000000b, 0x0000000d, 0x0003003e, 0x00000012, 0x00000010, 0x0004003d, 0x00000008, 0x00000016, | |
| 0x00000015, 0x00050041, 0x00000017, 0x00000018, 0x0000000b, 0x00000013, 0x0003003e, 0x00000018, | |
| 0x00000016, 0x0004003d, 0x00000008, 0x0000001d, 0x0000001c, 0x00050041, 0x00000021, 0x00000022, | |
| 0x00000020, 0x0000000d, 0x0004003d, 0x00000008, 0x00000023, 0x00000022, 0x00050085, 0x00000008, | |
| 0x00000024, 0x0000001d, 0x00000023, 0x00050041, 0x00000021, 0x00000025, 0x00000020, 0x00000013, | |
| 0x0004003d, 0x00000008, 0x00000026, 0x00000025, 0x00050081, 0x00000008, 0x00000027, 0x00000024, | |
| 0x00000026, 0x00050051, 0x00000006, 0x0000002a, 0x00000027, 0x00000000, 0x00050051, 0x00000006, | |
| 0x0000002b, 0x00000027, 0x00000001, 0x00070050, 0x00000007, 0x0000002c, 0x0000002a, 0x0000002b, | |
| 0x00000028, 0x00000029, 0x00050041, 0x00000011, 0x0000002d, 0x0000001b, 0x0000000d, 0x0003003e, | |
| 0x0000002d, 0x0000002c, 0x000100fd, 0x00010038}; | |
| // compiled with: | |
| // C:\VulkanSDK\1.3.280.0\Bin\glslangValidator -V mui.frag -o mui_frag.h --vn microui_frag_spv | |
| /* | |
| #version 450 core | |
| layout(location = 0) out vec4 fColor; | |
| layout(set=0, binding=0) uniform sampler2D sTexture; | |
| layout(location = 0) in struct { vec4 Color; vec2 UV; } In; | |
| void main() | |
| { | |
| fColor = In.Color * texture(sTexture, In.UV.st); | |
| } | |
| */ | |
| static uint32_t microui_frag_spv[] = | |
| { | |
| 0x07230203, 0x00010000, 0x0008000b, 0x0000001e, 0x00000000, 0x00020011, 0x00000001, 0x0006000b, | |
| 0x00000001, 0x4c534c47, 0x6474732e, 0x3035342e, 0x00000000, 0x0003000e, 0x00000000, 0x00000001, | |
| 0x0007000f, 0x00000004, 0x00000004, 0x6e69616d, 0x00000000, 0x00000009, 0x0000000d, 0x00030010, | |
| 0x00000004, 0x00000007, 0x00030003, 0x00000002, 0x000001c2, 0x00040005, 0x00000004, 0x6e69616d, | |
| 0x00000000, 0x00040005, 0x00000009, 0x6c6f4366, 0x0000726f, 0x00030005, 0x0000000b, 0x00000000, | |
| 0x00050006, 0x0000000b, 0x00000000, 0x6f6c6f43, 0x00000072, 0x00040006, 0x0000000b, 0x00000001, | |
| 0x00005655, 0x00030005, 0x0000000d, 0x00006e49, 0x00050005, 0x00000016, 0x78655473, 0x65727574, | |
| 0x00000000, 0x00040047, 0x00000009, 0x0000001e, 0x00000000, 0x00040047, 0x0000000d, 0x0000001e, | |
| 0x00000000, 0x00040047, 0x00000016, 0x00000022, 0x00000000, 0x00040047, 0x00000016, 0x00000021, | |
| 0x00000000, 0x00020013, 0x00000002, 0x00030021, 0x00000003, 0x00000002, 0x00030016, 0x00000006, | |
| 0x00000020, 0x00040017, 0x00000007, 0x00000006, 0x00000004, 0x00040020, 0x00000008, 0x00000003, | |
| 0x00000007, 0x0004003b, 0x00000008, 0x00000009, 0x00000003, 0x00040017, 0x0000000a, 0x00000006, | |
| 0x00000002, 0x0004001e, 0x0000000b, 0x00000007, 0x0000000a, 0x00040020, 0x0000000c, 0x00000001, | |
| 0x0000000b, 0x0004003b, 0x0000000c, 0x0000000d, 0x00000001, 0x00040015, 0x0000000e, 0x00000020, | |
| 0x00000001, 0x0004002b, 0x0000000e, 0x0000000f, 0x00000000, 0x00040020, 0x00000010, 0x00000001, | |
| 0x00000007, 0x00090019, 0x00000013, 0x00000006, 0x00000001, 0x00000000, 0x00000000, 0x00000000, | |
| 0x00000001, 0x00000000, 0x0003001b, 0x00000014, 0x00000013, 0x00040020, 0x00000015, 0x00000000, | |
| 0x00000014, 0x0004003b, 0x00000015, 0x00000016, 0x00000000, 0x0004002b, 0x0000000e, 0x00000018, | |
| 0x00000001, 0x00040020, 0x00000019, 0x00000001, 0x0000000a, 0x00050036, 0x00000002, 0x00000004, | |
| 0x00000000, 0x00000003, 0x000200f8, 0x00000005, 0x00050041, 0x00000010, 0x00000011, 0x0000000d, | |
| 0x0000000f, 0x0004003d, 0x00000007, 0x00000012, 0x00000011, 0x0004003d, 0x00000014, 0x00000017, | |
| 0x00000016, 0x00050041, 0x00000019, 0x0000001a, 0x0000000d, 0x00000018, 0x0004003d, 0x0000000a, | |
| 0x0000001b, 0x0000001a, 0x00050057, 0x00000007, 0x0000001c, 0x00000017, 0x0000001b, 0x00050085, | |
| 0x00000007, 0x0000001d, 0x00000012, 0x0000001c, 0x0003003e, 0x00000009, 0x0000001d, 0x000100fd, | |
| 0x00010038}; | |
| static void micro_update_texture(struct micro_renderer *mr, int width, int height, int bpp, void *tex_data) | |
| { | |
| KASSERT(mr); | |
| KASSERT(tex_data); | |
| KASSERT(width > 0 && height > 0); | |
| KASSERT(bpp > 0); | |
| VkFormat image_format = VK_FORMAT_R8_UNORM; | |
| // Create the Image: | |
| { | |
| VkImageCreateInfo info = {0}; | |
| info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; | |
| info.imageType = VK_IMAGE_TYPE_2D; | |
| info.format = image_format; // TODO : pass this in | |
| info.extent.width = width; | |
| info.extent.height = height; | |
| info.extent.depth = 1; | |
| info.mipLevels = 1; | |
| info.arrayLayers = 1; | |
| info.samples = VK_SAMPLE_COUNT_1_BIT; | |
| info.tiling = VK_IMAGE_TILING_OPTIMAL; | |
| info.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; | |
| info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; | |
| info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; | |
| CHECK_VK_RESULT(vkCreateImage(mr->setup.device, &info, mr->setup.allocator, &mr->tex_image)); | |
| VkMemoryRequirements req; | |
| vkGetImageMemoryRequirements(mr->setup.device, mr->tex_image, &req); | |
| VkMemoryAllocateInfo alloc_info = {0}; | |
| alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; | |
| alloc_info.allocationSize = req.size; | |
| alloc_info.memoryTypeIndex = vulkan_get_memory_type(mr->setup.physical_device, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, req.memoryTypeBits); | |
| CHECK_VK_RESULT(vkAllocateMemory(mr->setup.device, &alloc_info, mr->setup.allocator, &mr->tex_memory)); | |
| CHECK_VK_RESULT(vkBindImageMemory(mr->setup.device, mr->tex_image, mr->tex_memory, 0)); | |
| } | |
| // Create the Image View: | |
| { | |
| VkImageViewCreateInfo info = {0}; | |
| info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; | |
| info.image = mr->tex_image; | |
| info.viewType = VK_IMAGE_VIEW_TYPE_2D; | |
| info.format = image_format; | |
| info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; | |
| info.subresourceRange.levelCount = 1; | |
| info.subresourceRange.layerCount = 1; | |
| info.components = (VkComponentMapping){ | |
| .r = VK_COMPONENT_SWIZZLE_ONE, | |
| .g = VK_COMPONENT_SWIZZLE_ONE, | |
| .b = VK_COMPONENT_SWIZZLE_ONE, | |
| .a = VK_COMPONENT_SWIZZLE_R, | |
| }; | |
| CHECK_VK_RESULT(vkCreateImageView(mr->setup.device, &info, mr->setup.allocator, &mr->tex_image_view)); | |
| } | |
| // NOTE : here we are using the VkDescriptorSetLayout set in the "pipeline", think it would be better | |
| // to do this outwith the function | |
| // Create the Descriptor Set | |
| VkDescriptorSet descriptor_set; | |
| { | |
| VkDescriptorSetAllocateInfo alloc_info = {0}; | |
| alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; | |
| alloc_info.descriptorPool = mr->setup.descriptor_pool; | |
| alloc_info.descriptorSetCount = 1; | |
| alloc_info.pSetLayouts = &mr->descriptor_set_layout; | |
| CHECK_VK_RESULT(vkAllocateDescriptorSets(mr->setup.device, &alloc_info, &descriptor_set)); | |
| mr->descriptor_set = descriptor_set; | |
| } | |
| // Update the Descriptor Set | |
| { | |
| VkDescriptorImageInfo desc_image[1] = {0}; | |
| desc_image[0].sampler = mr->tex_sampler; | |
| desc_image[0].imageView = mr->tex_image_view; | |
| desc_image[0].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; | |
| VkWriteDescriptorSet write_desc[1] = {0}; | |
| write_desc[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; | |
| write_desc[0].dstSet = descriptor_set; | |
| write_desc[0].descriptorCount = 1; | |
| write_desc[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; | |
| write_desc[0].pImageInfo = desc_image; | |
| vkUpdateDescriptorSets(mr->setup.device, 1, write_desc, 0, NULL); | |
| } | |
| // Now actually upload the texture data... | |
| { | |
| // Create the Upload Buffer: | |
| VkDeviceMemory upload_buffer_memory; | |
| VkBuffer upload_buffer; | |
| VkDeviceSize upload_size = vulkan_align_buffer_size(height * width, 64); | |
| { | |
| VkBufferCreateInfo buffer_info = {0}; | |
| buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; | |
| buffer_info.size = upload_size; | |
| buffer_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; | |
| buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; | |
| CHECK_VK_RESULT(vkCreateBuffer(mr->setup.device, &buffer_info, mr->setup.allocator, &upload_buffer)); | |
| VkMemoryRequirements req; | |
| vkGetBufferMemoryRequirements(mr->setup.device, upload_buffer, &req); | |
| VkMemoryAllocateInfo alloc_info = {0}; | |
| alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; | |
| alloc_info.allocationSize = req.size; | |
| alloc_info.memoryTypeIndex = vulkan_get_memory_type(mr->setup.physical_device, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, req.memoryTypeBits); | |
| CHECK_VK_RESULT(vkAllocateMemory(mr->setup.device, &alloc_info, mr->setup.allocator, &upload_buffer_memory)); | |
| CHECK_VK_RESULT(vkBindBufferMemory(mr->setup.device, upload_buffer, upload_buffer_memory, 0)); | |
| } | |
| // Upload to Buffer: | |
| { | |
| char *map = NULL; | |
| CHECK_VK_RESULT(vkMapMemory(mr->setup.device, upload_buffer_memory, 0, upload_size, 0, (void **)(&map))); | |
| KASSERT(map); | |
| memcpy(map, tex_data, (size_t)(upload_size)); | |
| VkMappedMemoryRange range[1] = {0}; | |
| range[0].sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; | |
| range[0].memory = upload_buffer_memory; | |
| range[0].size = upload_size; | |
| CHECK_VK_RESULT(vkFlushMappedMemoryRanges(mr->setup.device, 1, range)); | |
| vkUnmapMemory(mr->setup.device, upload_buffer_memory); | |
| } | |
| // Start command buffer | |
| { | |
| CHECK_VK_RESULT(vkResetCommandPool(mr->setup.device, mr->tex_command_pool, 0)); | |
| VkCommandBufferBeginInfo begin_info = {0}; | |
| begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; | |
| begin_info.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; | |
| CHECK_VK_RESULT(vkBeginCommandBuffer(mr->tex_command_buffer, &begin_info)); | |
| } | |
| // Copy to Image: | |
| { | |
| VkBufferMemoryBarrier upload_barrier[1] = {0}; | |
| upload_barrier[0].sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER; | |
| upload_barrier[0].srcAccessMask = VK_ACCESS_HOST_WRITE_BIT; | |
| upload_barrier[0].dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; | |
| upload_barrier[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; | |
| upload_barrier[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; | |
| upload_barrier[0].buffer = upload_buffer; | |
| upload_barrier[0].offset = 0; | |
| upload_barrier[0].size = upload_size; | |
| VkImageMemoryBarrier copy_barrier[1] = {0}; | |
| copy_barrier[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; | |
| copy_barrier[0].dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; | |
| copy_barrier[0].oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; | |
| copy_barrier[0].newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; | |
| copy_barrier[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; | |
| copy_barrier[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; | |
| copy_barrier[0].image = mr->tex_image; | |
| copy_barrier[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; | |
| copy_barrier[0].subresourceRange.levelCount = 1; | |
| copy_barrier[0].subresourceRange.layerCount = 1; | |
| vkCmdPipelineBarrier(mr->tex_command_buffer, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, NULL, 1, upload_barrier, 1, copy_barrier); | |
| VkBufferImageCopy region = {0}; | |
| region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; | |
| region.imageSubresource.layerCount = 1; | |
| region.imageExtent.width = width; | |
| region.imageExtent.height = height; | |
| region.imageExtent.depth = 1; | |
| region.imageOffset.x = 0; | |
| region.imageOffset.y = 0; | |
| vkCmdCopyBufferToImage(mr->tex_command_buffer, upload_buffer, mr->tex_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); | |
| VkImageMemoryBarrier use_barrier[1] = {0}; | |
| use_barrier[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; | |
| use_barrier[0].srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; | |
| use_barrier[0].dstAccessMask = VK_ACCESS_SHADER_READ_BIT; | |
| use_barrier[0].oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; | |
| use_barrier[0].newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; | |
| use_barrier[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; | |
| use_barrier[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; | |
| use_barrier[0].image = mr->tex_image; | |
| use_barrier[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; | |
| use_barrier[0].subresourceRange.levelCount = 1; | |
| use_barrier[0].subresourceRange.layerCount = 1; | |
| vkCmdPipelineBarrier(mr->tex_command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, use_barrier); | |
| } | |
| // End command buffer | |
| { | |
| VkSubmitInfo end_info = {0}; | |
| end_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; | |
| end_info.commandBufferCount = 1; | |
| end_info.pCommandBuffers = &mr->tex_command_buffer; | |
| CHECK_VK_RESULT(vkEndCommandBuffer(mr->tex_command_buffer)); | |
| CHECK_VK_RESULT(vkQueueSubmit(mr->setup.queue, 1, &end_info, VK_NULL_HANDLE)); | |
| } | |
| CHECK_VK_RESULT(vkQueueWaitIdle(mr->setup.queue)); | |
| vkDestroyBuffer(mr->setup.device, upload_buffer, mr->setup.allocator); | |
| vkFreeMemory(mr->setup.device, upload_buffer_memory, mr->setup.allocator); | |
| } | |
| } | |
| static void micro_init_font(struct micro_renderer *r) | |
| { | |
| KASSERT(r); | |
| const int atlas_width = ATLAS_WIDTH; | |
| const int atlas_height = ATLAS_HEIGHT; | |
| micro_update_texture(r, atlas_width, atlas_height, 1, atlas_texture); | |
| } | |
| static void micro_create_pipeline(struct micro_renderer *mr) | |
| { | |
| KASSERT(mr); | |
| mr->vert = vulkan_create_shader(microui_vert_spv, sizeof(microui_vert_spv)); | |
| mr->frag = vulkan_create_shader(microui_frag_spv, sizeof(microui_frag_spv)); | |
| KASSERT(mr->vert && mr->frag); | |
| VkPipelineShaderStageCreateInfo stage[2] = {0}; | |
| { | |
| stage[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; | |
| stage[0].stage = VK_SHADER_STAGE_VERTEX_BIT; | |
| stage[0].module = mr->vert; | |
| stage[0].pName = "main"; | |
| stage[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; | |
| stage[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; | |
| stage[1].module = mr->frag; | |
| stage[1].pName = "main"; | |
| } | |
| VkVertexInputBindingDescription binding_desc[1] = {0}; | |
| binding_desc[0].binding = 0; | |
| binding_desc[0].stride = sizeof(struct micro_vertex); | |
| binding_desc[0].inputRate = VK_VERTEX_INPUT_RATE_VERTEX; | |
| VkVertexInputAttributeDescription attribute_desc[3] = {0}; | |
| { | |
| attribute_desc[0].location = 0; | |
| attribute_desc[0].binding = binding_desc[0].binding; | |
| attribute_desc[0].format = VK_FORMAT_R32G32_SFLOAT; | |
| attribute_desc[0].offset = offsetof(struct micro_vertex, pos); | |
| attribute_desc[1].location = 1; | |
| attribute_desc[1].binding = binding_desc[0].binding; | |
| attribute_desc[1].format = VK_FORMAT_R32G32_SFLOAT; | |
| attribute_desc[1].offset = offsetof(struct micro_vertex, uv); | |
| attribute_desc[2].location = 2; | |
| attribute_desc[2].binding = binding_desc[0].binding; | |
| attribute_desc[2].format = VK_FORMAT_R8G8B8A8_UNORM; | |
| attribute_desc[2].offset = offsetof(struct micro_vertex, col); | |
| } | |
| VkPipelineVertexInputStateCreateInfo vertex_info = {0}; | |
| vertex_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; | |
| vertex_info.vertexBindingDescriptionCount = 1; | |
| vertex_info.pVertexBindingDescriptions = binding_desc; | |
| vertex_info.vertexAttributeDescriptionCount = 3; | |
| vertex_info.pVertexAttributeDescriptions = attribute_desc; | |
| VkPipelineInputAssemblyStateCreateInfo ia_info = {0}; | |
| ia_info.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; | |
| ia_info.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; | |
| VkPipelineViewportStateCreateInfo viewport_info = {0}; | |
| viewport_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; | |
| viewport_info.viewportCount = 1; | |
| viewport_info.scissorCount = 1; | |
| VkPipelineRasterizationStateCreateInfo raster_info = {0}; | |
| raster_info.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; | |
| raster_info.polygonMode = VK_POLYGON_MODE_FILL; | |
| raster_info.cullMode = VK_CULL_MODE_NONE; | |
| raster_info.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; | |
| raster_info.lineWidth = 1.0f; | |
| VkPipelineMultisampleStateCreateInfo ms_info = {0}; | |
| ms_info.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; | |
| ms_info.rasterizationSamples = (mr->setup.MSAA_samples != 0) ? mr->setup.MSAA_samples : VK_SAMPLE_COUNT_1_BIT; | |
| VkPipelineColorBlendAttachmentState color_attachment[1] = {0}; | |
| color_attachment[0].blendEnable = VK_TRUE; | |
| color_attachment[0].srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; | |
| color_attachment[0].dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; | |
| color_attachment[0].colorBlendOp = VK_BLEND_OP_ADD; | |
| color_attachment[0].srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; | |
| color_attachment[0].dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; | |
| color_attachment[0].alphaBlendOp = VK_BLEND_OP_ADD; | |
| color_attachment[0].colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; | |
| VkPipelineDepthStencilStateCreateInfo depth_info = {0}; | |
| depth_info.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; | |
| VkPipelineColorBlendStateCreateInfo blend_info = {0}; | |
| blend_info.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; | |
| blend_info.attachmentCount = 1; | |
| blend_info.pAttachments = color_attachment; | |
| VkDynamicState dynamic_states[2] = {VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR}; | |
| VkPipelineDynamicStateCreateInfo dynamic_state = {0}; | |
| dynamic_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; | |
| dynamic_state.dynamicStateCount = (uint32_t)KARRAYSIZE(dynamic_states); | |
| dynamic_state.pDynamicStates = dynamic_states; | |
| VkGraphicsPipelineCreateInfo info = {0}; | |
| info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; | |
| info.flags = mr->pipeline_create_flags; | |
| info.stageCount = 2; | |
| info.pStages = stage; | |
| info.pVertexInputState = &vertex_info; | |
| info.pInputAssemblyState = &ia_info; | |
| info.pViewportState = &viewport_info; | |
| info.pRasterizationState = &raster_info; | |
| info.pMultisampleState = &ms_info; | |
| info.pDepthStencilState = &depth_info; | |
| info.pColorBlendState = &blend_info; | |
| info.pDynamicState = &dynamic_state; | |
| info.layout = mr->pipeline_layout; | |
| info.renderPass = mr->setup.render_pass; | |
| info.subpass = mr->setup.subpass; | |
| CHECK_VK_RESULT(vkCreateGraphicsPipelines(mr->setup.device, mr->pipeline_cache, 1, &info, mr->setup.allocator, &mr->pipeline)); | |
| } | |
| static bool _micro_init_device_objects(struct micro_renderer *mr) | |
| { | |
| KASSERT(mr); | |
| if (!mr->tex_sampler) | |
| { | |
| VkSamplerCreateInfo info = {0}; | |
| info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; | |
| info.pNext = NULL; | |
| info.magFilter = VK_FILTER_LINEAR; | |
| info.minFilter = VK_FILTER_LINEAR; | |
| // info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; | |
| info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST; | |
| info.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; | |
| info.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; | |
| info.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; | |
| // info.minLod = -1000.0f; | |
| // info.maxLod = 1000.0f; | |
| info.minLod = 0.0f; | |
| info.maxLod = 0.0f; | |
| info.maxAnisotropy = 1.0f; | |
| info.anisotropyEnable = VK_FALSE; | |
| info.unnormalizedCoordinates = VK_FALSE; | |
| CHECK_VK_RESULT(vkCreateSampler(mr->setup.device, &info, mr->setup.allocator, &mr->tex_sampler)); | |
| } | |
| if (!mr->descriptor_set_layout) | |
| { | |
| VkDescriptorSetLayoutBinding binding[1] = {0}; | |
| binding[0].binding = 0; | |
| binding[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; | |
| binding[0].descriptorCount = 1; | |
| binding[0].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; | |
| VkDescriptorSetLayoutCreateInfo info = {0}; | |
| info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; | |
| info.bindingCount = 1; | |
| info.pBindings = binding; | |
| CHECK_VK_RESULT(vkCreateDescriptorSetLayout(mr->setup.device, &info, mr->setup.allocator, &mr->descriptor_set_layout)); | |
| } | |
| if (!mr->pipeline_layout) | |
| { | |
| // Constants: we are using 'vec2 offset' and 'vec2 scale' instead of a full 3d projection matrix | |
| VkPushConstantRange push_constants[1] = {0}; | |
| push_constants[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT; | |
| push_constants[0].offset = sizeof(float) * 0; | |
| push_constants[0].size = sizeof(float) * 4; | |
| VkDescriptorSetLayout set_layout[1] = {mr->descriptor_set_layout}; | |
| VkPipelineLayoutCreateInfo layout_info = {0}; | |
| layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; | |
| layout_info.setLayoutCount = 1; | |
| layout_info.pSetLayouts = set_layout; | |
| layout_info.pushConstantRangeCount = 1; | |
| layout_info.pPushConstantRanges = push_constants; | |
| CHECK_VK_RESULT(vkCreatePipelineLayout(mr->setup.device, &layout_info, mr->setup.allocator, &mr->pipeline_layout)); | |
| } | |
| if (!mr->pipeline_cache) micro_create_pipeline(mr); | |
| // Create command pool/buffer for texture upload | |
| if (!mr->tex_command_pool) | |
| { | |
| VkCommandPoolCreateInfo info = {0}; | |
| info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; | |
| info.flags = 0; | |
| info.queueFamilyIndex = mr->setup.queue_family; | |
| CHECK_VK_RESULT(vkCreateCommandPool(mr->setup.device, &info, mr->setup.allocator, &mr->tex_command_pool)); | |
| } | |
| if (!mr->tex_command_buffer) | |
| { | |
| VkCommandBufferAllocateInfo info = {0}; | |
| info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; | |
| info.commandPool = mr->tex_command_pool; | |
| info.commandBufferCount = 1; | |
| CHECK_VK_RESULT(vkAllocateCommandBuffers(mr->setup.device, &info, &mr->tex_command_buffer)); | |
| } | |
| return true; | |
| } | |
| static bool micro_init_vulkan(struct micro_renderer *r, struct micro_setup *setup) | |
| { | |
| KASSERT(setup->instance != VK_NULL_HANDLE); | |
| KASSERT(setup->physical_device != VK_NULL_HANDLE); | |
| KASSERT(setup->device != VK_NULL_HANDLE); | |
| KASSERT(setup->queue != VK_NULL_HANDLE); | |
| KASSERT(setup->descriptor_pool != VK_NULL_HANDLE); | |
| KASSERT(setup->min_image_count >= 2); | |
| KASSERT(setup->image_count >= setup->min_image_count); | |
| // TODO : think of a better way than this | |
| r->setup = *setup; | |
| r->vertex_count = 0; // start empty | |
| r->vertices = malloc(sizeof(struct micro_vertex) * MICRO_MAX_VERTICES); | |
| KASSERT(r->vertices); | |
| // start with first block being full screen scissor | |
| r->render_block[0].count = 0; | |
| r->render_block[0].sx = 0; | |
| r->render_block[0].sy = 0; | |
| r->render_block[0].sw = (int)WINDOW_WIDTH; | |
| r->render_block[0].sh = (int)WINDOW_HEIGHT; | |
| if (!_micro_init_device_objects(r)) KASSERT(0); | |
| return true; | |
| } | |
| static void micro_destroy(struct micro_renderer *r) | |
| { | |
| KASSERT(r); | |
| for (uint32_t i = 0; i < 3; i++) | |
| { | |
| vkDestroyBuffer(r->setup.device, r->vertex_buffer[i], r->setup.allocator); | |
| vkFreeMemory(r->setup.device, r->vertex_memory[i], r->setup.allocator); | |
| } | |
| vkDestroyImageView(r->setup.device, r->tex_image_view, r->setup.allocator); | |
| vkDestroyImage(r->setup.device, r->tex_image, r->setup.allocator); | |
| vkFreeMemory(r->setup.device, r->tex_memory, r->setup.allocator); | |
| vkDestroySampler(r->setup.device, r->tex_sampler, r->setup.allocator); | |
| vkDestroyCommandPool(r->setup.device, r->tex_command_pool, r->setup.allocator); | |
| vkDestroyPipeline(r->setup.device, r->pipeline, r->setup.allocator); | |
| vkDestroyPipelineLayout(r->setup.device, r->pipeline_layout, r->setup.allocator); | |
| vkDestroyDescriptorSetLayout(r->setup.device, r->descriptor_set_layout, r->setup.allocator); | |
| vkDestroyShaderModule(r->setup.device, r->vert, r->setup.allocator); | |
| vkDestroyShaderModule(r->setup.device, r->frag, r->setup.allocator); | |
| if (r->vertices) free(r->vertices); | |
| if (r->ctx) free(r->ctx); | |
| memset(r->render_block, 0, sizeof(r->render_block)); | |
| } | |
| static int micro_text_width(mu_Font font, const char *text, int len) | |
| { | |
| KUNUSED(font); | |
| if (len == -1) len = (int)strlen(text); | |
| int width = 0; | |
| for (const char *p = text; *p && len--; p++) | |
| { | |
| if ((*p & 0xc0) == 0x80) continue; | |
| int chr = mu_min((unsigned char)*p, 127); | |
| width += atlas[ATLAS_FONT + chr].w; | |
| } | |
| return width; | |
| } | |
| static int micro_text_height(mu_Font font) | |
| { | |
| KUNUSED(font); | |
| return 18; | |
| } | |
| static void micro_append_quad(struct micro_renderer *r, | |
| float x0, float y0, | |
| float x1, float y1, | |
| float u0, float v0, | |
| float u1, float v1, | |
| mu_Color colour) | |
| { | |
| if (r->vertex_count + 6 > MICRO_MAX_VERTICES) return; | |
| KASSERT(u0 >= 0.0f && u0 <= 1.0f); | |
| KASSERT(u1 >= 0.0f && u1 <= 1.0f); | |
| KASSERT(v0 >= 0.0f && v0 <= 1.0f); | |
| KASSERT(v1 >= 0.0f && v1 <= 1.0f); | |
| struct micro_vertex *v = &r->vertices[r->vertex_count]; | |
| uint32_t rgba = (colour.r << 0) | (colour.g << 8) | (colour.b << 16) | (colour.a << 24); // little-endian | |
| // Triangle 1 | |
| v[0] = (struct micro_vertex){x0, y0, u0, v0, rgba}; | |
| v[1] = (struct micro_vertex){x1, y0, u1, v0, rgba}; | |
| v[2] = (struct micro_vertex){x1, y1, u1, v1, rgba}; | |
| // Triangle 2 | |
| v[3] = (struct micro_vertex){x0, y0, u0, v0, rgba}; | |
| v[4] = (struct micro_vertex){x1, y1, u1, v1, rgba}; | |
| v[5] = (struct micro_vertex){x0, y1, u0, v1, rgba}; | |
| r->vertex_count += 6; | |
| struct micro_render_block *curr_block = &r->render_block[r->render_block_index]; | |
| curr_block->count += 6; | |
| } | |
| static void micro_draw_text(struct micro_renderer *r, const char *text, int x, int y, mu_Color colour) | |
| { | |
| mu_Rect dst = {x, y, 0, 0}; | |
| // TODO : precompute these | |
| float ipw = 1.0f / ATLAS_WIDTH, iph = 1.0f / ATLAS_HEIGHT; | |
| for (const char *p = text; *p; p++) | |
| { | |
| if ((*p & 0xc0) == 0x80) continue; | |
| int chr = mu_min((unsigned char)*p, 127); | |
| mu_Rect src = atlas[ATLAS_FONT + chr]; | |
| float u0 = (float)(src.x * ipw); | |
| float v0 = (float)(src.y * iph); | |
| float u1 = (float)((src.x + src.w) * ipw); | |
| float v1 = (float)((src.y + src.h) * iph); | |
| float x0 = (float)(dst.x); | |
| float y0 = (float)(dst.y); | |
| float x1 = (float)(dst.x + src.w); | |
| float y1 = (float)(dst.y + src.h); | |
| micro_append_quad(r, x0, y0, x1, y1, u0, v0, u1, v1, colour); | |
| dst.x += src.w; | |
| } | |
| } | |
| static void micro_draw_rect(struct micro_renderer *r, mu_Rect rect, mu_Color colour) | |
| { | |
| // TODO : precompute these | |
| float ipw = 1.0f / ATLAS_WIDTH, iph = 1.0f / ATLAS_HEIGHT; | |
| mu_Rect src = atlas[ATLAS_WHITE]; | |
| float u0 = src.x * ipw; | |
| float v0 = src.y * iph; | |
| float u1 = (src.x + src.w) * ipw; | |
| float v1 = (src.y + src.h) * iph; | |
| float x0 = (float)rect.x; | |
| float y0 = (float)rect.y; | |
| float x1 = (float)(rect.x + rect.w); | |
| float y1 = (float)(rect.y + rect.h); | |
| micro_append_quad(r, x0, y0, x1, y1, u0, v0, u1, v1, colour); | |
| } | |
| static void micro_draw_icon(struct micro_renderer *r, int id, mu_Rect rect, mu_Color colour) | |
| { | |
| KASSERT(id > 0 && id < ATLAS_WHITE); | |
| // TODO : precompute these | |
| float ipw = 1.0f / ATLAS_WIDTH, iph = 1.0f / ATLAS_HEIGHT; | |
| mu_Rect src = atlas[id]; | |
| // uv coordinates | |
| float u0 = src.x * ipw; | |
| float v0 = src.y * iph; | |
| float u1 = (src.x + src.w) * ipw; | |
| float v1 = (src.y + src.h) * iph; | |
| float x0 = (float)(rect.x + (rect.w - src.w) / 2); | |
| float y0 = (float)(rect.y + (rect.h - src.h) / 2); | |
| float x1 = (float)(x0 + src.w); | |
| float y1 = (float)(y0 + src.h); | |
| micro_append_quad(r, x0, y0, x1, y1, u0, v0, u1, v1, colour); | |
| } | |
| static void micro_handle_commands(struct micro_renderer *mr) | |
| { | |
| mu_Command *cmd = NULL; | |
| while (mu_next_command(mr->ctx, &cmd)) | |
| { | |
| switch (cmd->type) | |
| { | |
| case MU_COMMAND_TEXT: | |
| micro_draw_text(mr, cmd->text.str, cmd->text.pos.x, cmd->text.pos.y, cmd->text.color); | |
| break; | |
| case MU_COMMAND_RECT: | |
| micro_draw_rect(mr, cmd->rect.rect, cmd->rect.color); | |
| break; | |
| case MU_COMMAND_ICON: | |
| micro_draw_icon(mr, cmd->icon.id, cmd->icon.rect, cmd->icon.color); | |
| break; | |
| case MU_COMMAND_CLIP: | |
| // NOTE : scissor is set first, before the next draw commands are collected | |
| // there is potential for the first call to be a scissor call, but block [0] would have | |
| // a 0 vertex count, so should be safley skipped. | |
| // | |
| // Increment first since we never get sent an initial full screen scissor to start with | |
| // so we rely on our intial block values to be full screen | |
| mr->render_block_index++; | |
| KASSERT(mr->render_block_index < MICRO_MAX_RENDER_INFO); | |
| struct micro_render_block *curr_block = &mr->render_block[mr->render_block_index]; | |
| curr_block->sx = cmd->clip.rect.x; | |
| curr_block->sy = cmd->clip.rect.y; | |
| curr_block->sw = cmd->clip.rect.w; | |
| curr_block->sh = cmd->clip.rect.h; | |
| break; | |
| } | |
| } | |
| } | |
| static void micro_draw(struct micro_renderer *mr, uint32_t image_index) | |
| { | |
| VkCommandBuffer command_buffer = vk_state.command_buffers[image_index]; | |
| // Bind pipeline: | |
| { | |
| vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, mr->pipeline); | |
| } | |
| // Setup viewport: | |
| { | |
| VkViewport viewport = {0}; | |
| viewport.x = 0; | |
| viewport.y = 0; | |
| viewport.width = (float)WINDOW_WIDTH; | |
| viewport.height = (float)WINDOW_HEIGHT; | |
| viewport.minDepth = 0.0f; | |
| viewport.maxDepth = 1.0f; | |
| vkCmdSetViewport(command_buffer, 0, 1, &viewport); | |
| } | |
| // Setup scale and translation: | |
| { | |
| float scale[2]; // NOTE : Used to convert our pixel space ui coordinates to NDC screen space | |
| scale[0] = 2.0f / WINDOW_WIDTH; | |
| scale[1] = 2.0f / WINDOW_HEIGHT; | |
| float translate[2]; | |
| translate[0] = -1.0f; | |
| translate[1] = -1.0f; | |
| vkCmdPushConstants(command_buffer, mr->pipeline_layout, VK_SHADER_STAGE_VERTEX_BIT, sizeof(float) * 0, sizeof(float) * 2, scale); | |
| vkCmdPushConstants(command_buffer, mr->pipeline_layout, VK_SHADER_STAGE_VERTEX_BIT, sizeof(float) * 2, sizeof(float) * 2, translate); | |
| } | |
| VkDescriptorSet desc_set = mr->descriptor_set; | |
| vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, mr->pipeline_layout, 0, 1, &desc_set, 0, NULL); | |
| // Update our vertex buffer | |
| if (mr->vertex_count > 0) | |
| { | |
| // Create or resize the vertex/index buffers | |
| // VkDeviceSize vertex_aligned_size = vulkan_align_buffer_size(MICRO_MAX_VERTICES * sizeof(struct micro_vertex), 256); // Get correct alignment | |
| VkDeviceSize vertex_aligned_size = MICRO_MAX_VERTICES * sizeof(struct micro_vertex); | |
| if (mr->vertex_buffer[image_index] == VK_NULL_HANDLE || mr->vertex_size < vertex_aligned_size) | |
| { | |
| vulkan_create_or_resize_buffer(mr->setup.device, mr->setup.physical_device, mr->setup.allocator, | |
| &mr->vertex_buffer[image_index], &mr->vertex_memory[image_index], vertex_aligned_size, | |
| VK_BUFFER_USAGE_VERTEX_BUFFER_BIT); | |
| mr->vertex_size = vertex_aligned_size; | |
| } | |
| struct micro_vertex *vtx_dst = NULL; | |
| CHECK_VK_RESULT(vkMapMemory(mr->setup.device, mr->vertex_memory[image_index], 0, vertex_aligned_size, 0, (void **)&vtx_dst)); | |
| KASSERT(vtx_dst); | |
| memcpy(vtx_dst, mr->vertices, mr->vertex_count * sizeof(struct micro_vertex)); | |
| VkMappedMemoryRange range[1] = {0}; | |
| { | |
| range[0].sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; | |
| range[0].memory = mr->vertex_memory[image_index]; | |
| range[0].offset = 0; | |
| range[0].size = vertex_aligned_size; | |
| } | |
| CHECK_VK_RESULT(vkFlushMappedMemoryRanges(mr->setup.device, KARRAYSIZE(range), range)); | |
| vkUnmapMemory(mr->setup.device, mr->vertex_memory[image_index]); | |
| VkDeviceSize offsets = 0; | |
| vkCmdBindVertexBuffers(command_buffer, 0, 1, &mr->vertex_buffer[image_index], &offsets); | |
| } | |
| uint32_t rolling_count = 0; | |
| for (uint32_t i = 0; i < (mr->render_block_index + 1); i++) | |
| { | |
| struct micro_render_block *block = mr->render_block + i; | |
| if (block->count == 0) continue; | |
| #if 0 | |
| // NOTE : is this clamping even needed? | |
| int sx = KCLAMP(block->sx, 0, WINDOW_WIDTH); | |
| int sy = KCLAMP(block->sy, 0, WINDOW_HEIGHT); | |
| int sw = KCLAMP(block->sw, 0, WINDOW_WIDTH); | |
| int sh = KCLAMP(block->sh, 0, WINDOW_HEIGHT); | |
| VkRect2D ui_scissor = {{(int32_t)sx, (int32_t)sy}, | |
| {(uint32_t)sw, (uint32_t)sh}}; | |
| #else | |
| VkRect2D ui_scissor = {{(int32_t)block->sx, (int32_t)block->sy}, | |
| {(uint32_t)block->sw, (uint32_t)block->sh}}; | |
| #endif | |
| vkCmdSetScissor(command_buffer, 0, 1, &ui_scissor); | |
| uint32_t first = rolling_count; | |
| rolling_count += block->count; | |
| vkCmdDraw(command_buffer, block->count, 1, first, 0); | |
| block->count = 0; | |
| block->sx = 0; | |
| block->sy = 0; | |
| block->sw = (int)WINDOW_WIDTH; | |
| block->sh = (int)WINDOW_HEIGHT; | |
| } | |
| // Return back to full screen scissor | |
| VkRect2D scissor = {{0, 0}, {(uint32_t)WINDOW_WIDTH, (uint32_t)WINDOW_HEIGHT}}; | |
| vkCmdSetScissor(command_buffer, 0, 1, &scissor); | |
| mr->render_block_index = 0; | |
| mr->vertex_count = 0; | |
| } | |
| // | |
| // MICROUI EXAMPLE | |
| // | |
| #include "microui\demo.c" | |
| // | |
| // APP | |
| // | |
| static VkDescriptorPool app_create_descriptor_pool(void); | |
| static bool app_create_basic_triangle_graphics_pipeline(struct vulkan_context *ctx); | |
| static void app_draw_frame(struct vulkan_context *ctx); | |
| static uint32_t app_render_begin(struct vulkan_context *ctx); | |
| static void app_render_end(uint32_t image_index); | |
| static void app_draw(uint32_t image_index); | |
| static void app_present(struct vulkan_context *ctx, uint32_t image_index); | |
| int main(void) | |
| { | |
| RGFW_window *win = RGFW_createWindow("Vulkan Example", 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, RGFW_windowCenter); | |
| RGFW_window_setExitKey(win, RGFW_escape); | |
| struct vulkan_context ctx; | |
| if (!vulkan_init_device(win, &ctx)) return 1; | |
| if (!vulkan_init(&ctx)) return 1; | |
| if (!app_create_basic_triangle_graphics_pipeline(&ctx)) return 1; | |
| vk_state.pool = app_create_descriptor_pool(); | |
| struct micro_renderer mu_r = {0}; | |
| struct micro_setup mu_setup = {0}; | |
| mu_setup.instance = vk_state.instance; | |
| mu_setup.physical_device = vk_state.physical_device; | |
| mu_setup.device = vk_state.device; | |
| mu_setup.queue = vk_state.graphics_queue; | |
| mu_setup.queue_family = vk_state.graphics_family_index; | |
| mu_setup.descriptor_pool = vk_state.pool; | |
| mu_setup.render_pass = vk_state.render_pass; | |
| mu_setup.min_image_count = MAX_FRAMES_IN_FLIGHT; | |
| mu_setup.image_count = ctx.image_count; | |
| mu_setup.MSAA_samples = VK_SAMPLE_COUNT_1_BIT; | |
| mu_setup.allocator = NULL; | |
| mu_r.ctx = malloc(sizeof(*mu_r.ctx)); | |
| mu_init(mu_r.ctx); | |
| mu_r.ctx->text_width = micro_text_width; | |
| mu_r.ctx->text_height = micro_text_height; | |
| micro_init_vulkan(&mu_r, &mu_setup); | |
| micro_init_font(&mu_r); | |
| static const char button_map[4] = { | |
| [RGFW_mouseLeft & 0xff] = MU_MOUSE_LEFT, | |
| [RGFW_mouseMiddle & 0xff] = MU_MOUSE_MIDDLE, | |
| [RGFW_mouseRight & 0xff] = MU_MOUSE_RIGHT, | |
| }; | |
| static const char key_map[256] = { | |
| [RGFW_shiftL & 0xff] = MU_KEY_SHIFT, | |
| [RGFW_shiftR & 0xff] = MU_KEY_SHIFT, | |
| [RGFW_controlL & 0xff] = MU_KEY_CTRL, | |
| [RGFW_controlR & 0xff] = MU_KEY_CTRL, | |
| [RGFW_altL & 0xff] = MU_KEY_ALT, | |
| [RGFW_altR & 0xff] = MU_KEY_ALT, | |
| [RGFW_return & 0xff] = MU_KEY_RETURN, | |
| [RGFW_backSpace & 0xff] = MU_KEY_BACKSPACE, | |
| }; | |
| bool running = true; | |
| while (running && !RGFW_window_isKeyPressed(win, RGFW_escape)) | |
| { | |
| RGFW_event event; | |
| while (RGFW_window_checkEvent(win, &event)) | |
| { | |
| switch (event.type) | |
| { | |
| case RGFW_quit: running = false; break; | |
| case RGFW_mousePosChanged: mu_input_mousemove(mu_r.ctx, event.mouse.x, event.mouse.y); break; | |
| case RGFW_mouseScroll: mu_input_scroll(mu_r.ctx, (int)(event.scroll.x), (int)(event.scroll.y)); break; | |
| case RGFW_mouseButtonPressed: | |
| case RGFW_mouseButtonReleased: | |
| { | |
| i32 x, y; | |
| RGFW_window_getMouse(win, &x, &y); | |
| int b = button_map[event.button.value & 0xff]; | |
| if (b && event.type == RGFW_mouseButtonPressed) { mu_input_mousedown(mu_r.ctx, x, y, b); } | |
| if (b && event.type == RGFW_mouseButtonReleased) { mu_input_mouseup(mu_r.ctx, x, y, b); } | |
| break; | |
| } | |
| case RGFW_keyPressed: | |
| { | |
| int c = key_map[event.key.value & 0xff]; | |
| if (c) mu_input_keydown(mu_r.ctx, c); | |
| if (event.key.sym >= 32 && event.key.sym <= 126) | |
| { | |
| char str[2] = {(char)event.key.sym, '\0'}; | |
| mu_input_text(mu_r.ctx, str); | |
| } | |
| } | |
| case RGFW_keyReleased: | |
| { | |
| int c = key_map[event.key.value & 0xff]; | |
| if (c) mu_input_keyup(mu_r.ctx, c); | |
| break; | |
| } | |
| default: break; | |
| } | |
| } | |
| micro_run_demo(mu_r.ctx); | |
| micro_handle_commands(&mu_r); | |
| uint32_t image_index = app_render_begin(&ctx); | |
| { | |
| app_draw(image_index); | |
| micro_draw(&mu_r, image_index); | |
| } | |
| app_render_end(image_index); | |
| app_present(&ctx, image_index); | |
| } | |
| micro_destroy(&mu_r); | |
| vulkan_destroy(&ctx); | |
| RGFW_window_close(win); | |
| return 0; | |
| } | |
| static VkDescriptorPool app_create_descriptor_pool(void) | |
| { | |
| VkDescriptorPoolSize pool_sizes[] = { | |
| {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1}, | |
| }; | |
| VkDescriptorPoolCreateInfo pool_info = {0}; | |
| pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; | |
| pool_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT; | |
| pool_info.maxSets = 0; | |
| for (int i = 0; i < KARRAYSIZE(pool_sizes); i++) | |
| { | |
| VkDescriptorPoolSize pool_size = pool_sizes[i]; | |
| pool_info.maxSets += pool_size.descriptorCount; | |
| } | |
| pool_info.poolSizeCount = (uint32_t)KARRAYSIZE(pool_sizes); | |
| pool_info.pPoolSizes = pool_sizes; | |
| VkDescriptorPool pool; | |
| CHECK_VK_RESULT(vkCreateDescriptorPool(vk_state.device, &pool_info, NULL, &pool)); | |
| return pool; | |
| } | |
| static bool app_create_basic_triangle_graphics_pipeline(struct vulkan_context *ctx) | |
| { | |
| VkShaderModule vert_module = vulkan_create_shader(triangle_vert_spv, sizeof(triangle_vert_spv)); | |
| VkShaderModule frag_module = vulkan_create_shader(triangle_frag_spv, sizeof(triangle_frag_spv)); | |
| if (vert_module == VK_NULL_HANDLE || frag_module == VK_NULL_HANDLE) | |
| { | |
| printf("failed to create shader module\n"); | |
| return false; | |
| } | |
| VkPipelineShaderStageCreateInfo vert_stage_info = {0}; | |
| vert_stage_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; | |
| vert_stage_info.stage = VK_SHADER_STAGE_VERTEX_BIT; | |
| vert_stage_info.module = vert_module; | |
| vert_stage_info.pName = "main"; | |
| VkPipelineShaderStageCreateInfo frag_stage_info = {0}; | |
| frag_stage_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; | |
| frag_stage_info.stage = VK_SHADER_STAGE_FRAGMENT_BIT; | |
| frag_stage_info.module = frag_module; | |
| frag_stage_info.pName = "main"; | |
| VkPipelineShaderStageCreateInfo shader_stages[] = {vert_stage_info, | |
| frag_stage_info}; | |
| VkPipelineVertexInputStateCreateInfo vertex_input_info = {0}; | |
| vertex_input_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; | |
| vertex_input_info.vertexBindingDescriptionCount = 0; | |
| vertex_input_info.vertexAttributeDescriptionCount = 0; | |
| VkPipelineInputAssemblyStateCreateInfo input_assembly = {0}; | |
| input_assembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; | |
| input_assembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; | |
| input_assembly.primitiveRestartEnable = VK_FALSE; | |
| VkViewport viewport = {0}; | |
| viewport.x = 0.0f; | |
| viewport.y = 0.0f; | |
| viewport.width = (float)ctx->width; | |
| viewport.height = (float)ctx->height; | |
| viewport.minDepth = 0.0f; | |
| viewport.maxDepth = 1.0f; | |
| VkRect2D scissor = {0}; | |
| scissor.offset.x = 0; | |
| scissor.offset.y = 0; | |
| scissor.extent = (VkExtent2D){ctx->width, ctx->height}; | |
| VkPipelineViewportStateCreateInfo viewport_state = {0}; | |
| viewport_state.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; | |
| viewport_state.viewportCount = 1; | |
| viewport_state.pViewports = &viewport; | |
| viewport_state.scissorCount = 1; | |
| viewport_state.pScissors = &scissor; | |
| VkPipelineRasterizationStateCreateInfo rasterizer = {0}; | |
| rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; | |
| rasterizer.depthClampEnable = VK_FALSE; | |
| rasterizer.rasterizerDiscardEnable = VK_FALSE; | |
| rasterizer.polygonMode = VK_POLYGON_MODE_FILL; | |
| rasterizer.lineWidth = 1.0f; | |
| rasterizer.cullMode = VK_CULL_MODE_BACK_BIT; | |
| rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE; | |
| rasterizer.depthBiasEnable = VK_FALSE; | |
| VkPipelineMultisampleStateCreateInfo multisampling = {0}; | |
| multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; | |
| multisampling.sampleShadingEnable = VK_FALSE; | |
| multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; | |
| VkPipelineColorBlendAttachmentState colour_blend_attachment = {0}; | |
| colour_blend_attachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; | |
| colour_blend_attachment.blendEnable = VK_FALSE; | |
| VkPipelineColorBlendStateCreateInfo colour_blending = {0}; | |
| colour_blending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; | |
| colour_blending.logicOpEnable = VK_FALSE; | |
| colour_blending.logicOp = VK_LOGIC_OP_COPY; | |
| colour_blending.attachmentCount = 1; | |
| colour_blending.pAttachments = &colour_blend_attachment; | |
| colour_blending.blendConstants[0] = 0.0f; | |
| colour_blending.blendConstants[1] = 0.0f; | |
| colour_blending.blendConstants[2] = 0.0f; | |
| colour_blending.blendConstants[3] = 0.0f; | |
| VkPipelineLayoutCreateInfo pipeline_layout_info = {0}; | |
| pipeline_layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; | |
| pipeline_layout_info.setLayoutCount = 0; | |
| pipeline_layout_info.pushConstantRangeCount = 0; | |
| pipeline_layout_info.pPushConstantRanges = NULL; | |
| CHECK_VK_RESULT(vkCreatePipelineLayout(vk_state.device, &pipeline_layout_info, NULL, &vk_state.pipeline_layout)); | |
| VkDynamicState dynamic_states[] = {VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR}; | |
| VkPipelineDynamicStateCreateInfo dynamic_info = {0}; | |
| dynamic_info.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; | |
| dynamic_info.dynamicStateCount = KARRAYSIZE(dynamic_states); | |
| dynamic_info.pDynamicStates = dynamic_states; | |
| VkGraphicsPipelineCreateInfo pipeline_info = {0}; | |
| pipeline_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; | |
| pipeline_info.pStages = shader_stages; | |
| pipeline_info.stageCount = KARRAYSIZE(shader_stages); | |
| pipeline_info.pVertexInputState = &vertex_input_info; | |
| pipeline_info.pInputAssemblyState = &input_assembly; | |
| pipeline_info.pViewportState = &viewport_state; | |
| pipeline_info.pRasterizationState = &rasterizer; | |
| pipeline_info.pMultisampleState = &multisampling; | |
| pipeline_info.pColorBlendState = &colour_blending; | |
| pipeline_info.pDynamicState = &dynamic_info; | |
| pipeline_info.layout = vk_state.pipeline_layout; | |
| pipeline_info.renderPass = vk_state.render_pass; | |
| pipeline_info.subpass = 0; | |
| pipeline_info.basePipelineHandle = VK_NULL_HANDLE; | |
| CHECK_VK_RESULT(vkCreateGraphicsPipelines(vk_state.device, VK_NULL_HANDLE, 1, &pipeline_info, NULL, &vk_state.graphics_pipeline)); | |
| vkDestroyShaderModule(vk_state.device, frag_module, NULL); | |
| vkDestroyShaderModule(vk_state.device, vert_module, NULL); | |
| return true; | |
| } | |
| static uint32_t app_render_begin(struct vulkan_context *ctx) | |
| { | |
| CHECK_VK_RESULT(vkWaitForFences(vk_state.device, | |
| 1, | |
| &vk_state.in_flight_fences[vk_state.current_frame], | |
| VK_TRUE, UINT64_MAX)); | |
| uint32_t image_index = 0; | |
| CHECK_VK_RESULT(vkAcquireNextImageKHR(vk_state.device, | |
| ctx->swapchain, | |
| UINT64_MAX, | |
| vk_state.available_semaphores[vk_state.current_frame], | |
| VK_NULL_HANDLE, | |
| &image_index)); | |
| if (vk_state.image_in_flight[image_index] != VK_NULL_HANDLE) | |
| { | |
| CHECK_VK_RESULT(vkWaitForFences(vk_state.device, | |
| 1, | |
| &vk_state.image_in_flight[image_index], VK_TRUE, | |
| UINT64_MAX)); | |
| } | |
| vk_state.image_in_flight[image_index] = vk_state.in_flight_fences[vk_state.current_frame]; | |
| VkCommandBufferBeginInfo begin_info = {0}; | |
| begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; | |
| CHECK_VK_RESULT(vkBeginCommandBuffer(vk_state.command_buffers[image_index], &begin_info)); | |
| VkRenderPassBeginInfo render_pass_info = {0}; | |
| render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; | |
| render_pass_info.renderPass = vk_state.render_pass; | |
| render_pass_info.framebuffer = vk_state.framebuffers[image_index]; | |
| render_pass_info.renderArea.offset.x = 0; | |
| render_pass_info.renderArea.offset.y = 0; | |
| render_pass_info.renderArea.extent = (VkExtent2D){ctx->width, ctx->height}; | |
| VkClearValue clear_colour; | |
| clear_colour.color.float32[0] = micro_demo_bg[0] / 255.0f; | |
| clear_colour.color.float32[1] = micro_demo_bg[1] / 255.0f; | |
| clear_colour.color.float32[2] = micro_demo_bg[2] / 255.0f; | |
| clear_colour.color.float32[3] = 1.0f; | |
| render_pass_info.clearValueCount = 1; | |
| render_pass_info.pClearValues = &clear_colour; | |
| VkViewport viewport; | |
| viewport.x = 0.0f; | |
| viewport.y = 0.0f; | |
| viewport.width = (float)ctx->width; | |
| viewport.height = (float)ctx->height; | |
| viewport.minDepth = 0.0f; | |
| viewport.maxDepth = 1.0f; | |
| VkRect2D scissor; | |
| scissor.offset.x = 0; | |
| scissor.offset.y = 0; | |
| scissor.extent = (VkExtent2D){ctx->width, ctx->height}; | |
| vkCmdSetScissor(vk_state.command_buffers[image_index], 0, 1, &scissor); | |
| vkCmdSetViewport(vk_state.command_buffers[image_index], 0, 1, &viewport); | |
| vkCmdBeginRenderPass(vk_state.command_buffers[image_index], &render_pass_info, | |
| VK_SUBPASS_CONTENTS_INLINE); | |
| return image_index; | |
| } | |
| static void app_draw(uint32_t image_index) | |
| { | |
| vkCmdBindPipeline(vk_state.command_buffers[image_index], | |
| VK_PIPELINE_BIND_POINT_GRAPHICS, | |
| vk_state.graphics_pipeline); | |
| vkCmdDraw(vk_state.command_buffers[image_index], 3, 1, 0, 0); | |
| } | |
| static void app_render_end(uint32_t image_index) | |
| { | |
| vkCmdEndRenderPass(vk_state.command_buffers[image_index]); | |
| CHECK_VK_RESULT(vkEndCommandBuffer(vk_state.command_buffers[image_index])); | |
| VkPipelineStageFlags wait_stages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}; | |
| VkSubmitInfo submit_info = {0}; | |
| submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; | |
| submit_info.waitSemaphoreCount = 1; | |
| submit_info.pWaitSemaphores = &vk_state.available_semaphores[vk_state.current_frame]; | |
| submit_info.pWaitDstStageMask = wait_stages; | |
| submit_info.commandBufferCount = 1; | |
| submit_info.pCommandBuffers = &vk_state.command_buffers[image_index]; | |
| submit_info.signalSemaphoreCount = 1; | |
| submit_info.pSignalSemaphores = &vk_state.finished_semaphores[vk_state.current_frame]; | |
| CHECK_VK_RESULT(vkResetFences(vk_state.device, 1, &vk_state.in_flight_fences[vk_state.current_frame])); | |
| CHECK_VK_RESULT(vkQueueSubmit(vk_state.graphics_queue, 1, &submit_info, vk_state.in_flight_fences[vk_state.current_frame])); | |
| } | |
| static void app_present(struct vulkan_context *ctx, uint32_t image_index) | |
| { | |
| VkPresentInfoKHR present_info = {0}; | |
| present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; | |
| present_info.waitSemaphoreCount = 1; | |
| present_info.pWaitSemaphores = &vk_state.finished_semaphores[vk_state.current_frame]; | |
| present_info.swapchainCount = 1; | |
| present_info.pSwapchains = &ctx->swapchain; | |
| present_info.pImageIndices = &image_index; | |
| CHECK_VK_RESULT(vkQueuePresentKHR(vk_state.present_queue, &present_info)); | |
| vk_state.current_frame = (vk_state.current_frame + 1) % MAX_FRAMES_IN_FLIGHT; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment