From 0eb556b8f9b85128331d9d24d69b6cb30717329e Mon Sep 17 00:00:00 2001 From: Bill Currie Date: Tue, 14 Dec 2021 10:02:35 +0900 Subject: [PATCH] [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. --- include/QF/Vulkan/qf_sprite.h | 98 +++++++++++ libs/models/sprite/vulkan_model_sprite.c | 201 +++++++++++++++++++++++ libs/video/renderer/vid_render_vulkan.c | 2 + 3 files changed, 301 insertions(+) create mode 100644 include/QF/Vulkan/qf_sprite.h diff --git a/include/QF/Vulkan/qf_sprite.h b/include/QF/Vulkan/qf_sprite.h new file mode 100644 index 000000000..38b8f3e86 --- /dev/null +++ b/include/QF/Vulkan/qf_sprite.h @@ -0,0 +1,98 @@ +/* + qf_sprite.h + + Vulkan specific sprite model stuff + + Copyright (C) 2012 Bill Currie + Copyright (C) 2021 Bill Currie + + Author: Bill Currie + 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 diff --git a/libs/models/sprite/vulkan_model_sprite.c b/libs/models/sprite/vulkan_model_sprite.c index e69de29bb..434de6310 100644 --- a/libs/models/sprite/vulkan_model_sprite.c +++ b/libs/models/sprite/vulkan_model_sprite.c @@ -0,0 +1,201 @@ +/* + vulkan_model_sprite.c + + Sprite model mesh processing for Vulkan + + Copyright (C) 2021 Bill Currie + + Author: Bill Currie + 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 +#endif +#ifdef HAVE_STRINGS_H +# include +#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); +} diff --git a/libs/video/renderer/vid_render_vulkan.c b/libs/video/renderer/vid_render_vulkan.c index c2a5cca21..bfaa95d0e 100644 --- a/libs/video/renderer/vid_render_vulkan.c +++ b/libs/video/renderer/vid_render_vulkan.c @@ -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