Skip to content

Instantly share code, notes, and snippets.

@ClearlyKyle
Created December 9, 2025 16:36
Show Gist options
  • Select an option

  • Save ClearlyKyle/515d741a0f12284c1334fb23361a5991 to your computer and use it in GitHub Desktop.

Select an option

Save ClearlyKyle/515d741a0f12284c1334fb23361a5991 to your computer and use it in GitHub Desktop.
microui demo using vulkan and RGFW
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, &micro_demo_bg[0], 0, 255);
mu_label(ctx, "Green:");
mu_slider(ctx, &micro_demo_bg[1], 0, 255);
mu_label(ctx, "Blue:");
mu_slider(ctx, &micro_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);
}
#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, &region);
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