[vulkan] Load sprite model data

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.
This commit is contained in:
Bill Currie 2021-12-14 10:02:35 +09:00
parent c5cfcc7bfd
commit 0eb556b8f9
3 changed files with 301 additions and 0 deletions

View file

@ -0,0 +1,98 @@
/*
qf_sprite.h
Vulkan specific sprite model stuff
Copyright (C) 2012 Bill Currie <bill@taniwha.org>
Copyright (C) 2021 Bill Currie <bill@taniwha.org>
Author: Bill Currie <bill@taniwha.org>
Date: 2012/1/7
Date: 2021/1/18
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
*/
#ifndef __QF_Vulkan_qf_sprite_h
#define __QF_Vulkan_qf_sprite_h
#include "QF/darray.h"
#include "QF/model.h"
#include "QF/modelgen.h"
#include "QF/Vulkan/qf_vid.h"
#include "QF/Vulkan/command.h"
typedef struct spritevrt_s {
float x, y, u, v;
} spritevrt_t;
typedef struct qfv_sprite_s {
VkDeviceMemory memory;
VkBuffer verts;
VkImage image;
VkImageView view;
} qfv_sprite_t;
typedef enum {
QFV_spriteDepth,
QFV_spriteGBuffer,
QFV_spriteTranslucent,
QFV_spriteNumPasses
} QFV_SpriteSubpass;
typedef struct spriteframe_s {
qfv_cmdbufferset_t cmdSet;
} spriteframe_t;
typedef struct spriteframeset_s
DARRAY_TYPE (spriteframe_t) spriteframeset_t;
typedef struct spriteindset_s
DARRAY_TYPE (unsigned) spriteindset_t;
typedef struct spritectx_s {
spriteframeset_t frames;
VkPipeline depth;
VkPipeline gbuf;
VkDescriptorSet descriptors;
VkDescriptorPool pool;
VkDescriptorSetLayout setLayout;
VkPipelineLayout layout;
unsigned maxImages;
VkSampler sampler;
spriteindset_t texindices;
} spritectx_t;
struct vulkan_ctx_s;
struct qfv_renderframe_s;
struct entity_s;
struct mod_sprite_ctx_s;
void Vulkan_Mod_SpriteLoadFrames (struct mod_sprite_ctx_s *sprite_ctx,
struct vulkan_ctx_s *ctx);
void Vulkan_SpriteBegin (struct qfv_renderframe_s *rFrame);
void Vulkan_DrawSprite (struct entity_s *ent, struct qfv_renderframe_s *rFrame);
void Vulkan_SpriteEnd (struct qfv_renderframe_s *rFrame);
void Vulkan_Sprite_Init (struct vulkan_ctx_s *ctx);
void Vulkan_Sprite_Shutdown (struct vulkan_ctx_s *ctx);
#endif//__QF_Vulkan_qf_sprite_h

View file

@ -0,0 +1,201 @@
/*
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, &copy_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, &copy);
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);
}

View file

@ -49,6 +49,7 @@
#include "QF/Vulkan/qf_main.h"
#include "QF/Vulkan/qf_matrices.h"
#include "QF/Vulkan/qf_particles.h"
#include "QF/Vulkan/qf_sprite.h"
#include "QF/Vulkan/qf_texture.h"
#include "QF/Vulkan/qf_vid.h"
#include "QF/Vulkan/capture.h"
@ -507,6 +508,7 @@ vulkan_Mod_IQMFinish (model_t *mod)
static void
vulkan_Mod_SpriteLoadFrames (mod_sprite_ctx_t *sprite_ctx)
{
Vulkan_Mod_SpriteLoadFrames (sprite_ctx, vulkan_ctx);
}
static void