quakeforge/libs/models/iqm/vulkan_model_iqm.c
Bill Currie 119e9766b9 [iqm] Use 32-bit indices for large models
That is, those with more than 65520 vertices. Not properly supported for
sw or gl, and glsl isn't rendering properly for some reason (renderdoc
does see the meshes, though, so maybe depth or winding issues).
2024-01-09 13:36:46 +09:00

471 lines
14 KiB
C

/*
vulkan_model_iqm.c
iqm model processing for Vulkan
Copyright (C) 2011 Bill Currie <bill@taniwha.org>
Author: Bill Currie <bill@taniwha.org>
Date: 2022/05/03
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#ifdef HAVE_STRING_H
# include <string.h>
#endif
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
#include <stdlib.h>
#include "QF/dstring.h"
#include "QF/image.h"
#include "QF/quakefs.h"
#include "QF/va.h"
#include "QF/Vulkan/barrier.h"
#include "QF/Vulkan/device.h"
#include "QF/Vulkan/image.h"
#include "QF/Vulkan/resource.h"
#include "QF/Vulkan/staging.h"
#include "QF/Vulkan/qf_iqm.h"
#include "QF/Vulkan/qf_texture.h"
#include "mod_internal.h"
#include "r_shared.h"
#include "vid_vulkan.h"
static byte null_texture[] = {
204, 204, 204, 255,
204, 204, 204, 255,
204, 204, 204, 255,
204, 204, 204, 255,
};
#if 0
static byte null_normmap[] = {
127, 127, 255, 255,
127, 127, 255, 255,
127, 127, 255, 255,
127, 127, 255, 255,
};
#endif
static void
vulkan_iqm_clear (model_t *mod, void *data)
{
vulkan_ctx_t *ctx = data;
qfv_device_t *device = ctx->device;
iqm_t *iqm = (iqm_t *) mod->aliashdr;
qfv_iqm_t *mesh = iqm->extra_data;
mod->needload = true;
for (int i = 0; i < iqm->num_meshes; i++) {
Vulkan_IQMRemoveSkin (ctx, &mesh->skins[i]);
}
Vulkan_IQMRemoveBones (ctx, iqm);//FIXME doesn't belong here (per-instance)
QFV_DestroyResource (device, mesh->bones);
QFV_DestroyResource (device, mesh->mesh);
free (mesh);
Mod_FreeIQM (iqm);
}
static void
vulkan_iqm_init_image (iqm_t *iqm, int meshnum, qfv_resobj_t *image)
{
const char *material = iqm->text + iqm->meshes[meshnum].material;
dstring_t *str = dstring_new ();
dstring_copystr (str, material);
QFS_StripExtension (str->str, str->str);
tex_t dummy_tex;
tex_t *tex;
if (!(tex = LoadImage (va (0, "textures/%s", str->str), 0))) {
dummy_tex = (tex_t) {
.width = 2,
.height = 2,
.format = tex_rgba,
};
tex = &dummy_tex;
}
QFV_ResourceInitTexImage (image, material, 1, tex);
dstring_delete (str);
}
static void
iqm_transfer_texture (tex_t *tex, VkImage image, qfv_stagebuf_t *stage,
qfv_device_t *device)
{
qfv_devfuncs_t *dfunc = device->funcs;
if (tex->format != tex_rgb && tex->format != tex_rgba) {
Sys_Error ("can't transfer iqm image");
}
// FIXME correct only for rgb and rgba
size_t layer_size = tex->width * tex->height * tex->format;
qfv_packet_t *packet = QFV_PacketAcquire (stage);
byte *dst = QFV_PacketExtend (packet, layer_size);
qfv_imagebarrier_t ib = imageBarriers[qfv_LT_Undefined_to_TransferDst];
ib.barrier.image = image;
ib.barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS;
ib.barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS;
dfunc->vkCmdPipelineBarrier (packet->cmd, ib.srcStages, ib.dstStages,
0, 0, 0, 0, 0,
1, &ib.barrier);
memcpy (dst, tex->data, layer_size);
VkBufferImageCopy copy = {
packet->offset, 0, 0,
{VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1},
{0, 0, 0}, {tex->width, tex->height, 1},
};
dfunc->vkCmdCopyBufferToImage (packet->cmd, packet->stage->buffer,
image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1, &copy);
int mipLevels = QFV_MipLevels (tex->width, tex->height);
if (mipLevels == 1) {
ib = imageBarriers[qfv_LT_TransferDst_to_ShaderReadOnly];
ib.barrier.image = image;
ib.barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS;
ib.barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS;
dfunc->vkCmdPipelineBarrier (packet->cmd, ib.srcStages, ib.dstStages,
0, 0, 0, 0, 0,
1, &ib.barrier);
} else {
QFV_GenerateMipMaps (device, packet->cmd, image, mipLevels,
tex->width, tex->height, 1);
}
QFV_PacketSubmit (packet);
}
static void
vulkan_iqm_load_textures (model_t *mod, iqm_t *iqm, qfv_iqm_t *mesh,
vulkan_ctx_t *ctx)
{
qfv_device_t *device = ctx->device;
dstring_t *str = dstring_new ();
tex_t *tex;
size_t buff_size = 0;
qfv_resobj_t *objects = mesh->mesh->objects;
for (int i = 0; i < iqm->num_meshes; i++) {
int image_ind = 3 + 2 * i;
VkExtent3D extent = objects[image_ind].image.extent;
// probably 3 or 4 bytes per pixel FIXME
buff_size = max (buff_size, extent.width * extent.height * 4);
}
qfv_stagebuf_t *stage = QFV_CreateStagingBuffer (device,
va (ctx->va_ctx, "iqm:%s",
mod->name),
4 * buff_size,
ctx->cmdpool);
for (int i = 0; i < iqm->num_meshes; i++) {
int image_ind = 3 + 2 * i;
__auto_type image = &objects[image_ind].image;
__auto_type view = &objects[image_ind + 1].image_view;
qfv_iqm_skin_t *skin = &mesh->skins[i];
*skin = (qfv_iqm_skin_t) {
.view = view->view,
.colora = { 255, 255, 255, 255 },
.colorb = { 255, 255, 255, 255 },
};
dstring_copystr (str, iqm->text + iqm->meshes[i].material);
QFS_StripExtension (str->str, str->str);
if (!(tex = LoadImage (va (0, "textures/%s", str->str), 1))) {
static tex_t null_tex = {
.width = 2,
.height = 2,
.format = tex_rgba,
.data = null_texture,
};
tex = &null_tex;
}
iqm_transfer_texture (tex, image->image, stage, device);
Vulkan_IQMAddSkin (ctx, skin);
}
dstring_delete (str);
QFV_DestroyStagingBuffer (stage);
}
static void
vulkan_iqm_load_arrays (model_t *mod, iqm_t *iqm, qfv_iqm_t *mesh,
vulkan_ctx_t *ctx)
{
qfv_device_t *device = ctx->device;
qfv_devfuncs_t *dfunc = device->funcs;
iqmctx_t *ictx = ctx->iqm_context;
size_t geom_size = iqm->num_verts * sizeof (iqmgvert_t);
size_t rend_size = iqm->num_verts * sizeof (iqmrvert_t);
size_t elem_size = iqm->num_elements * sizeof (uint16_t);
if (iqm->num_verts > 0xfff0) {
elem_size = iqm->num_elements * sizeof (uint32_t);
}
size_t buff_size = geom_size + rend_size + elem_size + 1024;
qfv_stagebuf_t *stage = QFV_CreateStagingBuffer (device,
va (ctx->va_ctx, "iqm:%s",
mod->name),
buff_size, ctx->cmdpool);
qfv_packet_t *gpacket = QFV_PacketAcquire (stage);
iqmgvert_t *gverts = QFV_PacketExtend (gpacket, geom_size);
qfv_packet_t *rpacket = QFV_PacketAcquire (stage);
iqmrvert_t *rverts = QFV_PacketExtend (rpacket, rend_size);
qfv_packet_t *epacket = QFV_PacketAcquire (stage);
uint16_t *elements = QFV_PacketExtend (epacket, elem_size);
//FIXME this whole thing is silly, but some person went and interleaved
//all the vertex data prematurely
for (int i = 0; i < iqm->num_verts; i++) {
byte *data = iqm->vertices + i * iqm->stride;
iqmgvert_t *gv = gverts + i;
iqmrvert_t *rv = rverts + i;
for (int j = 0; j < iqm->num_arrays; j++) {
__auto_type va = &iqm->vertexarrays[j];
// FIXME assumes standard iqm sizes
size_t size = 0;
switch (va->type) {
case IQM_POSITION:
size = sizeof (gv->vertex);
memcpy (gv->vertex, data, size);
break;
case IQM_TEXCOORD:
size = sizeof (rv->uv);
memcpy (rv->uv, data, size);
break;
case IQM_NORMAL:
size = sizeof (rv->normal);
memcpy (rv->normal, data, size);
break;
case IQM_TANGENT:
size = sizeof (rv->tangent);
memcpy (rv->tangent, data, size);
break;
case IQM_BLENDINDEXES:
size = sizeof (gv->bones);
memcpy (gv->bones, data, size);
break;
case IQM_BLENDWEIGHTS:
size = sizeof (gv->weights);
memcpy (gv->weights, data, size);
break;
case IQM_COLOR:
size = sizeof (rv->color);
memcpy (rv->color, data, size);
break;
case IQM_CUSTOM:
// FIXME model loader doesn't handle these, so nothing to do
break;
}
data += size;
}
}
memcpy (elements, iqm->elements16, elem_size);
qfv_bufferbarrier_t bb[] = {
bufferBarriers[qfv_BB_Unknown_to_TransferWrite],
bufferBarriers[qfv_BB_Unknown_to_TransferWrite],
bufferBarriers[qfv_BB_Unknown_to_TransferWrite],
};
bb[0].barrier.buffer = mesh->geom_buffer;
bb[0].barrier.size = geom_size;
bb[1].barrier.buffer = mesh->rend_buffer;
bb[1].barrier.size = rend_size;
bb[2].barrier.buffer = mesh->index_buffer;
bb[2].barrier.size = elem_size;
VkBufferCopy copy_region[] = {
{ gpacket->offset, 0, geom_size },
{ rpacket->offset, 0, rend_size },
{ epacket->offset, 0, elem_size },
};
dfunc->vkCmdPipelineBarrier (gpacket->cmd, bb[0].srcStages, bb[0].dstStages,
0, 0, 0, 1, &bb[0].barrier, 0, 0);
dfunc->vkCmdPipelineBarrier (rpacket->cmd, bb[0].srcStages, bb[0].dstStages,
0, 0, 0, 1, &bb[1].barrier, 0, 0);
dfunc->vkCmdPipelineBarrier (epacket->cmd, bb[0].srcStages, bb[0].dstStages,
0, 0, 0, 1, &bb[2].barrier, 0, 0);
dfunc->vkCmdCopyBuffer (gpacket->cmd, stage->buffer,
mesh->geom_buffer, 1, &copy_region[0]);
dfunc->vkCmdCopyBuffer (rpacket->cmd, stage->buffer,
mesh->rend_buffer, 1, &copy_region[1]);
dfunc->vkCmdCopyBuffer (epacket->cmd, stage->buffer,
mesh->index_buffer, 1, &copy_region[2]);
bb[0] = bufferBarriers[qfv_BB_TransferWrite_to_VertexAttrRead];
bb[1] = bufferBarriers[qfv_BB_TransferWrite_to_VertexAttrRead];
bb[2] = bufferBarriers[qfv_BB_TransferWrite_to_VertexAttrRead];
bb[0].barrier.buffer = mesh->geom_buffer;
bb[0].barrier.size = geom_size;
bb[1].barrier.buffer = mesh->rend_buffer;
bb[1].barrier.size = rend_size;
bb[2].barrier.buffer = mesh->index_buffer;
bb[2].barrier.size = elem_size;
dfunc->vkCmdPipelineBarrier (gpacket->cmd, bb[0].srcStages, bb[0].dstStages,
0, 0, 0, 1, &bb[0].barrier, 0, 0);
dfunc->vkCmdPipelineBarrier (rpacket->cmd, bb[0].srcStages, bb[0].dstStages,
0, 0, 0, 1, &bb[1].barrier, 0, 0);
dfunc->vkCmdPipelineBarrier (epacket->cmd, bb[0].srcStages, bb[0].dstStages,
0, 0, 0, 1, &bb[2].barrier, 0, 0);
QFV_PacketSubmit (gpacket);
QFV_PacketSubmit (rpacket);
QFV_PacketSubmit (epacket);
QFV_DestroyStagingBuffer (stage);
vec4f_t *bone_data;
dfunc->vkMapMemory (device->dev, mesh->bones->memory, 0, VK_WHOLE_SIZE,
0, (void **)&bone_data);
for (size_t i = 0; i < ictx->frames.size * iqm->num_joints; i++) {
vec4f_t *bone = bone_data + i * 3;
bone[0] = (vec4f_t) {1, 0, 0, 0};
bone[1] = (vec4f_t) {0, 1, 0, 0};
bone[2] = (vec4f_t) {0, 0, 1, 0};
}
VkMappedMemoryRange range = {
VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, 0,
mesh->bones->memory, 0, VK_WHOLE_SIZE,
};
dfunc->vkFlushMappedMemoryRanges (device->dev, 1, &range);
dfunc->vkUnmapMemory (device->dev, mesh->bones->memory);
Vulkan_IQMAddBones (ctx, iqm); //FIXME doesn't belong here (per-instance)
}
void
Vulkan_Mod_IQMFinish (model_t *mod, vulkan_ctx_t *ctx)
{
qfv_device_t *device = ctx->device;
iqmctx_t *ictx = ctx->iqm_context;
iqm_t *iqm = (iqm_t *) mod->aliashdr;
mod->clear = vulkan_iqm_clear;
mod->data = ctx;
// FIXME assumes only one texture per mesh (currently the case, but
// when materials are added...)
// 2 is for image + image view
int num_objects = 4 + 2 * iqm->num_meshes;
qfv_iqm_t *mesh = calloc (1, sizeof (qfv_iqm_t)
+ ictx->frames.size * sizeof (VkDescriptorSet)
+ 2 * sizeof (qfv_resource_t)
+ num_objects * sizeof (qfv_resobj_t)
+ iqm->num_meshes * sizeof (qfv_iqm_skin_t));
mesh->bones_descriptors = (VkDescriptorSet *) &mesh[1];
mesh->bones = (qfv_resource_t *)&mesh->bones_descriptors[ictx->frames.size];
mesh->mesh = &mesh->bones[1];
mesh->bones[0] = (qfv_resource_t) {
.name = mod->name,
.va_ctx = ctx->va_ctx,
.memory_properties = VK_MEMORY_PROPERTY_HOST_CACHED_BIT,
.num_objects = 1,
.objects = (qfv_resobj_t *) &mesh->bones[2],
};
mesh->bones->objects[0] = (qfv_resobj_t) {
.name = "bones",
.type = qfv_res_buffer,
.buffer = {
.size = ictx->frames.size * iqm->num_joints * 3 * sizeof (vec4f_t),
.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT
| VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
},
};
mesh->mesh[0] = (qfv_resource_t) {
.name = "mesh",
.va_ctx = ctx->va_ctx,
.memory_properties = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
.num_objects = num_objects - 1,
.objects = mesh->bones->objects + 1,
};
mesh->mesh->objects[0] = (qfv_resobj_t) {
.name = "geom",
.type = qfv_res_buffer,
.buffer = {
.size = iqm->num_verts * sizeof (iqmgvert_t),
.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT
| VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
},
};
mesh->mesh->objects[1] = (qfv_resobj_t) {
.name = "rend",
.type = qfv_res_buffer,
.buffer = {
.size = iqm->num_verts * sizeof (iqmrvert_t),
.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT
| VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
},
};
size_t elem_size = iqm->num_elements * sizeof (uint16_t);
if (iqm->num_verts > 0xfff0) {
elem_size = iqm->num_elements * sizeof (uint32_t);
}
mesh->mesh->objects[2] = (qfv_resobj_t) {
.name = "index",
.type = qfv_res_buffer,
.buffer = {
.size = elem_size,
.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT
| VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
},
};
for (int i = 0; i < iqm->num_meshes; i++) {
int image_ind = 3 + 2 * i;
__auto_type image = &mesh->mesh->objects[image_ind];
vulkan_iqm_init_image (iqm, i, image);
mesh->mesh->objects[image_ind + 1] = (qfv_resobj_t) {
.name = "view",
.type = qfv_res_image_view,
.image_view = {
.image = image_ind,
.type = VK_IMAGE_VIEW_TYPE_2D,
.format = mesh->mesh->objects[image_ind].image.format,
.subresourceRange = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.levelCount = VK_REMAINING_MIP_LEVELS,
.layerCount = VK_REMAINING_ARRAY_LAYERS,
},
},
};
}
mesh->skins = (qfv_iqm_skin_t *) &mesh->bones->objects[num_objects];
QFV_CreateResource (device, mesh->mesh);
QFV_CreateResource (device, mesh->bones);
mesh->geom_buffer = mesh->mesh->objects[0].buffer.buffer;
mesh->rend_buffer = mesh->mesh->objects[1].buffer.buffer;
mesh->index_buffer = mesh->mesh->objects[2].buffer.buffer;
mesh->bones_buffer = mesh->bones->objects[0].buffer.buffer;
iqm->extra_data = mesh;
vulkan_iqm_load_textures (mod, iqm, mesh, ctx);
vulkan_iqm_load_arrays (mod, iqm, mesh, ctx);
}