mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2025-02-25 13:11:00 +00:00
I got a sync validation error on a scatter command (I think) thus the setting was probably wrong. Most of the parameters are still what they were, but I'll be able to tweak the barriers as necessary. Unfortunately, it didn't help with the hang on fetching the light cull query data when starting in fisheye mode (no hang when enabling fisheye after startup). I'm not sure what's going on there other than the queries aren't getting updated: the counts seem to be fine so maybe the commands aren't running. I've probably got a tangled mess of pseudo-parallel command buffers: I need to go through my system and clean everything up.
454 lines
14 KiB
C
454 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 = {
|
|
.width = 2,
|
|
.height = 2,
|
|
.format = tex_rgba,
|
|
};
|
|
tex_t *tex;
|
|
if (!(tex = LoadImage (va (0, "textures/%s", str->str), 0))) {
|
|
tex = &dummy_tex;
|
|
}
|
|
QFV_ResourceInitTexImage (image, material, 1, tex);
|
|
image->image.num_layers = 3;
|
|
dstring_delete (str);
|
|
}
|
|
|
|
static void
|
|
iqm_transfer_texture (tex_t *tex, VkImage image, qfv_stagebuf_t *stage,
|
|
qfv_device_t *device)
|
|
{
|
|
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 * 3);
|
|
|
|
memcpy (dst, tex->data, layer_size);
|
|
// don't have glow/color maps yet
|
|
memset (dst + layer_size, 0, 2 * layer_size);
|
|
|
|
int mipLevels = QFV_MipLevels (tex->width, tex->height);
|
|
auto sb = &imageBarriers[qfv_LT_Undefined_to_TransferDst];
|
|
auto ib = mipLevels == 1
|
|
? &imageBarriers[qfv_LT_TransferDst_to_ShaderReadOnly]
|
|
: nullptr;
|
|
QFV_PacketCopyImage (packet, image, tex->width, tex->height, sb, ib);
|
|
|
|
if (mipLevels != 1) {
|
|
QFV_GenerateMipMaps (device, packet->cmd, image, mipLevels,
|
|
tex->width, tex->height, 3);
|
|
}
|
|
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, ©_region[0]);
|
|
dfunc->vkCmdCopyBuffer (rpacket->cmd, stage->buffer,
|
|
mesh->rend_buffer, 1, ©_region[1]);
|
|
dfunc->vkCmdCopyBuffer (epacket->cmd, stage->buffer,
|
|
mesh->index_buffer, 1, ©_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_ARRAY,
|
|
.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);
|
|
}
|