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:
parent
08b1d75582
commit
c7eafd9254
3 changed files with 111 additions and 80 deletions
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue