blob: 1525f198f0bab9c503826ce462c345c5e531ed79 [file] [log] [blame]
//
// Copyright 2016 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// vk_format_utils:
// Helper for Vulkan format code.
#ifdef UNSAFE_BUFFERS_BUILD
# pragma allow_unsafe_buffers
#endif
#include "libANGLE/renderer/vulkan/vk_format_utils.h"
#include "image_util/loadimage.h"
#include "libANGLE/Texture.h"
#include "libANGLE/formatutils.h"
#include "libANGLE/renderer/load_functions_table.h"
#include "libANGLE/renderer/vulkan/ContextVk.h"
#include "libANGLE/renderer/vulkan/vk_caps_utils.h"
#include "libANGLE/renderer/vulkan/vk_renderer.h"
namespace rx
{
namespace
{
void FillTextureFormatCaps(vk::Renderer *renderer,
angle::FormatID formatID,
gl::TextureCaps *outTextureCaps)
{
const VkPhysicalDeviceLimits &physicalDeviceLimits =
renderer->getPhysicalDeviceProperties().limits;
bool hasColorAttachmentFeatureBit =
renderer->hasImageFormatFeatureBits(formatID, VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT);
bool hasDepthAttachmentFeatureBit = renderer->hasImageFormatFeatureBits(
formatID, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT);
outTextureCaps->texturable =
renderer->hasImageFormatFeatureBits(formatID, VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT);
outTextureCaps->filterable = renderer->hasImageFormatFeatureBits(
formatID, VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT);
outTextureCaps->blendable =
renderer->hasImageFormatFeatureBits(formatID, VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT);
// For renderbuffer and texture attachments we require transfer and sampling for
// GLES 2.0 CopyTexImage support. Sampling is also required for other features like
// blits and EGLImages.
outTextureCaps->textureAttachment =
outTextureCaps->texturable &&
(hasColorAttachmentFeatureBit || hasDepthAttachmentFeatureBit);
outTextureCaps->renderbuffer = outTextureCaps->textureAttachment;
if (outTextureCaps->renderbuffer)
{
VkPhysicalDeviceImageFormatInfo2 imageFormatInfo = {};
imageFormatInfo.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2;
imageFormatInfo.format = GetVkFormatFromFormatID(renderer, formatID);
imageFormatInfo.type = VK_IMAGE_TYPE_2D;
imageFormatInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
imageFormatInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT;
if (hasColorAttachmentFeatureBit)
{
imageFormatInfo.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
}
if (hasDepthAttachmentFeatureBit)
{
imageFormatInfo.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
}
VkImageFormatProperties2 imageFormatProperties2 = {};
imageFormatProperties2.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2;
VkResult result = vkGetPhysicalDeviceImageFormatProperties2(
renderer->getPhysicalDevice(), &imageFormatInfo, &imageFormatProperties2);
if (result == VK_SUCCESS)
{
if (hasColorAttachmentFeatureBit)
{
vk_gl::AddSampleCounts(imageFormatProperties2.imageFormatProperties.sampleCounts &
physicalDeviceLimits.framebufferColorSampleCounts,
&outTextureCaps->sampleCounts);
}
if (hasDepthAttachmentFeatureBit)
{
// Some drivers report different depth and stencil sample counts. We'll AND those
// counts together, limiting all depth and/or stencil formats to the lower number of
// sample counts.
vk_gl::AddSampleCounts((imageFormatProperties2.imageFormatProperties.sampleCounts &
physicalDeviceLimits.framebufferDepthSampleCounts &
physicalDeviceLimits.framebufferStencilSampleCounts),
&outTextureCaps->sampleCounts);
}
}
}
}
bool HasFullBufferFormatSupport(vk::Renderer *renderer, angle::FormatID formatID)
{
// Note: GL_EXT_texture_buffer support uses the same vkBufferFormat that is determined by
// Format::initBufferFallback, which uses this function. That relies on the fact that formats
// required for GL_EXT_texture_buffer all have mandatory VERTEX_BUFFER feature support in
// Vulkan. If this function is changed to test for more features in such a way that makes any
// of those formats use a fallback format, the implementation of GL_EXT_texture_buffer must be
// modified not to use vkBufferFormat.
return renderer->hasBufferFormatFeatureBits(formatID, VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT);
}
using SupportTest = bool (*)(vk::Renderer *renderer, angle::FormatID formatID);
template <class FormatInitInfo>
int FindSupportedFormat(vk::Renderer *renderer,
const FormatInitInfo *info,
size_t skip,
int numInfo,
SupportTest hasSupport)
{
ASSERT(numInfo > 0);
for (int i = static_cast<int>(skip); i < numInfo; ++i)
{
ASSERT(info[i].format != angle::FormatID::NONE);
if (hasSupport(renderer, info[i].format))
{
return i;
}
}
// We couldn't find a valid fallback, ignore the skip and return 0
return 0;
}
bool HasNonFilterableTextureFormatSupport(vk::Renderer *renderer, angle::FormatID formatID)
{
constexpr uint32_t kBitsColor =
VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT;
constexpr uint32_t kBitsDepth = VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT;
return renderer->hasImageFormatFeatureBits(formatID, kBitsColor) ||
renderer->hasImageFormatFeatureBits(formatID, kBitsDepth);
}
} // anonymous namespace
namespace vk
{
// Format implementation.
Format::Format()
: mIntendedFormatID(angle::FormatID::NONE),
mIntendedGLFormat(GL_NONE),
mActualSampleOnlyImageFormatID(angle::FormatID::NONE),
mActualRenderableImageFormatID(angle::FormatID::NONE),
mActualBufferFormatID(angle::FormatID::NONE),
mImageInitializerFunction(nullptr),
mTextureLoadFunctions(),
mRenderableTextureLoadFunctions(),
mVertexLoadFunction(nullptr),
mCompressedVertexLoadFunction(nullptr),
mVertexLoadRequiresConversion(false),
mCompressedVertexLoadRequiresConversion(false),
mVkBufferFormatIsPacked(false),
mVkFormatIsInt(false),
mVkFormatIsUnsigned(false)
{}
void Format::initImageFallback(Renderer *renderer, const ImageFormatInitInfo *info, int numInfo)
{
size_t skip = renderer->getFeatures().forceFallbackFormat.enabled ? 1 : 0;
// For R5G6B5, the fallback B5G6R5 is for performance reasons, and only used for some
// platforms by enabling the proper feature flag. Therefore, forcing format fallback should
// not apply to R5G6B5.
skip += (info[0].format == angle::FormatID::R5G6B5_UNORM &&
renderer->getFeatures().preferBGR565ToRGB565.enabled)
? 1
: 0;
SupportTest testFunction = HasNonRenderableTextureFormatSupport;
const angle::Format &format = angle::Format::Get(info[0].format);
if (format.isInt() || (format.isFloat() && format.redBits >= 32))
{
// Integer formats don't support filtering in GL, so don't test for it.
// Filtering of 32-bit float textures is not supported on Android, and
// it's enabled by the extension OES_texture_float_linear, which is
// enabled automatically by examining format capabilities.
testFunction = HasNonFilterableTextureFormatSupport;
}
int i = FindSupportedFormat(renderer, info, skip, static_cast<uint32_t>(numInfo), testFunction);
mActualSampleOnlyImageFormatID = info[i].format;
mImageInitializerFunction = info[i].initializer;
// Set renderable format.
if (testFunction != HasNonFilterableTextureFormatSupport &&
!(format.isSnorm() && format.channelCount == 3) && !format.isBlock)
{
// Rendering to RGB SNORM textures is not supported on Android.
// Compressed textures also need to perform this check.
testFunction = HasFullTextureFormatSupport;
i = FindSupportedFormat(renderer, info, skip, static_cast<uint32_t>(numInfo), testFunction);
mActualRenderableImageFormatID = info[i].format;
}
}
void Format::initBufferFallback(Renderer *renderer,
const BufferFormatInitInfo *info,
int numInfo,
int compressedStartIndex)
{
{
size_t skip = renderer->getFeatures().forceFallbackFormat.enabled ? 1 : 0;
int i = FindSupportedFormat(renderer, info, skip, compressedStartIndex,
HasFullBufferFormatSupport);
mActualBufferFormatID = info[i].format;
mVkBufferFormatIsPacked = info[i].vkFormatIsPacked;
mVertexLoadFunction = info[i].vertexLoadFunction;
mVertexLoadRequiresConversion = info[i].vertexLoadRequiresConversion;
}
}
size_t Format::getVertexInputAlignment() const
{
const angle::Format &bufferFormat = getActualBufferFormat();
size_t pixelBytes = bufferFormat.pixelBytes;
return mVkBufferFormatIsPacked ? pixelBytes : (pixelBytes / bufferFormat.channelCount);
}
bool HasEmulatedImageChannels(const angle::Format &intendedFormat,
const angle::Format &actualFormat)
{
return (intendedFormat.alphaBits == 0 && actualFormat.alphaBits > 0) ||
(intendedFormat.blueBits == 0 && actualFormat.blueBits > 0) ||
(intendedFormat.greenBits == 0 && actualFormat.greenBits > 0) ||
(intendedFormat.depthBits == 0 && actualFormat.depthBits > 0) ||
(intendedFormat.stencilBits == 0 && actualFormat.stencilBits > 0);
}
bool HasEmulatedImageFormat(angle::FormatID intendedFormatID, angle::FormatID actualFormatID)
{
return actualFormatID != intendedFormatID;
}
bool operator==(const Format &lhs, const Format &rhs)
{
return &lhs == &rhs;
}
bool operator!=(const Format &lhs, const Format &rhs)
{
return &lhs != &rhs;
}
// FormatTable implementation.
FormatTable::FormatTable() {}
FormatTable::~FormatTable() {}
void FormatTable::initialize(Renderer *renderer, gl::TextureCapsMap *outTextureCapsMap)
{
for (size_t formatIndex = 0; formatIndex < angle::kNumANGLEFormats; ++formatIndex)
{
Format &format = mFormatData[formatIndex];
const auto intendedFormatID = static_cast<angle::FormatID>(formatIndex);
const angle::Format &intendedAngleFormat = angle::Format::Get(intendedFormatID);
// Skip querying device caps for ASTC 3D formats if VK_EXT_texture_compression_astc_3d is
// not enabled
if (renderer->getFeatures().supportsAstc3d.enabled == false &&
IsASTC3DFormat(intendedFormatID))
{
continue;
}
format.initialize(renderer, intendedAngleFormat);
format.mIntendedFormatID = intendedFormatID;
if (!format.valid())
{
continue;
}
// No sample-able or render-able formats, so nothing left to do. This includes skipping the
// rest of the loop for buffer-only formats, since they are not texturable.
if (format.mActualSampleOnlyImageFormatID == angle::FormatID::NONE)
{
continue;
}
bool transcodeEtcToBc = false;
if (renderer->getFeatures().supportsComputeTranscodeEtcToBc.enabled &&
IsETCFormat(intendedFormatID) &&
!angle::Format::Get(format.mActualSampleOnlyImageFormatID).isBlock)
{
// Check BC format support
angle::FormatID bcFormat = GetTranscodeBCFormatID(intendedFormatID);
if (HasNonRenderableTextureFormatSupport(renderer, bcFormat))
{
format.mActualSampleOnlyImageFormatID = bcFormat;
transcodeEtcToBc = true;
}
}
if (format.mActualRenderableImageFormatID == angle::FormatID::NONE)
{
// If renderable format was not set, it means there is no fallback format for
// renderable. We populate this the same formatID as sampleOnly formatID so that
// getActualFormatID() will be simpler.
format.mActualRenderableImageFormatID = format.mActualSampleOnlyImageFormatID;
}
gl::TextureCaps textureCaps;
FillTextureFormatCaps(renderer, format.mActualSampleOnlyImageFormatID, &textureCaps);
if (textureCaps.texturable)
{
format.mTextureLoadFunctions = GetLoadFunctionsMap(
format.mIntendedGLFormat,
transcodeEtcToBc ? intendedFormatID : format.mActualSampleOnlyImageFormatID);
}
if (format.mActualRenderableImageFormatID == format.mActualSampleOnlyImageFormatID)
{
outTextureCapsMap->set(intendedFormatID, textureCaps);
format.mRenderableTextureLoadFunctions = format.mTextureLoadFunctions;
}
else
{
FillTextureFormatCaps(renderer, format.mActualRenderableImageFormatID, &textureCaps);
outTextureCapsMap->set(intendedFormatID, textureCaps);
if (textureCaps.texturable)
{
format.mRenderableTextureLoadFunctions = GetLoadFunctionsMap(
format.mIntendedGLFormat, format.mActualRenderableImageFormatID);
}
}
}
}
angle::FormatID ExternalFormatTable::getOrAllocExternalFormatID(uint64_t externalFormat,
VkFormat colorAttachmentFormat,
VkFormatFeatureFlags formatFeatures)
{
std::unique_lock<angle::SimpleMutex> lock(mExternalYuvFormatMutex);
for (size_t index = 0; index < mExternalYuvFormats.size(); index++)
{
if (mExternalYuvFormats[index].externalFormat == externalFormat)
{
// Found a match. Just return existing formatID
return angle::FormatID(ToUnderlying(angle::FormatID::EXTERNAL0) + index);
}
}
if (mExternalYuvFormats.size() >= kMaxExternalFormatCountSupported)
{
ERR() << "ANGLE only suports maximum " << kMaxExternalFormatCountSupported
<< " external renderable formats";
ASSERT(false);
return angle::FormatID::NONE;
}
mExternalYuvFormats.push_back({externalFormat, colorAttachmentFormat, formatFeatures});
return angle::FormatID(ToUnderlying(angle::FormatID::EXTERNAL0) + mExternalYuvFormats.size() -
1);
}
const ExternalYuvFormatInfo &ExternalFormatTable::getExternalFormatInfo(
angle::FormatID formatID) const
{
ASSERT(formatID >= angle::FormatID::EXTERNAL0);
size_t index = ToUnderlying(formatID) - ToUnderlying(angle::FormatID::EXTERNAL0);
ASSERT(index < mExternalYuvFormats.size());
return mExternalYuvFormats[index];
}
bool IsYUVExternalFormat(angle::FormatID formatID)
{
return formatID >= angle::FormatID::EXTERNAL0 && formatID <= angle::FormatID::EXTERNAL7;
}
size_t GetImageCopyBufferAlignment(angle::FormatID actualFormatID)
{
// vkCmdCopyBufferToImage must have an offset that is a multiple of 4 as well as a multiple
// of the texel size (if uncompressed) or pixel block size (if compressed).
// https://www.khronos.org/registry/vulkan/specs/1.0/man/html/VkBufferImageCopy.html
//
// We need lcm(4, texelSize) (lcm = least common multiplier). For compressed images,
// |texelSize| would contain the block size. Since 4 is constant, this can be calculated as:
//
// | texelSize texelSize % 4 == 0
// | 4 * texelSize texelSize % 4 == 1
// lcm(4, texelSize) = <
// | 2 * texelSize texelSize % 4 == 2
// | 4 * texelSize texelSize % 4 == 3
//
// This means:
//
// - texelSize % 2 != 0 gives a 4x multiplier
// - else texelSize % 4 != 0 gives a 2x multiplier
// - else there's no multiplier.
//
const angle::Format &actualFormat = angle::Format::Get(actualFormatID);
ASSERT(actualFormat.pixelBytes != 0);
const size_t texelSize = actualFormat.pixelBytes;
const size_t multiplier = texelSize % 2 != 0 ? 4 : texelSize % 4 != 0 ? 2 : 1;
const size_t alignment = multiplier * texelSize;
return alignment;
}
size_t GetValidImageCopyBufferAlignment(angle::FormatID intendedFormatID,
angle::FormatID actualFormatID)
{
constexpr size_t kMinimumAlignment = 16;
return (intendedFormatID == angle::FormatID::NONE)
? kMinimumAlignment
: GetImageCopyBufferAlignment(actualFormatID);
}
VkImageUsageFlags GetMaximalImageUsageFlags(Renderer *renderer, angle::FormatID formatID)
{
constexpr VkFormatFeatureFlags kImageUsageFeatureBits =
VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT |
VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT |
VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT;
VkFormatFeatureFlags featureBits =
renderer->getImageFormatFeatureBits(formatID, kImageUsageFeatureBits);
VkImageUsageFlags imageUsageFlags = 0;
if (featureBits & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)
imageUsageFlags |= VK_IMAGE_USAGE_SAMPLED_BIT;
if (featureBits & VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT)
imageUsageFlags |= VK_IMAGE_USAGE_STORAGE_BIT;
if (featureBits & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT)
imageUsageFlags |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
if (featureBits & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT)
imageUsageFlags |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
if (featureBits & VK_FORMAT_FEATURE_TRANSFER_SRC_BIT)
imageUsageFlags |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
if (featureBits & VK_FORMAT_FEATURE_TRANSFER_DST_BIT)
imageUsageFlags |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
imageUsageFlags |= VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
return imageUsageFlags;
}
VkImageCreateFlags GetMinimalImageCreateFlags(Renderer *renderer,
gl::TextureType textureType,
VkImageUsageFlags usage)
{
switch (textureType)
{
case gl::TextureType::CubeMap:
case gl::TextureType::CubeMapArray:
return VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
case gl::TextureType::_3D:
{
// Slices of this image may be used as:
//
// - Render target: The VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT flag is needed for that.
// - Sampled or storage image: The VK_IMAGE_CREATE_2D_VIEW_COMPATIBLE_BIT_EXT flag is
// needed for this. If VK_EXT_image_2d_view_of_3d is not supported, we tolerate the
// VVL error as drivers seem to support this behavior anyway.
VkImageCreateFlags flags = VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT;
if ((usage & VK_IMAGE_USAGE_STORAGE_BIT) != 0)
{
if (renderer->getFeatures().supportsImage2dViewOf3d.enabled)
{
flags |= VK_IMAGE_CREATE_2D_VIEW_COMPATIBLE_BIT_EXT;
}
}
else if ((usage & VK_IMAGE_USAGE_SAMPLED_BIT) != 0)
{
if (renderer->getFeatures().supportsSampler2dViewOf3d.enabled)
{
flags |= VK_IMAGE_CREATE_2D_VIEW_COMPATIBLE_BIT_EXT;
}
}
return flags;
}
default:
return 0;
}
}
} // namespace vk
bool HasFullTextureFormatSupport(vk::Renderer *renderer, angle::FormatID formatID)
{
constexpr uint32_t kBitsColor = VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT |
VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT |
VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT;
// In OpenGL ES, all renderable formats except 32-bit floating-point support blending.
// 32-bit floating-point case validation is handled by ANGLE's frontend.
uint32_t kBitsColorFull = kBitsColor;
switch (formatID)
{
case angle::FormatID::R32_FLOAT:
case angle::FormatID::R32G32_FLOAT:
case angle::FormatID::R32G32B32A32_FLOAT:
break;
default:
const angle::Format &format = angle::Format::Get(formatID);
if (!format.isYUV)
{
// EXT_yuv_target does not support blend anyway, so no need to ask for blend bit.
kBitsColorFull |= VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT;
}
break;
}
constexpr uint32_t kBitsDepth = VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT;
return renderer->hasImageFormatFeatureBits(formatID, kBitsColorFull) ||
renderer->hasImageFormatFeatureBits(formatID, kBitsDepth);
}
bool HasNonRenderableTextureFormatSupport(vk::Renderer *renderer, angle::FormatID formatID)
{
constexpr uint32_t kBitsColor =
VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT;
constexpr uint32_t kBitsDepth = VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT;
return renderer->hasImageFormatFeatureBits(formatID, kBitsColor) ||
renderer->hasImageFormatFeatureBits(formatID, kBitsDepth);
}
// Checks if it is a ETC texture format
bool IsETCFormat(angle::FormatID formatID)
{
return formatID >= angle::FormatID::EAC_R11G11_SNORM_BLOCK &&
formatID <= angle::FormatID::ETC2_R8G8B8_UNORM_BLOCK;
}
// Checks if it is an ASTC 3D texture format
bool IsASTC3DFormat(angle::FormatID formatID)
{
switch (formatID)
{
case angle::FormatID::ASTC_3x3x3_UNORM_BLOCK:
case angle::FormatID::ASTC_3x3x3_UNORM_SRGB_BLOCK:
case angle::FormatID::ASTC_4x3x3_UNORM_BLOCK:
case angle::FormatID::ASTC_4x3x3_UNORM_SRGB_BLOCK:
case angle::FormatID::ASTC_4x4x3_UNORM_BLOCK:
case angle::FormatID::ASTC_4x4x3_UNORM_SRGB_BLOCK:
case angle::FormatID::ASTC_4x4x4_UNORM_BLOCK:
case angle::FormatID::ASTC_4x4x4_UNORM_SRGB_BLOCK:
case angle::FormatID::ASTC_5x4x4_UNORM_BLOCK:
case angle::FormatID::ASTC_5x4x4_UNORM_SRGB_BLOCK:
case angle::FormatID::ASTC_5x5x4_UNORM_BLOCK:
case angle::FormatID::ASTC_5x5x4_UNORM_SRGB_BLOCK:
case angle::FormatID::ASTC_5x5x5_UNORM_BLOCK:
case angle::FormatID::ASTC_5x5x5_UNORM_SRGB_BLOCK:
case angle::FormatID::ASTC_6x5x5_UNORM_BLOCK:
case angle::FormatID::ASTC_6x5x5_UNORM_SRGB_BLOCK:
case angle::FormatID::ASTC_6x6x5_UNORM_BLOCK:
case angle::FormatID::ASTC_6x6x5_UNORM_SRGB_BLOCK:
case angle::FormatID::ASTC_6x6x6_UNORM_BLOCK:
case angle::FormatID::ASTC_6x6x6_UNORM_SRGB_BLOCK:
return true;
default:
return false;
}
}
// Checks if it is a BC texture format
bool IsBCFormat(angle::FormatID formatID)
{
return formatID >= angle::FormatID::BC1_RGBA_UNORM_BLOCK &&
formatID <= angle::FormatID::BC7_RGBA_UNORM_SRGB_BLOCK;
}
static constexpr int kNumETCFormats = 12;
static_assert((int)angle::FormatID::ETC2_R8G8B8_UNORM_BLOCK ==
(int)angle::FormatID::EAC_R11G11_SNORM_BLOCK + kNumETCFormats - 1);
static_assert((int)angle::FormatID::EAC_R11G11_UNORM_BLOCK ==
(int)angle::FormatID::EAC_R11G11_SNORM_BLOCK + 1);
static_assert((int)angle::FormatID::EAC_R11_SNORM_BLOCK ==
(int)angle::FormatID::EAC_R11G11_SNORM_BLOCK + 2);
static_assert((int)angle::FormatID::EAC_R11_UNORM_BLOCK ==
(int)angle::FormatID::EAC_R11G11_SNORM_BLOCK + 3);
static_assert((int)angle::FormatID::ETC1_LOSSY_DECODE_R8G8B8_UNORM_BLOCK ==
(int)angle::FormatID::EAC_R11G11_SNORM_BLOCK + 4);
static_assert((int)angle::FormatID::ETC1_R8G8B8_UNORM_BLOCK ==
(int)angle::FormatID::EAC_R11G11_SNORM_BLOCK + 5);
static_assert((int)angle::FormatID::ETC2_R8G8B8A1_SRGB_BLOCK ==
(int)angle::FormatID::EAC_R11G11_SNORM_BLOCK + 6);
static_assert((int)angle::FormatID::ETC2_R8G8B8A1_UNORM_BLOCK ==
(int)angle::FormatID::EAC_R11G11_SNORM_BLOCK + 7);
static_assert((int)angle::FormatID::ETC2_R8G8B8A8_SRGB_BLOCK ==
(int)angle::FormatID::EAC_R11G11_SNORM_BLOCK + 8);
static_assert((int)angle::FormatID::ETC2_R8G8B8A8_UNORM_BLOCK ==
(int)angle::FormatID::EAC_R11G11_SNORM_BLOCK + 9);
static_assert((int)angle::FormatID::ETC2_R8G8B8_SRGB_BLOCK ==
(int)angle::FormatID::EAC_R11G11_SNORM_BLOCK + 10);
static const std::array<LoadImageFunction, kNumETCFormats> kEtcToBcLoadingFunc = {
angle::LoadEACRG11SToBC5, // EAC_R11G11_SNORM
angle::LoadEACRG11ToBC5, // EAC_R11G11_UNORM
angle::LoadEACR11SToBC4, // EAC_R11_SNORM
angle::LoadEACR11ToBC4, // EAC_R11_UNORM_BLOCK
angle::LoadETC1RGB8ToBC1, // ETC1_LOSSY_DECODE_R8G8B8_UNORM
angle::LoadETC2RGB8ToBC1, // ETC1_R8G8B8_UNORM
angle::LoadETC2SRGB8A1ToBC1, // ETC2_R8G8B8A1_SRGB
angle::LoadETC2RGB8A1ToBC1, // ETC2_R8G8B8A1_UNORM
angle::LoadETC2SRGBA8ToBC3, // ETC2_R8G8B8A8_SRGB
angle::LoadETC2RGBA8ToBC3, // ETC2_R8G8B8A8_UNORM
angle::LoadETC2SRGB8ToBC1, // ETC2_R8G8B8_SRGB
angle::LoadETC2RGB8ToBC1, // ETC2_R8G8B8_UNORM
};
LoadImageFunctionInfo GetEtcToBcTransCodingFunc(angle::FormatID formatID)
{
ASSERT(IsETCFormat(formatID));
return LoadImageFunctionInfo(
kEtcToBcLoadingFunc[static_cast<uint32_t>(formatID) -
static_cast<uint32_t>(angle::FormatID::EAC_R11G11_SNORM_BLOCK)],
true);
}
static constexpr angle::FormatID kEtcToBcFormatMapping[] = {
angle::FormatID::BC5_RG_SNORM_BLOCK, // EAC_R11G11_SNORM
angle::FormatID::BC5_RG_UNORM_BLOCK, // EAC_R11G11_UNORM
angle::FormatID::BC4_RED_SNORM_BLOCK, // EAC_R11_SNORM
angle::FormatID::BC4_RED_UNORM_BLOCK, // EAC_R11_UNORM_BLOCK
angle::FormatID::BC1_RGB_UNORM_BLOCK, // ETC1_LOSSY_DECODE_R8G8B8_UNORM
angle::FormatID::BC1_RGB_UNORM_BLOCK, // ETC1_R8G8B8_UNORM
angle::FormatID::BC1_RGBA_UNORM_SRGB_BLOCK, // ETC2_R8G8B8A1_SRGB
angle::FormatID::BC1_RGBA_UNORM_BLOCK, // ETC2_R8G8B8A1_UNORM
angle::FormatID::BC3_RGBA_UNORM_SRGB_BLOCK, // ETC2_R8G8B8A8_SRGB
angle::FormatID::BC3_RGBA_UNORM_BLOCK, // ETC2_R8G8B8A8_UNORM
angle::FormatID::BC1_RGB_UNORM_SRGB_BLOCK, // ETC2_R8G8B8_SRGB
angle::FormatID::BC1_RGB_UNORM_BLOCK, // ETC2_R8G8B8_UNORM
};
angle::FormatID GetTranscodeBCFormatID(angle::FormatID formatID)
{
ASSERT(IsETCFormat(formatID));
return kEtcToBcFormatMapping[static_cast<uint32_t>(formatID) -
static_cast<uint32_t>(angle::FormatID::EAC_R11G11_SNORM_BLOCK)];
}
VkFormat AdjustASTCFormatForHDR(const vk::Renderer *renderer, VkFormat vkFormat)
{
ASSERT(renderer != nullptr);
const bool hdrEnabled = renderer->supportsAstcHdr();
if (hdrEnabled == false)
{
return vkFormat;
}
// When KHR_texture_compression_astc_hdr is enabled,
// VK_FORMAT_ASTC_nxm_UNORM_BLOCK should be converted to VK_FORMAT_ASTC_nxm_SFLOAT_BLOCK
auto transformFormat = [](VkFormat vkFormat) -> VkFormat {
if (vkFormat >= VK_FORMAT_ASTC_4x4_UNORM_BLOCK &&
vkFormat <= VK_FORMAT_ASTC_12x12_UNORM_BLOCK && (vkFormat & 1) == 1)
{
return static_cast<VkFormat>(((vkFormat - VK_FORMAT_ASTC_4x4_UNORM_BLOCK) >> 1) +
VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK);
}
return vkFormat;
};
static_assert(
transformFormat(VK_FORMAT_ASTC_4x4_UNORM_BLOCK) == VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK,
"VK_FORMAT_ASTC_4x4_UNORM_BLOCK should be converted to VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK");
static_assert(
transformFormat(VK_FORMAT_ASTC_5x4_UNORM_BLOCK) == VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK,
"VK_FORMAT_ASTC_5x4_UNORM_BLOCK should be converted to VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK");
static_assert(
transformFormat(VK_FORMAT_ASTC_5x5_UNORM_BLOCK) == VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK,
"VK_FORMAT_ASTC_5x5_UNORM_BLOCK should be converted to VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK");
static_assert(
transformFormat(VK_FORMAT_ASTC_6x5_UNORM_BLOCK) == VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK,
"VK_FORMAT_ASTC_6x5_UNORM_BLOCK should be converted to VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK");
static_assert(
transformFormat(VK_FORMAT_ASTC_6x6_UNORM_BLOCK) == VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK,
"VK_FORMAT_ASTC_6x6_UNORM_BLOCK should be converted to VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK");
static_assert(
transformFormat(VK_FORMAT_ASTC_8x5_UNORM_BLOCK) == VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK,
"VK_FORMAT_ASTC_8x5_UNORM_BLOCK should be converted to VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK");
static_assert(
transformFormat(VK_FORMAT_ASTC_8x6_UNORM_BLOCK) == VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK,
"VK_FORMAT_ASTC_8x6_UNORM_BLOCK should be converted to VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK");
static_assert(
transformFormat(VK_FORMAT_ASTC_8x8_UNORM_BLOCK) == VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK,
"VK_FORMAT_ASTC_8x8_UNORM_BLOCK should be converted to VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK");
static_assert(
transformFormat(VK_FORMAT_ASTC_10x5_UNORM_BLOCK) == VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK,
"VK_FORMAT_ASTC_10x5_UNORM_BLOCK should be converted to VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK");
static_assert(
transformFormat(VK_FORMAT_ASTC_10x6_UNORM_BLOCK) == VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK,
"VK_FORMAT_ASTC_10x6_UNORM_BLOCK should be converted to VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK");
static_assert(
transformFormat(VK_FORMAT_ASTC_10x8_UNORM_BLOCK) == VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK,
"VK_FORMAT_ASTC_10x8_UNORM_BLOCK should be converted to VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK");
static_assert(
transformFormat(VK_FORMAT_ASTC_10x10_UNORM_BLOCK) == VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK,
"VK_FORMAT_ASTC_10x10_UNORM_BLOCK should be converted to"
"VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK");
static_assert(
transformFormat(VK_FORMAT_ASTC_12x10_UNORM_BLOCK) == VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK,
"VK_FORMAT_ASTC_12x10_UNORM_BLOCK should be converted to"
"VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK");
static_assert(
transformFormat(VK_FORMAT_ASTC_12x12_UNORM_BLOCK) == VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK,
"VK_FORMAT_ASTC_12x12_UNORM_BLOCK should be converted to"
"VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK");
return transformFormat(vkFormat);
}
GLenum GetSwizzleStateComponent(const gl::SwizzleState &swizzleState, GLenum component)
{
switch (component)
{
case GL_RED:
return swizzleState.swizzleRed;
case GL_GREEN:
return swizzleState.swizzleGreen;
case GL_BLUE:
return swizzleState.swizzleBlue;
case GL_ALPHA:
return swizzleState.swizzleAlpha;
default:
return component;
}
}
gl::SwizzleState ApplySwizzle(const gl::SwizzleState &formatSwizzle,
const gl::SwizzleState &toApply)
{
gl::SwizzleState result;
result.swizzleRed = GetSwizzleStateComponent(formatSwizzle, toApply.swizzleRed);
result.swizzleGreen = GetSwizzleStateComponent(formatSwizzle, toApply.swizzleGreen);
result.swizzleBlue = GetSwizzleStateComponent(formatSwizzle, toApply.swizzleBlue);
result.swizzleAlpha = GetSwizzleStateComponent(formatSwizzle, toApply.swizzleAlpha);
return result;
}
gl::SwizzleState GetFormatSwizzle(const angle::Format &angleFormat, const bool sized)
{
gl::SwizzleState internalSwizzle;
if (angleFormat.isLUMA())
{
GLenum swizzleRGB, swizzleA;
if (angleFormat.luminanceBits > 0)
{
swizzleRGB = GL_RED;
swizzleA = (angleFormat.alphaBits > 0 ? GL_GREEN : GL_ONE);
}
else
{
swizzleRGB = GL_ZERO;
swizzleA = GL_RED;
}
internalSwizzle.swizzleRed = swizzleRGB;
internalSwizzle.swizzleGreen = swizzleRGB;
internalSwizzle.swizzleBlue = swizzleRGB;
internalSwizzle.swizzleAlpha = swizzleA;
}
else
{
if (angleFormat.hasDepthOrStencilBits())
{
// In OES_depth_texture/ARB_depth_texture, depth
// textures are treated as luminance.
// If the internalformat was not sized, use OES_depth_texture behavior
bool hasGB = angleFormat.depthBits > 0 && !sized;
internalSwizzle.swizzleRed = GL_RED;
internalSwizzle.swizzleGreen = hasGB ? GL_RED : GL_ZERO;
internalSwizzle.swizzleBlue = hasGB ? GL_RED : GL_ZERO;
internalSwizzle.swizzleAlpha = GL_ONE;
}
else
{
// Color bits are all zero for blocked formats
if (!angleFormat.isBlock)
{
// Set any missing channel to default in case the emulated format has that channel.
internalSwizzle.swizzleRed = angleFormat.redBits > 0 ? GL_RED : GL_ZERO;
internalSwizzle.swizzleGreen = angleFormat.greenBits > 0 ? GL_GREEN : GL_ZERO;
internalSwizzle.swizzleBlue = angleFormat.blueBits > 0 ? GL_BLUE : GL_ZERO;
internalSwizzle.swizzleAlpha = angleFormat.alphaBits > 0 ? GL_ALPHA : GL_ONE;
}
}
}
return internalSwizzle;
}
} // namespace rx