diff --git a/include/QF/Vulkan/funclist.h b/include/QF/Vulkan/funclist.h index 63c5bde29..209ee0561 100644 --- a/include/QF/Vulkan/funclist.h +++ b/include/QF/Vulkan/funclist.h @@ -162,6 +162,7 @@ DEVICE_LEVEL_VULKAN_FUNCTION (vkCmdPipelineBarrier) DEVICE_LEVEL_VULKAN_FUNCTION (vkCmdCopyBuffer) DEVICE_LEVEL_VULKAN_FUNCTION (vkCmdCopyBufferToImage) DEVICE_LEVEL_VULKAN_FUNCTION (vkCmdCopyImageToBuffer) +DEVICE_LEVEL_VULKAN_FUNCTION (vkCmdBlitImage) DEVICE_LEVEL_VULKAN_FUNCTION (vkCmdBeginRenderPass) DEVICE_LEVEL_VULKAN_FUNCTION (vkCmdNextSubpass) DEVICE_LEVEL_VULKAN_FUNCTION (vkCmdEndRenderPass) diff --git a/include/QF/Vulkan/qf_texture.h b/include/QF/Vulkan/qf_texture.h new file mode 100644 index 000000000..a1750682d --- /dev/null +++ b/include/QF/Vulkan/qf_texture.h @@ -0,0 +1,13 @@ +#ifndef __QF_Vulkan_qf_texture_h +#define __QF_Vulkan_qf_texture_h + +#include "QF/image.h" + +typedef struct qfv_tex_s qfv_tex_t; +struct vulkan_ctx_s; + +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); + +#endif//__QF_Vulkan_qf_texture_h diff --git a/libs/video/renderer/Makemodule.am b/libs/video/renderer/Makemodule.am index 7a323f118..d44139a22 100644 --- a/libs/video/renderer/Makemodule.am +++ b/libs/video/renderer/Makemodule.am @@ -237,6 +237,7 @@ libs_video_renderer_vid_render_vulkan_la_SOURCES = \ libs/video/renderer/vulkan/vkparse.c \ libs/video/renderer/vulkan/vulkan_draw.c \ libs/video/renderer/vulkan/vulkan_matrices.c \ + libs/video/renderer/vulkan/vulkan_texture.c \ libs/video/renderer/vulkan/vulkan_vid_common.c libs/video/renderer/vulkan/vkparse.lo: libs/video/renderer/vulkan/vkparse.c $(vkparse_src) diff --git a/libs/video/renderer/vulkan/vulkan_texture.c b/libs/video/renderer/vulkan/vulkan_texture.c new file mode 100644 index 000000000..0b2c2e102 --- /dev/null +++ b/libs/video/renderer/vulkan/vulkan_texture.c @@ -0,0 +1,249 @@ +/* + vulkan_texuture.c + + Quake specific Vulkan texuture manager + + Copyright (C) 2021 Bill Currie + + 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_MATH_H +# include +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "QF/alloc.h" +#include "QF/cvar.h" +#include "QF/dstring.h" +#include "QF/hash.h" +#include "QF/image.h" +#include "QF/mathlib.h" +#include "QF/qfplist.h" +#include "QF/quakefs.h" +#include "QF/render.h" +#include "QF/sys.h" +#include "QF/Vulkan/qf_vid.h" +#include "QF/Vulkan/qf_texture.h" +#include "QF/Vulkan/barrier.h" +#include "QF/Vulkan/buffer.h" +#include "QF/Vulkan/command.h" +#include "QF/Vulkan/device.h" +#include "QF/Vulkan/image.h" +#include "QF/Vulkan/instance.h" +#include "QF/Vulkan/staging.h" +#include "QF/Vulkan/texture.h" + +#include "r_scrap.h" +#include "vid_vulkan.h" + +struct qfv_tex_s { // qfv_tex_t + VkDeviceMemory memory; + size_t offset; + VkImage image; + VkImageView view; +}; + +static int +ilog2 (unsigned x) +{ + if (x > 0x7fffffff) { + // avoid overflow + return 31; + } + x--; + x |= x >> 1; + x |= x >> 2; + x |= x >> 4; + x |= x >> 8; + x |= x >> 16; + x++; + int y = 0; + y |= ((x & 0xffff0000) != 0) << 4; + y |= ((x & 0xff00ff00) != 0) << 3; + y |= ((x & 0xf0f0f0f0) != 0) << 2; + y |= ((x & 0xcccccccc) != 0) << 1; + y |= ((x & 0xaaaaaaaa) != 0) << 0; + return y; +} + +qfv_tex_t * +Vulkan_LoadTex (vulkan_ctx_t *ctx, tex_t *tex, int mip) +{ + qfv_device_t *device = ctx->device; + qfv_devfuncs_t *dfunc = device->funcs; + int bpp = 0; + VkFormat format = VK_FORMAT_UNDEFINED; + + switch (tex->format) { + case tex_l: + case tex_a: + format = VK_FORMAT_R8_UNORM; + bpp = 1; + break; + case tex_la: + format = VK_FORMAT_R8G8_UNORM; + bpp = 2; + break; + case tex_palette: + if (!tex->palette) { + return 0; + } + format = VK_FORMAT_R8G8B8_UNORM; + bpp = 3; + break; + case tex_rgb: + format = VK_FORMAT_R8G8B8_UNORM; + bpp = 3; + break; + case tex_rgba: + format = VK_FORMAT_R8G8B8A8_UNORM; + bpp = 4; + break; + } + if (format == VK_FORMAT_UNDEFINED) { + return 0; + } + + if (mip) { + mip = ilog2 (max (tex->width, tex->height)) + 1; + } else { + mip = 1; + } + + //qfv_devfuncs_t *dfunc = device->funcs; + //FIXME this whole thing is ineffiecient, especially for small textures + qfv_tex_t *qtex = malloc (sizeof (qfv_tex_t)); + + VkExtent3D extent = { tex->width, tex->height, 1 }; + qtex->image = QFV_CreateImage (device, 0, VK_IMAGE_TYPE_2D, format, extent, + mip, 1, VK_SAMPLE_COUNT_1_BIT, + VK_IMAGE_USAGE_TRANSFER_DST_BIT + | VK_IMAGE_USAGE_SAMPLED_BIT); + qtex->memory = QFV_AllocImageMemory (device, qtex->image, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + 0, 0); + QFV_BindImageMemory (device, qtex->image, qtex->memory, 0); + + size_t bytes = bpp * tex->width * tex->height; + qfv_packet_t *packet = QFV_PacketAcquire (ctx->staging); + 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++; + } + } else { + memcpy (tex_data, tex->data, bytes); + } + + VkImageMemoryBarrier barrier; + qfv_pipelinestagepair_t stages; + + stages = imageLayoutTransitionStages[qfv_LT_Undefined_to_TransferDst]; + barrier = imageLayoutTransitionBarriers[qfv_LT_Undefined_to_TransferDst]; + barrier.image = qtex->image; + dfunc->vkCmdPipelineBarrier (packet->cmd, stages.src, stages.dst, + 0, 0, 0, 0, 0, + 1, &barrier); + VkBufferImageCopy copy = { + packet->offset, 0, 0, + {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}, + {0, 0, 0}, {tex->width, tex->height, 1}, + }; + dfunc->vkCmdCopyBufferToImage (packet->cmd, packet->stage->buffer, + qtex->image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 1, ©); + barrier.subresourceRange.baseMipLevel = 0; + stages.src = VK_PIPELINE_STAGE_TRANSFER_BIT; + stages.dst = VK_PIPELINE_STAGE_TRANSFER_BIT; + VkImageBlit blit = { + {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}, + {{0, 0, 0}, {tex->width, tex->height, 1}}, + {VK_IMAGE_ASPECT_COLOR_BIT, 1, 0, 1}, + {{0, 0, 0}, {(tex->width + 1) >> 1, (tex->height + 1) >> 1, 1}}, + }; + while (--mip > 0) { + barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + + dfunc->vkCmdPipelineBarrier (packet->cmd, stages.src, stages.dst, 0, + 0, 0, 0, 0, + 1, &barrier); + + dfunc->vkCmdBlitImage (packet->cmd, + qtex->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + qtex->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 1, &blit, VK_FILTER_LINEAR); + + barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + + dfunc->vkCmdPipelineBarrier (packet->cmd, stages.src, stages.dst, 0, + 0, 0, 0, 0, + 1, &barrier); + + blit.srcSubresource.mipLevel++; + blit.srcOffsets[1].x = blit.dstOffsets[1].x; + blit.srcOffsets[1].y = blit.dstOffsets[1].y; + blit.dstSubresource.mipLevel++; + blit.dstOffsets[1].x = (blit.dstOffsets[1].x + 1) >> 1; + blit.dstOffsets[1].y = (blit.dstOffsets[1].y + 1) >> 1; + barrier.subresourceRange.baseMipLevel++; + } + QFV_PacketSubmit (packet); + return 0; +} + +VkImageView +Vulkan_TexImageView (qfv_tex_t *tex) +{ + return tex->view; +} + +void +Vulkan_UnloadTex (vulkan_ctx_t *ctx, qfv_tex_t *tex) +{ + qfv_device_t *device = ctx->device; + qfv_devfuncs_t *dfunc = device->funcs; + + dfunc->vkDestroyImageView (device->dev, tex->view, 0); + dfunc->vkDestroyImage (device->dev, tex->image, 0); + dfunc->vkFreeMemory (device->dev, tex->memory, 0); + free (tex); +}