Starting rework for GPU driven UI rendering

main
noah metz 2024-10-18 14:26:41 -06:00
parent d04f7769ee
commit 6129e9c5b9
10 changed files with 347 additions and 669 deletions

@ -92,8 +92,8 @@ VkResult init_vulkan(
VkResult draw_frame(
RenderContext* context,
UIContext* ui_context,
UILayer* ui_layers,
UIContextStorage* ui_context,
UILayerStorage* ui_layers,
uint32_t ui_layer_count);
VkResult create_transfer_buffer(

@ -19,20 +19,16 @@ typedef struct GraphicsPipelineStruct {
VkPipeline pipeline;
} GraphicsPipeline;
typedef struct ColoredRectStruct {
typedef struct UIRectStruct {
vec3 pos;
uint32_t padding0;
float pad0;
vec2 size;
vec4 color;
} ColoredRect;
typedef struct UIUniformStruct {
mat4 screen;
} UIUniform;
} UIRect;
typedef struct StringStruct {
vec3 pos;
uint32_t padding0;
float pad0;
vec4 color;
float size;
uint32_t offset;
@ -46,27 +42,26 @@ typedef struct DrawCommandStruct {
uint32_t first_instance;
} DrawCommand;
typedef struct StringPointersStruct {
VkDeviceAddress strings;
VkDeviceAddress codes;
VkDeviceAddress characters;
VkDeviceAddress draw;
} StringPointers;
typedef struct DispatchCommandStruct {
uint32_t x;
uint32_t y;
uint32_t z;
} DispatchCommand;
typedef struct CharacterStruct {
vec3 pos;
uint32_t padding0;
float pad0;
vec4 color;
float size;
uint32_t code;
} Character;
typedef struct FontUniformStruct {
typedef struct FontStruct {
uint32_t num_symbols;
uint32_t width;
uint32_t height;
VkDeviceAddress symbol_list;
} FontUniform;
} Font;
typedef struct SymbolInfoStruct {
int32_t top;
@ -76,17 +71,15 @@ typedef struct SymbolInfoStruct {
uint32_t advance;
} SymbolInfo;
typedef struct FontStruct {
typedef struct FontStorageStruct {
VmaAllocation symbol_memory;
VmaAllocation uniform_memory;
VmaAllocation image_memory;
VkBuffer symbols;
VkBuffer uniform;
VkImage image;
VkImageView view;
VkSampler sampler;
VkDescriptorSet set;
} Font;
uint32_t index;
} FontStorage;
typedef struct TextPointersMemoryStruct {
VmaAllocation pointers_memory;
@ -102,46 +95,72 @@ typedef struct TextPointersMemoryStruct {
VkBuffer characters_buffer;
} TextPointersMemory;
typedef struct UIlayerStorageStruct {
VkBuffer strings;
VkBuffer chars;
VkBuffer codes;
VkBuffer rects;
VkBuffer layer;
VmaAllocation strings_memory;
VmaAllocation rects_memory;
VmaAllocation chars_memory;
VmaAllocation codes_memory;
VmaAllocation layer_memory;
VkDeviceAddress address;
} UILayerStorage;
typedef struct UILayerStruct {
VkDeviceAddress colored_rects;
uint32_t colored_rect_count;
VkDeviceAddress rects;
uint32_t rect_count;
VkBuffer string_draw;
VkBuffer chars;
VkDeviceAddress string_pointers;
uint32_t string_count;
uint32_t chars_count;
Font font;
VkDeviceAddress strings;
uint32_t font_index;
DrawCommand draw_chars;
DrawCommand draw_rects;
DispatchCommand dispatch_strings;
} UILayer;
typedef struct UIContextStruct {
VkBuffer ui_descriptor_buffer;
VmaAllocation ui_descriptor_memory;
VkDescriptorSetLayout ui_descriptor_layout;
VkDescriptorPool ui_descriptor_pool;
VkDescriptorSet ui_descriptor_set;
VkDescriptorPool font_pool;
VkDescriptorSetLayout font_layout;
GraphicsPipeline ui_pipeline_rect;
GraphicsPipeline ui_pipeline_text;
ComputePipeline ui_compute_text;
mat4 screen;
VkDeviceAddress font_infos;
} UIContext;
VkResult init_pipelines(
typedef struct UIContextStorageStruct {
VkDeviceAddress address;
VkBuffer context;
VmaAllocation context_memory;
VkBuffer font_infos;
VmaAllocation font_infos_memory;
VkDescriptorSet font_samplers;
VkDescriptorSet font_textures;
VkDescriptorSetLayout font_samplers_layout;
VkDescriptorSetLayout font_textures_layout;
VkDescriptorPool fonts_pool;
GraphicsPipeline rect_pipeline;
GraphicsPipeline char_pipeline;
ComputePipeline string_pipeline;
} UIContextStorage;
VkResult create_ui_context(
VkDevice device,
VmaAllocator allocator,
VkExtent2D swapchain_extent,
vec2 window_scale,
VkRenderPass render_pass,
UIContext* context);
UIContextStorage* memory);
VkResult load_font(
VkDevice device,
VmaAllocator allocator,
VkDescriptorSetLayout layout,
VkDescriptorPool pool,
VkBuffer font_infos,
VkDescriptorSet font_samplers,
VkDescriptorSet font_textures,
uint32_t last_index,
VkCommandPool transfer_pool,
Queue transfer_queue,
FT_Library library,
@ -149,16 +168,16 @@ VkResult load_font(
uint32_t size,
VkBool32 antialias,
uint32_t** charmap,
Font* descriptor);
FontStorage* memory);
VkResult create_text_pointers(
VkResult create_layer(
uint32_t max_strings,
uint32_t max_characters,
uint32_t max_rects,
VkDevice device,
VmaAllocator allocator,
VkCommandPool transfer_pool,
Queue transfer_queue,
TextPointersMemory* memory,
UILayerStorage* memory,
VkDeviceAddress* address);
#endif

@ -1,10 +1,6 @@
#version 450
#extension GL_EXT_buffer_reference : require
layout(set = 0, binding = 0) uniform UIUniform {
mat4 screen;
} ubo;
struct Rect {
vec3 pos;
vec2 size;
@ -15,7 +11,12 @@ layout(std430, buffer_reference) readonly buffer RectList {
Rect r[];
};
layout(std430, buffer_reference) readonly buffer ScreenInfo {
mat4 bounds;
};
layout(std430, push_constant) uniform PushConstant {
ScreenInfo screen;
RectList rects;
} pc;
@ -32,7 +33,7 @@ const vec2 square[6] = {
void main() {
Rect rect = pc.rects.r[gl_InstanceIndex];
gl_Position = ubo.screen * vec4(vec3(square[gl_VertexIndex] * rect.size, 0.0) + rect.pos.xyz, 1.0);
gl_Position = pc.screen.bounds * vec4(vec3(square[gl_VertexIndex] * rect.size, 0.0) + rect.pos.xyz, 1.0);
fragColor = rect.color;
}

@ -1,7 +1,7 @@
#version 450
#extension GL_EXT_buffer_reference : require
struct Symbol {
layout(buffer_reference, std430) buffer Symbol {
int top;
uint left;
uint width;
@ -9,14 +9,14 @@ struct Symbol {
uint advance;
};
struct Character {
layout(buffer_reference, std430) buffer Character {
vec3 pos;
vec4 color;
float size;
uint code;
};
struct String {
layout(buffer_reference, std430) buffer String {
vec3 pos;
vec4 color;
float size;
@ -40,12 +40,12 @@ layout(buffer_reference, std430) readonly buffer Strings{
String strings[];
};
layout(set = 0, binding = 0) uniform Font {
layout(buffer_reference, std430) readonly buffer Font {
uint num_symbols;
uint width;
uint height;
SymbolList symbol_list;
} font;
};
layout(buffer_reference, std430) buffer DrawCommand {
uint vertex_count;
@ -61,7 +61,17 @@ layout(buffer_reference, std430) readonly buffer Pointers {
DrawCommand draw;
};
layout(buffer_reference, std430) readonly buffer FontList {
Font fonts[];
};
layout(buffer_reference, std430) readonly buffer UIContext {
mat4 screen;
FontList fonts;
};
layout(std430, push_constant) uniform Push {
UIContext ui;
Pointers pointers;
} push;
@ -70,6 +80,7 @@ layout(local_size_x = 1) in;
void main() {
uint gID = gl_GlobalInvocationID.x;
String string = push.pointers.strings.strings[gID];
Font font = push.ui.fonts.fonts[0];
uint buffer_pos = atomicAdd(push.pointers.draw.instance_count, string.len);
float x = 0;

@ -1,6 +1,8 @@
#version 450
#extension GL_EXT_nonuniform_qualifier : enable
layout(set = 1, binding = 1) uniform sampler2DArray font;
layout(set = 0, binding = 0) uniform sampler font_samplers[];
layout(set = 1, binding = 0) uniform texture2DArray font_textures[];
layout(location = 0) in vec4 fragColor;
layout(location = 1) in vec2 fragUV;
@ -9,6 +11,6 @@ layout(location = 2) flat in uint code;
layout(location = 0) out vec4 outColor;
void main() {
outColor = fragColor * texture(font, vec3(fragUV, code));
outColor = fragColor * texture(sampler2DArray(font_textures[0], font_samplers[0]), vec3(fragUV, code));
}

@ -1,7 +1,7 @@
#version 450
#extension GL_EXT_buffer_reference : require
struct Symbol {
layout(buffer_reference, std430) buffer Symbol {
int top;
uint left;
uint width;
@ -9,7 +9,7 @@ struct Symbol {
uint advance;
};
struct Character {
layout(buffer_reference, std430) buffer Character {
vec3 pos;
vec4 color;
float size;
@ -24,23 +24,30 @@ layout(buffer_reference, std430) readonly buffer CharacterList{
Character characters[];
};
layout(set = 0, binding = 0) uniform UIUniform {
mat4 screen;
} ubo;
layout(set = 1, binding = 0) uniform Font {
layout(buffer_reference, std430) readonly buffer Font {
uint num_symbols;
uint width;
uint height;
SymbolList symbol_list;
} font;
};
layout(buffer_reference, std430) readonly buffer Pointers {
uint padding[4];
CharacterList characters;
};
layout(buffer_reference, std430) readonly buffer FontList {
Font fonts[];
};
layout(buffer_reference, std430) readonly buffer UIContext {
mat4 screen;
FontList fonts;
};
layout(std430, push_constant) uniform Push {
UIContext ui;
Pointers pointers;
} push;
@ -59,6 +66,7 @@ const vec2 square[6] = {
void main() {
Character character = push.pointers.characters.characters[gl_InstanceIndex];
Font font = push.ui.fonts.fonts[0];
Symbol symbol = font.symbol_list.symbols[character.code];
float fragU = square[gl_VertexIndex].x * symbol.width/font.width;
@ -68,7 +76,7 @@ void main() {
fragUV = vec2(fragU, fragV);
fragColor = character.color;
gl_Position = ubo.screen * vec4(vec3(x, y, 0.0) + character.pos, 1.0);
gl_Position = push.ui.screen * vec4(vec3(x, y, 0.0) + character.pos, 1.0);
code = character.code;
}

@ -10,168 +10,42 @@
VkResult render_thread(GLFWwindow* window, RenderContext* render_context) {
VkResult result;
UIContext ui_context;
VkBuffer colored_rect_buffer;
VmaAllocation colored_rect_memory;
result = init_pipelines(render_context->device, render_context->allocator, render_context->swapchain_extent, render_context->window_scale, render_context->render_pass, &ui_context);
if(result != VK_SUCCESS) {
return result;
}
UIContextStorage ui;
FT_Library library;
if(FT_Init_FreeType(&library) != FT_Err_Ok) {
return VK_ERROR_UNKNOWN;
}
Font test_font;
FontStorage font;
uint32_t* charmap;
result = load_font(render_context->device, render_context->allocator, ui_context.font_layout, ui_context.font_pool, render_context->transfer_pool, render_context->transfer_queue, library, "test.ttf", 16, VK_TRUE, &charmap, &test_font);
if(result != VK_SUCCESS) {
return result;
}
VkBufferCreateInfo colored_rect_buffer_info = {
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT,
.size = 3*sizeof(ColoredRect),
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
};
VmaAllocationCreateInfo colored_rect_memory_info = {
.usage = VMA_MEMORY_USAGE_CPU_TO_GPU,
};
VkBuffer temp_buffer;
VmaAllocation temp_memory;
void* mapped;
UILayerStorage layer = {};
result = vmaCreateBuffer(render_context->allocator, &colored_rect_buffer_info, &colored_rect_memory_info, &colored_rect_buffer, &colored_rect_memory, NULL);
result = create_ui_context(render_context->device, render_context->allocator, render_context->render_pass, &ui);
if(result != VK_SUCCESS) {
return result;
}
ColoredRect* colored_rects;
result = vmaMapMemory(render_context->allocator, colored_rect_memory, (void**)&colored_rects);
if(result != VK_SUCCESS) {
return result;
if(FT_Init_FreeType(&library) != FT_Err_Ok) {
return VK_ERROR_UNKNOWN;
}
colored_rects[0].pos[0] = 0.0;
colored_rects[0].pos[1] = 0.0;
colored_rects[0].pos[2] = 0.5;
colored_rects[0].size[0] = 100.0;
colored_rects[0].size[1] = 100.0;
colored_rects[0].color[0] = 1.0;
colored_rects[0].color[1] = 0.0;
colored_rects[0].color[2] = 0.0;
colored_rects[0].color[3] = 1.0;
colored_rects[1].pos[0] = 0.0;
colored_rects[1].pos[1] = 100.0;
colored_rects[1].pos[2] = 0.5;
colored_rects[1].size[0] = 100.0;
colored_rects[1].size[1] = 100.0;
colored_rects[1].color[0] = 0.0;
colored_rects[1].color[1] = 0.0;
colored_rects[1].color[2] = 1.0;
colored_rects[1].color[3] = 1.0;
colored_rects[2].pos[0] = 100.0;
colored_rects[2].pos[1] = 0.0;
colored_rects[2].pos[2] = 0.5;
colored_rects[2].size[0] = 100.0;
colored_rects[2].size[1] = 100.0;
colored_rects[2].color[0] = 0.0;
colored_rects[2].color[1] = 1.0;
colored_rects[2].color[2] = 0.0;
colored_rects[2].color[3] = 1.0;
vmaUnmapMemory(render_context->allocator, colored_rect_memory);
TextPointersMemory text_pointers;
VkDeviceAddress text_pointers_address;
result = create_text_pointers(2, 100, render_context->device, render_context->allocator, render_context->transfer_pool, render_context->transfer_queue, &text_pointers, &text_pointers_address);
result = load_font(render_context->device, render_context->allocator, ui.font_infos, ui.font_samplers, ui.font_textures, 0, render_context->transfer_pool, render_context->transfer_queue, library, "test.ttf", 16, VK_TRUE, &charmap, &font);
if(result != VK_SUCCESS) {
return result;
}
VkBuffer temp_buffer;
VmaAllocation temp_memory;
void* mapped;
result = create_transfer_buffer(2*sizeof(String) + 100*sizeof(uint32_t), render_context->allocator, &temp_buffer, &temp_memory, (void**)&mapped);
if(result != VK_SUCCESS) {
return result;
}
String* mapped_strings = (String*)mapped;
mapped_strings[0].pos[0] = 0;
mapped_strings[0].pos[1] = 200.0;
mapped_strings[0].pos[2] = 0.2;
mapped_strings[0].color[0] = 1.0;
mapped_strings[0].color[1] = 1.0;
mapped_strings[0].color[2] = 1.0;
mapped_strings[0].color[3] = 1.0;
mapped_strings[0].size = 50.0;
mapped_strings[0].length = 50;
mapped_strings[0].offset = 0;
mapped_strings[1].pos[0] = 0;
mapped_strings[1].pos[1] = 225.0;
mapped_strings[1].pos[2] = 0.3;
mapped_strings[1].color[0] = 1.0;
mapped_strings[1].color[1] = 1.0;
mapped_strings[1].color[2] = 1.0;
mapped_strings[1].color[3] = 1.0;
mapped_strings[1].size = 50.0;
mapped_strings[1].length = 50;
mapped_strings[1].offset = 50;
uint32* mapped_codes = (uint32_t*)(mapped + 2*sizeof(String));
for(uint32_t i = 0; i < 100; i++) {
mapped_codes[i] = i;
}
VkCommandBuffer command_buffer = command_begin_single(render_context->device, render_context->transfer_pool);
VkBufferCopy copy_string = {
.size = 2*sizeof(String),
.srcOffset = 0,
};
vkCmdCopyBuffer(command_buffer, temp_buffer, text_pointers.strings_buffer, 1, &copy_string);
VkBufferCopy copy_codes = {
.size = 100*sizeof(uint32_t),
.srcOffset = 2*sizeof(String),
};
vkCmdCopyBuffer(command_buffer, temp_buffer, text_pointers.codes_buffer, 1, &copy_codes);
result = command_end_single(render_context->device, command_buffer, render_context->transfer_pool, render_context->transfer_queue);
if(result != VK_SUCCESS) {
return result;
}
vkQueueWaitIdle(render_context->transfer_queue.handle);
vmaUnmapMemory(render_context->allocator, temp_memory);
vmaDestroyBuffer(render_context->allocator, temp_buffer, temp_memory);
VkBufferDeviceAddressInfo colored_rect_address_info = {
.sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO,
.buffer = colored_rect_buffer,
};
UILayer test_layer = {
.colored_rects = vkGetBufferDeviceAddress(render_context->device, &colored_rect_address_info),
.colored_rect_count = 3,
.font = test_font,
.chars = text_pointers.characters_buffer,
.string_count = 2,
.chars_count = 100,
.string_pointers = text_pointers_address,
.string_draw = text_pointers.draw_buffer,
};
while(glfwWindowShouldClose(window) == 0) {
glfwPollEvents();
result = draw_frame(render_context, &ui_context, &test_layer, 1);
result = draw_frame(render_context, &ui, &layer, 1);
if(result != VK_SUCCESS) {
fprintf(stderr, "draw_frame error: %s\n", string_VkResult(result));
return result;

@ -344,6 +344,7 @@ VkResult create_logical_device(VkPhysicalDevice physical_device, VkSurfaceKHR su
VkPhysicalDeviceVulkan12Features features_12 = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES,
.bufferDeviceAddress = VK_TRUE,
.runtimeDescriptorArray = VK_TRUE,
.descriptorIndexing = VK_TRUE,
.descriptorBindingPartiallyBound = VK_TRUE,
.descriptorBindingVariableDescriptorCount = VK_TRUE,
@ -976,7 +977,7 @@ VkResult init_vulkan(GLFWwindow* window, RenderContext* context) {
return VK_SUCCESS;
}
VkResult draw_frame(RenderContext* context, UIContext* ui_context, UILayer* ui_layers, uint32_t ui_layer_count) {
VkResult draw_frame(RenderContext* context, UIContextStorage* ui_context, UILayerStorage* ui_layers, uint32_t ui_layer_count) {
VkResult result;
result = vkWaitForFences(context->device, 1, &context->in_flight_fences[context->current_frame], VK_TRUE, UINT64_MAX);
@ -996,7 +997,6 @@ VkResult draw_frame(RenderContext* context, UIContext* ui_context, UILayer* ui_l
return result;
}
result = vkResetCommandBuffer(command_buffer, 0);
if(result != VK_SUCCESS) {
return result;
@ -1035,82 +1035,32 @@ VkResult draw_frame(RenderContext* context, UIContext* ui_context, UILayer* ui_l
.pClearValues = clear_values,
};
// UI strings compute pass
vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, ui_context->ui_compute_text.pipeline);
VkDeviceAddress push[2] = {ui_context->address, 0};
// Compute Pass
vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, ui_context->string_pipeline.pipeline);
for(uint32_t i = 0; i < ui_layer_count; i++) {
if(ui_layers[i].string_count > 0) {
VkBufferMemoryBarrier draw_barrier = {
.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
.size = sizeof(DrawCommand),
.buffer = ui_layers[i].string_draw,
.srcAccessMask = VK_ACCESS_INDIRECT_COMMAND_READ_BIT,
.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
};
vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT, VK_PIPELINE_STAGE_2_ALL_TRANSFER_BIT, VK_DEPENDENCY_BY_REGION_BIT, 0, NULL, 1, &draw_barrier, 0, NULL);
vkCmdFillBuffer(command_buffer, ui_layers[i].string_draw, offsetof(DrawCommand, instance_count), sizeof(uint32_t), 0);
VkBufferMemoryBarrier clear_barrier = {
.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
.size = sizeof(uint32_t),
.offset = offsetof(DrawCommand, instance_count),
.buffer = ui_layers[i].string_draw,
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT,
};
vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_2_ALL_TRANSFER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_DEPENDENCY_BY_REGION_BIT, 0, NULL, 1, &clear_barrier, 0, NULL);
vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, ui_context->ui_compute_text.layout, 0, 1, &ui_layers[i].font.set, 0, NULL);
vkCmdPushConstants(command_buffer, ui_context->ui_pipeline_text.layout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_COMPUTE_BIT, 0, 8, &ui_layers[i].string_pointers);
vkCmdDispatch(command_buffer, ui_layers[i].string_count, 1, 1);
VkBufferMemoryBarrier character_compute_barrier = {
.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
.size = ui_layers[i].chars_count,
.buffer = ui_layers[i].chars,
.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
};
VkBufferMemoryBarrier draw_compute_barrier = {
.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
.size = sizeof(DrawCommand),
.buffer = ui_layers[i].string_draw,
.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
.dstAccessMask = VK_ACCESS_INDIRECT_COMMAND_READ_BIT,
};
vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT, VK_DEPENDENCY_BY_REGION_BIT, 0, NULL, 1, &character_compute_barrier, 0, NULL);
vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT, VK_DEPENDENCY_BY_REGION_BIT, 0, NULL, 1, &draw_compute_barrier, 0, NULL);
}
push[1] = ui_layers[i].address;
vkCmdPushConstants(command_buffer, ui_context->string_pipeline.layout, VK_SHADER_STAGE_COMPUTE_BIT, 0, 16, push);
vkCmdDispatchIndirect(command_buffer, ui_layers[i].layer, offsetof(UILayer, dispatch_strings));
}
// Render Pass
vkCmdBeginRenderPass(command_buffer, &render_pass_begin, VK_SUBPASS_CONTENTS_INLINE);
// World subpass
vkCmdNextSubpass(command_buffer, VK_SUBPASS_CONTENTS_INLINE);
// UI subpass
// Draw UI colored rects ////////////////////////////////
vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, ui_context->ui_pipeline_rect.pipeline);
vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, ui_context->ui_pipeline_rect.layout, 0, 1, &ui_context->ui_descriptor_set, 0, NULL);
vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, ui_context->rect_pipeline.layout, 0, 1, &ui_context->font_samplers, 0, NULL);
vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, ui_context->rect_pipeline.layout, 1, 1, &ui_context->font_textures, 0, NULL);
for(uint32_t i = 0; i < ui_layer_count; i++) {
if(ui_layers[i].colored_rect_count > 0) {
vkCmdPushConstants(command_buffer, ui_context->ui_pipeline_rect.layout, VK_SHADER_STAGE_VERTEX_BIT, 0, 8, &ui_layers[i].colored_rects);
vkCmdDraw(command_buffer, 6, ui_layers[i].colored_rect_count, 0, 0);
}
}
/////////////////////////////////////////////////////////
// Draw UI text /////////////////////////////////////////
vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, ui_context->ui_pipeline_text.pipeline);
vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, ui_context->ui_pipeline_text.layout, 0, 1, &ui_context->ui_descriptor_set, 0, NULL);
for(uint32_t i = 0; i < ui_layer_count; i++) {
if(ui_layers[i].string_count > 0) {
// Bind Font Descriptor
vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, ui_context->ui_pipeline_text.layout, 1, 1, &ui_layers[i].font.set, 0, NULL);
// Push pointers
vkCmdPushConstants(command_buffer, ui_context->ui_pipeline_text.layout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_COMPUTE_BIT, 0, 8, &ui_layers[i].string_pointers);
vkCmdDrawIndirect(command_buffer, ui_layers[i].string_draw, 0, 1, sizeof(DrawCommand));
}
push[1] = ui_layers[i].address;
vkCmdPushConstants(command_buffer, ui_context->rect_pipeline.layout, VK_SHADER_STAGE_VERTEX_BIT, 0, 16, push);
vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, ui_context->rect_pipeline.pipeline);
vkCmdDrawIndirect(command_buffer, ui_layers[i].layer, offsetof(UILayer, draw_rects), 1, 0);
vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, ui_context->char_pipeline.pipeline);
vkCmdDrawIndirect(command_buffer, ui_layers[i].layer, offsetof(UILayer, draw_chars), 1, 0);
}
/////////////////////////////////////////////////////////
vkCmdEndRenderPass(command_buffer);

@ -53,14 +53,33 @@ VkShaderModule load_shader_file(const char* path, VkDevice device) {
VkResult create_ui_pipeline(
VkDevice device,
VkRenderPass render_pass,
VkDescriptorSetLayout font_samplers_layout,
VkDescriptorSetLayout font_textures_layout,
VkPipelineShaderStageCreateInfo* shader_stages,
uint32_t shader_stage_count,
VkPipelineVertexInputStateCreateInfo input_info,
VkPipelineLayoutCreateInfo layout_info,
VkPipelineInputAssemblyStateCreateInfo input_assembly_info,
GraphicsPipeline* pipeline) {
VkResult result;
VkPushConstantRange push_constants[] = {
{
.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT,
.size = 16,
.offset = 0,
},
};
VkDescriptorSetLayout set_layouts[] = {font_samplers_layout, font_textures_layout};
VkPipelineLayoutCreateInfo layout_info = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.pushConstantRangeCount = sizeof(push_constants)/sizeof(VkPushConstantRange),
.pPushConstantRanges = push_constants,
.setLayoutCount = sizeof(set_layouts)/sizeof(VkDescriptorSetLayout),
.pSetLayouts = set_layouts,
};
result = vkCreatePipelineLayout(device, &layout_info, 0, &pipeline->layout);
if(result != VK_SUCCESS) {
return result;
@ -176,12 +195,12 @@ VkResult create_ui_pipeline(
return VK_SUCCESS;
}
VkResult create_ui_colored_rect_pipeline(VkDevice device, VkRenderPass render_pass, VkDescriptorSetLayout ui_descriptor_layout, GraphicsPipeline* pipeline) {
VkShaderModule vert_shader = load_shader_file("shader_src/ui_colored_rect.vert.spv", device);
VkResult create_ui_rect_pipeline(VkDevice device, VkRenderPass render_pass, VkDescriptorSetLayout font_samplers_layout, VkDescriptorSetLayout font_textures_layout, GraphicsPipeline* pipeline) {
VkShaderModule vert_shader = load_shader_file("shader_src/ui_rect.vert.spv", device);
if(vert_shader == VK_NULL_HANDLE) {
return VK_ERROR_UNKNOWN;
}
VkShaderModule frag_shader = load_shader_file("shader_src/ui_colored_rect.frag.spv", device);
VkShaderModule frag_shader = load_shader_file("shader_src/ui_rect.frag.spv", device);
if(frag_shader == VK_NULL_HANDLE) {
return VK_ERROR_UNKNOWN;
}
@ -210,26 +229,13 @@ VkResult create_ui_colored_rect_pipeline(VkDevice device, VkRenderPass render_pa
.vertexAttributeDescriptionCount = sizeof(attributes)/sizeof(VkVertexInputAttributeDescription),
};
VkPushConstantRange push_constant = {
.stageFlags = VK_SHADER_STAGE_VERTEX_BIT,
.size = 8,
};
VkPipelineLayoutCreateInfo layout_info = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.setLayoutCount = 1,
.pSetLayouts = &ui_descriptor_layout,
.pPushConstantRanges = &push_constant,
.pushConstantRangeCount = 1,
};
VkPipelineInputAssemblyStateCreateInfo input_assembly_info = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
.primitiveRestartEnable = VK_FALSE,
};
VkResult result = create_ui_pipeline(device, render_pass, shader_stages, sizeof(shader_stages)/sizeof(VkPipelineShaderStageCreateInfo), input_info, layout_info, input_assembly_info, pipeline);
VkResult result = create_ui_pipeline(device, render_pass, font_samplers_layout, font_textures_layout, shader_stages, sizeof(shader_stages)/sizeof(VkPipelineShaderStageCreateInfo), input_info, input_assembly_info, pipeline);
if(result != VK_SUCCESS) {
return result;
}
@ -237,7 +243,7 @@ VkResult create_ui_colored_rect_pipeline(VkDevice device, VkRenderPass render_pa
return VK_SUCCESS;
}
VkResult create_ui_text_pipeline(VkDevice device, VkRenderPass render_pass, VkDescriptorSetLayout ui_descriptor_layout, VkDescriptorSetLayout font_layout, GraphicsPipeline* pipeline, ComputePipeline* compute) {
VkResult create_ui_text_pipeline(VkDevice device, VkRenderPass render_pass, VkDescriptorSetLayout font_samplers_layout, VkDescriptorSetLayout font_textures_layout, GraphicsPipeline* pipeline, ComputePipeline* compute) {
VkResult result;
VkShaderModule compute_shader = load_shader_file("shader_src/ui_text.comp.spv", device);
if(compute_shader == VK_NULL_HANDLE) {
@ -252,18 +258,14 @@ VkResult create_ui_text_pipeline(VkDevice device, VkRenderPass render_pass, VkDe
};
VkPushConstantRange push_constant = {
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT | VK_SHADER_STAGE_VERTEX_BIT,
.size = 8,
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
.size = 16,
};
VkDescriptorSetLayout compute_descriptors[] = {font_layout};
VkPipelineLayoutCreateInfo compute_layout_info = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.pushConstantRangeCount = 1,
.pPushConstantRanges = &push_constant,
.setLayoutCount = sizeof(compute_descriptors)/sizeof(VkDescriptorSetLayout),
.pSetLayouts = compute_descriptors,
};
result = vkCreatePipelineLayout(device, &compute_layout_info, NULL, &compute->layout);
@ -320,23 +322,13 @@ VkResult create_ui_text_pipeline(VkDevice device, VkRenderPass render_pass, VkDe
.vertexAttributeDescriptionCount = sizeof(attributes)/sizeof(VkVertexInputAttributeDescription),
};
VkDescriptorSetLayout all_layouts[] = {ui_descriptor_layout, font_layout};
VkPipelineLayoutCreateInfo layout_info = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.setLayoutCount = sizeof(all_layouts)/sizeof(VkDescriptorSetLayout),
.pSetLayouts = all_layouts,
.pPushConstantRanges = &push_constant,
.pushConstantRangeCount = 1,
};
VkPipelineInputAssemblyStateCreateInfo input_assembly_info = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
.primitiveRestartEnable = VK_FALSE,
};
result = create_ui_pipeline(device, render_pass, shader_stages, sizeof(shader_stages)/sizeof(VkPipelineShaderStageCreateInfo), input_info, layout_info, input_assembly_info, pipeline);
result = create_ui_pipeline(device, render_pass, font_samplers_layout, font_textures_layout, shader_stages, sizeof(shader_stages)/sizeof(VkPipelineShaderStageCreateInfo), input_info, input_assembly_info, pipeline);
if(result != VK_SUCCESS) {
return result;
}
@ -344,219 +336,36 @@ VkResult create_ui_text_pipeline(VkDevice device, VkRenderPass render_pass, VkDe
return VK_SUCCESS;
}
VkResult create_font_descriptor_pools(VkDevice device, uint32_t max_sets, VkDescriptorPool* font_pool, VkDescriptorSetLayout* font_layout) {
VkResult result;
VkDescriptorPoolSize font_pool_sizes[] = {
{
.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.descriptorCount = 1,
},
{
.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.descriptorCount = 1,
},
};
VkDescriptorPoolCreateInfo font_pool_info = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
.pPoolSizes = font_pool_sizes,
.poolSizeCount = sizeof(font_pool_sizes)/sizeof(VkDescriptorPoolSize),
.maxSets = max_sets,
};
result = vkCreateDescriptorPool(device, &font_pool_info, NULL, font_pool);
if(result != VK_SUCCESS) {
return result;
}
VkDescriptorSetLayoutBinding font_descriptor_bindings[] = {
{
.binding = 0,
.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_COMPUTE_BIT,
},
{
.binding = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
},
};
VkDescriptorSetLayoutCreateInfo font_descriptor_info = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.pBindings = font_descriptor_bindings,
.bindingCount = sizeof(font_descriptor_bindings)/sizeof(VkDescriptorSetLayoutBinding),
};
result = vkCreateDescriptorSetLayout(device, &font_descriptor_info, NULL, font_layout);
if(result != VK_SUCCESS) {
return result;
}
return VK_SUCCESS;
}
VkResult create_text_pointers(
VkResult create_layer(
uint32_t max_strings,
uint32_t max_characters,
uint32_t max_rects,
VkDevice device,
VmaAllocator allocator,
VkCommandPool transfer_pool,
Queue transfer_queue,
TextPointersMemory* memory,
UILayerStorage* memory,
VkDeviceAddress* address) {
VkResult result;
VmaAllocationCreateInfo memory_info = {
.usage = VMA_MEMORY_USAGE_GPU_ONLY,
};
VkBufferCreateInfo pointers_info = {
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.size = sizeof(StringPointers),
.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
};
result = vmaCreateBuffer(allocator, &pointers_info, &memory_info, &memory->pointers_buffer, &memory->pointers_memory, NULL);
if(result != VK_SUCCESS) {
return result;
}
VkBufferCreateInfo draw_info = {
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.size = sizeof(DrawCommand),
.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
};
result = vmaCreateBuffer(allocator, &draw_info, &memory_info, &memory->draw_buffer, &memory->draw_memory, NULL);
if(result != VK_SUCCESS) {
return result;
}
VkBufferCreateInfo strings_info = {
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.size = max_strings*sizeof(String),
.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
};
result = vmaCreateBuffer(allocator, &strings_info, &memory_info, &memory->strings_buffer, &memory->strings_memory, NULL);
if(result != VK_SUCCESS) {
return result;
}
VkBufferCreateInfo codes_info = {
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.size = max_characters*sizeof(DrawCommand),
.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
};
result = vmaCreateBuffer(allocator, &codes_info, &memory_info, &memory->codes_buffer, &memory->codes_memory, NULL);
if(result != VK_SUCCESS) {
return result;
}
VkBufferCreateInfo characters_info = {
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.size = max_characters*sizeof(DrawCommand),
.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
};
result = vmaCreateBuffer(allocator, &characters_info, &memory_info, &memory->characters_buffer, &memory->characters_memory, NULL);
if(result != VK_SUCCESS) {
return result;
}
VkBufferDeviceAddressInfo pointers_address = {
.sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO,
.buffer = memory->pointers_buffer,
};
VkBufferDeviceAddressInfo strings_address = {
.sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO,
.buffer = memory->strings_buffer,
};
VkBufferDeviceAddressInfo codes_address = {
.sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO,
.buffer = memory->codes_buffer,
};
VkBufferDeviceAddressInfo characters_address = {
.sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO,
.buffer = memory->characters_buffer,
};
VkBufferDeviceAddressInfo draw_address = {
.sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO,
.buffer = memory->draw_buffer,
};
VkBuffer temp_buffer;
VmaAllocation temp_memory;
VkBufferCreateInfo temp_buffer_info = {
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.size = sizeof(StringPointers) +
sizeof(DrawCommand),
};
VmaAllocationCreateInfo temp_memory_info = {
.usage = VMA_MEMORY_USAGE_CPU_TO_GPU,
};
result = vmaCreateBuffer(allocator, &temp_buffer_info, &temp_memory_info, &temp_buffer, &temp_memory, NULL);
if(result != VK_SUCCESS) {
return result;
}
void* mapped;
result = vmaMapMemory(allocator, temp_memory, &mapped);
if(result != VK_SUCCESS) {
return result;
}
DrawCommand draw = {
.vertex_count = 6,
};
memcpy(mapped + 0, &draw, sizeof(DrawCommand));
StringPointers pointers = {
.strings = vkGetBufferDeviceAddress(device, &strings_address),
.codes = vkGetBufferDeviceAddress(device, &codes_address),
.characters = vkGetBufferDeviceAddress(device, &characters_address),
.draw = vkGetBufferDeviceAddress(device, &draw_address),
};
memcpy(mapped + sizeof(DrawCommand), &pointers, sizeof(StringPointers));
vmaUnmapMemory(allocator, temp_memory);
VkCommandBuffer command_buffer = command_begin_single(device, transfer_pool);
VkBufferCopy copy_draw = {
.size = sizeof(DrawCommand),
.srcOffset = 0,
};
vkCmdCopyBuffer(command_buffer, temp_buffer, memory->draw_buffer, 1, &copy_draw);
VkBufferCopy copy_pointers = {
.size = sizeof(StringPointers),
.srcOffset = sizeof(DrawCommand),
};
vkCmdCopyBuffer(command_buffer, temp_buffer, memory->pointers_buffer, 1, &copy_pointers);
result = command_end_single(device, command_buffer, transfer_pool, transfer_queue);
if(result != VK_SUCCESS) {
return result;
}
vmaDestroyBuffer(allocator, temp_buffer, temp_memory);
*address = vkGetBufferDeviceAddress(device, &pointers_address);
return VK_SUCCESS;
}
VkResult load_font(VkDevice device, VmaAllocator allocator, VkDescriptorSetLayout layout, VkDescriptorPool pool,VkCommandPool transfer_pool, Queue transfer_queue, FT_Library library, const char* ttf_file, uint32_t size, VkBool32 antialias, uint32_t** charmap, Font* descriptor) {
VkResult load_font(
VkDevice device,
VmaAllocator allocator,
VkBuffer font_infos,
VkDescriptorSet font_samplers,
VkDescriptorSet font_textures,
uint32_t index,
VkCommandPool transfer_pool,
Queue transfer_queue,
FT_Library library,
const char* ttf_file,
uint32_t size,
VkBool32 antialias,
uint32_t** charmap,
FontStorage* memory) {
FT_Face face;
int error;
@ -572,7 +381,7 @@ VkResult load_font(VkDevice device, VmaAllocator allocator, VkDescriptorSetLayou
uint32_t* tmp_charmap = malloc(sizeof(uint32_t)*face->num_glyphs);
SymbolInfo* symbols = malloc(sizeof(SymbolInfo)*face->num_glyphs);
FontUniform uniform;
Font info;
uint32_t glyph_index;
uint32_t max_height = 0;
@ -607,9 +416,9 @@ VkResult load_font(VkDevice device, VmaAllocator allocator, VkDescriptorSetLayou
c = FT_Get_Next_Char(face, c, &glyph_index);
}
uniform.width = max_width;
uniform.height = max_height;
uniform.num_symbols = symbol_count;
info.width = max_width;
info.height = max_height;
info.num_symbols = symbol_count;
uint32_t image_size = max_width*max_height*sizeof(uint32_t);
uint32_t* images = malloc(image_size*symbol_count);
memset(images, 0x00, image_size*symbol_count);
@ -636,53 +445,29 @@ VkResult load_font(VkDevice device, VmaAllocator allocator, VkDescriptorSetLayou
}
VkResult result;
VkDescriptorSetAllocateInfo set_allocate_info = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
.pSetLayouts = &layout,
.descriptorSetCount = 1,
.descriptorPool = pool,
};
result = vkAllocateDescriptorSets(device, &set_allocate_info, &descriptor->set);
if(result != VK_SUCCESS) {
return result;
}
VkBufferCreateInfo symbol_buffer_info = {
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.size = sizeof(SymbolInfo)*uniform.num_symbols,
.size = sizeof(SymbolInfo)*info.num_symbols,
};
VmaAllocationCreateInfo symbol_memory_info = {
.usage = VMA_MEMORY_USAGE_GPU_ONLY,
};
result = vmaCreateBuffer(allocator, &symbol_buffer_info, &symbol_memory_info, &descriptor->symbols, &descriptor->symbol_memory, NULL);
result = vmaCreateBuffer(allocator, &symbol_buffer_info, &symbol_memory_info, &memory->symbols, &memory->symbol_memory, NULL);
if(result != VK_SUCCESS) {
return result;
}
VkBufferCreateInfo uniform_buffer_info = {
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
.size = sizeof(FontUniform),
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
};
VmaAllocationCreateInfo uniform_memory_info = {
.usage = VMA_MEMORY_USAGE_GPU_ONLY,
};
result = vmaCreateBuffer(allocator, &uniform_buffer_info, &uniform_memory_info, &descriptor->uniform, &descriptor->uniform_memory, NULL);
VkImageCreateInfo image_info = {
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
.extent.depth = 1,
.extent.width = uniform.width,
.extent.height = uniform.height,
.extent.width = info.width,
.extent.height = info.height,
.mipLevels = 1,
.arrayLayers = uniform.num_symbols,
.arrayLayers = info.num_symbols,
.format = VK_FORMAT_R8G8B8A8_SRGB,
.tiling = VK_IMAGE_TILING_OPTIMAL,
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
@ -694,7 +479,7 @@ VkResult load_font(VkDevice device, VmaAllocator allocator, VkDescriptorSetLayou
.usage = VMA_MEMORY_USAGE_GPU_ONLY,
};
result = vmaCreateImage(allocator, &image_info, &image_memory_info, &descriptor->image, &descriptor->image_memory, NULL);
result = vmaCreateImage(allocator, &image_info, &image_memory_info, &memory->image, &memory->image_memory, NULL);
if(result != VK_SUCCESS) {
return result;
}
@ -703,7 +488,7 @@ VkResult load_font(VkDevice device, VmaAllocator allocator, VkDescriptorSetLayou
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.size = sizeof(FontUniform) + image_size*uniform.num_symbols + sizeof(SymbolInfo)*uniform.num_symbols,
.size = sizeof(Font) + image_size*info.num_symbols + sizeof(SymbolInfo)*info.num_symbols,
};
VmaAllocationCreateInfo staging_memory_info = {
@ -722,66 +507,66 @@ VkResult load_font(VkDevice device, VmaAllocator allocator, VkDescriptorSetLayou
if(result != VK_SUCCESS) {
return result;
}
memcpy(mapped_staging + image_size*uniform.num_symbols + sizeof(FontUniform), symbols, sizeof(SymbolInfo)*uniform.num_symbols);
memcpy(mapped_staging + image_size*info.num_symbols + sizeof(Font), symbols, sizeof(SymbolInfo)*info.num_symbols);
VkBufferDeviceAddressInfo address_info = {
.sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO,
.buffer = descriptor->symbols,
.buffer = memory->symbols,
};
uniform.symbol_list = vkGetBufferDeviceAddress(device, &address_info);
memcpy(mapped_staging + image_size*uniform.num_symbols, &uniform, sizeof(FontUniform));
memcpy(mapped_staging, images, image_size*uniform.num_symbols);
info.symbol_list = vkGetBufferDeviceAddress(device, &address_info);
memcpy(mapped_staging + image_size*info.num_symbols, &info, sizeof(Font));
memcpy(mapped_staging, images, image_size*info.num_symbols);
vmaUnmapMemory(allocator, staging_memory);
free(images);
free(symbols);
VkCommandBuffer command_buffer = command_begin_single(device, transfer_pool);
VkBufferCopy uniform_copy_info = {
.size = sizeof(FontUniform),
.srcOffset = image_size*uniform.num_symbols,
.dstOffset = 0,
VkBufferCopy info_copy = {
.size = sizeof(Font),
.srcOffset = image_size*info.num_symbols,
.dstOffset = index*sizeof(Font),
};
vkCmdCopyBuffer(command_buffer, staging_buffer, descriptor->uniform, 1, &uniform_copy_info);
vkCmdCopyBuffer(command_buffer, staging_buffer, font_infos, 1, &info_copy);
VkBufferCopy symbol_copy_info = {
.size = sizeof(SymbolInfo)*uniform.num_symbols,
.srcOffset = image_size*uniform.num_symbols + sizeof(FontUniform),
VkBufferCopy symbol_copy = {
.size = sizeof(SymbolInfo)*info.num_symbols,
.srcOffset = image_size*info.num_symbols + sizeof(Font),
.dstOffset = 0,
};
vkCmdCopyBuffer(command_buffer, staging_buffer, descriptor->symbols, 1, &symbol_copy_info);
vkCmdCopyBuffer(command_buffer, staging_buffer, memory->symbols, 1, &symbol_copy);
VkImageMemoryBarrier first_barrier = {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.image = descriptor->image,
.image = memory->image,
.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.subresourceRange.levelCount = 1,
.subresourceRange.layerCount = uniform.num_symbols,
.subresourceRange.layerCount = info.num_symbols,
.srcAccessMask = 0,
.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
};
vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, NULL, 0, NULL, 1, &first_barrier);
VkBufferImageCopy image_copy = {
.imageSubresource.layerCount = uniform.num_symbols,
.imageSubresource.layerCount = info.num_symbols,
.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.imageExtent = image_info.extent,
};
vkCmdCopyBufferToImage(command_buffer, staging_buffer, descriptor->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &image_copy);
vkCmdCopyBufferToImage(command_buffer, staging_buffer, memory->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &image_copy);
VkImageMemoryBarrier second_barrier = {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.image = descriptor->image,
.image = memory->image,
.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.subresourceRange.levelCount = 1,
.subresourceRange.layerCount = uniform.num_symbols,
.subresourceRange.layerCount = info.num_symbols,
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
};
@ -795,18 +580,18 @@ VkResult load_font(VkDevice device, VmaAllocator allocator, VkDescriptorSetLayou
VkImageViewCreateInfo view_info = {
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.image = descriptor->image,
.image = memory->image,
.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY,
.format = VK_FORMAT_R8G8B8A8_SRGB,
.subresourceRange = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.layerCount = uniform.num_symbols,
.layerCount = info.num_symbols,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
},
};
result = vkCreateImageView(device, &view_info, NULL, &descriptor->view);
result = vkCreateImageView(device, &view_info, NULL, &memory->view);
if(result != VK_SUCCESS) {
return result;
}
@ -819,171 +604,199 @@ VkResult load_font(VkDevice device, VmaAllocator allocator, VkDescriptorSetLayou
.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT,
.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT,
};
result = vkCreateSampler(device, &sampler_info, NULL, &descriptor->sampler);
result = vkCreateSampler(device, &sampler_info, NULL, &memory->sampler);
if(result != VK_SUCCESS) {
return result;
}
VkDescriptorBufferInfo desc_uniform_info = {
.offset = 0,
.range = sizeof(FontUniform),
.buffer = descriptor->uniform,
VkDescriptorImageInfo desc_sampler_info = {
.sampler = memory->sampler,
};
VkDescriptorImageInfo desc_image_info = {
.sampler = descriptor->sampler,
VkDescriptorImageInfo desc_texture_info = {
.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
.imageView = descriptor->view,
.imageView = memory->view,
};
VkWriteDescriptorSet descriptor_writes[] = {
{
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = descriptor->set,
.dstSet = font_textures,
.dstBinding = 0,
.dstArrayElement = 0,
.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.dstArrayElement = index,
.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,
.descriptorCount = 1,
.pBufferInfo = &desc_uniform_info,
.pImageInfo = &desc_texture_info,
},
{
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = descriptor->set,
.dstBinding = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.dstSet = font_samplers,
.dstBinding = 0,
.dstArrayElement = index,
.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER,
.descriptorCount = 1,
.pImageInfo = &desc_image_info,
}
.pImageInfo = &desc_sampler_info,
},
};
vkUpdateDescriptorSets(device, 2, descriptor_writes, 0, NULL);
vkUpdateDescriptorSets(device, sizeof(descriptor_writes)/sizeof(VkWriteDescriptorSet), descriptor_writes, 0, NULL);
return VK_SUCCESS;
}
VkResult create_ui_descriptor_set(VkDevice device, VmaAllocator allocator, VkExtent2D swapchain_extent, vec2 window_scale, VkDescriptorSetLayout* ui_descriptor_layout, VkDescriptorPool* ui_descriptor_pool, VkDescriptorSet* ui_descriptor_set, VmaAllocation* ui_descriptor_memory, VkBuffer* ui_descriptor_buffer) {
VkDescriptorSetLayoutBinding ui_descriptor_bindings[] = {
{
.binding = 0,
.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_VERTEX_BIT,
}
VkResult create_ui_descriptor(VkDevice device, VmaAllocator allocator, uint32_t max_fonts, VkBuffer* font_infos, VmaAllocation* font_infos_memory, VkDescriptorSet* font_samplers, VkDescriptorSet* font_textures, VkDescriptorSetLayout* font_sampler_layout, VkDescriptorSetLayout* font_texture_layout, VkDescriptorPool* font_pool) {
VkBufferCreateInfo font_infos_buffer = {
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.size = sizeof(Font)*max_fonts,
.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
};
VkDescriptorSetLayoutCreateInfo ui_descriptor_info = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.pBindings = ui_descriptor_bindings,
.bindingCount = sizeof(ui_descriptor_bindings)/sizeof(VkDescriptorSetLayoutBinding),
VmaAllocationCreateInfo font_infos_memory_info = {
.usage = VMA_MEMORY_USAGE_GPU_ONLY,
};
VkResult result;
result = vkCreateDescriptorSetLayout(device, &ui_descriptor_info, NULL, ui_descriptor_layout);
result = vmaCreateBuffer(allocator, &font_infos_buffer, &font_infos_memory_info, font_infos, font_infos_memory, NULL);
if(result != VK_SUCCESS) {
return result;
}
VkDescriptorPoolSize pool_size = {
.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.descriptorCount = 1,
VkDescriptorSetLayoutBinding font_sampler_bindings[] = {
{
.binding = 0,
.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER,
.descriptorCount = max_fonts,
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
},
};
VkDescriptorPoolCreateInfo ui_pool_info = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
.pPoolSizes = &pool_size,
.poolSizeCount = 1,
.maxSets = 1,
VkDescriptorBindingFlags bindless_flags[] = {
VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT | VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT | VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT,
};
result = vkCreateDescriptorPool(device, &ui_pool_info, NULL, ui_descriptor_pool);
if(result != VK_SUCCESS) {
return result;
}
VkDescriptorSetLayoutBindingFlagsCreateInfo font_sampler_bindings_info = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO,
.bindingCount = sizeof(font_sampler_bindings)/sizeof(VkDescriptorSetLayoutBinding),
.pBindingFlags = bindless_flags,
};
VkDescriptorSetAllocateInfo ui_descriptor_allocate_info = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
.pSetLayouts = ui_descriptor_layout,
.descriptorSetCount = 1,
.descriptorPool = *ui_descriptor_pool,
VkDescriptorSetLayoutCreateInfo font_sampler_info = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.pBindings = font_sampler_bindings,
.bindingCount = sizeof(font_sampler_bindings)/sizeof(VkDescriptorSetLayoutBinding),
.flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT,
.pNext = &font_sampler_bindings_info,
};
result = vkAllocateDescriptorSets(device, &ui_descriptor_allocate_info, ui_descriptor_set);
result = vkCreateDescriptorSetLayout(device, &font_sampler_info, NULL, font_sampler_layout);
if(result != VK_SUCCESS) {
return result;
}
VkBufferCreateInfo ui_uniform_buffer_info = {
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
.size = sizeof(UIUniform),
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
VkDescriptorSetLayoutBinding font_texture_bindings[] = {
{
.binding = 0,
.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,
.descriptorCount = max_fonts,
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
},
};
VmaAllocationCreateInfo ui_uniform_memory_info = {
.usage = VMA_MEMORY_USAGE_CPU_TO_GPU,
VkDescriptorSetLayoutBindingFlagsCreateInfo font_texture_bindings_info = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO,
.bindingCount = sizeof(font_texture_bindings)/sizeof(VkDescriptorSetLayoutBinding),
.pBindingFlags = bindless_flags,
};
result = vmaCreateBuffer(allocator, &ui_uniform_buffer_info, &ui_uniform_memory_info, ui_descriptor_buffer, ui_descriptor_memory, NULL);
VkDescriptorSetLayoutCreateInfo font_texture_info = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.pBindings = font_texture_bindings,
.bindingCount = sizeof(font_texture_bindings)/sizeof(VkDescriptorSetLayoutBinding),
.flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT,
.pNext = &font_texture_bindings_info,
};
result = vkCreateDescriptorSetLayout(device, &font_texture_info, NULL, font_texture_layout);
if(result != VK_SUCCESS) {
return result;
}
void* mapped;
result = vmaMapMemory(allocator, *ui_descriptor_memory, &mapped);
VkDescriptorPoolSize font_pool_sizes[] = {
{
.type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,
.descriptorCount = max_fonts,
},
{
.type = VK_DESCRIPTOR_TYPE_SAMPLER,
.descriptorCount = max_fonts,
},
};
VkDescriptorPoolCreateInfo font_pool_info = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
.pPoolSizes = font_pool_sizes,
.poolSizeCount = 2,
.maxSets = 2,
.flags = VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT,
};
result = vkCreateDescriptorPool(device, &font_pool_info, NULL, font_pool);
if(result != VK_SUCCESS) {
return result;
}
UIUniform ui_uniform;
vec3 screen_offset = {-1.0, -1.0, 0.0};
vec3 screen_scale = {1.0/(float)swapchain_extent.width*window_scale[0], 1.0/(float)swapchain_extent.height*window_scale[1], 1.0};
glm_mat4_identity(ui_uniform.screen);
glm_translate(ui_uniform.screen, screen_offset);
glm_scale(ui_uniform.screen, screen_scale);
memcpy(mapped, &ui_uniform, sizeof(ui_uniform));
vmaUnmapMemory(allocator, *ui_descriptor_memory);
VkDescriptorBufferInfo ui_uniform_info = {
.offset = 0,
.range = sizeof(ui_uniform),
.buffer = *ui_descriptor_buffer,
uint32_t max_font_binding = max_fonts - 1;
VkDescriptorSetVariableDescriptorCountAllocateInfo count_info = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_ALLOCATE_INFO,
.descriptorSetCount = 1,
.pDescriptorCounts = &max_font_binding,
};
VkWriteDescriptorSet ui_uniform_write = {
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = *ui_descriptor_set,
.dstBinding = 0,
.dstArrayElement = 0,
.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.descriptorCount = 1,
.pBufferInfo = &ui_uniform_info,
VkDescriptorSetAllocateInfo allocate_font_samplers = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
.pSetLayouts = font_sampler_layout,
.descriptorSetCount = 1,
.descriptorPool = *font_pool,
.pNext = &count_info,
};
vkUpdateDescriptorSets(device, 1, &ui_uniform_write, 0, NULL);
return VK_SUCCESS;
}
VkResult init_pipelines(VkDevice device, VmaAllocator allocator, VkExtent2D swapchain_extent, vec2 window_scale, VkRenderPass render_pass, UIContext* context) {
VkDescriptorSetAllocateInfo allocate_font_textures = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
.pSetLayouts = font_texture_layout,
.descriptorSetCount = 1,
.descriptorPool = *font_pool,
.pNext = &count_info,
};
VkResult result;
result = create_ui_descriptor_set(device, allocator, swapchain_extent, window_scale, &context->ui_descriptor_layout, &context->ui_descriptor_pool, &context->ui_descriptor_set, &context->ui_descriptor_memory, &context->ui_descriptor_buffer);
result = vkAllocateDescriptorSets(device, &allocate_font_samplers, font_samplers);
if(result != VK_SUCCESS) {
return result;
}
result = create_ui_colored_rect_pipeline(device, render_pass, context->ui_descriptor_layout, &context->ui_pipeline_rect);
result = vkAllocateDescriptorSets(device, &allocate_font_textures, font_textures);
if(result != VK_SUCCESS) {
return result;
}
result = create_font_descriptor_pools(device, 10, &context->font_pool, &context->font_layout);
return VK_SUCCESS;
}
VkResult create_ui_context(VkDevice device, VmaAllocator allocator, VkRenderPass render_pass, UIContextStorage* memory) {
VkResult result;
result = create_ui_descriptor(device, allocator, 10, &memory->font_infos, &memory->font_infos_memory, &memory->font_samplers, &memory->font_textures, &memory->font_samplers_layout, &memory->font_textures_layout, &memory->fonts_pool);
if(result != VK_SUCCESS) {
return result;
}
result = create_ui_text_pipeline(device, render_pass, context->ui_descriptor_layout, context->font_layout, &context->ui_pipeline_text, &context->ui_compute_text);
result = create_ui_rect_pipeline(device, render_pass, memory->font_samplers_layout, memory->font_textures_layout, &memory->rect_pipeline);
if(result != VK_SUCCESS) {
return result;
}
result = create_ui_text_pipeline(device, render_pass, memory->font_samplers_layout, memory->font_textures_layout, &memory->char_pipeline, &memory->string_pipeline);
if(result != VK_SUCCESS) {
return result;
}
return VK_SUCCESS;
}