From cd66dbfc9767d1b9765f4e24bfce158a8c3c7c90 Mon Sep 17 00:00:00 2001 From: Bill Currie Date: Fri, 2 Sep 2022 14:23:19 +0900 Subject: [PATCH] [vulkan] Implement font rendering It is currently an ugly hack for dealing with the separate quad queue, and the pipeline handling code needs a lot of cleanup, but it works quite well, though I do plan on moving to HarfBuzz for text shaping. One nice development is I got updating of descriptor sets working (just need to ensure the set is no longer in use by the command queue, which the multiple frames in flight makes easy). --- libs/video/renderer/Makemodule.am | 5 + libs/video/renderer/vulkan/qfpipeline.plist | 17 ++- libs/video/renderer/vulkan/shader.c | 3 + .../renderer/vulkan/shader/twod_impalpha.frag | 17 +++ libs/video/renderer/vulkan/vulkan_draw.c | 140 ++++++++++++++++-- 5 files changed, 167 insertions(+), 15 deletions(-) create mode 100644 libs/video/renderer/vulkan/shader/twod_impalpha.frag diff --git a/libs/video/renderer/Makemodule.am b/libs/video/renderer/Makemodule.am index 49e229b1a..0b525df97 100644 --- a/libs/video/renderer/Makemodule.am +++ b/libs/video/renderer/Makemodule.am @@ -270,6 +270,8 @@ sprite_depthv_src = $(vkshaderpath)/sprite_depth.vert sprite_depthv_c = $(vkshaderpath)/sprite_depth.vert.spvc sprite_depthf_src = $(vkshaderpath)/sprite_depth.frag sprite_depthf_c = $(vkshaderpath)/sprite_depth.frag.spvc +twod_impalpha_src = $(vkshaderpath)/twod_impalpha.frag +twod_impalpha_c = $(vkshaderpath)/twod_impalpha.frag.spvc twodv_src = $(vkshaderpath)/twod.vert twodv_c = $(vkshaderpath)/twod.vert.spvc twodf_src = $(vkshaderpath)/twod.frag @@ -334,6 +336,8 @@ $(sprite_depthv_c): $(sprite_depthv_src) $(sprite_depthf_c): $(sprite_depthf_src) +$(twod_impalpha_c): $(twod_impalpha_src) + $(twodv_c): $(twodv_src) $(twodf_c): $(twodf_src) @@ -392,6 +396,7 @@ vkshader_c = \ $(sprite_gbuff_c) \ $(sprite_depthv_c) \ $(sprite_depthf_c) \ + $(twod_impalpha_c) \ $(twodv_c) \ $(twodf_c) \ $(quakebspv_c) \ diff --git a/libs/video/renderer/vulkan/qfpipeline.plist b/libs/video/renderer/vulkan/qfpipeline.plist index 9c78e01cb..287b48b2e 100644 --- a/libs/video/renderer/vulkan/qfpipeline.plist +++ b/libs/video/renderer/vulkan/qfpipeline.plist @@ -89,7 +89,7 @@ }; twod_pool = { flags = 0; - maxSets = $frames.size; + maxSets = "$frames.size * 2z"; bindings = ( { type = combined_image_sampler; @@ -1049,6 +1049,21 @@ }; layout = twod_layout; }; + twod_impalpha = { + @inherit = $properties.pipelines.twod; + stages = ( + { + stage = vertex; + name = main; + module = $builtin/twod.vert; + }, + { + stage = fragment; + name = main; + module = $builtin/twod_impalpha.frag; + }, + ); + }; lines = { @inherit = $properties.pipelines.twod; inputAssembly = $properties.inputAssembly.lines; diff --git a/libs/video/renderer/vulkan/shader.c b/libs/video/renderer/vulkan/shader.c index 0f7e19a7b..cfb62a7a8 100644 --- a/libs/video/renderer/vulkan/shader.c +++ b/libs/video/renderer/vulkan/shader.c @@ -57,6 +57,8 @@ static static #include "libs/video/renderer/vulkan/shader/sprite_depth.frag.spvc" static +#include "libs/video/renderer/vulkan/shader/twod_impalpha.frag.spvc" +static #include "libs/video/renderer/vulkan/shader/twod.vert.spvc" static #include "libs/video/renderer/vulkan/shader/twod.frag.spvc" @@ -121,6 +123,7 @@ static shaderdata_t builtin_shaders[] = { { "sprite_gbuf.frag", sprite_gbuf_frag, sizeof (sprite_gbuf_frag) }, { "sprite_depth.vert", sprite_depth_vert, sizeof (sprite_depth_vert) }, { "sprite_depth.frag", sprite_depth_frag, sizeof (sprite_depth_frag) }, + { "twod_impalpha.frag", twod_impalpha_frag, sizeof (twod_impalpha_frag) }, { "twod.vert", twod_vert, sizeof (twod_vert) }, { "twod.frag", twod_frag, sizeof (twod_frag) }, { "quakebsp.vert", quakebsp_vert, sizeof (quakebsp_vert) }, diff --git a/libs/video/renderer/vulkan/shader/twod_impalpha.frag b/libs/video/renderer/vulkan/shader/twod_impalpha.frag new file mode 100644 index 000000000..5f3c50865 --- /dev/null +++ b/libs/video/renderer/vulkan/shader/twod_impalpha.frag @@ -0,0 +1,17 @@ +#version 450 + +layout (set = 1, binding = 0) uniform sampler2D Texture; + +layout (location = 0) in vec2 st; +layout (location = 1) in vec4 color; + +layout (location = 0) out vec4 frag_color; + +void +main (void) +{ + vec4 pix; + + pix = texture(Texture, st).rrrr; + frag_color = pix * color; +} diff --git a/libs/video/renderer/vulkan/vulkan_draw.c b/libs/video/renderer/vulkan/vulkan_draw.c index 6b78a9b5d..da7f528e5 100644 --- a/libs/video/renderer/vulkan/vulkan_draw.c +++ b/libs/video/renderer/vulkan/vulkan_draw.c @@ -84,13 +84,17 @@ typedef struct cachepic_s { typedef struct drawframe_s { size_t vert_offset; + size_t iavert_offset; size_t line_offset; drawvert_t *quad_verts; + drawvert_t *iaquad_verts; drawvert_t *line_verts; uint32_t num_quads; + uint32_t num_iaquads; uint32_t num_lines; VkCommandBuffer cmd; VkDescriptorSet descriptors; + VkDescriptorSet iadescriptors; } drawframe_t; typedef struct drawframeset_s @@ -117,6 +121,7 @@ typedef struct drawctx_s { VkBuffer ind_buffer; VkDeviceMemory ind_memory; VkPipeline quad_pipeline; + VkPipeline iaquad_pipeline; VkPipeline line_pipeline; VkPipelineLayout layout; drawframeset_t frames; @@ -130,7 +135,11 @@ typedef struct drawctx_s { #define MAX_LINES (32768) #define VERTS_PER_LINE (2) -#define VERTS_PER_FRAME (MAX_LINES*VERTS_PER_LINE + MAX_QUADS*VERTS_PER_QUAD) +#define QUADS_OFFSET 0 +#define IAQUADS_OFFSET (MAX_QUADS * VERTS_PER_QUAD) +#define LINES_OFFSET (IAQUADS_OFFSET + (MAX_QUADS * VERTS_PER_QUAD)) + +#define VERTS_PER_FRAME (LINES_OFFSET + MAX_LINES*VERTS_PER_LINE) static void create_quad_buffers (vulkan_ctx_t *ctx) @@ -175,11 +184,14 @@ create_quad_buffers (vulkan_ctx_t *ctx) for (size_t f = 0; f < frames; f++) { drawframe_t *frame = &dctx->frames.a[f]; size_t ind = f * VERTS_PER_FRAME; - size_t lind = ind + MAX_QUADS * VERTS_PER_QUAD; + size_t iaind = ind + IAQUADS_OFFSET; + size_t lind = ind + LINES_OFFSET; frame->vert_offset = ind * sizeof (drawvert_t); + frame->iavert_offset = iaind; frame->line_offset = lind; frame->quad_verts = vert_data + ind; - frame->line_verts = frame->quad_verts + MAX_QUADS * VERTS_PER_QUAD; + frame->iaquad_verts = frame->quad_verts + IAQUADS_OFFSET; + frame->line_verts = frame->quad_verts + LINES_OFFSET; frame->num_quads = 0; frame->num_lines = 0; } @@ -367,6 +379,7 @@ Vulkan_Draw_Shutdown (vulkan_ctx_t *ctx) destroy_quad_buffers (ctx); dfunc->vkDestroyPipeline (device->dev, dctx->quad_pipeline, 0); + dfunc->vkDestroyPipeline (device->dev, dctx->iaquad_pipeline, 0); dfunc->vkDestroyPipeline (device->dev, dctx->line_pipeline, 0); Hash_DelTable (dctx->pic_cache); delete_memsuper (dctx->pic_memsuper); @@ -433,6 +446,8 @@ Vulkan_Draw_Init (vulkan_ctx_t *ctx) flush_draw_scrap (ctx); dctx->quad_pipeline = Vulkan_CreateGraphicsPipeline (ctx, "twod"); + dctx->iaquad_pipeline = Vulkan_CreateGraphicsPipeline (ctx, + "twod_impalpha"); dctx->line_pipeline = Vulkan_CreateGraphicsPipeline (ctx, "lines"); dctx->layout = Vulkan_CreatePipelineLayout (ctx, "twod_layout"); @@ -448,21 +463,32 @@ Vulkan_Draw_Init (vulkan_ctx_t *ctx) QFV_ScrapImageView (dctx->scrap), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, }; + VkDescriptorImageInfo iaimageInfo = { + dctx->sampler, + Vulkan_TexImageView (ctx->default_magenta), + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + }; __auto_type cmdBuffers = QFV_AllocCommandBufferSet (frames, alloca); QFV_AllocateCommandBuffers (device, ctx->cmdpool, 1, cmdBuffers); __auto_type sets = QFV_AllocateDescriptorSet (device, pool, layouts); + __auto_type iasets = QFV_AllocateDescriptorSet (device, pool, layouts); for (size_t i = 0; i < frames; i++) { __auto_type dframe = &dctx->frames.a[i]; dframe->descriptors = sets->a[i]; + dframe->iadescriptors = iasets->a[i]; VkWriteDescriptorSet write[] = { { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, 0, dframe->descriptors, 0, 0, 1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, &imageInfo, 0, 0 }, + { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, 0, + dframe->iadescriptors, 0, 0, 1, + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + &iaimageInfo, 0, 0 }, }; - dfunc->vkUpdateDescriptorSets (device->dev, 1, write, 0, 0); + dfunc->vkUpdateDescriptorSets (device->dev, 2, write, 0, 0); dframe->cmd = cmdBuffers->a[i]; QFV_duSetObjectName (device, VK_OBJECT_TYPE_COMMAND_BUFFER, dframe->cmd, @@ -868,7 +894,7 @@ Vulkan_FlushText (qfv_renderframe_t *rFrame) drawctx_t *dctx = ctx->draw_context; drawframe_t *dframe = &dctx->frames.a[ctx->curFrame]; - if (!dframe->num_quads && !dframe->num_lines) { + if (!dframe->num_quads && !dframe->num_iaquads && !dframe->num_lines) { return; } @@ -880,6 +906,9 @@ Vulkan_FlushText (qfv_renderframe_t *rFrame) { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, 0, dctx->vert_memory, dframe->vert_offset, dframe->num_quads * VERTS_PER_QUAD * sizeof (drawvert_t) }, + { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, 0, + dctx->vert_memory, dframe->iavert_offset, + dframe->num_iaquads * VERTS_PER_QUAD * sizeof (drawvert_t) }, { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, 0, dctx->vert_memory, dframe->line_offset, dframe->num_lines * VERTS_PER_LINE * sizeof (drawvert_t) }, @@ -906,33 +935,72 @@ Vulkan_FlushText (qfv_renderframe_t *rFrame) dfunc->vkCmdBindVertexBuffers (cmd, 0, 1, &dctx->vert_buffer, offsets); dfunc->vkCmdBindIndexBuffer (cmd, dctx->ind_buffer, 0, VK_INDEX_TYPE_UINT32); - VkDescriptorSet set[2] = { - Vulkan_Matrix_Descriptors (ctx, ctx->curFrame), - dframe->descriptors, - }; - VkPipelineLayout layout = dctx->layout; - dfunc->vkCmdBindDescriptorSets (cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, - layout, 0, 2, set, 0, 0); if (dframe->num_quads) { + VkDescriptorSet set[2] = { + Vulkan_Matrix_Descriptors (ctx, ctx->curFrame), + dframe->descriptors, + }; + VkPipelineLayout layout = dctx->layout; + dfunc->vkCmdBindDescriptorSets (cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, + layout, 0, 2, set, 0, 0); + dfunc->vkCmdBindPipeline (cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, dctx->quad_pipeline); dfunc->vkCmdSetViewport (cmd, 0, 1, &rFrame->renderpass->viewport); dfunc->vkCmdSetScissor (cmd, 0, 1, &rFrame->renderpass->scissor); dfunc->vkCmdDrawIndexed (cmd, dframe->num_quads * INDS_PER_QUAD, - 1, 0, 0, 0); + 1, 0, QUADS_OFFSET, 0); + } + if (dframe->num_iaquads) { + VkDescriptorImageInfo iaimageInfo = { + dctx->sampler, + Vulkan_TexImageView (dctx->font_tex), + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + }; + VkWriteDescriptorSet write[] = { + { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, 0, + dframe->iadescriptors, 0, 0, 1, + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + &iaimageInfo, 0, 0 }, + }; + dfunc->vkUpdateDescriptorSets (device->dev, 1, write, 0, 0); + + VkDescriptorSet set[2] = { + Vulkan_Matrix_Descriptors (ctx, ctx->curFrame), + dframe->iadescriptors, + }; + VkPipelineLayout layout = dctx->layout; + dfunc->vkCmdBindDescriptorSets (cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, + layout, 0, 2, set, 0, 0); + + dfunc->vkCmdBindPipeline (cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, + dctx->iaquad_pipeline); + dfunc->vkCmdSetViewport (cmd, 0, 1, &rFrame->renderpass->viewport); + dfunc->vkCmdSetScissor (cmd, 0, 1, &rFrame->renderpass->scissor); + dfunc->vkCmdDrawIndexed (cmd, dframe->num_iaquads * INDS_PER_QUAD, + 1, 0, IAQUADS_OFFSET, 0); } if (dframe->num_lines) { + VkDescriptorSet set[2] = { + Vulkan_Matrix_Descriptors (ctx, ctx->curFrame), + dframe->descriptors, + }; + VkPipelineLayout layout = dctx->layout; + dfunc->vkCmdBindDescriptorSets (cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, + layout, 0, 2, set, 0, 0); + dfunc->vkCmdBindPipeline (cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, dctx->line_pipeline); dfunc->vkCmdDraw (cmd, dframe->num_lines * VERTS_PER_LINE, - 1, MAX_QUADS * VERTS_PER_QUAD, 0); + 1, LINES_OFFSET, 0); } QFV_duCmdEndLabel (device, cmd); dfunc->vkEndCommandBuffer (cmd); dframe->num_quads = 0; + dframe->num_iaquads = 0; dframe->num_lines = 0; } @@ -976,4 +1044,48 @@ Vulkan_Draw_AddFont (rfont_t *font, vulkan_ctx_t *ctx) void Vulkan_Draw_FontString (int x, int y, const char *str, vulkan_ctx_t *ctx) { + drawctx_t *dctx = ctx->draw_context; + if (!dctx->font) { + return; + } + //FIXME ewwwwwww + drawframe_t *dframe = &dctx->frames.a[ctx->curFrame]; + uint32_t num_quads = dframe->num_quads; + drawvert_t *quad_verts = dframe->quad_verts; + dframe->num_quads = dframe->num_iaquads; + dframe->quad_verts = dframe->iaquad_verts; + subpic_t glyph_subpic = { + .width = dctx->font->scrap.width, + .height = dctx->font->scrap.height, + .size = 1.0 / dctx->font->scrap.width, + }; + struct { + qpic_t pic; + subpic_t *subpic; + } glyph_pic = { + .pic = { + .width = 1, + .height = 1, + }, + .subpic = &glyph_subpic, + }; + quat_t color = { 0.5, 1, 0.6, 1 }; + for (const char *c = str; *c; c++) { + rglyph_t search = { .charcode = *c }; + rglyph_t *glyph = Hash_FindElement (dctx->font->glyphmap, &search); + if (!glyph) { + continue; + } + *((vrect_t **) &glyph_subpic.rect) = glyph->rect; + int gw = glyph->rect->width; + int gh = glyph->rect->height; + vec2i_t pos = (vec2i_t) { x, y } + glyph->bearing * (vec2i_t) { 1, -1 }; + draw_pic (pos[0], pos[1], gw, gh, &glyph_pic.pic, + 0, 0, gw, gh, color, dframe); + x += glyph->advance / 64; + } + dframe->num_iaquads = dframe->num_quads; + dframe->iaquad_verts = dframe->quad_verts; + dframe->num_quads = num_quads; + dframe->quad_verts = quad_verts; }