0
0
Fork 0
mirror of https://git.code.sf.net/p/quake/quakeforge synced 2025-03-21 18:01:15 +00:00

[vulkan] Make the mip map generation public

I've decided that alias model skins should be in a single four-level
array texture rather than spread over four textures, but there's no way
I want to write that code again: getting it right was hard enough the
first time :P
This commit is contained in:
Bill Currie 2021-02-01 12:16:05 +09:00
parent 08b1d75582
commit c7eafd9254
3 changed files with 111 additions and 80 deletions
include/QF/Vulkan
libs/video/renderer/vulkan

View file

@ -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

View file

@ -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);
}

View file

@ -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;