diff --git a/include/QF/Vulkan/image.h b/include/QF/Vulkan/image.h index 489c86e40..71ca57389 100644 --- a/include/QF/Vulkan/image.h +++ b/include/QF/Vulkan/image.h @@ -57,4 +57,30 @@ VkImageView QFV_CreateImageView (struct qfv_device_s *device, VkImage image, VkImageViewType type, VkFormat format, VkImageAspectFlags aspect); +/** Generate all mipmaps for a given texture down to a 1x1 pixel. + * + * Uses the GPU blit command from one mip level to the next, thus the base mip + * level data must have already been transfered to the image and the image is + * expected to be in VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL. This includes any + * array levels. + * + * \param device The device owning the command buffer. + * \param cmd The command buffer to which the barrier and blit commands + * will be written. + * \param image The image to be processed. All array layers of the base mip + * level must be initialized and in "transfer dst optimal" + * layout. All remaining mip levels must be in "undefined" + * oayout. + * \param mips The total number of mip levels of the processed image. + * \param width The pixel width of the base image. + * \param height The pixel height of the base image. + * \param layers The number of array layers in the mbase image. + * + * \note The processed image will be in "shader read only optimal" layout on + * completion. + */ +void QFV_GenerateMipMaps (struct qfv_device_s *device, VkCommandBuffer cmd, + VkImage image, unsigned mips, + unsigned width, unsigned height, unsigned layers); + #endif//__QF_Vulkan_image_h diff --git a/libs/video/renderer/vulkan/image.c b/libs/video/renderer/vulkan/image.c index bd71cc162..92d584f80 100644 --- a/libs/video/renderer/vulkan/image.c +++ b/libs/video/renderer/vulkan/image.c @@ -49,6 +49,7 @@ #include "QF/va.h" #include "QF/vid.h" #include "QF/Vulkan/qf_vid.h" +#include "QF/Vulkan/barrier.h" #include "QF/Vulkan/device.h" #include "QF/Vulkan/image.h" #include "QF/Vulkan/instance.h" @@ -192,3 +193,85 @@ QFV_CreateImageView (qfv_device_t *device, VkImage image, dfunc->vkCreateImageView (dev, &createInfo, 0, &view); return view; } + +void +QFV_GenerateMipMaps (qfv_device_t *device, VkCommandBuffer cmd, + VkImage image, unsigned mips, + unsigned width, unsigned height, unsigned layers) +{ + qfv_devfuncs_t *dfunc = device->funcs; + + qfv_pipelinestagepair_t pre_stages = { + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, + }; + qfv_pipelinestagepair_t post_stages = { + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + }; + qfv_pipelinestagepair_t final_stages = { + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + }; + VkImageMemoryBarrier pre_barrier = { + VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, 0, + VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, + image, + { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, layers } + }; + VkImageMemoryBarrier post_barrier = { + VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, 0, + VK_ACCESS_TRANSFER_READ_BIT, VK_ACCESS_SHADER_READ_BIT, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, + image, + { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, layers } + }; + VkImageMemoryBarrier final_barrier = { + VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, 0, + VK_ACCESS_TRANSFER_READ_BIT, VK_ACCESS_SHADER_READ_BIT, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, + image, + { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, layers } + }; + + VkImageBlit blit = { + {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, layers}, + {{0, 0, 0}, {width, height, 1}}, + {VK_IMAGE_ASPECT_COLOR_BIT, 1, 0, layers}, + {{0, 0, 0}, {max (width >> 1, 1), max (height >> 1, 1), 1}}, + }; + + while (--mips > 0) { + dfunc->vkCmdPipelineBarrier (cmd, pre_stages.src, pre_stages.dst, 0, + 0, 0, 0, 0, + 1, &pre_barrier); + dfunc->vkCmdBlitImage (cmd, + image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 1, &blit, VK_FILTER_LINEAR); + + dfunc->vkCmdPipelineBarrier (cmd, post_stages.src, post_stages.dst, 0, + 0, 0, 0, 0, + 1, &post_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 = max (blit.dstOffsets[1].x >> 1, 1); + blit.dstOffsets[1].y = max (blit.dstOffsets[1].y >> 1, 1); + pre_barrier.subresourceRange.baseMipLevel++; + post_barrier.subresourceRange.baseMipLevel++; + final_barrier.subresourceRange.baseMipLevel++; + } + dfunc->vkCmdPipelineBarrier (cmd, final_stages.src, final_stages.dst, 0, + 0, 0, 0, 0, + 1, &final_barrier); +} diff --git a/libs/video/renderer/vulkan/vulkan_texture.c b/libs/video/renderer/vulkan/vulkan_texture.c index 9bff6e72b..4a1e2d2ab 100644 --- a/libs/video/renderer/vulkan/vulkan_texture.c +++ b/libs/video/renderer/vulkan/vulkan_texture.c @@ -121,85 +121,6 @@ Vulkan_ExpandPalette (byte *dst, const byte *src, const byte *palette, } } -static void -blit_mips (int mips, VkImage image, tex_t *tex, - qfv_devfuncs_t *dfunc, VkCommandBuffer cmd) -{ - qfv_pipelinestagepair_t pre_stages = { - VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_PIPELINE_STAGE_TRANSFER_BIT, - }; - qfv_pipelinestagepair_t post_stages = { - VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, - }; - qfv_pipelinestagepair_t final_stages = { - VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, - }; - VkImageMemoryBarrier pre_barrier = { - VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, 0, - VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, - VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, - image, - { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 } - }; - VkImageMemoryBarrier post_barrier = { - VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, 0, - VK_ACCESS_TRANSFER_READ_BIT, VK_ACCESS_SHADER_READ_BIT, - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, - image, - { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 } - }; - VkImageMemoryBarrier final_barrier = { - VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, 0, - VK_ACCESS_TRANSFER_READ_BIT, VK_ACCESS_SHADER_READ_BIT, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, - image, - { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 } - }; - - 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}, {max (tex->width >> 1, 1), max (tex->height >> 1, 1), 1}}, - }; - - while (--mips > 0) { - dfunc->vkCmdPipelineBarrier (cmd, pre_stages.src, pre_stages.dst, 0, - 0, 0, 0, 0, - 1, &pre_barrier); - dfunc->vkCmdBlitImage (cmd, - image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, - image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - 1, &blit, VK_FILTER_LINEAR); - - dfunc->vkCmdPipelineBarrier (cmd, post_stages.src, post_stages.dst, 0, - 0, 0, 0, 0, - 1, &post_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 = max (blit.dstOffsets[1].x >> 1, 1); - blit.dstOffsets[1].y = max (blit.dstOffsets[1].y >> 1, 1); - pre_barrier.subresourceRange.baseMipLevel++; - post_barrier.subresourceRange.baseMipLevel++; - final_barrier.subresourceRange.baseMipLevel++; - } - dfunc->vkCmdPipelineBarrier (cmd, final_stages.src, final_stages.dst, 0, - 0, 0, 0, 0, - 1, &final_barrier); -} - qfv_tex_t * Vulkan_LoadTex (vulkan_ctx_t *ctx, tex_t *tex, int mip, const char *name) { @@ -311,7 +232,8 @@ Vulkan_LoadTex (vulkan_ctx_t *ctx, tex_t *tex, int mip, const char *name) 0, 0, 0, 0, 0, 1, &barrier); } else { - blit_mips (mip, qtex->image, tex, dfunc, packet->cmd); + QFV_GenerateMipMaps (device, packet->cmd, qtex->image, + mip, tex->width, tex->height, 1); } QFV_PacketSubmit (packet); return qtex;