[vullkan] Rework Draw implementation

Line rendering now has its own pipeline (removing the texture issue).

Glyph rendering (for fonts) has been reworked to use instanced quad
rendering, with the geometry (position and texture coords) in a static
buffer (uniform texture buffer), and each instance has a glyph index,
color, and 2d base position.

Multiple fonts can be loaded, but aren't used yet: still just the one
(work needs to be done on the queues to support multiple
textures/fonts).

Quads haven't changed much, but buffer creation and destruction has been
cleaned up to use the resource functions.
This commit is contained in:
Bill Currie 2022-10-03 10:29:49 +09:00
parent c9e7810864
commit f477f2b96e
9 changed files with 535 additions and 200 deletions

View file

@ -275,6 +275,16 @@ vkparse_plist = \
vkshaderpath = libs/video/renderer/vulkan/shader
glyph_src = $(vkshaderpath)/glyph.vert
glyph_c = $(vkshaderpath)/glyph.vert.spvc
glyph_color_src = $(vkshaderpath)/glyph_color.frag
glyph_color_c = $(vkshaderpath)/glyph_color.frag.spvc
glyph_coverage_src = $(vkshaderpath)/glyph_coverage.frag
glyph_coverage_c = $(vkshaderpath)/glyph_coverage.frag.spvc
linev_src = $(vkshaderpath)/line.vert
linev_c = $(vkshaderpath)/line.vert.spvc
linef_src = $(vkshaderpath)/line.frag
linef_c = $(vkshaderpath)/line.frag.spvc
partphysicsc_src = $(vkshaderpath)/partphysics.comp
partphysicsc_c = $(vkshaderpath)/partphysics.comp.spvc
partupdatec_src = $(vkshaderpath)/partupdate.comp
@ -293,8 +303,6 @@ 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
@ -343,6 +351,13 @@ fstriangle_c = $(vkshaderpath)/fstriangle.vert.spvc
pushcolor_src = $(vkshaderpath)/pushcolor.frag
pushcolor_c = $(vkshaderpath)/pushcolor.frag.spvc
$(glyph_vert_c): $(glyph_vert_src)
$(glyph_color_c): $(glyph_color_src)
$(glyph_coverage_c): $(glyph_coverage_src)
$(linev_c): $(linev_src)
$(linef_c): $(linef_src)
$(partphysicsc_c): $(partphysicsc_src)
$(partupdatec_c): $(partupdatec_src)
$(particlev_c): $(particlev_src)
@ -357,8 +372,6 @@ $(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)
@ -406,6 +419,11 @@ $(fstriangle_c): $(fstriangle_src)
$(pushcolor_c): $(pushcolor_src)
vkshader_c = \
$(glyph_c) \
$(glyph_color_c) \
$(glyph_coverage_c) \
$(linev_c) \
$(linef_c) \
$(partphysicsc_c) \
$(partupdatec_c) \
$(particlev_c) \
@ -415,7 +433,6 @@ vkshader_c = \
$(sprite_gbuff_c) \
$(sprite_depthv_c) \
$(sprite_depthf_c) \
$(twod_impalpha_c) \
$(twodv_c) \
$(twodf_c) \
$(quakebspv_c) \

View file

