899 lines
36 KiB
C++
899 lines
36 KiB
C++
// Copyright 2022 The Android Open Source Project
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
#include "CompressedImageInfo.h"
|
|
|
|
#include "aemu/base/ArraySize.h"
|
|
#include "stream-servers/vulkan/VkFormatUtils.h"
|
|
#include "stream-servers/vulkan/emulated_textures/shaders/DecompressionShaders.h"
|
|
|
|
namespace gfxstream {
|
|
namespace vk {
|
|
namespace {
|
|
|
|
#define _RETURN_ON_FAILURE(cmd) \
|
|
{ \
|
|
VkResult result = cmd; \
|
|
if (result != VK_SUCCESS) { \
|
|
WARN("Warning: %s %s:%d vulkan failure %d", __func__, __FILE__, __LINE__, result); \
|
|
return result; \
|
|
} \
|
|
}
|
|
|
|
using android::base::arraySize;
|
|
|
|
struct Etc2PushConstant {
|
|
uint32_t compFormat;
|
|
uint32_t baseLayer;
|
|
};
|
|
|
|
struct AstcPushConstant {
|
|
uint32_t blockSize[2];
|
|
uint32_t baseLayer;
|
|
uint32_t smallBlock;
|
|
};
|
|
|
|
struct ShaderData {
|
|
const uint32_t* code; // Pointer to shader's compiled spir-v code
|
|
const size_t size; // size of the code in bytes
|
|
};
|
|
|
|
struct ShaderGroup {
|
|
ShaderData shader1D;
|
|
ShaderData shader2D;
|
|
ShaderData shader3D;
|
|
};
|
|
|
|
// Helper macro to declare the shader goups
|
|
#define DECLARE_SHADER_GROUP(Format) \
|
|
constexpr ShaderGroup kShader##Format { \
|
|
.shader1D = {.code = decompression_shaders::Format##_1D, \
|
|
.size = sizeof(decompression_shaders::Format##_1D)}, \
|
|
.shader2D = {.code = decompression_shaders::Format##_2D, \
|
|
.size = sizeof(decompression_shaders::Format##_2D)}, \
|
|
.shader3D = {.code = decompression_shaders::Format##_3D, \
|
|
.size = sizeof(decompression_shaders::Format##_3D)}, \
|
|
}
|
|
|
|
DECLARE_SHADER_GROUP(Astc);
|
|
DECLARE_SHADER_GROUP(EacR11Snorm);
|
|
DECLARE_SHADER_GROUP(EacR11Unorm);
|
|
DECLARE_SHADER_GROUP(EacRG11Snorm);
|
|
DECLARE_SHADER_GROUP(EacRG11Unorm);
|
|
DECLARE_SHADER_GROUP(Etc2RGB8);
|
|
DECLARE_SHADER_GROUP(Etc2RGBA8);
|
|
|
|
#undef DECLARE_SHADER_GROUP
|
|
|
|
// Returns the group of shaders that can decompress a given format, or null if none is found.
|
|
const ShaderGroup* getShaderGroup(VkFormat format) {
|
|
switch (format) {
|
|
case VK_FORMAT_ASTC_4x4_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_5x4_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_5x5_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_6x5_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_6x6_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_8x5_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_8x6_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_8x8_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_10x5_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_10x6_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_10x8_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_10x10_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_12x10_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_12x12_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_4x4_SRGB_BLOCK:
|
|
case VK_FORMAT_ASTC_5x4_SRGB_BLOCK:
|
|
case VK_FORMAT_ASTC_5x5_SRGB_BLOCK:
|
|
case VK_FORMAT_ASTC_6x5_SRGB_BLOCK:
|
|
case VK_FORMAT_ASTC_6x6_SRGB_BLOCK:
|
|
case VK_FORMAT_ASTC_8x5_SRGB_BLOCK:
|
|
case VK_FORMAT_ASTC_8x6_SRGB_BLOCK:
|
|
case VK_FORMAT_ASTC_8x8_SRGB_BLOCK:
|
|
case VK_FORMAT_ASTC_10x5_SRGB_BLOCK:
|
|
case VK_FORMAT_ASTC_10x6_SRGB_BLOCK:
|
|
case VK_FORMAT_ASTC_10x8_SRGB_BLOCK:
|
|
case VK_FORMAT_ASTC_10x10_SRGB_BLOCK:
|
|
case VK_FORMAT_ASTC_12x10_SRGB_BLOCK:
|
|
case VK_FORMAT_ASTC_12x12_SRGB_BLOCK:
|
|
return &kShaderAstc;
|
|
|
|
case VK_FORMAT_EAC_R11_SNORM_BLOCK:
|
|
return &kShaderEacR11Snorm;
|
|
|
|
case VK_FORMAT_EAC_R11_UNORM_BLOCK:
|
|
return &kShaderEacR11Unorm;
|
|
|
|
case VK_FORMAT_EAC_R11G11_SNORM_BLOCK:
|
|
return &kShaderEacRG11Snorm;
|
|
|
|
case VK_FORMAT_EAC_R11G11_UNORM_BLOCK:
|
|
return &kShaderEacRG11Unorm;
|
|
|
|
case VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK:
|
|
case VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK:
|
|
case VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK:
|
|
case VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK:
|
|
return &kShaderEtc2RGB8;
|
|
|
|
case VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK:
|
|
case VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK:
|
|
return &kShaderEtc2RGBA8;
|
|
|
|
default:
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
// Returns the shader that can decompress a given image format and type
|
|
const ShaderData* getDecompressionShader(VkFormat format, VkImageType imageType) {
|
|
const ShaderGroup* group = getShaderGroup(format);
|
|
if (!group) return nullptr;
|
|
|
|
switch (imageType) {
|
|
case VK_IMAGE_TYPE_1D:
|
|
return &group->shader1D;
|
|
case VK_IMAGE_TYPE_2D:
|
|
return &group->shader2D;
|
|
case VK_IMAGE_TYPE_3D:
|
|
return &group->shader3D;
|
|
default:
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
// Returns x / y, rounded up. E.g. ceil_div(7, 2) == 4
|
|
// Note the potential integer overflow for large numbers.
|
|
inline constexpr uint32_t ceil_div(uint32_t x, uint32_t y) { return (x + y - 1) / y; }
|
|
|
|
VkImageView createDefaultImageView(VulkanDispatch* vk, VkDevice device, VkImage image,
|
|
VkFormat format, VkImageType imageType, uint32_t mipLevel,
|
|
uint32_t layerCount) {
|
|
VkImageViewCreateInfo imageViewInfo = {};
|
|
imageViewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
|
imageViewInfo.image = image;
|
|
switch (imageType) {
|
|
case VK_IMAGE_TYPE_1D:
|
|
imageViewInfo.viewType = VK_IMAGE_VIEW_TYPE_1D_ARRAY;
|
|
break;
|
|
case VK_IMAGE_TYPE_2D:
|
|
imageViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
|
|
break;
|
|
case VK_IMAGE_TYPE_3D:
|
|
imageViewInfo.viewType = VK_IMAGE_VIEW_TYPE_3D;
|
|
break;
|
|
default:
|
|
imageViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
|
|
break;
|
|
}
|
|
imageViewInfo.format = format;
|
|
imageViewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
|
|
imageViewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
|
|
imageViewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
|
|
imageViewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
|
|
imageViewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
imageViewInfo.subresourceRange.baseMipLevel = mipLevel;
|
|
imageViewInfo.subresourceRange.levelCount = 1;
|
|
imageViewInfo.subresourceRange.baseArrayLayer = 0;
|
|
imageViewInfo.subresourceRange.layerCount = layerCount;
|
|
VkImageView imageView;
|
|
if (vk->vkCreateImageView(device, &imageViewInfo, nullptr, &imageView) != VK_SUCCESS) {
|
|
WARN("Warning: %s %s:%d failure", __func__, __FILE__, __LINE__);
|
|
return VK_NULL_HANDLE;
|
|
}
|
|
return imageView;
|
|
}
|
|
|
|
VkExtent2D getBlockSize(VkFormat format) {
|
|
switch (format) {
|
|
case VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK:
|
|
case VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK:
|
|
case VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK:
|
|
case VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK:
|
|
case VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK:
|
|
case VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK:
|
|
case VK_FORMAT_EAC_R11_UNORM_BLOCK:
|
|
case VK_FORMAT_EAC_R11_SNORM_BLOCK:
|
|
case VK_FORMAT_EAC_R11G11_UNORM_BLOCK:
|
|
case VK_FORMAT_EAC_R11G11_SNORM_BLOCK:
|
|
return {4, 4};
|
|
case VK_FORMAT_ASTC_4x4_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_4x4_SRGB_BLOCK:
|
|
return {4, 4};
|
|
case VK_FORMAT_ASTC_5x4_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_5x4_SRGB_BLOCK:
|
|
return {5, 4};
|
|
case VK_FORMAT_ASTC_5x5_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_5x5_SRGB_BLOCK:
|
|
return {5, 5};
|
|
case VK_FORMAT_ASTC_6x5_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_6x5_SRGB_BLOCK:
|
|
return {6, 5};
|
|
case VK_FORMAT_ASTC_6x6_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_6x6_SRGB_BLOCK:
|
|
return {6, 6};
|
|
case VK_FORMAT_ASTC_8x5_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_8x5_SRGB_BLOCK:
|
|
return {8, 5};
|
|
case VK_FORMAT_ASTC_8x6_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_8x6_SRGB_BLOCK:
|
|
return {8, 6};
|
|
case VK_FORMAT_ASTC_8x8_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_8x8_SRGB_BLOCK:
|
|
return {8, 8};
|
|
case VK_FORMAT_ASTC_10x5_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_10x5_SRGB_BLOCK:
|
|
return {10, 5};
|
|
case VK_FORMAT_ASTC_10x6_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_10x6_SRGB_BLOCK:
|
|
return {10, 6};
|
|
case VK_FORMAT_ASTC_10x8_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_10x8_SRGB_BLOCK:
|
|
return {10, 8};
|
|
case VK_FORMAT_ASTC_10x10_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_10x10_SRGB_BLOCK:
|
|
return {10, 10};
|
|
case VK_FORMAT_ASTC_12x10_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_12x10_SRGB_BLOCK:
|
|
return {12, 10};
|
|
case VK_FORMAT_ASTC_12x12_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_12x12_SRGB_BLOCK:
|
|
return {12, 12};
|
|
default:
|
|
return {1, 1};
|
|
}
|
|
}
|
|
|
|
// Returns whether a given memory barrier puts the image in a layout where it can be read from.
|
|
bool imageWillBecomeReadable(const VkImageMemoryBarrier& barrier) {
|
|
return barrier.oldLayout != VK_IMAGE_LAYOUT_UNDEFINED &&
|
|
(barrier.newLayout == VK_IMAGE_LAYOUT_GENERAL ||
|
|
barrier.newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL ||
|
|
barrier.newLayout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
CompressedImageInfo::CompressedImageInfo(VkDevice device) : mDevice(device) {}
|
|
|
|
CompressedImageInfo::CompressedImageInfo(VkDevice device, const VkImageCreateInfo& createInfo)
|
|
: mDevice(device),
|
|
mCompressedFormat(createInfo.format),
|
|
mDecompressedFormat(getDecompressedFormat(mCompressedFormat)),
|
|
mCompressedMipmapsFormat(getCompressedMipmapsFormat(mCompressedFormat)),
|
|
mImageType(createInfo.imageType),
|
|
mExtent(createInfo.extent),
|
|
mBlock(getBlockSize(mCompressedFormat)),
|
|
mLayerCount(createInfo.arrayLayers),
|
|
mMipLevels(createInfo.mipLevels) {}
|
|
|
|
// static
|
|
VkFormat CompressedImageInfo::getDecompressedFormat(VkFormat compFmt) {
|
|
switch (compFmt) {
|
|
case VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK:
|
|
case VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK:
|
|
case VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK:
|
|
return VK_FORMAT_R8G8B8A8_UNORM;
|
|
case VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK:
|
|
case VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK:
|
|
case VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK:
|
|
return VK_FORMAT_R8G8B8A8_SRGB;
|
|
case VK_FORMAT_EAC_R11_UNORM_BLOCK:
|
|
return VK_FORMAT_R16_UNORM;
|
|
case VK_FORMAT_EAC_R11_SNORM_BLOCK:
|
|
return VK_FORMAT_R16_SNORM;
|
|
case VK_FORMAT_EAC_R11G11_UNORM_BLOCK:
|
|
return VK_FORMAT_R16G16_UNORM;
|
|
case VK_FORMAT_EAC_R11G11_SNORM_BLOCK:
|
|
return VK_FORMAT_R16G16_SNORM;
|
|
case VK_FORMAT_ASTC_4x4_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_5x4_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_5x5_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_6x5_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_6x6_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_8x5_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_8x6_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_8x8_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_10x5_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_10x6_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_10x8_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_10x10_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_12x10_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_12x12_UNORM_BLOCK:
|
|
return VK_FORMAT_R8G8B8A8_UNORM;
|
|
case VK_FORMAT_ASTC_4x4_SRGB_BLOCK:
|
|
case VK_FORMAT_ASTC_5x4_SRGB_BLOCK:
|
|
case VK_FORMAT_ASTC_5x5_SRGB_BLOCK:
|
|
case VK_FORMAT_ASTC_6x5_SRGB_BLOCK:
|
|
case VK_FORMAT_ASTC_6x6_SRGB_BLOCK:
|
|
case VK_FORMAT_ASTC_8x5_SRGB_BLOCK:
|
|
case VK_FORMAT_ASTC_8x6_SRGB_BLOCK:
|
|
case VK_FORMAT_ASTC_8x8_SRGB_BLOCK:
|
|
case VK_FORMAT_ASTC_10x5_SRGB_BLOCK:
|
|
case VK_FORMAT_ASTC_10x6_SRGB_BLOCK:
|
|
case VK_FORMAT_ASTC_10x8_SRGB_BLOCK:
|
|
case VK_FORMAT_ASTC_10x10_SRGB_BLOCK:
|
|
case VK_FORMAT_ASTC_12x10_SRGB_BLOCK:
|
|
case VK_FORMAT_ASTC_12x12_SRGB_BLOCK:
|
|
return VK_FORMAT_R8G8B8A8_SRGB;
|
|
default:
|
|
return compFmt;
|
|
}
|
|
}
|
|
|
|
// static
|
|
VkFormat CompressedImageInfo::getCompressedMipmapsFormat(VkFormat compFmt) {
|
|
switch (compFmt) {
|
|
case VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK:
|
|
case VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK:
|
|
case VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK:
|
|
case VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK:
|
|
return VK_FORMAT_R16G16B16A16_UINT;
|
|
case VK_FORMAT_EAC_R11_UNORM_BLOCK:
|
|
case VK_FORMAT_EAC_R11_SNORM_BLOCK:
|
|
return VK_FORMAT_R32G32_UINT;
|
|
case VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK:
|
|
case VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK:
|
|
case VK_FORMAT_EAC_R11G11_UNORM_BLOCK:
|
|
case VK_FORMAT_EAC_R11G11_SNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_4x4_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_5x4_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_5x5_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_6x5_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_6x6_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_8x5_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_8x6_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_8x8_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_10x5_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_10x6_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_10x8_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_10x10_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_12x10_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_12x12_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_4x4_SRGB_BLOCK:
|
|
case VK_FORMAT_ASTC_5x4_SRGB_BLOCK:
|
|
case VK_FORMAT_ASTC_5x5_SRGB_BLOCK:
|
|
case VK_FORMAT_ASTC_6x5_SRGB_BLOCK:
|
|
case VK_FORMAT_ASTC_6x6_SRGB_BLOCK:
|
|
case VK_FORMAT_ASTC_8x5_SRGB_BLOCK:
|
|
case VK_FORMAT_ASTC_8x6_SRGB_BLOCK:
|
|
case VK_FORMAT_ASTC_8x8_SRGB_BLOCK:
|
|
case VK_FORMAT_ASTC_10x5_SRGB_BLOCK:
|
|
case VK_FORMAT_ASTC_10x6_SRGB_BLOCK:
|
|
case VK_FORMAT_ASTC_10x8_SRGB_BLOCK:
|
|
case VK_FORMAT_ASTC_10x10_SRGB_BLOCK:
|
|
case VK_FORMAT_ASTC_12x10_SRGB_BLOCK:
|
|
case VK_FORMAT_ASTC_12x12_SRGB_BLOCK:
|
|
return VK_FORMAT_R32G32B32A32_UINT;
|
|
default:
|
|
return compFmt;
|
|
}
|
|
}
|
|
|
|
// static
|
|
bool CompressedImageInfo::isEtc2(VkFormat format) {
|
|
switch (format) {
|
|
case VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK:
|
|
case VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK:
|
|
case VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK:
|
|
case VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK:
|
|
case VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK:
|
|
case VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK:
|
|
case VK_FORMAT_EAC_R11_UNORM_BLOCK:
|
|
case VK_FORMAT_EAC_R11_SNORM_BLOCK:
|
|
case VK_FORMAT_EAC_R11G11_UNORM_BLOCK:
|
|
case VK_FORMAT_EAC_R11G11_SNORM_BLOCK:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// static
|
|
bool CompressedImageInfo::isAstc(VkFormat format) {
|
|
switch (format) {
|
|
case VK_FORMAT_ASTC_4x4_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_4x4_SRGB_BLOCK:
|
|
case VK_FORMAT_ASTC_5x4_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_5x4_SRGB_BLOCK:
|
|
case VK_FORMAT_ASTC_5x5_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_5x5_SRGB_BLOCK:
|
|
case VK_FORMAT_ASTC_6x5_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_6x5_SRGB_BLOCK:
|
|
case VK_FORMAT_ASTC_6x6_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_6x6_SRGB_BLOCK:
|
|
case VK_FORMAT_ASTC_8x5_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_8x5_SRGB_BLOCK:
|
|
case VK_FORMAT_ASTC_8x6_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_8x6_SRGB_BLOCK:
|
|
case VK_FORMAT_ASTC_8x8_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_8x8_SRGB_BLOCK:
|
|
case VK_FORMAT_ASTC_10x5_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_10x5_SRGB_BLOCK:
|
|
case VK_FORMAT_ASTC_10x6_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_10x6_SRGB_BLOCK:
|
|
case VK_FORMAT_ASTC_10x8_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_10x8_SRGB_BLOCK:
|
|
case VK_FORMAT_ASTC_10x10_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_10x10_SRGB_BLOCK:
|
|
case VK_FORMAT_ASTC_12x10_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_12x10_SRGB_BLOCK:
|
|
case VK_FORMAT_ASTC_12x12_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_12x12_SRGB_BLOCK:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// static
|
|
bool CompressedImageInfo::needEmulatedAlpha(VkFormat format) {
|
|
switch (format) {
|
|
case VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK:
|
|
case VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool CompressedImageInfo::isEtc2() const { return isEtc2(mCompressedFormat); }
|
|
|
|
bool CompressedImageInfo::isAstc() const { return isAstc(mCompressedFormat); }
|
|
|
|
VkImageCreateInfo CompressedImageInfo::getDecompressedCreateInfo(
|
|
const VkImageCreateInfo& createInfo) const {
|
|
VkImageCreateInfo result = createInfo;
|
|
result.format = mDecompressedFormat;
|
|
result.flags &= ~VK_IMAGE_CREATE_BLOCK_TEXEL_VIEW_COMPATIBLE_BIT_KHR;
|
|
result.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
|
|
result.usage |= VK_IMAGE_USAGE_STORAGE_BIT;
|
|
return result;
|
|
}
|
|
|
|
void CompressedImageInfo::createCompressedMipmapImages(VulkanDispatch* vk,
|
|
const VkImageCreateInfo& createInfo) {
|
|
if (!mCompressedMipmaps.empty()) {
|
|
return;
|
|
}
|
|
|
|
VkImageCreateInfo createInfoCopy = createInfo;
|
|
createInfoCopy.format = mCompressedMipmapsFormat;
|
|
createInfoCopy.usage |= VK_IMAGE_USAGE_STORAGE_BIT;
|
|
createInfoCopy.flags &= ~VK_IMAGE_CREATE_BLOCK_TEXEL_VIEW_COMPATIBLE_BIT_KHR;
|
|
createInfoCopy.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
|
|
createInfoCopy.mipLevels = 1;
|
|
|
|
mCompressedMipmaps.resize(mMipLevels);
|
|
for (uint32_t i = 0; i < mMipLevels; i++) {
|
|
createInfoCopy.extent = compressedMipmapExtent(i);
|
|
vk->vkCreateImage(mDevice, &createInfoCopy, nullptr, mCompressedMipmaps.data() + i);
|
|
}
|
|
|
|
// Get the size of all images (decompressed image and compressed mipmaps)
|
|
std::vector<VkDeviceSize> memSizes(mMipLevels + 1);
|
|
memSizes[0] = getImageSize(vk, mDecompressedImage);
|
|
for (size_t i = 0; i < mMipLevels; i++) {
|
|
memSizes[i + 1] = getImageSize(vk, mCompressedMipmaps[i]);
|
|
}
|
|
|
|
// Initialize the memory offsets
|
|
mMemoryOffsets.resize(mMipLevels + 1);
|
|
for (size_t i = 0; i < mMipLevels + 1; i++) {
|
|
VkDeviceSize alignedSize = memSizes[i];
|
|
if (mAlignment != 0) {
|
|
alignedSize = ceil_div(alignedSize, mAlignment) * mAlignment;
|
|
}
|
|
mMemoryOffsets[i] = (i == 0 ? 0 : mMemoryOffsets[i - 1]) + alignedSize;
|
|
}
|
|
}
|
|
|
|
void CompressedImageInfo::initAstcCpuDecompression(VulkanDispatch* vk,
|
|
VkPhysicalDevice physicalDevice) {
|
|
mAstcTexture = std::make_unique<AstcTexture>(vk, mDevice, physicalDevice, mExtent, mBlock.width,
|
|
mBlock.height, &AstcCpuDecompressor::get());
|
|
}
|
|
|
|
bool CompressedImageInfo::decompressIfNeeded(VulkanDispatch* vk, VkCommandBuffer commandBuffer,
|
|
VkPipelineStageFlags srcStageMask,
|
|
VkPipelineStageFlags dstStageMask,
|
|
const VkImageMemoryBarrier& targetBarrier,
|
|
std::vector<VkImageMemoryBarrier>& outputBarriers) {
|
|
std::vector<VkImageMemoryBarrier> imageBarriers = getImageBarriers(targetBarrier);
|
|
|
|
if (!imageWillBecomeReadable(targetBarrier)) {
|
|
// We're not going to read from the image, no need to decompress it.
|
|
// Apply the target barrier to the compressed mipmaps and the decompressed image.
|
|
outputBarriers.insert(outputBarriers.end(), imageBarriers.begin(), imageBarriers.end());
|
|
return false;
|
|
}
|
|
|
|
VkResult result = initializeDecompressionPipeline(vk, mDevice);
|
|
if (result != VK_SUCCESS) {
|
|
WARN("Failed to initialize pipeline for texture decompression");
|
|
return false;
|
|
}
|
|
|
|
// Transition the layout of all the compressed mipmaps so that the shader can read from them.
|
|
for (auto& barrier : imageBarriers) {
|
|
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
|
barrier.newLayout = VK_IMAGE_LAYOUT_GENERAL;
|
|
}
|
|
|
|
// Transition the layout of the decompressed image so that we can write to it.
|
|
imageBarriers.back().srcAccessMask = 0;
|
|
imageBarriers.back().oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
|
imageBarriers.back().dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
|
|
imageBarriers.back().newLayout = VK_IMAGE_LAYOUT_GENERAL;
|
|
|
|
// Do the layout transitions
|
|
vk->vkCmdPipelineBarrier(commandBuffer, srcStageMask, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0,
|
|
0, nullptr, 0, nullptr, imageBarriers.size(), imageBarriers.data());
|
|
|
|
// Run the decompression shader
|
|
decompress(vk, commandBuffer, getImageSubresourceRange(targetBarrier.subresourceRange));
|
|
|
|
// Finally, transition the layout of all images to match the target barrier.
|
|
for (auto& barrier : imageBarriers) {
|
|
barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
|
barrier.oldLayout = VK_IMAGE_LAYOUT_GENERAL;
|
|
barrier.dstAccessMask = targetBarrier.dstAccessMask;
|
|
barrier.newLayout = targetBarrier.newLayout;
|
|
}
|
|
// (adjust the last barrier since it's for the decompressed image)
|
|
imageBarriers.back().srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
|
|
|
|
// Do the layout transitions
|
|
vk->vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, dstStageMask, 0,
|
|
0, nullptr, 0, nullptr, imageBarriers.size(), imageBarriers.data());
|
|
|
|
return true;
|
|
}
|
|
|
|
void CompressedImageInfo::decompressOnCpu(VkCommandBuffer commandBuffer, uint8_t* srcAstcData,
|
|
size_t astcDataSize, VkImage dstImage,
|
|
VkImageLayout dstImageLayout, uint32_t regionCount,
|
|
const VkBufferImageCopy* pRegions,
|
|
const VkDecoderContext& context) {
|
|
mAstcTexture->on_vkCmdCopyBufferToImage(commandBuffer, srcAstcData, astcDataSize, dstImage,
|
|
dstImageLayout, regionCount, pRegions, context);
|
|
}
|
|
|
|
VkMemoryRequirements CompressedImageInfo::getMemoryRequirements() const {
|
|
return {
|
|
.size = mMemoryOffsets.back(),
|
|
.alignment = mAlignment,
|
|
};
|
|
}
|
|
|
|
VkResult CompressedImageInfo::bindCompressedMipmapsMemory(VulkanDispatch* vk, VkDeviceMemory memory,
|
|
VkDeviceSize memoryOffset) {
|
|
VkResult result = VK_SUCCESS;
|
|
for (size_t i = 0; i < mCompressedMipmaps.size(); i++) {
|
|
VkResult res = vk->vkBindImageMemory(mDevice, mCompressedMipmaps[i], memory,
|
|
memoryOffset + mMemoryOffsets[i]);
|
|
if (res != VK_SUCCESS) result = res;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
VkBufferImageCopy CompressedImageInfo::getBufferImageCopy(
|
|
const VkBufferImageCopy& origRegion) const {
|
|
VkBufferImageCopy region = origRegion;
|
|
uint32_t mipLevel = region.imageSubresource.mipLevel;
|
|
region.imageSubresource.mipLevel = 0;
|
|
region.bufferRowLength /= mBlock.width;
|
|
region.bufferImageHeight /= mBlock.height;
|
|
region.imageOffset.x /= mBlock.width;
|
|
region.imageOffset.y /= mBlock.height;
|
|
region.imageExtent = compressedMipmapPortion(region.imageExtent, mipLevel);
|
|
return region;
|
|
}
|
|
|
|
// static
|
|
VkImageCopy CompressedImageInfo::getCompressedMipmapsImageCopy(const VkImageCopy& origRegion,
|
|
const CompressedImageInfo& srcImg,
|
|
const CompressedImageInfo& dstImg,
|
|
bool needEmulatedSrc,
|
|
bool needEmulatedDst) {
|
|
VkImageCopy region = origRegion;
|
|
if (needEmulatedSrc) {
|
|
uint32_t mipLevel = region.srcSubresource.mipLevel;
|
|
region.srcSubresource.mipLevel = 0;
|
|
region.srcOffset.x /= srcImg.mBlock.width;
|
|
region.srcOffset.y /= srcImg.mBlock.height;
|
|
region.extent = srcImg.compressedMipmapPortion(region.extent, mipLevel);
|
|
}
|
|
if (needEmulatedDst) {
|
|
region.dstSubresource.mipLevel = 0;
|
|
region.dstOffset.x /= dstImg.mBlock.width;
|
|
region.dstOffset.y /= dstImg.mBlock.height;
|
|
}
|
|
return region;
|
|
}
|
|
|
|
void CompressedImageInfo::destroy(VulkanDispatch* vk) {
|
|
for (const auto& image : mCompressedMipmaps) {
|
|
vk->vkDestroyImage(mDevice, image, nullptr);
|
|
}
|
|
vk->vkDestroyDescriptorSetLayout(mDevice, mDecompDescriptorSetLayout, nullptr);
|
|
vk->vkDestroyDescriptorPool(mDevice, mDecompDescriptorPool, nullptr);
|
|
vk->vkDestroyShaderModule(mDevice, mDecompShader, nullptr);
|
|
vk->vkDestroyPipelineLayout(mDevice, mDecompPipelineLayout, nullptr);
|
|
vk->vkDestroyPipeline(mDevice, mDecompPipeline, nullptr);
|
|
for (const auto& imageView : mCompressedMipmapsImageViews) {
|
|
vk->vkDestroyImageView(mDevice, imageView, nullptr);
|
|
}
|
|
for (const auto& imageView : mDecompImageViews) {
|
|
vk->vkDestroyImageView(mDevice, imageView, nullptr);
|
|
}
|
|
vk->vkDestroyImage(mDevice, mDecompressedImage, nullptr);
|
|
}
|
|
|
|
VkDeviceSize CompressedImageInfo::getImageSize(VulkanDispatch* vk, VkImage image) {
|
|
VkMemoryRequirements memRequirements;
|
|
vk->vkGetImageMemoryRequirements(mDevice, image, &memRequirements);
|
|
mAlignment = std::max(mAlignment, memRequirements.alignment);
|
|
return memRequirements.size;
|
|
}
|
|
|
|
std::vector<VkImageMemoryBarrier> CompressedImageInfo::getImageBarriers(
|
|
const VkImageMemoryBarrier& srcBarrier) {
|
|
const VkImageSubresourceRange range = getImageSubresourceRange(srcBarrier.subresourceRange);
|
|
|
|
std::vector<VkImageMemoryBarrier> imageBarriers;
|
|
imageBarriers.reserve(range.levelCount + 1);
|
|
|
|
// Add the barriers for the compressed mipmaps
|
|
VkImageMemoryBarrier mipmapBarrier = srcBarrier;
|
|
mipmapBarrier.subresourceRange.baseMipLevel = 0;
|
|
mipmapBarrier.subresourceRange.levelCount = 1;
|
|
imageBarriers.insert(imageBarriers.begin(), range.levelCount, mipmapBarrier);
|
|
for (uint32_t j = 0; j < range.levelCount; j++) {
|
|
imageBarriers[j].image = mCompressedMipmaps[range.baseMipLevel + j];
|
|
}
|
|
|
|
// Add a barrier for the decompressed image
|
|
imageBarriers.push_back(srcBarrier);
|
|
imageBarriers.back().image = mDecompressedImage;
|
|
|
|
return imageBarriers;
|
|
}
|
|
|
|
VkImageSubresourceRange CompressedImageInfo::getImageSubresourceRange(
|
|
const VkImageSubresourceRange& range) const {
|
|
VkImageSubresourceRange result = range;
|
|
if (result.levelCount == VK_REMAINING_MIP_LEVELS) {
|
|
result.levelCount = mMipLevels - range.baseMipLevel;
|
|
}
|
|
if (result.layerCount == VK_REMAINING_ARRAY_LAYERS) {
|
|
result.layerCount = mLayerCount - range.baseArrayLayer;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
VkResult CompressedImageInfo::initializeDecompressionPipeline(VulkanDispatch* vk, VkDevice device) {
|
|
if (mDecompPipeline != nullptr) {
|
|
return VK_SUCCESS;
|
|
}
|
|
|
|
const ShaderData* shader = getDecompressionShader(mCompressedFormat, mImageType);
|
|
if (!shader) {
|
|
WARN("No decompression shader found for format %s and img type %s",
|
|
string_VkFormat(mCompressedFormat), string_VkImageType(mImageType));
|
|
return VK_ERROR_FORMAT_NOT_SUPPORTED;
|
|
}
|
|
|
|
VkShaderModuleCreateInfo shaderInfo = {
|
|
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
|
|
.codeSize = shader->size,
|
|
.pCode = shader->code,
|
|
};
|
|
_RETURN_ON_FAILURE(vk->vkCreateShaderModule(device, &shaderInfo, nullptr, &mDecompShader));
|
|
|
|
VkDescriptorSetLayoutBinding dsLayoutBindings[] = {
|
|
{
|
|
.binding = 0,
|
|
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
|
|
.descriptorCount = 1,
|
|
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
|
|
},
|
|
{
|
|
.binding = 1,
|
|
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
|
|
.descriptorCount = 1,
|
|
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
|
|
},
|
|
};
|
|
VkDescriptorSetLayoutCreateInfo dsLayoutInfo = {
|
|
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
|
|
.bindingCount = 2,
|
|
.pBindings = dsLayoutBindings,
|
|
};
|
|
_RETURN_ON_FAILURE(vk->vkCreateDescriptorSetLayout(device, &dsLayoutInfo, nullptr,
|
|
&mDecompDescriptorSetLayout));
|
|
|
|
VkDescriptorPoolSize poolSize = {
|
|
.type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
|
|
.descriptorCount = 2 * mMipLevels,
|
|
};
|
|
VkDescriptorPoolCreateInfo dsPoolInfo = {
|
|
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
|
|
.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT,
|
|
.maxSets = mMipLevels,
|
|
.poolSizeCount = 1,
|
|
.pPoolSizes = &poolSize,
|
|
};
|
|
_RETURN_ON_FAILURE(
|
|
vk->vkCreateDescriptorPool(device, &dsPoolInfo, nullptr, &mDecompDescriptorPool));
|
|
|
|
std::vector<VkDescriptorSetLayout> layouts(mMipLevels, mDecompDescriptorSetLayout);
|
|
|
|
VkDescriptorSetAllocateInfo dsInfo = {
|
|
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
|
|
.descriptorPool = mDecompDescriptorPool,
|
|
.descriptorSetCount = mMipLevels,
|
|
.pSetLayouts = layouts.data(),
|
|
};
|
|
mDecompDescriptorSets.resize(mMipLevels);
|
|
_RETURN_ON_FAILURE(vk->vkAllocateDescriptorSets(device, &dsInfo, mDecompDescriptorSets.data()));
|
|
|
|
VkPushConstantRange pushConstant = {.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT};
|
|
if (isEtc2()) {
|
|
pushConstant.size = sizeof(Etc2PushConstant);
|
|
} else if (isAstc()) {
|
|
pushConstant.size = sizeof(AstcPushConstant);
|
|
}
|
|
|
|
VkPipelineLayoutCreateInfo pipelineLayoutInfo = {
|
|
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
|
|
.setLayoutCount = 1,
|
|
.pSetLayouts = &mDecompDescriptorSetLayout,
|
|
.pushConstantRangeCount = 1,
|
|
.pPushConstantRanges = &pushConstant,
|
|
};
|
|
_RETURN_ON_FAILURE(
|
|
vk->vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &mDecompPipelineLayout));
|
|
|
|
VkComputePipelineCreateInfo computePipelineInfo = {
|
|
.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,
|
|
.stage = {.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
|
.stage = VK_SHADER_STAGE_COMPUTE_BIT,
|
|
.module = mDecompShader,
|
|
.pName = "main"},
|
|
.layout = mDecompPipelineLayout,
|
|
};
|
|
_RETURN_ON_FAILURE(vk->vkCreateComputePipelines(device, nullptr, 1, &computePipelineInfo,
|
|
nullptr, &mDecompPipeline));
|
|
|
|
VkFormat intermediateFormat = VK_FORMAT_R8G8B8A8_UINT;
|
|
switch (mCompressedFormat) {
|
|
case VK_FORMAT_EAC_R11_UNORM_BLOCK:
|
|
case VK_FORMAT_EAC_R11_SNORM_BLOCK:
|
|
case VK_FORMAT_EAC_R11G11_UNORM_BLOCK:
|
|
case VK_FORMAT_EAC_R11G11_SNORM_BLOCK:
|
|
intermediateFormat = mDecompressedFormat;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
mCompressedMipmapsImageViews.resize(mMipLevels);
|
|
mDecompImageViews.resize(mMipLevels);
|
|
|
|
VkDescriptorImageInfo compressedMipmapsDescriptorImageInfo = {.imageLayout =
|
|
VK_IMAGE_LAYOUT_GENERAL};
|
|
VkDescriptorImageInfo mDecompDescriptorImageInfo = {.imageLayout = VK_IMAGE_LAYOUT_GENERAL};
|
|
VkWriteDescriptorSet writeDescriptorSets[2] = {
|
|
{
|
|
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
|
.dstBinding = 0,
|
|
.descriptorCount = 1,
|
|
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
|
|
.pImageInfo = &compressedMipmapsDescriptorImageInfo,
|
|
},
|
|
{
|
|
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
|
.dstBinding = 1,
|
|
.descriptorCount = 1,
|
|
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
|
|
.pImageInfo = &mDecompDescriptorImageInfo,
|
|
}};
|
|
|
|
for (uint32_t i = 0; i < mMipLevels; i++) {
|
|
mCompressedMipmapsImageViews[i] =
|
|
createDefaultImageView(vk, device, mCompressedMipmaps[i], mCompressedMipmapsFormat,
|
|
mImageType, 0, mLayerCount);
|
|
mDecompImageViews[i] = createDefaultImageView(
|
|
vk, device, mDecompressedImage, intermediateFormat, mImageType, i, mLayerCount);
|
|
compressedMipmapsDescriptorImageInfo.imageView = mCompressedMipmapsImageViews[i];
|
|
mDecompDescriptorImageInfo.imageView = mDecompImageViews[i];
|
|
writeDescriptorSets[0].dstSet = mDecompDescriptorSets[i];
|
|
writeDescriptorSets[1].dstSet = mDecompDescriptorSets[i];
|
|
vk->vkUpdateDescriptorSets(device, 2, writeDescriptorSets, 0, nullptr);
|
|
}
|
|
return VK_SUCCESS;
|
|
}
|
|
|
|
void CompressedImageInfo::decompress(VulkanDispatch* vk, VkCommandBuffer commandBuffer,
|
|
const VkImageSubresourceRange& range) {
|
|
vk->vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, mDecompPipeline);
|
|
uint32_t dispatchZ = mExtent.depth == 1 ? range.layerCount : mExtent.depth;
|
|
|
|
if (isEtc2()) {
|
|
const Etc2PushConstant pushConstant = {
|
|
.compFormat = (uint32_t)mCompressedFormat,
|
|
.baseLayer = mExtent.depth == 1 ? range.baseArrayLayer : 0};
|
|
vk->vkCmdPushConstants(commandBuffer, mDecompPipelineLayout, VK_SHADER_STAGE_COMPUTE_BIT, 0,
|
|
sizeof(pushConstant), &pushConstant);
|
|
} else if (isAstc()) {
|
|
uint32_t smallBlock = false;
|
|
switch (mCompressedFormat) {
|
|
case VK_FORMAT_ASTC_4x4_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_5x4_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_5x5_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_6x5_UNORM_BLOCK:
|
|
case VK_FORMAT_ASTC_4x4_SRGB_BLOCK:
|
|
case VK_FORMAT_ASTC_5x4_SRGB_BLOCK:
|
|
case VK_FORMAT_ASTC_5x5_SRGB_BLOCK:
|
|
case VK_FORMAT_ASTC_6x5_SRGB_BLOCK:
|
|
smallBlock = true;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
const AstcPushConstant pushConstant = {
|
|
.blockSize = {mBlock.width, mBlock.height},
|
|
.baseLayer = mExtent.depth == 1 ? range.baseArrayLayer : 0,
|
|
.smallBlock = smallBlock};
|
|
vk->vkCmdPushConstants(commandBuffer, mDecompPipelineLayout, VK_SHADER_STAGE_COMPUTE_BIT, 0,
|
|
sizeof(pushConstant), &pushConstant);
|
|
}
|
|
for (uint32_t i = range.baseMipLevel; i < range.baseMipLevel + range.levelCount; i++) {
|
|
vk->vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE,
|
|
mDecompPipelineLayout, 0, 1, mDecompDescriptorSets.data() + i,
|
|
0, nullptr);
|
|
VkExtent3D compExtent = compressedMipmapExtent(i);
|
|
vk->vkCmdDispatch(commandBuffer, compExtent.width, compExtent.height, dispatchZ);
|
|
}
|
|
}
|
|
|
|
VkExtent3D CompressedImageInfo::mipmapExtent(uint32_t level) const {
|
|
return {
|
|
.width = std::max<uint32_t>(mExtent.width >> level, 1),
|
|
.height = std::max<uint32_t>(mExtent.height >> level, 1),
|
|
.depth = std::max<uint32_t>(mExtent.depth >> level, 1),
|
|
};
|
|
}
|
|
|
|
VkExtent3D CompressedImageInfo::compressedMipmapExtent(uint32_t level) const {
|
|
VkExtent3D result = mipmapExtent(level);
|
|
result.width = ceil_div(result.width, mBlock.width);
|
|
result.height = ceil_div(result.height, mBlock.height);
|
|
return result;
|
|
}
|
|
|
|
VkExtent3D CompressedImageInfo::compressedMipmapPortion(const VkExtent3D& origExtent,
|
|
uint32_t level) const {
|
|
VkExtent3D maxExtent = compressedMipmapExtent(level);
|
|
return {
|
|
.width = std::min(ceil_div(origExtent.width, mBlock.width), maxExtent.width),
|
|
.height = std::min(ceil_div(origExtent.height, mBlock.height), maxExtent.height),
|
|
.depth = origExtent.depth,
|
|
};
|
|
}
|
|
|
|
} // namespace vk
|
|
} // namespace gfxstream
|