diff --git a/include/QF/Vulkan/image.h b/include/QF/Vulkan/image.h index 501a5025e..489c86e40 100644 --- a/include/QF/Vulkan/image.h +++ b/include/QF/Vulkan/image.h @@ -28,6 +28,8 @@ typedef struct qfv_imagetransitionset_s DARRAY_TYPE (qfv_imagetransition_t) qfv_imagetransitionset_t; typedef struct qfv_imagebarrierset_s DARRAY_TYPE (VkImageMemoryBarrier) qfv_imagebarrierset_t; +#define QFV_AllocImageBarrierSet(num, allocator) \ + DARRAY_ALLOCFIXED (qfv_imagebarrierset_t, num, allocator) struct qfv_device_s; VkImage QFV_CreateImage (struct qfv_device_s *device, int cubemap, diff --git a/include/QF/Vulkan/qf_texture.h b/include/QF/Vulkan/qf_texture.h index 90906873c..a2ffbd38e 100644 --- a/include/QF/Vulkan/qf_texture.h +++ b/include/QF/Vulkan/qf_texture.h @@ -11,6 +11,8 @@ typedef struct qfv_tex_s { VkImageView view; } qfv_tex_t; +void Vulkan_ExpandPalette (byte *dst, const byte *src, const byte *palette, + int alpha, int count); qfv_tex_t *Vulkan_LoadTex (struct vulkan_ctx_s *ctx, tex_t *tex, int mip); VkImageView Vulkan_TexImageView (qfv_tex_t *tex) __attribute__((pure)); void Vulkan_UnloadTex (struct vulkan_ctx_s *ctx, qfv_tex_t *tex); diff --git a/libs/models/brush/vulkan_model_brush.c b/libs/models/brush/vulkan_model_brush.c index 52826d280..2b5ed2021 100644 --- a/libs/models/brush/vulkan_model_brush.c +++ b/libs/models/brush/vulkan_model_brush.c @@ -48,9 +48,13 @@ #include "QF/Vulkan/qf_bsp.h" #include "QF/Vulkan/qf_model.h" #include "QF/Vulkan/qf_texture.h" +#include "QF/Vulkan/barrier.h" +#include "QF/Vulkan/command.h" #include "QF/Vulkan/device.h" #include "QF/Vulkan/image.h" +#include "QF/Vulkan/staging.h" +#include "qfalloca.h" #include "compat.h" #include "mod_internal.h" #include "r_internal.h" @@ -96,6 +100,75 @@ get_image_size (VkImage image, qfv_device_t *device) return size; } +static void +transfer_mips (byte *dst, const void *_src, const texture_t *tx) +{ + const byte *src = _src; + unsigned width = tx->width; + unsigned height = tx->height; + unsigned count, offset; + + for (int i = 0; i < MIPLEVELS; i++) { + // mip offsets are relative to the texture pointer rather than the + // end of the texture struct + offset = tx->offsets[i] - sizeof (texture_t); + count = width * height; + Vulkan_ExpandPalette (dst, src + offset, vid.palette, 1, count); + dst += count; + width >>= 1; + height >>= 1; + } +} + +static void +copy_mips (qfv_packet_t *packet, texture_t *tx, qfv_tex_t *tex, + qfv_devfuncs_t *dfunc) +{ + // base copy + VkBufferImageCopy copy = { + tex->offset, tx->width, 0, + {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}, + {0, 0, 0}, {tx->width, tx->height, 1}, + }; + int is_sky = 0; + int sky_offset = 0; + size_t size = tx->width * tx->height * 4; + int copy_count = MIPLEVELS; + + if (strncmp (tx->name, "sky", 3) == 0) { + if (tx->width == 2 * tx->height) { + copy.imageExtent.width /= 2; + sky_offset = tx->width * 4 / 2; + } + is_sky = 1; + copy_count *= 2; + } + + __auto_type copies = QFV_AllocBufferImageCopy (copy_count, alloca); + copies->size = 0; + + for (int i = 0; i < MIPLEVELS; i++) { + __auto_type c = &copies->a[copies->size++]; + *c = copy; + if (is_sky) { + __auto_type c = &copies->a[copies->size++]; + *c = copy; + c->bufferOffset += sky_offset * 4; + c->imageSubresource.baseArrayLayer = 1; + } + copy.bufferOffset += size; + size >>= 2; + copy.bufferRowLength >>= 1; + copy.imageExtent.width >>= 1; + copy.imageExtent.height >>= 1; + sky_offset >>= 1; + } + dfunc->vkCmdCopyBufferToImage (packet->cmd, packet->stage->buffer, + tex->image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + copies->size, copies->a); +} + static void load_textures (model_t *model, vulkan_ctx_t *ctx) { @@ -103,7 +176,10 @@ load_textures (model_t *model, vulkan_ctx_t *ctx) qfv_devfuncs_t *dfunc = device->funcs; modelctx_t *mctx = model->data; VkImage image = 0; + byte *buffer; + size_t image_count = 0; + size_t copy_count = 0; size_t memsize = 0; for (int i = 0; i < model->numtextures; i++) { texture_t *tx = model->textures[i]; @@ -113,11 +189,18 @@ load_textures (model_t *model, vulkan_ctx_t *ctx) vulktex_t *tex = tx->render; tex->tex->offset = memsize; memsize += get_image_size (tex->tex->image, device); + image_count++; + copy_count += MIPLEVELS; + if (strncmp (tx->name, "sky", 3) == 0) { + copy_count += MIPLEVELS; + } // just so we have one in the end image = tex->tex->image; if (tex->glow) { + copy_count += MIPLEVELS; tex->glow->offset = memsize; memsize += get_image_size (tex->glow->image, device); + image_count++; } } VkDeviceMemory mem; @@ -126,6 +209,11 @@ load_textures (model_t *model, vulkan_ctx_t *ctx) memsize, 0); mctx->texture_memory = mem; + qfv_stagebuf_t *stage = QFV_CreateStagingBuffer (device, memsize, 1, + ctx->cmdpool); + qfv_packet_t *packet = QFV_PacketAcquire (stage); + buffer = QFV_PacketExtend (packet, memsize); + for (int i = 0; i < model->numtextures; i++) { texture_t *tx = model->textures[i]; if (!tx) { @@ -142,6 +230,7 @@ load_textures (model_t *model, vulkan_ctx_t *ctx) tex->tex->view = QFV_CreateImageView (device, tex->tex->image, type, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_ASPECT_COLOR_BIT); + transfer_mips (buffer + tex->tex->offset, tx + 1, tx); if (tex->glow) { dfunc->vkBindImageMemory (device->dev, tex->glow->image, mem, tex->glow->offset); @@ -151,8 +240,67 @@ load_textures (model_t *model, vulkan_ctx_t *ctx) VK_IMAGE_VIEW_TYPE_2D, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_ASPECT_COLOR_BIT); + transfer_mips (buffer + tex->glow->offset, tex->glow->memory, tx); } } + + // base barrier + VkImageMemoryBarrier barrier; + barrier = imageLayoutTransitionBarriers[qfv_LT_Undefined_to_TransferDst]; + barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS; + barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS; + + __auto_type barriers = QFV_AllocImageBarrierSet (image_count, malloc); + barriers->size = 0; + for (int i = 0; i < model->numtextures; i++) { + texture_t *tx = model->textures[i]; + if (!tx) { + continue; + } + vulktex_t *tex = tx->render; + __auto_type b = &barriers->a[barriers->size++]; + *b = barrier; + b->image = tex->tex->image; + if (tex->glow) { + b = &barriers->a[barriers->size++]; + *b = barrier; + b->image = tex->glow->image; + } + } + qfv_pipelinestagepair_t stages; + + stages = imageLayoutTransitionStages[qfv_LT_Undefined_to_TransferDst]; + dfunc->vkCmdPipelineBarrier (packet->cmd, stages.src, stages.dst, + 0, 0, 0, 0, 0, + barriers->size, barriers->a); + for (int i = 0, j = 0; i < model->numtextures; i++) { + texture_t *tx = model->textures[i]; + if (!tx) { + continue; + } + vulktex_t *tex = tx->render; + __auto_type b = &barriers->a[j++]; + b->oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + b->newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + b->srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + b->dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + copy_mips (packet, tx, tex->tex, dfunc); + if (tex->glow) { + b = &barriers->a[j++]; + b->oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + b->newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + b->srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + b->dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + copy_mips (packet, tx, tex->glow, dfunc); + } + } + + stages=imageLayoutTransitionStages[qfv_LT_TransferDst_to_ShaderReadOnly]; + dfunc->vkCmdPipelineBarrier (packet->cmd, stages.src, stages.dst, + 0, 0, 0, 0, 0, + barriers->size, barriers->a); + QFV_PacketSubmit (packet); + QFV_DestroyStagingBuffer (stage); } void @@ -173,12 +321,18 @@ Vulkan_Mod_ProcessTexture (texture_t *tx, vulkan_ctx_t *ctx) } vulktex_t *tex = tx->render; + tex->texture = tx; tex->tex = (qfv_tex_t *) (tx + 1); VkExtent3D extent = { tx->width, tx->height, 1 }; int layers = 1; if (strncmp (tx->name, "sky", 3) == 0) { layers = 2; + // the sky texture is normally 2 side-by-side squares, but + // some maps have just a single square + if (tx->width == 2 * tx->height) { + extent.width /= 2; + } } tex->tex->image = QFV_CreateImage (device, 0, VK_IMAGE_TYPE_2D, diff --git a/libs/video/renderer/vulkan/vulkan_texture.c b/libs/video/renderer/vulkan/vulkan_texture.c index 05d602d63..bc95431b0 100644 --- a/libs/video/renderer/vulkan/vulkan_texture.c +++ b/libs/video/renderer/vulkan/vulkan_texture.c @@ -85,6 +85,30 @@ ilog2 (unsigned x) return y; } +void +Vulkan_ExpandPalette (byte *dst, const byte *src, const byte *palette, + int alpha, int count) +{ + if (alpha) { + while (count-- > 0) { + byte pix = *src++; + const byte *col = palette + pix; + *dst++ = *col++; + *dst++ = *col++; + *dst++ = *col++; + *dst++ = 0xff; + } + } else { + while (count-- > 0) { + byte pix = *src++; + const byte *col = palette + pix; + *dst++ = *col++; + *dst++ = *col++; + *dst++ = *col++; + } + } +} + qfv_tex_t * Vulkan_LoadTex (vulkan_ctx_t *ctx, tex_t *tex, int mip) { @@ -152,14 +176,8 @@ Vulkan_LoadTex (vulkan_ctx_t *ctx, tex_t *tex, int mip) byte *tex_data = QFV_PacketExtend (packet, bytes); if (tex->format == tex_palette) { - byte *data = tex->data; - for (size_t count = tex->width * tex->height; count-- > 0; ) { - byte pix = *data++; - byte *col = tex->palette + pix; - *tex_data++ = *col++; - *tex_data++ = *col++; - *tex_data++ = *col++; - } + Vulkan_ExpandPalette (tex_data, tex->data, tex->palette, + 0, tex->width * tex->height); } else { memcpy (tex_data, tex->data, bytes); }