0
0
Fork 0
mirror of https://git.code.sf.net/p/quake/quakeforge synced 2025-04-24 10:31:35 +00:00

[vulkan] Use resource for allocating alias models

So far only the mesh data, so it doesn't make any difference to
allocations, but it's the first step to reducing memory object
allocation.
This commit is contained in:
Bill Currie 2023-12-19 03:28:29 +09:00
parent 4ed1ef3353
commit 2e9e247ca4
2 changed files with 186 additions and 191 deletions
include/QF/Vulkan
libs/models/alias

View file

@ -56,7 +56,7 @@ typedef struct qfv_alias_mesh_s {
VkBuffer vertex_buffer;
VkBuffer uv_buffer;
VkBuffer index_buffer;
VkDeviceMemory memory;
struct qfv_resource_s *resources;
} qfv_alias_mesh_t;
typedef struct qfv_alias_skin_s {

View file

@ -53,6 +53,7 @@
#include "QF/Vulkan/debug.h"
#include "QF/Vulkan/image.h"
#include "QF/Vulkan/instance.h"
#include "QF/Vulkan/resource.h"
#include "QF/Vulkan/staging.h"
#include "mod_internal.h"
@ -81,7 +82,6 @@ vulkan_alias_clear (model_t *m, void *data)
{
vulkan_ctx_t *ctx = data;
qfv_device_t *device = ctx->device;
qfv_devfuncs_t *dfunc = device->funcs;
aliashdr_t *hdr;
qfv_alias_mesh_t *mesh;
@ -92,10 +92,7 @@ vulkan_alias_clear (model_t *m, void *data)
hdr = Cache_Get (&m->cache);
}
mesh = (qfv_alias_mesh_t *) ((byte *) hdr + hdr->commands);
dfunc->vkDestroyBuffer (device->dev, mesh->vertex_buffer, 0);
dfunc->vkDestroyBuffer (device->dev, mesh->uv_buffer, 0);
dfunc->vkDestroyBuffer (device->dev, mesh->index_buffer, 0);
dfunc->vkFreeMemory (device->dev, mesh->memory, 0);
QFV_DestroyResource (device, mesh->resources);
__auto_type skins = (maliasskindesc_t *) ((byte *) hdr + hdr->skindesc);
for (int i = 0; i < hdr->mdl.numskins; i++) {
@ -239,67 +236,14 @@ Vulkan_Mod_LoadAllSkins (mod_alias_ctx_t *alias_ctx, vulkan_ctx_t *ctx)
}
}
void
Vulkan_Mod_FinalizeAliasModel (mod_alias_ctx_t *alias_ctx, vulkan_ctx_t *ctx)
static int
separate_verts (int *indexmap, int numverts, int numtris,
const mod_alias_ctx_t *alias_ctx)
{
alias_ctx->mod->clear = vulkan_alias_clear;
alias_ctx->mod->data = ctx;
}
void
Vulkan_Mod_LoadExternalSkins (mod_alias_ctx_t *alias_ctx, vulkan_ctx_t *ctx)
{
}
static size_t
get_buffer_size (qfv_device_t *device, VkBuffer buffer)
{
qfv_devfuncs_t *dfunc = device->funcs;
size_t size;
size_t align;
VkMemoryRequirements requirements;
dfunc->vkGetBufferMemoryRequirements (device->dev, buffer, &requirements);
size = requirements.size;
align = requirements.alignment - 1;
size = (size + align) & ~(align);
return size;
}
void
Vulkan_Mod_MakeAliasModelDisplayLists (mod_alias_ctx_t *alias_ctx, void *_m,
int _s, int extra, vulkan_ctx_t *ctx)
{
aliashdr_t *header = alias_ctx->header;
qfv_device_t *device = ctx->device;
qfv_devfuncs_t *dfunc = device->funcs;
aliasvrt_t *verts;
aliasuv_t *uv;
trivertx_t *pv;
int *indexmap;
uint32_t *indices;
int numverts;
int numtris;
int i, j;
int pose;
vec3_t pos;
if (header->mdl.ident == HEADER_MDL16)
VectorScale (header->mdl.scale, 1/256.0, header->mdl.scale);
numverts = header->mdl.numverts;
numtris = header->mdl.numtris;
// initialize indexmap to -1 (unduplicated). any other value indicates
// both that the vertex has been duplicated and the index of the
// duplicate vertex.
indexmap = malloc (numverts * sizeof (int));
memset (indexmap, -1, numverts * sizeof (int));
// check for onseam verts, and duplicate any that are associated with
// back-facing triangles
for (i = 0; i < numtris; i++) {
for (j = 0; j < 3; j++) {
for (int i = 0; i < numtris; i++) {
for (int j = 0; j < 3; j++) {
int vind = alias_ctx->triangles.a[i].vertindex[j];
if (alias_ctx->stverts.a[vind].onseam
&& !alias_ctx->triangles.a[i].facesfront) {
@ -311,92 +255,27 @@ Vulkan_Mod_MakeAliasModelDisplayLists (mod_alias_ctx_t *alias_ctx, void *_m,
}
}
}
return numverts;
}
// we now know exactly how many vertices we need, so built the vertex
// and index data arrays
// The layout is:
// vbuf:{vertex, normal} * (numposes * numverts)
// uvbuf:{uv} * (numverts)
// ibuf:{index} * (numtris * 3)
// numverts includes the duplicated seam vertices.
// The vertex buffer will be bound with various offsets based on the
// current and previous pose, uvbuff "statically" bound as uvs are not
// animated by pose, and the same for ibuf: indices will never change for
// the mesh
size_t vert_count = numverts * header->numposes;
size_t vert_size = vert_count * sizeof (aliasvrt_t);
size_t uv_size = numverts * sizeof (aliasuv_t);
size_t ind_size = 3 * numtris * sizeof (uint32_t);
VkBuffer vbuff = QFV_CreateBuffer (device, vert_size,
VK_BUFFER_USAGE_TRANSFER_DST_BIT
| VK_BUFFER_USAGE_VERTEX_BUFFER_BIT);
VkBuffer uvbuff = QFV_CreateBuffer (device, uv_size,
VK_BUFFER_USAGE_TRANSFER_DST_BIT
| VK_BUFFER_USAGE_VERTEX_BUFFER_BIT);
VkBuffer ibuff = QFV_CreateBuffer (device, ind_size,
VK_BUFFER_USAGE_TRANSFER_DST_BIT
| VK_BUFFER_USAGE_INDEX_BUFFER_BIT);
QFV_duSetObjectName (device, VK_OBJECT_TYPE_BUFFER, vbuff,
va (ctx->va_ctx, "buffer:alias:vertex:%s",
alias_ctx->mod->name));
QFV_duSetObjectName (device, VK_OBJECT_TYPE_BUFFER, uvbuff,
va (ctx->va_ctx, "buffer:alias:uv:%s",
alias_ctx->mod->name));
QFV_duSetObjectName (device, VK_OBJECT_TYPE_BUFFER, ibuff,
va (ctx->va_ctx, "buffer:alias:index:%s",
alias_ctx->mod->name));
size_t voffs = 0;
size_t uvoffs = voffs + get_buffer_size (device, vbuff);
size_t ioffs = uvoffs + get_buffer_size (device, uvbuff);
size_t buff_size = ioffs + get_buffer_size (device, ibuff);
VkDeviceMemory mem;
mem = QFV_AllocBufferMemory (device, vbuff,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
buff_size, 0);
QFV_duSetObjectName (device, VK_OBJECT_TYPE_DEVICE_MEMORY, mem,
va (ctx->va_ctx, "memory:alias:vuvi:%s",
alias_ctx->mod->name));
QFV_BindBufferMemory (device, vbuff, mem, voffs);
QFV_BindBufferMemory (device, uvbuff, mem, uvoffs);
QFV_BindBufferMemory (device, ibuff, mem, ioffs);
qfv_stagebuf_t *stage = QFV_CreateStagingBuffer (device,
va (ctx->va_ctx,
"alias:%s",
alias_ctx->mod->name),
buff_size, ctx->cmdpool);
qfv_packet_t *packet = QFV_PacketAcquire (stage);
verts = QFV_PacketExtend (packet, vert_size);
uv = QFV_PacketExtend (packet, uv_size);
indices = QFV_PacketExtend (packet, ind_size);
// populate the uvs, duplicating and shifting any that are on the seam
// and associated with back-facing triangles (marked by non-negative
// indexmap entry).
// the s coordinate is shifted right by half the skin width.
for (i = 0; i < header->mdl.numverts; i++) {
int vind = indexmap[i];
uv[i].u = (float) alias_ctx->stverts.a[i].s / header->mdl.skinwidth;
uv[i].v = (float) alias_ctx->stverts.a[i].t / header->mdl.skinheight;
if (vind != -1) {
uv[vind] = uv[i];
uv[vind].u += 0.5;
}
}
// poputlate the vertex position and normal data, duplicating for
static void
build_verts (aliasvrt_t *verts, int numposes, int numverts, const mdl_t *mdl,
const int *indexmap, const mod_alias_ctx_t *alias_ctx)
{
int i, pose;
// populate the vertex position and normal data, duplicating for
// back-facing on-seam verts (indicated by non-negative indexmap entry)
for (i = 0, pose = 0; i < header->numposes; i++, pose += numverts) {
for (j = 0; j < header->mdl.numverts; j++) {
pv = &alias_ctx->poseverts.a[i][j];
if (extra) {
VectorMultAdd (pv[header->mdl.numverts].v, 256, pv->v, pos);
for (i = 0, pose = 0; i < numposes; i++, pose += numverts) {
for (int j = 0; j < mdl->numverts; j++) {
auto pv = &alias_ctx->poseverts.a[i][j];
vec3_t pos;
if (mdl->ident == HEADER_MDL16) {
VectorMultAdd (pv[mdl->numverts].v, 256, pv->v, pos);
} else {
VectorCopy (pv->v, pos);
}
VectorCompMultAdd (header->mdl.scale_origin, header->mdl.scale,
pos, verts[pose + j].vertex);
VectorCompMultAdd (mdl->scale_origin, mdl->scale, pos,
verts[pose + j].vertex);
verts[pose + j].vertex[3] = 1;
VectorCopy (vertex_normals[pv->lightnormalindex],
verts[pose + j].normal);
@ -407,10 +286,35 @@ Vulkan_Mod_MakeAliasModelDisplayLists (mod_alias_ctx_t *alias_ctx, void *_m,
}
}
}
}
static void
build_uvs (aliasuv_t *uv, const mdl_t *mdl, const int *indexmap,
const mod_alias_ctx_t *alias_ctx)
{
// populate the uvs, duplicating and shifting any that are on the seam
// and associated with back-facing triangles (marked by non-negative
// indexmap entry).
// the s coordinate is shifted right by half the skin width.
for (int i = 0; i < mdl->numverts; i++) {
int vind = indexmap[i];
uv[i].u = (float) alias_ctx->stverts.a[i].s / mdl->skinwidth;
uv[i].v = (float) alias_ctx->stverts.a[i].t / mdl->skinheight;
if (vind != -1) {
uv[vind] = uv[i];
uv[vind].u += 0.5;
}
}
}
static void
build_inds (uint32_t *indices, int numtris, const int *indexmap,
const mod_alias_ctx_t *alias_ctx)
{
// now build the indices for DrawElements
for (i = 0; i < numtris; i++) {
for (j = 0; j < 3; j++) {
for (int i = 0; i < numtris; i++) {
for (int j = 0; j < 3; j++) {
int vind = alias_ctx->triangles.a[i].vertindex[j];
// can't use indexmap to do the test because it indicates only
// that the vertex has been duplicated, not whether or not
@ -422,57 +326,148 @@ Vulkan_Mod_MakeAliasModelDisplayLists (mod_alias_ctx_t *alias_ctx, void *_m,
indices[3 * i + j] = vind;
}
}
// finished with indexmap
free (indexmap);
}
header->poseverts = numverts;
void
Vulkan_Mod_FinalizeAliasModel (mod_alias_ctx_t *alias_ctx, vulkan_ctx_t *ctx)
{
auto device = ctx->device;
qfv_bufferbarrier_t bb = bufferBarriers[qfv_BB_Unknown_to_TransferWrite];
VkBufferMemoryBarrier wr_barriers[] = {
bb.barrier, bb.barrier, bb.barrier,
alias_ctx->mod->clear = vulkan_alias_clear;
alias_ctx->mod->data = ctx;
auto hdr = alias_ctx->header;
int numverts = hdr->mdl.numverts;
int numtris = hdr->mdl.numtris;
int indexmap[numverts];
// initialize indexmap to -1 (unduplicated). any other value indicates
// both that the vertex has been duplicated and the index of the
// duplicate vertex.
memset (indexmap, -1, sizeof (indexmap));
numverts = separate_verts (indexmap, numverts, numtris, alias_ctx);
hdr->poseverts = numverts;
// we now know exactly how many vertices we need, so build the vertex
// and index data arrays
// The layout is:
// vbuf:{vertex, normal} * (numposes * numverts)
// uvbuf:{uv} * (numverts)
// ibuf:{index} * (numtris * 3)
// numverts includes the duplicated seam vertices.
// The vertex buffer will be bound with various offsets based on the
// current and previous pose, uvbuff "statically" bound as uvs are not
// animated by pose, and the same for ibuf: indices will never change for
// the mesh
size_t vert_count = numverts * hdr->numposes;
size_t vert_size = vert_count * sizeof (aliasvrt_t);
size_t uv_size = numverts * sizeof (aliasuv_t);
size_t ind_size = 3 * numtris * sizeof (uint32_t);
auto mesh = (qfv_alias_mesh_t *) ((byte *) hdr + hdr->commands);
mesh->resources = malloc (sizeof (qfv_resource_t)
+ sizeof (qfv_resobj_t)
+ sizeof (qfv_resobj_t)
+ sizeof (qfv_resobj_t));
mesh->resources[0] = (qfv_resource_t) {
.name = va (ctx->va_ctx, "alias:%s", alias_ctx->mod->name),
.va_ctx = ctx->va_ctx,
.memory_properties = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
.num_objects = 3,
.objects = (qfv_resobj_t *) &mesh->resources[1],
};
wr_barriers[0].buffer = vbuff;
wr_barriers[0].size = vert_size;
wr_barriers[1].buffer = uvbuff;
wr_barriers[1].size = uv_size;
wr_barriers[2].buffer = ibuff;
wr_barriers[2].size = ind_size;
dfunc->vkCmdPipelineBarrier (packet->cmd, bb.srcStages, bb.dstStages,
0, 0, 0, 3, wr_barriers, 0, 0);
VkBufferCopy copy_region[] = {
{ packet->offset, 0, vert_size },
{ packet->offset + vert_size, 0, uv_size },
{ packet->offset + vert_size + uv_size, 0, ind_size },
auto vert_obj = mesh->resources->objects;
auto uv_obj = &vert_obj[1];
auto index_obj = &uv_obj[1];
*vert_obj = (qfv_resobj_t) {
.name = "vertex",
.type = qfv_res_buffer,
.buffer = {
.size = vert_size,
.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT
| VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
},
};
dfunc->vkCmdCopyBuffer (packet->cmd, stage->buffer,
vbuff, 1, &copy_region[0]);
dfunc->vkCmdCopyBuffer (packet->cmd, stage->buffer,
uvbuff, 1, &copy_region[1]);
dfunc->vkCmdCopyBuffer (packet->cmd, stage->buffer,
ibuff, 1, &copy_region[2]);
// both qfv_BB_TransferWrite_to_VertexAttrRead and
// qfv_BB_TransferWrite_to_IndexRead have the same stage flags
bb = bufferBarriers[qfv_BB_TransferWrite_to_VertexAttrRead];
VkBufferMemoryBarrier rd_barriers[] = {
bufferBarriers[qfv_BB_TransferWrite_to_VertexAttrRead].barrier,
bufferBarriers[qfv_BB_TransferWrite_to_VertexAttrRead].barrier,
bufferBarriers[qfv_BB_TransferWrite_to_IndexRead].barrier,
*uv_obj = (qfv_resobj_t) {
.name = "uv",
.type = qfv_res_buffer,
.buffer = {
.size = uv_size,
.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT
| VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
},
};
rd_barriers[0].buffer = vbuff;
rd_barriers[0].size = vert_size;
rd_barriers[1].buffer = uvbuff;
rd_barriers[1].size = uv_size;
rd_barriers[2].buffer = ibuff;
rd_barriers[2].size = ind_size;
dfunc->vkCmdPipelineBarrier (packet->cmd, bb.srcStages, bb.dstStages,
0, 0, 0, 3, rd_barriers, 0, 0);
*index_obj = (qfv_resobj_t) {
.name = "index",
.type = qfv_res_buffer,
.buffer = {
.size = ind_size,
.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT
| VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
},
};
QFV_CreateResource (device, mesh->resources);
mesh->vertex_buffer = vert_obj->buffer.buffer;
mesh->uv_buffer = uv_obj->buffer.buffer;
mesh->index_buffer = index_obj->buffer.buffer;
size_t packet_size = vert_size + uv_size + ind_size;
auto packet = QFV_PacketAcquire (ctx->staging);
byte *packet_start = QFV_PacketExtend (packet, packet_size);
byte *packet_data = packet_start;
qfv_scatter_t vert_scatter = {
.srcOffset = packet_data - packet_start,
.dstOffset = 0,
.length = vert_size,
};
auto verts = (aliasvrt_t *) packet_data;
packet_data += vert_scatter.length;
build_verts (verts, hdr->numposes, numverts, &hdr->mdl,
indexmap, alias_ctx);
qfv_scatter_t uv_scatter = {
.srcOffset = packet_data - packet_start,
.dstOffset = 0,
.length = uv_size,
};
auto uv = (aliasuv_t *) packet_data;
packet_data += uv_scatter.length;
build_uvs (uv, &hdr->mdl, indexmap, alias_ctx);
qfv_scatter_t ind_scatter = {
.srcOffset = packet_data - packet_start,
.dstOffset = 0,
.length = ind_size,
};
auto indices = (uint32_t *) packet_data;
packet_data += ind_scatter.length;
build_inds (indices, hdr->mdl.numtris, indexmap, alias_ctx);
QFV_PacketScatterBuffer (packet, mesh->vertex_buffer, 1, &vert_scatter,
&bufferBarriers[qfv_BB_TransferWrite_to_VertexAttrRead]);
QFV_PacketScatterBuffer (packet, mesh->uv_buffer, 1, &uv_scatter,
&bufferBarriers[qfv_BB_TransferWrite_to_VertexAttrRead]);
QFV_PacketScatterBuffer (packet, mesh->index_buffer, 1, &ind_scatter,
&bufferBarriers[qfv_BB_TransferWrite_to_IndexRead]);
QFV_PacketSubmit (packet);
QFV_DestroyStagingBuffer (stage);
}
void
Vulkan_Mod_LoadExternalSkins (mod_alias_ctx_t *alias_ctx, vulkan_ctx_t *ctx)
{
}
void
Vulkan_Mod_MakeAliasModelDisplayLists (mod_alias_ctx_t *alias_ctx, void *_m,
int _s, int extra, vulkan_ctx_t *ctx)
{
aliashdr_t *header = alias_ctx->header;
if (header->mdl.ident == HEADER_MDL16)
VectorScale (header->mdl.scale, 1/256.0, header->mdl.scale);
qfv_alias_mesh_t *mesh = Hunk_Alloc (0, sizeof (qfv_alias_mesh_t));
mesh->vertex_buffer = vbuff;
mesh->uv_buffer = uvbuff;
mesh->index_buffer = ibuff;
mesh->memory = mem;
header->commands = (byte *) mesh - (byte *) header;
}