mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-22 20:41:20 +00:00
0eb556b8f9
The vertices and frame images are loaded into the one memory object, with the vertices first followed by the images. The vertices are 2D xy+uv sets meant to be applied to the model transform frame, and are pre-computed for the sprite size (this part does support sprites with varying frame image sizes). The frame images are loaded into one image with each frame on its own layer. This will cause some problems if any sprites with varying frame image sizes are found, but the three sprites in quake are all uniform size.
201 lines
7.1 KiB
C
201 lines
7.1 KiB
C
/*
|
|
vulkan_model_sprite.c
|
|
|
|
Sprite model mesh processing for Vulkan
|
|
|
|
Copyright (C) 2021 Bill Currie <bill@taniwha.org>
|
|
|
|
Author: Bill Currie <bill@taniwha.org>
|
|
Date: 2021/12/13
|
|
|
|
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 "QF/cvar.h"
|
|
#include "QF/darray.h"
|
|
#include "QF/image.h"
|
|
#include "QF/quakefs.h"
|
|
#include "QF/va.h"
|
|
#include "QF/Vulkan/qf_sprite.h"
|
|
#include "QF/Vulkan/qf_texture.h"
|
|
#include "QF/Vulkan/barrier.h"
|
|
#include "QF/Vulkan/buffer.h"
|
|
#include "QF/Vulkan/debug.h"
|
|
#include "QF/Vulkan/device.h"
|
|
#include "QF/Vulkan/image.h"
|
|
#include "QF/Vulkan/instance.h"
|
|
#include "QF/Vulkan/staging.h"
|
|
|
|
#include "compat.h"
|
|
#include "mod_internal.h"
|
|
#include "r_internal.h"
|
|
#include "vid_vulkan.h"
|
|
|
|
static void
|
|
vulkan_sprite_clear (model_t *m, void *data)
|
|
{
|
|
vulkan_ctx_t *ctx = data;
|
|
qfv_device_t *device = ctx->device;
|
|
qfv_devfuncs_t *dfunc = device->funcs;
|
|
msprite_t *msprite = m->cache.data;
|
|
__auto_type sprite = (qfv_sprite_t *) ((byte *) msprite + msprite->data);
|
|
|
|
dfunc->vkDestroyBuffer (device->dev, sprite->verts, 0);
|
|
dfunc->vkDestroyImageView (device->dev, sprite->view, 0);
|
|
dfunc->vkDestroyImage (device->dev, sprite->image, 0);
|
|
dfunc->vkFreeMemory (device->dev, sprite->memory, 0);
|
|
}
|
|
|
|
void
|
|
Vulkan_Mod_SpriteLoadFrames (mod_sprite_ctx_t *sprite_ctx, vulkan_ctx_t *ctx)
|
|
{
|
|
qfvPushDebug (ctx, va (ctx->va_ctx, "sprite.load_frames: %s",
|
|
sprite_ctx->mod->name));
|
|
|
|
qfv_device_t *device = ctx->device;
|
|
qfv_devfuncs_t *dfunc = device->funcs;
|
|
model_t *mod = sprite_ctx->mod;
|
|
dsprite_t *dsprite = sprite_ctx->dsprite;
|
|
mod->clear = vulkan_sprite_clear;
|
|
mod->data = ctx;
|
|
|
|
qfv_sprite_t *sprite = Hunk_AllocName (0, sizeof (*sprite), mod->name);
|
|
int mipLevels = QFV_MipLevels (dsprite->width, dsprite->height);
|
|
VkExtent3D extent = { dsprite->width, dsprite->height, 1 };
|
|
sprite->image = QFV_CreateImage (device, 0, VK_IMAGE_TYPE_2D,
|
|
VK_FORMAT_R8G8B8A8_UNORM, extent,
|
|
mipLevels, sprite_ctx->numframes,
|
|
VK_SAMPLE_COUNT_1_BIT,
|
|
VK_IMAGE_USAGE_SAMPLED_BIT
|
|
| VK_IMAGE_USAGE_TRANSFER_DST_BIT
|
|
| VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
|
|
QFV_duSetObjectName (device, VK_OBJECT_TYPE_IMAGE, sprite->image,
|
|
va (ctx->va_ctx, "image:%s", mod->name));
|
|
|
|
int numverts = 4 * sprite_ctx->numframes;
|
|
sprite->verts = QFV_CreateBuffer (device, numverts * sizeof (spritevrt_t),
|
|
VK_BUFFER_USAGE_TRANSFER_DST_BIT
|
|
| VK_BUFFER_USAGE_VERTEX_BUFFER_BIT);
|
|
QFV_duSetObjectName (device, VK_OBJECT_TYPE_BUFFER, sprite->verts,
|
|
va (ctx->va_ctx, "buffer:sprite:vertex:%s",
|
|
mod->name));
|
|
|
|
VkMemoryRequirements ireq;
|
|
dfunc->vkGetImageMemoryRequirements (device->dev, sprite->image, &ireq);
|
|
VkMemoryRequirements vreq;
|
|
dfunc->vkGetBufferMemoryRequirements (device->dev, sprite->verts, &vreq);
|
|
size_t size = QFV_NextOffset (QFV_NextOffset (0, 1, &vreq), 1, &ireq);
|
|
|
|
sprite->memory = QFV_AllocBufferMemory (device, sprite->verts,
|
|
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
|
|
size, 0);
|
|
QFV_duSetObjectName (device, VK_OBJECT_TYPE_DEVICE_MEMORY, sprite->memory,
|
|
va (ctx->va_ctx, "memory:sprite:%s",
|
|
sprite_ctx->mod->name));
|
|
|
|
QFV_BindBufferMemory (device, sprite->verts, sprite->memory, 0);
|
|
QFV_BindImageMemory (device, sprite->image, sprite->memory,
|
|
QFV_NextOffset (0, 1, &vreq));
|
|
sprite->view = QFV_CreateImageView (device, sprite->image,
|
|
VK_IMAGE_VIEW_TYPE_2D_ARRAY,
|
|
VK_FORMAT_R8G8B8A8_UNORM,
|
|
VK_IMAGE_ASPECT_COLOR_BIT);
|
|
QFV_duSetObjectName (device, VK_OBJECT_TYPE_IMAGE_VIEW, sprite->view,
|
|
va (ctx->va_ctx, "view:sprite:%s",
|
|
sprite_ctx->mod->name));
|
|
|
|
qfv_stagebuf_t *stage = QFV_CreateStagingBuffer (device,
|
|
va (ctx->va_ctx,
|
|
"sprite:%s",
|
|
sprite_ctx->mod->name),
|
|
size, ctx->cmdpool);
|
|
qfv_packet_t *packet = QFV_PacketAcquire (stage);
|
|
spritevrt_t *verts = QFV_PacketExtend (packet,
|
|
numverts * sizeof (spritevrt_t));
|
|
int texsize = 4 * dsprite->width * dsprite->height;
|
|
byte *pixels = QFV_PacketExtend (packet,
|
|
sprite_ctx->numframes * texsize);
|
|
|
|
for (int i = 0; i < sprite_ctx->numframes; i++) {
|
|
__auto_type dframe = sprite_ctx->dframes[i];
|
|
mspriteframe_t f;
|
|
Mod_LoadSpriteFrame (&f, dframe);
|
|
verts[i * 4 + 0] = (spritevrt_t) { f.left, f.up, 0, 0 };
|
|
verts[i * 4 + 1] = (spritevrt_t) { f.right, f.up, 1, 0 };
|
|
verts[i * 4 + 2] = (spritevrt_t) { f.right, f.down, 1, 1 };
|
|
verts[i * 4 + 3] = (spritevrt_t) { f.left, f.down, 0, 1 };
|
|
Vulkan_ExpandPalette (pixels + i * texsize, (const byte *)(dframe + 1),
|
|
vid.palette32, 2, texsize / 4);
|
|
}
|
|
|
|
qfv_bufferbarrier_t bb = bufferBarriers[qfv_BB_Unknown_to_TransferWrite];
|
|
bb.barrier.buffer = sprite->verts;
|
|
bb.barrier.size = numverts * sizeof (spritevrt_t);
|
|
dfunc->vkCmdPipelineBarrier (packet->cmd, bb.srcStages, bb.dstStages,
|
|
0, 0, 0, 1, &bb.barrier, 0, 0);
|
|
VkBufferCopy copy_region[] = {
|
|
{ packet->offset, 0, numverts * sizeof (spritevrt_t) },
|
|
};
|
|
dfunc->vkCmdCopyBuffer (packet->cmd, stage->buffer,
|
|
sprite->verts, 1, ©_region[0]);
|
|
bb = bufferBarriers[qfv_BB_TransferWrite_to_VertexAttrRead];
|
|
bb.barrier.buffer = sprite->verts;
|
|
bb.barrier.size = numverts * sizeof (spritevrt_t);
|
|
dfunc->vkCmdPipelineBarrier (packet->cmd, bb.srcStages, bb.dstStages,
|
|
0, 0, 0, 1, &bb.barrier, 0, 0);
|
|
|
|
qfv_imagebarrier_t ib = imageBarriers[qfv_LT_Undefined_to_TransferDst];
|
|
ib.barrier.image = sprite->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);
|
|
|
|
VkBufferImageCopy copy = {
|
|
packet->offset + numverts * sizeof (spritevrt_t), 0, 0,
|
|
{VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, sprite_ctx->numframes},
|
|
{0, 0, 0}, {dsprite->width, dsprite->height, 1},
|
|
};
|
|
dfunc->vkCmdCopyBufferToImage (packet->cmd, packet->stage->buffer,
|
|
sprite->image,
|
|
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
|
1, ©);
|
|
QFV_GenerateMipMaps (device, packet->cmd, sprite->image,
|
|
mipLevels, dsprite->width, dsprite->height,
|
|
sprite_ctx->numframes);
|
|
|
|
QFV_PacketSubmit (packet);
|
|
QFV_DestroyStagingBuffer (stage);
|
|
|
|
sprite_ctx->sprite->data = (byte *) sprite - (byte *) sprite_ctx->sprite;
|
|
|
|
qfvPopDebug (ctx);
|
|
}
|