[vulkan] Add support for 9-slice rendering

There's no API yet as I need to look into the handling of qpic_t before
I can get any of this into the other renderers (or even vulkan, for that
matter).

However, the current design for slice rendering is based on glyphs (ie,
using instances and vertex pulling), with 3 strips of 3 quads, 16 verts,
and 26 indices (2 reset). Hacky testing seems to work, but real tests
need the API.
This commit is contained in:
Bill Currie 2022-11-18 09:44:01 +09:00
parent b1d7bad2e3
commit bffe9413b7
5 changed files with 221 additions and 32 deletions

View file

@ -275,6 +275,8 @@ vkparse_plist = \
vkshaderpath = libs/video/renderer/vulkan/shader
slice_src = $(vkshaderpath)/slice.vert
slice_c = $(vkshaderpath)/slice.vert.spvc
glyph_src = $(vkshaderpath)/glyph.vert
glyph_c = $(vkshaderpath)/glyph.vert.spvc
glyph_color_src = $(vkshaderpath)/glyph_color.frag
@ -351,6 +353,8 @@ fstriangle_c = $(vkshaderpath)/fstriangle.vert.spvc
pushcolor_src = $(vkshaderpath)/pushcolor.frag
pushcolor_c = $(vkshaderpath)/pushcolor.frag.spvc
$(slice_vert_c): $(slice_vert_src)
$(glyph_vert_c): $(glyph_vert_src)
$(glyph_color_c): $(glyph_color_src)
$(glyph_coverage_c): $(glyph_coverage_src)
@ -419,6 +423,7 @@ $(fstriangle_c): $(fstriangle_src)
$(pushcolor_c): $(pushcolor_src)
vkshader_c = \
$(slice_c) \
$(glyph_c) \
$(glyph_color_c) \
$(glyph_coverage_c) \

View file