@ -97,6 +97,21 @@
},
);
};
glyph_pool = {
maxSets = 16;
bindings = (
{
// glyph geometry data (offset and uv)
type = uniform_texel_buffer;
descriptorCount = 16;
},
{
// glyph texture data
type = combined_image_sampler;
descriptorCount = 16;
},
);
};
entity_pool = {
flags = free_descriptor_set;
maxSets = 512;
@ -216,6 +231,24 @@
},
);
};
glyph_data_set = {
bindings = (
{
// glyph geometry data (offset and uv)
binding = 0;
descriptorType = uniform_texel_buffer;
descriptorCount = 1;
stageFlags = vertex;
},
{
// glyph texture data
binding = 1;
descriptorType = combined_image_sampler;
descriptorCount = 1;
stageFlags = fragment;
},
);
};
texture_set = {
bindings = (
{
@ -359,6 +392,12 @@
twod_layout = {
setLayouts = (matrix_set, twod_set);
};
glyph_layout = {
setLayouts = (matrix_set, glyph_data_set);
};
lines_layout = {
setLayouts = (matrix_set);
};
quakebsp_layout = {
setLayouts = (matrix_set, entity_set, texture_set, texture_set);
pushConstantRanges = (
@ -526,6 +565,19 @@
{ location = 2; binding = 1; format = r32_uint; offset = 0; },
);
};
glyph = {
bindings = (
{ binding = 0; stride = "4 + 4 + 2 * 4"; inputRate = instance; },
);
attributes = (
// glyph index
{ location = 0; binding = 0; format = r32_uint; offset = 0; },
// glyph color
{ location = 1; binding = 0; format = r8g8b8a8_unorm; offset = 4; },
// glyph position (2d)
{ location = 2; binding = 0; format = r32g32_sfloat; offset = 8; },
);
};
iqm = {
bindings = (
{ binding = 0; stride = 20; inputRate = vertex; },
@ -1046,24 +1098,44 @@
};
layout = twod_layout;
};
twod_impalpha = {
@inherit = $properties.pipelines.twod;
glyph = {
@inherit = $properties.pipelines.trans_base;//FIXME should be sparate
vertexInput = $properties.vertexInput.glyph;
inputAssembly = {
// glyphs are drawn using instanced quads, so primitive restart
// is not needed as only four vertices are drawn per instance.
topology = triangle_strip;
primitiveRestartEnable = false;
};
rasterization = $properties.rasterization.counter_cw_cull_back;
colorBlend = {
logicOpEnable = false;
attachments = ($properties.attachmentBlendOp.alpha_blend);
};
layout = glyph_layout;
};
glyph_color = {
@inherit = $properties.pipelines.glyph;
stages = (
{
stage = vertex;
name = main;
module = $builtin/twod.vert;
},
{
stage = fragment;
name = main;
module = $builtin/twod_impalpha.frag;
},
{ stage = vertex; name = main; module = $builtin/glyph.vert; },
{ stage = fragment; name = main; module = $builtin/glyph_color.frag; },
);
};
glyph_coverage = {
@inherit = $properties.pipelines.glyph;
stages = (
{ stage = vertex; name = main; module = $builtin/glyph.vert; },
{ stage = fragment; name = main; module = $builtin/glyph_coverage.frag; },
);
};
lines = {
@inherit = $properties.pipelines.twod;
stages = (
{ stage = vertex; name = main; module = $builtin/line.vert; },
{ stage = fragment; name = main; module = $builtin/line.frag; },
);
inputAssembly = $properties.inputAssembly.lines;
layout = lines_layout;
};
lighting = {
@inherit = $properties.pipelines.comp_base;

View file

@ -38,6 +38,16 @@
#include "QF/Vulkan/device.h"
#include "QF/Vulkan/shader.h"
static
#include "libs/video/renderer/vulkan/shader/glyph.vert.spvc"
static
#include "libs/video/renderer/vulkan/shader/glyph_color.frag.spvc"
static
#include "libs/video/renderer/vulkan/shader/glyph_coverage.frag.spvc"
static
#include "libs/video/renderer/vulkan/shader/line.vert.spvc"
static
#include "libs/video/renderer/vulkan/shader/line.frag.spvc"
static
#include "libs/video/renderer/vulkan/shader/particle.vert.spvc"
static
@ -57,8 +67,6 @@ 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"
@ -112,6 +120,11 @@ typedef struct shaderdata_s {
} shaderdata_t;
static shaderdata_t builtin_shaders[] = {
{ "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) },
{ "line.vert", line_vert, sizeof (line_vert) },
{ "line.frag", line_frag, sizeof (line_frag) },
{ "particle.vert", particle_vert, sizeof (particle_vert) },
{ "particle.geom", particle_geom, sizeof (particle_geom) },
{ "particle.frag", particle_frag, sizeof (particle_frag) },
@ -121,7 +134,6 @@ 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) },

View file

@ -0,0 +1,32 @@
#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;
// rg -> offset x,y
// ba -> texture u,v
layout (location = 0) out vec2 uv;
layout (location = 1) out vec4 color;
void
main (void)
{
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;
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

@ -1,8 +1,8 @@
#version 450
layout (set = 1, binding = 0) uniform sampler2D Texture;
layout (set = 1, binding = 1) uniform sampler2D Texture;
layout (location = 0) in vec2 st;
layout (location = 0) in vec2 uv;
layout (location = 1) in vec4 color;
layout (location = 0) out vec4 frag_color;
@ -12,6 +12,6 @@ main (void)
{
vec4 pix;
pix = texture(Texture, st).rrrr;
pix = texture (Texture, uv);
frag_color = pix * color;
}

View file

@ -0,0 +1,17 @@
#version 450
layout (set = 1, binding = 1) uniform sampler2D Texture;
layout (location = 0) in vec2 uv;
layout (location = 1) in vec4 color;
layout (location = 0) out vec4 frag_color;
void
main (void)
{
vec4 pix;
pix = texture (Texture, uv).rrrr;
frag_color = pix * color;
}

View file

@ -0,0 +1,11 @@
#version 450
layout (location = 0) in vec4 color;
layout (location = 0) out vec4 frag_color;
void
main (void)
{
frag_color = color;
}

View file

@ -0,0 +1,20 @@
#version 450
layout (set = 0, binding = 0) uniform Matrices {
mat4 Projection3d;
mat4 View;
mat4 Sky;
mat4 Projection2d;
};
layout (location = 0) in vec2 vertex;
layout (location = 1) in vec2 uv;
layout (location = 2) in vec4 vcolor;
layout (location = 0) out vec4 color;
void
main (void)
{
gl_Position = Projection2d * vec4 (vertex.xy, 0.0, 1.0);
color = vcolor;
}

View file

@ -63,6 +63,7 @@
#include "QF/Vulkan/device.h"
#include "QF/Vulkan/image.h"
#include "QF/Vulkan/instance.h"
#include "QF/Vulkan/resource.h"
#include "QF/Vulkan/scrap.h"
#include "QF/Vulkan/staging.h"
#include "QF/ui/view.h"
@ -78,6 +79,17 @@ typedef struct {
float color[4];
} drawvert_t;
typedef struct {
uint32_t index;
byte color[4];
float position[2];
} glyphinst_t;
typedef struct {
float offset[2];
float uv[2];
} glyphvert_t;
typedef struct cachepic_s {
char *name;
qpic_t *pic;
@ -89,26 +101,48 @@ typedef struct vertqueue_s {
int size;
} vertqueue_t;
typedef struct glyphqueue_s {
glyphinst_t *glyphs;
int count;
int size;
} glyphqueue_t;
typedef struct drawframe_s {
size_t vert_offset;
size_t iavert_offset;
size_t quad_offset;
size_t glyph_offset;
size_t line_offset;
VkBuffer quad_buffer;
VkBuffer glyph_buffer;
VkBuffer line_buffer;
vertqueue_t quad_verts;
vertqueue_t iaquad_verts;
glyphqueue_t glyph_insts;
vertqueue_t line_verts;
VkCommandBuffer cmd;
VkDescriptorSet descriptors;
VkDescriptorSet iadescriptors;
} drawframe_t;
typedef struct drawframeset_s
DARRAY_TYPE (drawframe_t) drawframeset_t;
typedef struct drawfontres_s {
qfv_resource_t resource;
qfv_resobj_t glyph_data;
qfv_resobj_t glyph_bview;
qfv_resobj_t glyph_image;
qfv_resobj_t glyph_iview;
} drawfontres_t;
typedef struct drawfont_s {
VkDescriptorSet set;
drawfontres_t *resource;
rfont_t *font;
} drawfont_t;
typedef struct drawfontset_s
DARRAY_TYPE (drawfont_t) drawfontset_t;
typedef struct drawctx_s {
VkSampler sampler;
scrap_t *scrap;
rfont_t *font;
qfv_tex_t *font_tex;
qfv_stagebuf_t *stage;
qpic_t *crosshair;
qpic_t *conchars;
@ -120,15 +154,18 @@ typedef struct drawctx_s {
memsuper_t *pic_memsuper;
memsuper_t *string_memsuper;
hashtab_t *pic_cache;
VkBuffer vert_buffer;
VkDeviceMemory vert_memory;
VkBuffer ind_buffer;
VkDeviceMemory ind_memory;
qfv_resource_t *draw_resource;
qfv_resobj_t *quad_objects;
qfv_resobj_t *glyph_objects;
qfv_resobj_t *line_objects;
VkPipeline quad_pipeline;
VkPipeline iaquad_pipeline;
VkPipeline glyph_coverage_pipeline;
VkPipeline line_pipeline;
VkPipelineLayout layout;
VkPipelineLayout glyph_layout;
VkDescriptorSet quad_set;
drawframeset_t frames;
drawfontset_t fonts;
} drawctx_t;
// enough for a full screen of 8x8 chars at 1920x1080 plus some extras (368)
@ -136,6 +173,8 @@ typedef struct drawctx_s {
#define VERTS_PER_QUAD (4)
#define INDS_PER_QUAD (5) // one per vert plus primitive reset
#define MAX_GLYPHS (32768)
#define MAX_LINES (32768)
#define VERTS_PER_LINE (2)
@ -150,66 +189,108 @@ create_quad_buffers (vulkan_ctx_t *ctx)
{
qfv_device_t *device = ctx->device;
qfv_devfuncs_t *dfunc = device->funcs;
drawctx_t *dctx = ctx->draw_context;
size_t vert_size;
size_t ind_size;
size_t frames = ctx->frames.size;
VkBuffer vbuf, ibuf;
VkDeviceMemory vmem, imem;
vert_size = frames * VERTS_PER_FRAME * sizeof (drawvert_t);
ind_size = MAX_QUADS * INDS_PER_QUAD * sizeof (uint32_t);
dctx->draw_resource = malloc (2 * sizeof (qfv_resource_t)
// quads: index + frames vertex buffers
+ (1 + 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->line_objects = &dctx->glyph_objects[frames];
vbuf = QFV_CreateBuffer (device, vert_size,
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT);
ibuf = QFV_CreateBuffer (device, ind_size,
VK_BUFFER_USAGE_INDEX_BUFFER_BIT
| VK_BUFFER_USAGE_TRANSFER_DST_BIT);
vmem = QFV_AllocBufferMemory (device, vbuf,
VK_MEMORY_PROPERTY_HOST_CACHED_BIT,
vert_size, 0);
imem = QFV_AllocBufferMemory (device, ibuf,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
ind_size, 0);
QFV_BindBufferMemory (device, vbuf, vmem, 0);
QFV_BindBufferMemory (device, ibuf, imem, 0);
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,
};
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,
};
dctx->vert_buffer = vbuf;
dctx->vert_memory = vmem;
dctx->ind_buffer = ibuf;
dctx->ind_memory = imem;
dctx->quad_objects[0] = (qfv_resobj_t) {
.name = "quads.index",
.type = qfv_res_buffer,
.buffer = {
.size = MAX_QUADS * INDS_PER_QUAD * sizeof (uint32_t),
.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT
| VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
},
};
__auto_type ind_buffer = &dctx->quad_objects[0];
for (size_t i = 0; i < frames; i++) {
dctx->quad_objects[i + 1] = (qfv_resobj_t) {
.name = "quads.geom",
.type = qfv_res_buffer,
.buffer = {
.size = MAX_QUADS * VERTS_PER_QUAD * sizeof (drawvert_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,
.buffer = {
.size = MAX_GLYPHS * sizeof (glyphinst_t),
.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT
| VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
},
};
dctx->line_objects[i] = (qfv_resobj_t) {
.name = "lines.geom",
.type = qfv_res_buffer,
.buffer = {
.size = MAX_LINES * VERTS_PER_LINE * sizeof (drawvert_t),
.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT
| VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
},
};
}
QFV_CreateResource (device, &dctx->draw_resource[0]);
QFV_CreateResource (device, &dctx->draw_resource[1]);
void *data;
dfunc->vkMapMemory (device->dev, vmem, 0, vert_size, 0, &data);
drawvert_t *vert_data = data;
VkDeviceMemory memory = dctx->draw_resource[1].memory;
dfunc->vkMapMemory (device->dev, memory, 0, VK_WHOLE_SIZE, 0, &data);
for (size_t f = 0; f < frames; f++) {
drawframe_t *frame = &dctx->frames.a[f];
size_t ind = f * VERTS_PER_FRAME;
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_buffer = dctx->quad_objects[1 + f].buffer.buffer;
frame->quad_offset = dctx->quad_objects[1 + 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;
frame->line_offset = dctx->line_objects[f].buffer.offset;
frame->quad_verts = (vertqueue_t) {
.verts = vert_data + ind,
.verts = (drawvert_t *) ((byte *)data + frame->quad_offset),
.size = MAX_QUADS,
};
frame->iaquad_verts = (vertqueue_t) {
.verts = frame->quad_verts.verts + IAQUADS_OFFSET,
frame->glyph_insts = (glyphqueue_t) {
.glyphs = (glyphinst_t *) ((byte *)data + frame->glyph_offset),
.size = MAX_QUADS,
};
frame->line_verts = (vertqueue_t) {
.verts = frame->quad_verts.verts + LINES_OFFSET,
.size = MAX_LINES,
.verts = (drawvert_t *) ((byte *)data + frame->line_offset),
.size = MAX_QUADS,
};
}
// 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_size);
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;
@ -217,37 +298,11 @@ create_quad_buffers (vulkan_ctx_t *ctx)
// mark end of primitive
*ind++ = -1;
}
qfv_bufferbarrier_t bb = bufferBarriers[qfv_BB_Unknown_to_TransferWrite];
bb.barrier.buffer = ibuf;
bb.barrier.size = ind_size;
dfunc->vkCmdPipelineBarrier (packet->cmd, bb.srcStages, bb.dstStages,
0, 0, 0, 1, &bb.barrier, 0, 0);
VkBufferCopy copy_region = { packet->offset, 0, ind_size };
dfunc->vkCmdCopyBuffer (packet->cmd, ctx->staging->buffer, ibuf,
1, &copy_region);
bb = bufferBarriers[qfv_BB_TransferWrite_to_IndexRead];
bb.barrier.buffer = ibuf;
bb.barrier.size = ind_size;
dfunc->vkCmdPipelineBarrier (packet->cmd, bb.srcStages, bb.dstStages,
0, 0, 0, 1, &bb.barrier, 0, 0);
QFV_PacketCopyBuffer (packet, ind_buffer->buffer.buffer,
&bufferBarriers[qfv_BB_TransferWrite_to_IndexRead]);
QFV_PacketSubmit (packet);
}
static void
destroy_quad_buffers (vulkan_ctx_t *ctx)
{
qfv_device_t *device = ctx->device;
qfv_devfuncs_t *dfunc = device->funcs;
drawctx_t *dctx = ctx->draw_context;
dfunc->vkUnmapMemory (device->dev, dctx->vert_memory);
dfunc->vkFreeMemory (device->dev, dctx->vert_memory, 0);
dfunc->vkFreeMemory (device->dev, dctx->ind_memory, 0);
dfunc->vkDestroyBuffer (device->dev, dctx->vert_buffer, 0);
dfunc->vkDestroyBuffer (device->dev, dctx->ind_buffer, 0);
}
static void
flush_draw_scrap (vulkan_ctx_t *ctx)
{
@ -387,19 +442,21 @@ Vulkan_Draw_Shutdown (vulkan_ctx_t *ctx)
qfv_devfuncs_t *dfunc = device->funcs;
drawctx_t *dctx = ctx->draw_context;
destroy_quad_buffers (ctx);
QFV_DestroyResource (device, &dctx->draw_resource[0]);
QFV_DestroyResource (device, &dctx->draw_resource[1]);
for (size_t i = 0; i < dctx->fonts.size; i++) {
QFV_DestroyResource (device, &dctx->fonts.a[i].resource->resource);
free (dctx->fonts.a[i].resource);
}
dfunc->vkDestroyPipeline (device->dev, dctx->quad_pipeline, 0);
dfunc->vkDestroyPipeline (device->dev, dctx->iaquad_pipeline, 0);
dfunc->vkDestroyPipeline (device->dev, dctx->glyph_coverage_pipeline, 0);
dfunc->vkDestroyPipeline (device->dev, dctx->line_pipeline, 0);
Hash_DelTable (dctx->pic_cache);
delete_memsuper (dctx->pic_memsuper);
delete_memsuper (dctx->string_memsuper);
QFV_DestroyScrap (dctx->scrap);
QFV_DestroyStagingBuffer (dctx->stage);
if (dctx->font_tex) {
Vulkan_UnloadTex (ctx, dctx->font_tex);
}
}
void
@ -417,6 +474,10 @@ Vulkan_Draw_Init (vulkan_ctx_t *ctx)
DARRAY_INIT (&dctx->frames, frames);
DARRAY_RESIZE (&dctx->frames, frames);
dctx->frames.grow = 0;
DARRAY_INIT (&dctx->fonts, 16);
DARRAY_RESIZE (&dctx->fonts, 16);
dctx->fonts.grow = 0;
dctx->fonts.size = 0;
dctx->pic_memsuper = new_memsuper ();
dctx->string_memsuper = new_memsuper ();
@ -457,16 +518,15 @@ 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->glyph_coverage_pipeline
= Vulkan_CreateGraphicsPipeline (ctx, "glyph_coverage");
dctx->line_pipeline = Vulkan_CreateGraphicsPipeline (ctx, "lines");
dctx->layout = Vulkan_CreatePipelineLayout (ctx, "twod_layout");
dctx->glyph_layout = Vulkan_CreatePipelineLayout (ctx, "glyph_layout");
__auto_type layouts = QFV_AllocDescriptorSetLayoutSet (frames, alloca);
for (size_t i = 0; i < layouts->size; i++) {
layouts->a[i] = Vulkan_CreateDescriptorSetLayout (ctx, "twod_set");
}
__auto_type layouts = QFV_AllocDescriptorSetLayoutSet (1, alloca);
layouts->a[0] = Vulkan_CreateDescriptorSetLayout (ctx, "twod_set");
__auto_type pool = Vulkan_CreateDescriptorPool (ctx, "twod_pool");
VkDescriptorImageInfo imageInfo = {
@ -474,38 +534,27 @@ 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);
dctx->quad_set = sets->a[0];
VkWriteDescriptorSet write[] = {
{ VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, 0,
dctx->quad_set, 0, 0, 1,
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
&imageInfo, 0, 0 },
};
free (sets);
dfunc->vkUpdateDescriptorSets (device->dev, 1, write, 0, 0);
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, 2, write, 0, 0);
dframe->cmd = cmdBuffers->a[i];
QFV_duSetObjectName (device, VK_OBJECT_TYPE_COMMAND_BUFFER,
dframe->cmd,
va (ctx->va_ctx, "cmd:draw:%zd", i));
}
free (sets);
qfvPopDebug (ctx);
}
@ -948,7 +997,7 @@ 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->iaquad_verts.count
if (!dframe->quad_verts.count && !dframe->glyph_insts.count
&& !dframe->line_verts.count) {
return;
}
@ -957,18 +1006,19 @@ Vulkan_FlushText (qfv_renderframe_t *rFrame)
//FIXME which pass?
DARRAY_APPEND (&rFrame->subpassCmdSets[QFV_passTranslucent], cmd);
VkDeviceMemory memory = dctx->draw_resource[1].memory;
VkMappedMemoryRange ranges[] = {
{ VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, 0,
dctx->vert_memory, dframe->vert_offset,
memory, dframe->quad_offset,
dframe->quad_verts.count * VERTS_PER_QUAD * sizeof (drawvert_t) },
{ VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, 0,
dctx->vert_memory, dframe->iavert_offset,
dframe->iaquad_verts.count * VERTS_PER_QUAD * sizeof (drawvert_t) },
memory, dframe->glyph_offset,
dframe->glyph_insts.count * VERTS_PER_QUAD * sizeof (drawvert_t) },
{ VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, 0,
dctx->vert_memory, dframe->line_offset,
memory, dframe->line_offset,
dframe->line_verts.count * VERTS_PER_LINE * sizeof (drawvert_t) },
};
dfunc->vkFlushMappedMemoryRanges (device->dev, 2, ranges);
dfunc->vkFlushMappedMemoryRanges (device->dev, 3, ranges);
dfunc->vkResetCommandBuffer (cmd, 0);
VkCommandBufferInheritanceInfo inherit = {
@ -986,14 +1036,15 @@ Vulkan_FlushText (qfv_renderframe_t *rFrame)
QFV_duCmdBeginLabel (device, cmd, "twod", { 0.6, 0.2, 0, 1});
VkDeviceSize offsets[] = {dframe->vert_offset};
dfunc->vkCmdBindVertexBuffers (cmd, 0, 1, &dctx->vert_buffer, offsets);
dfunc->vkCmdBindIndexBuffer (cmd, dctx->ind_buffer, 0,
VK_INDEX_TYPE_UINT32);
if (dframe->quad_verts.count) {
VkBuffer quad_buffer = dframe->quad_buffer;
VkBuffer ind_buffer = dctx->quad_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);
VkDescriptorSet set[2] = {
Vulkan_Matrix_Descriptors (ctx, ctx->curFrame),
dframe->descriptors,
dctx->quad_set,
};
VkPipelineLayout layout = dctx->layout;
dfunc->vkCmdBindDescriptorSets (cmd, VK_PIPELINE_BIND_POINT_GRAPHICS,
@ -1004,58 +1055,50 @@ Vulkan_FlushText (qfv_renderframe_t *rFrame)
dfunc->vkCmdSetViewport (cmd, 0, 1, &rFrame->renderpass->viewport);
dfunc->vkCmdSetScissor (cmd, 0, 1, &rFrame->renderpass->scissor);
dfunc->vkCmdDrawIndexed (cmd, dframe->quad_verts.count * INDS_PER_QUAD,
1, 0, QUADS_OFFSET, 0);
1, 0, 0, 0);
}
if (dframe->iaquad_verts.count) {
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);
if (dframe->glyph_insts.count) {
VkBuffer glyph_buffer = dframe->glyph_buffer;
VkDeviceSize offsets[] = {0};
dfunc->vkCmdBindVertexBuffers (cmd, 0, 1, &glyph_buffer, offsets);
VkDescriptorSet set[2] = {
Vulkan_Matrix_Descriptors (ctx, ctx->curFrame),
dframe->iadescriptors,
dctx->fonts.a[0].set,
};
VkPipelineLayout layout = dctx->layout;
VkPipelineLayout layout = dctx->glyph_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);
dctx->glyph_coverage_pipeline);
dfunc->vkCmdSetViewport (cmd, 0, 1, &rFrame->renderpass->viewport);
dfunc->vkCmdSetScissor (cmd, 0, 1, &rFrame->renderpass->scissor);
dfunc->vkCmdDrawIndexed (cmd, dframe->iaquad_verts.count*INDS_PER_QUAD,
1, 0, IAQUADS_OFFSET, 0);
dfunc->vkCmdDraw (cmd, 4, dframe->glyph_insts.count, 0, 0);
}
if (dframe->line_verts.count) {
VkDescriptorSet set[2] = {
VkBuffer line_buffer = dframe->line_buffer;
VkDeviceSize offsets[] = {0};
dfunc->vkCmdBindVertexBuffers (cmd, 0, 1, &line_buffer, offsets);
VkDescriptorSet set[1] = {
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);
layout, 0, 1, set, 0, 0);
dfunc->vkCmdBindPipeline (cmd, VK_PIPELINE_BIND_POINT_GRAPHICS,
dctx->line_pipeline);
dfunc->vkCmdDraw (cmd, dframe->line_verts.count * VERTS_PER_LINE,
1, LINES_OFFSET, 0);
1, 0, 0);
}
QFV_duCmdEndLabel (device, cmd);
dfunc->vkEndCommandBuffer (cmd);
dframe->quad_verts.count = 0;
dframe->iaquad_verts.count = 0;
dframe->glyph_insts.count = 0;
dframe->line_verts.count = 0;
}
@ -1075,45 +1118,161 @@ Vulkan_Draw_BlendScreen (quat_t color, vulkan_ctx_t *ctx)
}
int
Vulkan_Draw_AddFont (rfont_t *font, vulkan_ctx_t *ctx)
Vulkan_Draw_AddFont (rfont_t *rfont, vulkan_ctx_t *ctx)
{
qfv_device_t *device = ctx->device;
qfv_devfuncs_t *dfunc = device->funcs;
drawctx_t *dctx = ctx->draw_context;
int fontid = dctx->fonts.size;
DARRAY_OPEN_AT (&dctx->fonts, fontid, 1);
drawfont_t *font = &dctx->fonts.a[fontid];
if (dctx->font_tex) {
Vulkan_UnloadTex (ctx, dctx->font_tex);
dctx->font_tex = 0;
}
dctx->font = font;
if (dctx->font) {
tex_t tex = {
.width = font->scrap.width,
.height = font->scrap.height,
.format = tex_l,
.loaded = 1,
.data = font->scrap_bitmap,
font->font = rfont;
font->resource = malloc (sizeof (drawfontres_t));
font->resource->resource = (qfv_resource_t) {
.name = "glyph_data", //FIXME include font info
.va_ctx = ctx->va_ctx,
.memory_properties = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
.num_objects = 4,
.objects = &font->resource->glyph_data,
};
font->resource->glyph_data = (qfv_resobj_t) {
.name = "geom",
.type = qfv_res_buffer,
.buffer = {
.size = rfont->num_glyphs * 4 * sizeof (glyphvert_t),
.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT
| VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT,
},
};
__auto_type glyph_data = &font->resource->glyph_data;
font->resource->glyph_bview = (qfv_resobj_t) {
.name = "geom_view",
.type = qfv_res_buffer_view,
.buffer_view = {
.buffer = 0,
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
.offset = 0,
.size = font->resource->glyph_data.buffer.size,
},
};
__auto_type glyph_bview = &font->resource->glyph_bview;
tex_t tex = {
.width = rfont->scrap.width,
.height = rfont->scrap.height,
.format = tex_l,
.loaded = 1,
.data = rfont->scrap_bitmap,
};
QFV_ResourceInitTexImage (&font->resource->glyph_image, "image", 0, &tex);
__auto_type glyph_image = &font->resource->glyph_image;
font->resource->glyph_iview = (qfv_resobj_t) {
.name = "image_view",
.type = qfv_res_image_view,
.image_view = {
.image = 2,
.type = VK_IMAGE_VIEW_TYPE_2D,
.format = font->resource->glyph_image.image.format,
.aspect = VK_IMAGE_ASPECT_COLOR_BIT,
},
};
__auto_type glyph_iview = &font->resource->glyph_iview;
QFV_CreateResource (ctx->device, &font->resource->resource);
qfv_packet_t *packet = QFV_PacketAcquire (ctx->staging);
glyphvert_t *verts = QFV_PacketExtend (packet, glyph_data->buffer.size);
for (FT_Long i = 0; i < rfont->num_glyphs; i++) {
rglyph_t *glyph = &rfont->glyphs[i];
float x = 0;//glyph->bearing[0];
float y = 0;//-glyph->bearing[1]; // glyph +Y goes up
float w = glyph->rect->width;
float h = glyph->rect->height;
float u = glyph->rect->x;
float v = glyph->rect->y;
// assumes the glyph image is square
float s = 1.0 / rfont->scrap.width;
verts[i * 4 + 0] = (glyphvert_t) {
.offset = { x + 0, y + 0 },
.uv = {(u + 0.25) * s, (v + 0.25) * s },
};
verts[i * 4 + 1] = (glyphvert_t) {
.offset = { x + 0, y + h },
.uv = {(u + 0.25) * s, (v + h - 0.25) * s },
};
verts[i * 4 + 2] = (glyphvert_t) {
.offset = { x + w, y + 0 },
.uv = {(u + w - 0.25) * s, (v + 0.25) * s },
};
verts[i * 4 + 3] = (glyphvert_t) {
.offset = { x + w, y + h },
.uv = {(u + w - 0.25) * s, (v + h - 0.25) * s },
};
dctx->font_tex = Vulkan_LoadTex (ctx, &tex, 0, "draw.font");
}
QFV_PacketCopyBuffer (packet, glyph_data->buffer.buffer,
&bufferBarriers[qfv_BB_TransferWrite_to_UniformRead]);
QFV_PacketSubmit (packet);
packet = QFV_PacketAcquire (ctx->staging);
byte *texels = QFV_PacketExtend (packet, tex.width * tex.height);
memcpy (texels, tex.data, tex.width * tex.height);
QFV_PacketCopyImage (packet, glyph_image->image.image,
tex.width, tex.height,
&imageBarriers[qfv_LT_TransferDst_to_ShaderReadOnly]);
QFV_PacketSubmit (packet);
__auto_type layouts = QFV_AllocDescriptorSetLayoutSet (1, alloca);
layouts->a[0] = Vulkan_CreateDescriptorSetLayout (ctx, "glyph_data_set");
__auto_type pool = Vulkan_CreateDescriptorPool (ctx, "glyph_pool");
__auto_type glyph_sets = QFV_AllocateDescriptorSet (device, pool, layouts);
font->set = glyph_sets->a[0];
VkDescriptorImageInfo imageInfo = {
dctx->sampler,
glyph_iview->image_view.view,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
};
VkWriteDescriptorSet write[] = {
{ VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, 0,
font->set, 0, 0, 1,
VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER,
0, 0, &glyph_bview->buffer_view.view },
{ VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, 0,
font->set, 1, 0, 1,
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
&imageInfo, 0, 0 },
};
dfunc->vkUpdateDescriptorSets (device->dev, 2, write, 0, 0);
free (glyph_sets);
return 0;
}
typedef struct {
drawframe_t *dframe;
subpic_t *subpic;
quat_t color;
drawframe_t *dframe;
drawfont_t *font;
byte color[4];
} rgctx_t;
static void
vulkan_render_glyph (rglyph_t *glyph, int x, int y, void *_rgctx)
{
rgctx_t *rgctx = _rgctx;
glyphqueue_t *queue = &rgctx->dframe->glyph_insts;;
*((vrect_t **) &rgctx->subpic->rect) = glyph->rect;
int gw = glyph->rect->width;
int gh = glyph->rect->height;
draw_pic (x, y, gw, gh, rgctx->subpic, 0, 0, gw, gh, rgctx->color,
&rgctx->dframe->iaquad_verts);
if (queue->count >= queue->size) {
return;
}
glyphinst_t *inst = &queue->glyphs[queue->count++];
inst->index = glyph->charcode;
QuatCopy (rgctx->color, inst->color);
inst->position[0] = x;
inst->position[1] = y;
}
void
@ -1121,21 +1280,16 @@ Vulkan_Draw_FontString (int x, int y, int fontid, const char *str,
vulkan_ctx_t *ctx)
{
drawctx_t *dctx = ctx->draw_context;
if (!dctx->font) {
if (fontid < 0 || (unsigned) fontid > dctx->fonts.size) {
return;
}
drawframe_t *dframe = &dctx->frames.a[ctx->curFrame];
//FIXME ewwwwwww
subpic_t glyph_subpic = {
.width = dctx->font->scrap.width,
.height = dctx->font->scrap.height,
.size = 1.0 / dctx->font->scrap.width,
};
rgctx_t rgctx = {
.dframe = dframe,
.subpic = &glyph_subpic,
.color = { 0.5, 1, 0.6, 1 },
.font = &dctx->fonts.a[fontid],
.color = { 127, 255, 153, 255 },
};
rtext_t text = {
.text = str,
@ -1144,7 +1298,7 @@ Vulkan_Draw_FontString (int x, int y, int fontid, const char *str,
.direction = HB_DIRECTION_LTR,
};
rshaper_t *shaper = RText_NewShaper (dctx->font);
rshaper_t *shaper = RText_NewShaper (rgctx.font->font);
RText_RenderText (shaper, &text, x, y, vulkan_render_glyph, &rgctx);
RText_DeleteShaper (shaper);
}