Vulkan: implement support for compressed textures

Textures that are GPU-compressed already (in practice: from DDS files
that are DXT1/DXT3/DXT5 compressed) now can stay GPU compressed
in Vulkan, similar to how that works on OpenGL.

Additionally, fixed lack of mipmaps in Vulkan textures. The textures
were created with mipmaps (good), the sampler too (good), but
the vulkan image view was always saying "yo, this is mip 0 only"
because mip range variables were never set to anything than zero.

Pull Request: https://projects.blender.org/blender/blender/pulls/119866
This commit is contained in:
Aras Pranckevicius 2024-03-26 14:49:53 +01:00 committed by Aras Pranckevicius
parent 4cdc62044e
commit 3663c8147c
2 changed files with 37 additions and 12 deletions

View File

@ -138,6 +138,16 @@ static ConversionType type_of_conversion_float(const eGPUTextureFormat host_form
case GPU_R11F_G11F_B10F:
return ConversionType::FLOAT_TO_B10F_G11F_R11F;
case GPU_SRGB8_A8_DXT1:
case GPU_SRGB8_A8_DXT3:
case GPU_SRGB8_A8_DXT5:
case GPU_RGBA8_DXT1:
case GPU_RGBA8_DXT3:
case GPU_RGBA8_DXT5:
/* Not an actual "conversion", but compressed texture upload code
* pretends that host data is a float. It is actually raw BCn bits. */
return ConversionType::PASS_THROUGH;
case GPU_RGB32F: /* GPU_RGB32F Not supported by vendors. */
case GPU_RGBA8UI:
case GPU_RGBA8I:
@ -169,12 +179,6 @@ static ConversionType type_of_conversion_float(const eGPUTextureFormat host_form
case GPU_RGB16:
case GPU_RGB32UI:
case GPU_RGB32I:
case GPU_SRGB8_A8_DXT1:
case GPU_SRGB8_A8_DXT3:
case GPU_SRGB8_A8_DXT5:
case GPU_RGBA8_DXT1:
case GPU_RGBA8_DXT3:
case GPU_RGBA8_DXT5:
case GPU_SRGB8:
case GPU_RGB9_E5:
case GPU_DEPTH_COMPONENT16:

View File

@ -60,6 +60,11 @@ void VKTexture::generate_mipmap()
if (mipmaps_ <= 1) {
return;
}
/* Allow users to provide mipmaps stored in compressed textures.
* Skip generating mipmaps to avoid overriding the existing ones. */
if (format_flag_ & GPU_FORMAT_COMPRESSED) {
return;
}
VKContext &context = *VKContext::get();
VKCommandBuffers &command_buffers = context.command_buffers_get();
@ -264,6 +269,8 @@ void VKTexture::read_sub(
void *VKTexture::read(int mip, eGPUDataFormat format)
{
BLI_assert(!(format_flag_ & GPU_FORMAT_COMPRESSED));
int mip_size[3] = {1, 1, 1};
VkImageType vk_image_type = to_vk_image_type(type_);
mip_size_get(mip, mip_size);
@ -298,13 +305,9 @@ void VKTexture::update_sub(
{
BLI_assert(!is_texture_view());
/* Vulkan images cannot be directly mapped to host memory and requires a staging buffer. */
VKContext &context = *VKContext::get();
int layers = vk_layer_count(1);
int3 extent = int3(extent_[0], max_ii(extent_[1], 1), max_ii(extent_[2], 1));
size_t sample_len = extent.x * extent.y * extent.z;
size_t device_memory_size = sample_len * to_bytesize(device_format_);
const bool is_compressed = (format_flag_ & GPU_FORMAT_COMPRESSED);
int3 extent = int3(extent_[0], max_ii(extent_[1], 1), max_ii(extent_[2], 1));
if (type_ & GPU_TEXTURE_1D) {
extent.y = 1;
extent.z = 1;
@ -313,6 +316,23 @@ void VKTexture::update_sub(
extent.z = 1;
}
/* Vulkan images cannot be directly mapped to host memory and requires a staging buffer. */
VKContext &context = *VKContext::get();
int layers = vk_layer_count(1);
size_t sample_len = size_t(extent.x) * extent.y * extent.z;
size_t device_memory_size = sample_len * to_bytesize(device_format_);
if (is_compressed) {
BLI_assert_msg(extent.z == 1, "Compressed 3D textures are not supported");
size_t block_size = to_block_size(device_format_);
size_t blocks_x = divide_ceil_u(extent.x, 4);
size_t blocks_y = divide_ceil_u(extent.y, 4);
device_memory_size = blocks_x * blocks_y * block_size;
/* `convert_buffer` later on will use `sample_len * to_bytesize(device_format_)`
* as total memory size calculation. Make that work for compressed case. */
sample_len = device_memory_size / to_bytesize(device_format_);
}
VKBuffer staging_buffer;
staging_buffer.create(device_memory_size, GPU_USAGE_DYNAMIC, VK_BUFFER_USAGE_TRANSFER_SRC_BIT);
convert_host_to_device(
@ -375,6 +395,7 @@ bool VKTexture::init_internal()
if (!allocate()) {
return false;
}
this->mip_range_set(0, mipmaps_ - 1);
return true;
}