@ -582,6 +582,21 @@
{ location = 2; binding = 1; format = r32_uint; offset = 0; },
);
};
slice = {
bindings = (
{ binding = 0; stride = "4 + 4 + 4 * 4"; inputRate = instance; },
);
attributes = (
// 9-slice index
{ location = 0; binding = 0; format = r32_uint; offset = 0; },
// 9-slice color
{ location = 1; binding = 0; format = r8g8b8a8_unorm; offset = 4; },
// 9-slice position (2d)
{ location = 2; binding = 0; format = r32g32_sfloat; offset = 8; },
// 9-slice size delta (2d)
{ location = 3; binding = 0; format = r32g32_sfloat; offset = 16; },
);
};
glyph = {
bindings = (
{ binding = 0; stride = "4 + 4 + 2 * 4"; inputRate = instance; },
@ -1115,6 +1130,25 @@
};
layout = twod_layout;
};
slice = {
@inherit = $properties.pipelines.trans_base;//FIXME should be sparate
stages = (
{ stage = vertex; name = main; module = $builtin/slice.vert; },
{ stage = fragment; name = main; module = $builtin/glyph_color.frag; },
);
vertexInput = $properties.vertexInput.slice;
inputAssembly = {
// slices are drawn using instanced slice triples
topology = triangle_strip;
primitiveRestartEnable = true;
};
rasterization = $properties.rasterization.counter_cw_cull_back;
colorBlend = {
logicOpEnable = false;
attachments = ($properties.attachmentBlendOp.alpha_blend);
};
layout = glyph_layout;//slices use the same descriptors as glyphs
};
glyph = {
@inherit = $properties.pipelines.trans_base;//FIXME should be sparate
vertexInput = $properties.vertexInput.glyph;

View file

@ -38,6 +38,8 @@
#include "QF/Vulkan/device.h"
#include "QF/Vulkan/shader.h"
static
#include "libs/video/renderer/vulkan/shader/slice.vert.spvc"
static
#include "libs/video/renderer/vulkan/shader/glyph.vert.spvc"
static
@ -120,6 +122,7 @@ typedef struct shaderdata_s {
} shaderdata_t;
static shaderdata_t builtin_shaders[] = {
{ "slice.vert", slice_vert, sizeof (slice_vert) },
{ "glyph.vert", glyph_vert, sizeof (glyph_vert) },
{ "glyph_color.frag", glyph_color_frag, sizeof (glyph_color_frag) },
{ "glyph_coverage.frag", glyph_coverage_frag, sizeof (glyph_coverage_frag) },

View file

@ -0,0 +1,34 @@
#version 450
layout (set = 0, binding = 0) uniform Matrices {
mat4 Projection3d;
mat4 View;
mat4 Sky;
mat4 Projection2d;
};
layout (set = 1, binding = 0) uniform textureBuffer glyph_data;
// per instance data
layout (location = 0) in uint glyph_index;
layout (location = 1) in vec4 glyph_color;
layout (location = 2) in vec2 glyph_position;
layout (location = 3) in vec2 glyph_offset; // for 9-slice
// rg -> offset x,y
// ba -> texture u,v
layout (location = 0) out vec2 uv;
layout (location = 1) out vec4 color;
void
main (void)
{
vec2 offset = vec2 ((gl_VertexIndex & 4) >> 2, (gl_VertexIndex & 8) >> 3);
vec4 glyph = texelFetch (glyph_data, int(glyph_index) * 4 + gl_VertexIndex);
// offset stored in glyph components 0 and 1
vec2 position = glyph_position + glyph.xy + offset * glyph_offset;
gl_Position = Projection2d * vec4 (position.xy, 0.0, 1.0);
// texture uv stored in glyph components 2 and 3
uv = glyph.pq;
color = glyph_color;
}

View file

@ -87,6 +87,13 @@ typedef struct {
float color[4];
} drawvert_t;
typedef struct {
uint32_t index;
byte color[4];
float position[2];
float offset[2];
} sliceinst_t;
typedef struct {
uint32_t index;
byte color[4];
@ -109,6 +116,12 @@ typedef struct vertqueue_s {
int size;
} vertqueue_t;
typedef struct slicequeue_s {
sliceinst_t *slices;
int count;
int size;
} slicequeue_t;
typedef struct glyphqueue_s {
glyphinst_t *glyphs;
int count;
@ -117,14 +130,18 @@ typedef struct glyphqueue_s {
typedef struct drawframe_s {
size_t quad_offset;
size_t slice_offset;
size_t glyph_offset;
size_t line_offset;
VkBuffer quad_buffer;
descbatchset_t quad_batch;
VkBuffer slice_buffer;
descbatchset_t slice_batch;
VkBuffer glyph_buffer;
descbatchset_t glyph_batch;
VkBuffer line_buffer;
vertqueue_t quad_verts;
slicequeue_t slice_insts;
glyphqueue_t glyph_insts;
vertqueue_t line_verts;
VkCommandBuffer cmd;
@ -166,14 +183,17 @@ typedef struct drawctx_s {
memsuper_t *string_memsuper;
hashtab_t *pic_cache;
qfv_resource_t *draw_resource;
qfv_resobj_t *ind_objects;
qfv_resobj_t *quad_objects;
qfv_resobj_t *slice_objects;
qfv_resobj_t *glyph_objects;
qfv_resobj_t *line_objects;
VkPipeline quad_pipeline;
VkPipeline slice_pipeline;
VkPipeline glyph_coverage_pipeline;
VkPipeline line_pipeline;
VkPipelineLayout layout;
VkPipelineLayout glyph_layout;
VkPipelineLayout glyph_layout;//slice pipeline uses same layout
VkDescriptorSet quad_set;
drawframeset_t frames;
drawfontset_t fonts;
@ -195,6 +215,39 @@ typedef struct drawctx_s {
#define VERTS_PER_FRAME (LINES_OFFSET + MAX_LINES*VERTS_PER_LINE)
static void
generate_quad_indices (qfv_stagebuf_t *staging, qfv_resobj_t *ind_buffer)
{
qfv_packet_t *packet = QFV_PacketAcquire (staging);
uint32_t *ind = QFV_PacketExtend (packet, ind_buffer->buffer.size);
for (int i = 0; i < MAX_QUADS; i++) {
for (int j = 0; j < VERTS_PER_QUAD; j++) {
*ind++ = i * VERTS_PER_QUAD + j;
}
// mark end of primitive
*ind++ = -1;
}
QFV_PacketCopyBuffer (packet, ind_buffer->buffer.buffer,
&bufferBarriers[qfv_BB_TransferWrite_to_IndexRead]);
QFV_PacketSubmit (packet);
}
static void
generate_slice_indices (qfv_stagebuf_t *staging, qfv_resobj_t *ind_buffer)
{
qfv_packet_t *packet = QFV_PacketAcquire (staging);
uint32_t *ind = QFV_PacketExtend (packet, ind_buffer->buffer.size);
for (int i = 0; i < 8; i++) {
ind[i] = i;
ind[i + 9] = i + 1 + (i & 1) * 6;
ind[i + 18] = i + 8;
}
ind[8] = ind[17] = ~0;
QFV_PacketCopyBuffer (packet, ind_buffer->buffer.buffer,
&bufferBarriers[qfv_BB_TransferWrite_to_IndexRead]);
QFV_PacketSubmit (packet);
}
static void
create_quad_buffers (vulkan_ctx_t *ctx)
{
@ -204,32 +257,38 @@ create_quad_buffers (vulkan_ctx_t *ctx)
size_t frames = ctx->frames.size;
dctx->draw_resource = malloc (2 * sizeof (qfv_resource_t)
// quads: index + frames vertex buffers
+ (1 + frames) * sizeof (qfv_resobj_t)
// index buffers
+ 2 * sizeof (qfv_resobj_t)
// quads: frames vertex buffers
+ (frames) * sizeof (qfv_resobj_t)
// slicess: frames instance vertex buffers
+ (frames) * sizeof (qfv_resobj_t)
// glyphs: frames instance vertex buffers
+ (frames) * sizeof (qfv_resobj_t)
// lines: frames vertex buffers
+ (frames) * sizeof (qfv_resobj_t));
dctx->quad_objects = (qfv_resobj_t *) &dctx->draw_resource[2];
dctx->glyph_objects = &dctx->quad_objects[1 + frames];
dctx->ind_objects = (qfv_resobj_t *) &dctx->draw_resource[2];
dctx->quad_objects = &dctx->ind_objects[2];
dctx->slice_objects = &dctx->quad_objects[frames];
dctx->glyph_objects = &dctx->slice_objects[frames];
dctx->line_objects = &dctx->glyph_objects[frames];
dctx->draw_resource[0] = (qfv_resource_t) {
.name = "draw",
.va_ctx = ctx->va_ctx,
.memory_properties = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
.num_objects = 1, // has only the quad index buffer
.objects = dctx->quad_objects,
.num_objects = 2, // quad and 9-slice indices
.objects = dctx->ind_objects,
};
dctx->draw_resource[1] = (qfv_resource_t) {
.name = "draw",
.va_ctx = ctx->va_ctx,
.memory_properties = VK_MEMORY_PROPERTY_HOST_CACHED_BIT,
.num_objects = (frames) + (frames) + (frames),
.objects = dctx->quad_objects + 1,
.num_objects = (frames) + (frames) + (frames) + (frames),
.objects = dctx->quad_objects,
};
dctx->quad_objects[0] = (qfv_resobj_t) {
dctx->ind_objects[0] = (qfv_resobj_t) {
.name = "quads.index",
.type = qfv_res_buffer,
.buffer = {
@ -238,10 +297,18 @@ create_quad_buffers (vulkan_ctx_t *ctx)
| VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
},
};
__auto_type ind_buffer = &dctx->quad_objects[0];
dctx->ind_objects[1] = (qfv_resobj_t) {
.name = "9-slice.index",
.type = qfv_res_buffer,
.buffer = {
.size = 26 * sizeof (uint32_t),
.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT
| VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
},
};
for (size_t i = 0; i < frames; i++) {
dctx->quad_objects[i + 1] = (qfv_resobj_t) {
dctx->quad_objects[i] = (qfv_resobj_t) {
.name = "quads.geom",
.type = qfv_res_buffer,
.buffer = {
@ -250,6 +317,15 @@ create_quad_buffers (vulkan_ctx_t *ctx)
| VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
},
};
dctx->slice_objects[i] = (qfv_resobj_t) {
.name = "slices.inst",
.type = qfv_res_buffer,
.buffer = {
.size = MAX_GLYPHS * sizeof (sliceinst_t),
.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT
| VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
},
};
dctx->glyph_objects[i] = (qfv_resobj_t) {
.name = "glyphs.inst",
.type = qfv_res_buffer,
@ -278,8 +354,10 @@ create_quad_buffers (vulkan_ctx_t *ctx)
for (size_t f = 0; f < frames; f++) {
drawframe_t *frame = &dctx->frames.a[f];
frame->quad_buffer = dctx->quad_objects[1 + f].buffer.buffer;
frame->quad_offset = dctx->quad_objects[1 + f].buffer.offset;
frame->quad_buffer = dctx->quad_objects[f].buffer.buffer;
frame->quad_offset = dctx->quad_objects[f].buffer.offset;
frame->slice_buffer = dctx->slice_objects[f].buffer.buffer;
frame->slice_offset = dctx->slice_objects[f].buffer.offset;
frame->glyph_buffer = dctx->glyph_objects[f].buffer.buffer;
frame->glyph_offset = dctx->glyph_objects[f].buffer.offset;
frame->line_buffer = dctx->line_objects[f].buffer.buffer;
@ -290,6 +368,11 @@ create_quad_buffers (vulkan_ctx_t *ctx)
.size = MAX_QUADS,
};
DARRAY_INIT (&frame->quad_batch, 16);
frame->slice_insts = (slicequeue_t) {
.slices = (sliceinst_t *) ((byte *)data + frame->slice_offset),
.size = MAX_QUADS,
};
DARRAY_INIT (&frame->slice_batch, 16);
frame->glyph_insts = (glyphqueue_t) {
.glyphs = (glyphinst_t *) ((byte *)data + frame->glyph_offset),
.size = MAX_QUADS,
@ -302,18 +385,8 @@ create_quad_buffers (vulkan_ctx_t *ctx)
}
// The indices will never change so pre-generate and stash them
qfv_packet_t *packet = QFV_PacketAcquire (ctx->staging);
uint32_t *ind = QFV_PacketExtend (packet, ind_buffer->buffer.size);
for (int i = 0; i < MAX_QUADS; i++) {
for (int j = 0; j < VERTS_PER_QUAD; j++) {
*ind++ = i * VERTS_PER_QUAD + j;
}
// mark end of primitive
*ind++ = -1;
}
QFV_PacketCopyBuffer (packet, ind_buffer->buffer.buffer,
&bufferBarriers[qfv_BB_TransferWrite_to_IndexRead]);
QFV_PacketSubmit (packet);
generate_quad_indices (ctx->staging, &dctx->ind_objects[0]);
generate_slice_indices (ctx->staging, &dctx->ind_objects[1]);
}
static void
@ -463,6 +536,7 @@ Vulkan_Draw_Shutdown (vulkan_ctx_t *ctx)
}
dfunc->vkDestroyPipeline (device->dev, dctx->quad_pipeline, 0);
dfunc->vkDestroyPipeline (device->dev, dctx->slice_pipeline, 0);
dfunc->vkDestroyPipeline (device->dev, dctx->glyph_coverage_pipeline, 0);
dfunc->vkDestroyPipeline (device->dev, dctx->line_pipeline, 0);
Hash_DelTable (dctx->pic_cache);
@ -536,6 +610,7 @@ Vulkan_Draw_Init (vulkan_ctx_t *ctx)
flush_draw_scrap (ctx);
dctx->quad_pipeline = Vulkan_CreateGraphicsPipeline (ctx, "twod");
dctx->slice_pipeline = Vulkan_CreateGraphicsPipeline (ctx, "slice");
dctx->glyph_coverage_pipeline
= Vulkan_CreateGraphicsPipeline (ctx, "glyph_coverage");
dctx->line_pipeline = Vulkan_CreateGraphicsPipeline (ctx, "lines");
@ -1045,8 +1120,8 @@ Vulkan_FlushText (qfv_renderframe_t *rFrame)
drawctx_t *dctx = ctx->draw_context;
drawframe_t *dframe = &dctx->frames.a[ctx->curFrame];
if (!dframe->quad_verts.count && !dframe->glyph_insts.count
&& !dframe->line_verts.count) {
if (!dframe->quad_verts.count && !dframe->slice_insts.count
&& !dframe->glyph_insts.count && !dframe->line_verts.count) {
return;
}
@ -1055,17 +1130,24 @@ Vulkan_FlushText (qfv_renderframe_t *rFrame)
DARRAY_APPEND (&rFrame->subpassCmdSets[QFV_passTranslucent], cmd);
VkDeviceMemory memory = dctx->draw_resource[1].memory;
size_t atom = device->physDev->properties->limits.nonCoherentAtomSize;
size_t atom_mask = atom - 1;
#define a(x) (((x) + atom_mask) & ~atom_mask)
VkMappedMemoryRange ranges[] = {
{ VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, 0,
memory, dframe->quad_offset,
dframe->quad_verts.count * VERTS_PER_QUAD * sizeof (drawvert_t) },
a(dframe->quad_verts.count * VERTS_PER_QUAD * sizeof (drawvert_t)) },
{ VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, 0,
memory, dframe->slice_offset,
a(dframe->slice_insts.count * sizeof (sliceinst_t)) },
{ VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, 0,
memory, dframe->glyph_offset,
dframe->glyph_insts.count * VERTS_PER_QUAD * sizeof (drawvert_t) },
a(dframe->glyph_insts.count * sizeof (glyphinst_t)) },
{ VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, 0,
memory, dframe->line_offset,
dframe->line_verts.count * VERTS_PER_LINE * sizeof (drawvert_t) },
a(dframe->line_verts.count * VERTS_PER_LINE * sizeof (drawvert_t)) },
};
#undef a
dfunc->vkFlushMappedMemoryRanges (device->dev, 3, ranges);
dfunc->vkResetCommandBuffer (cmd, 0);
@ -1086,7 +1168,7 @@ Vulkan_FlushText (qfv_renderframe_t *rFrame)
if (dframe->quad_verts.count) {
VkBuffer quad_buffer = dframe->quad_buffer;
VkBuffer ind_buffer = dctx->quad_objects[0].buffer.buffer;
VkBuffer ind_buffer = dctx->ind_objects[0].buffer.buffer;
VkDeviceSize offsets[] = {0};
dfunc->vkCmdBindVertexBuffers (cmd, 0, 1, &quad_buffer, offsets);
dfunc->vkCmdBindIndexBuffer (cmd, ind_buffer, 0, VK_INDEX_TYPE_UINT32);
@ -1106,6 +1188,36 @@ Vulkan_FlushText (qfv_renderframe_t *rFrame)
1, 0, 0, 0);
}
if (dframe->slice_insts.count) {
VkBuffer slice_buffer = dframe->slice_buffer;
VkBuffer ind_buffer = dctx->ind_objects[1].buffer.buffer;
VkDeviceSize offsets[] = {0};
dfunc->vkCmdBindVertexBuffers (cmd, 0, 1, &slice_buffer, offsets);
dfunc->vkCmdBindIndexBuffer (cmd, ind_buffer, 0, VK_INDEX_TYPE_UINT32);
dfunc->vkCmdBindPipeline (cmd, VK_PIPELINE_BIND_POINT_GRAPHICS,
dctx->slice_pipeline);
dfunc->vkCmdSetViewport (cmd, 0, 1, &rFrame->renderpass->viewport);
dfunc->vkCmdSetScissor (cmd, 0, 1, &rFrame->renderpass->scissor);
uint32_t inst_start = 0;
for (size_t i = 0; i < dframe->slice_batch.size; i++) {
int fontid = dframe->slice_batch.a[i].descid;
uint32_t inst_count = dframe->slice_batch.a[i].count;
VkDescriptorSet set[2] = {
Vulkan_Matrix_Descriptors (ctx, ctx->curFrame),
dctx->fonts.a[fontid].set,
};
VkPipelineLayout layout = dctx->glyph_layout;
dfunc->vkCmdBindDescriptorSets (cmd,
VK_PIPELINE_BIND_POINT_GRAPHICS,
layout, 0, 2, set, 0, 0);
dfunc->vkCmdDrawIndexed (cmd, 26, inst_count, 0, 0, inst_start);
inst_start += inst_count;
}
DARRAY_RESIZE (&dframe->slice_batch, 0);
}
if (dframe->glyph_insts.count) {
VkBuffer glyph_buffer = dframe->glyph_buffer;
VkDeviceSize offsets[] = {0};
@ -1155,6 +1267,7 @@ Vulkan_FlushText (qfv_renderframe_t *rFrame)
dfunc->vkEndCommandBuffer (cmd);
dframe->quad_verts.count = 0;
dframe->slice_insts.count = 0;
dframe->glyph_insts.count = 0;
dframe->line_verts.count = 0;
}