Files
MobileGL/MobileGL/MG_Util/Metrics/TextureMetrics.cpp
T

575 lines
24 KiB
C++

// MobileGL - MobileGL/MG_Util/Metrics/TextureMetrics.cpp
// Copyright (c) 2025-2026 MobileGL-Dev
// Licensed under the GNU Lesser General Public License v3.0:
// https://www.gnu.org/licenses/gpl-3.0.txt
// https://www.gnu.org/licenses/lgpl-3.0.txt
// SPDX-License-Identifier: LGPL-3.0-only
// End of Source File Header
#include "TextureMetrics.h"
#include "Defines.h"
#include "MG_Util/Math/VectorTypes.h"
namespace MobileGL {
namespace MG_Util {
SizeT GetSizedInternalFormatSizeInBytes(TextureInternalFormat internal) {
switch (internal) {
case TextureInternalFormat::R8:
case TextureInternalFormat::Red: // UNorm8 shadow layout
case TextureInternalFormat::R8Snorm:
case TextureInternalFormat::R8I:
case TextureInternalFormat::StencilIndex8:
case TextureInternalFormat::R8UI:
return 1;
case TextureInternalFormat::R16:
case TextureInternalFormat::R16Snorm:
case TextureInternalFormat::R16I:
case TextureInternalFormat::R16UI:
case TextureInternalFormat::R16F:
case TextureInternalFormat::RG8:
case TextureInternalFormat::RG: // UNorm8x2 shadow layout
case TextureInternalFormat::RG8Snorm:
case TextureInternalFormat::RG8I:
case TextureInternalFormat::RG8UI:
case TextureInternalFormat::DepthComponent16:
return 2;
case TextureInternalFormat::R3G3B2: // UNorm8x3 shadow layout
case TextureInternalFormat::RGB4:
case TextureInternalFormat::RGB5:
case TextureInternalFormat::RGB: // UNorm8x3 shadow layout
case TextureInternalFormat::RGB8:
case TextureInternalFormat::RGB8Snorm:
case TextureInternalFormat::SRGB8:
case TextureInternalFormat::RGB8I:
case TextureInternalFormat::RGB8UI:
return 3;
// Canonical depth shadow is a full 32-bit unorm word (see PixelStoreProcessor),
// converted at upload to the image's own 24/32-bit layout.
case TextureInternalFormat::DepthComponent24:
return 4;
case TextureInternalFormat::RGBA2:
case TextureInternalFormat::RGBA4:
case TextureInternalFormat::RGB5A1:
case TextureInternalFormat::RGBA: // UNorm8x4 shadow layout
case TextureInternalFormat::RGBA8:
case TextureInternalFormat::RGBA8Snorm:
case TextureInternalFormat::RGBA8I:
case TextureInternalFormat::RGBA8UI:
case TextureInternalFormat::SRGB8Alpha8:
case TextureInternalFormat::RGB10A2:
case TextureInternalFormat::RGB10A2UI:
case TextureInternalFormat::R32F:
case TextureInternalFormat::R32I:
case TextureInternalFormat::R32UI:
case TextureInternalFormat::DepthComponent:
case TextureInternalFormat::DepthComponent32:
case TextureInternalFormat::DepthComponent32F:
case TextureInternalFormat::DepthStencil:
case TextureInternalFormat::Depth24Stencil8:
case TextureInternalFormat::RG16:
case TextureInternalFormat::RG16Snorm:
case TextureInternalFormat::RG16I:
case TextureInternalFormat::RG16UI:
case TextureInternalFormat::RG16F:
return 4;
case TextureInternalFormat::RGB16:
case TextureInternalFormat::RGB10: // UNorm16x3 shadow layout
case TextureInternalFormat::RGB12: // UNorm16x3 shadow layout
case TextureInternalFormat::RGB16Snorm:
case TextureInternalFormat::RGB16F:
case TextureInternalFormat::RGB16I:
case TextureInternalFormat::RGB16UI:
return 6;
case TextureInternalFormat::RGBA12:
case TextureInternalFormat::RGBA16:
case TextureInternalFormat::RGBA16Snorm:
case TextureInternalFormat::RGBA16I:
case TextureInternalFormat::RGBA16UI:
case TextureInternalFormat::RGBA16F:
case TextureInternalFormat::RG32F:
case TextureInternalFormat::RG32I:
case TextureInternalFormat::RG32UI:
// Shadow bytes hold the GL_FLOAT_32_UNSIGNED_INT_24_8_REV wire format
// (float depth + a word whose low 8 bits are stencil), 8 bytes/texel.
case TextureInternalFormat::Depth32FStencil8:
return 8;
case TextureInternalFormat::RGB32F:
case TextureInternalFormat::RGB32I:
case TextureInternalFormat::RGB32UI:
return 12;
case TextureInternalFormat::RGBA32F:
case TextureInternalFormat::RGBA32I:
case TextureInternalFormat::RGBA32UI:
return 16;
case TextureInternalFormat::R11FG11FB10F:
case TextureInternalFormat::RGB9E5:
return 4;
default:
return 0;
}
}
SizeT GetBaseInputFormatComponentCount(TextureInputFormat format) {
switch (format) {
case TextureInputFormat::Red:
case TextureInputFormat::RInteger:
case TextureInputFormat::Green:
case TextureInputFormat::GreenInteger:
case TextureInputFormat::Blue:
case TextureInputFormat::BlueInteger:
case TextureInputFormat::Alpha:
case TextureInputFormat::AlphaInteger:
return 1;
case TextureInputFormat::RG:
case TextureInputFormat::RGInteger:
return 2;
case TextureInputFormat::RGB:
case TextureInputFormat::BGR:
case TextureInputFormat::RGBInteger:
case TextureInputFormat::BGRInteger:
return 3;
case TextureInputFormat::RGBA:
case TextureInputFormat::BGRA:
case TextureInputFormat::RGBAInteger:
case TextureInputFormat::BGRAInteger:
return 4;
case TextureInputFormat::StencilIndex:
case TextureInputFormat::DepthComponent:
case TextureInputFormat::DepthStencil:
return 1;
default:
MGLOG_D("%s: Unknown input format!", __func__);
return 0;
}
}
SizeT GetBaseInternalFormatComponentCount(TextureInternalFormat format) {
switch (format) {
case TextureInternalFormat::DepthComponent:
case TextureInternalFormat::DepthComponent16:
case TextureInternalFormat::DepthComponent24:
case TextureInternalFormat::DepthComponent32:
case TextureInternalFormat::DepthComponent32F:
case TextureInternalFormat::DepthStencil:
case TextureInternalFormat::Depth24Stencil8:
case TextureInternalFormat::Depth32FStencil8:
// Depth stencil is actually 2 components
// tho real formats always gives byte size in whole
// so we count this as one here
case TextureInternalFormat::Red:
case TextureInternalFormat::R8:
case TextureInternalFormat::R8Snorm:
case TextureInternalFormat::R8I:
case TextureInternalFormat::R8UI:
case TextureInternalFormat::R16:
case TextureInternalFormat::R16Snorm:
case TextureInternalFormat::R16I:
case TextureInternalFormat::R16UI:
case TextureInternalFormat::R16F:
case TextureInternalFormat::R32F:
case TextureInternalFormat::R32I:
case TextureInternalFormat::R32UI:
return 1;
case TextureInternalFormat::RG:
case TextureInternalFormat::RG8:
case TextureInternalFormat::RG8Snorm:
case TextureInternalFormat::RG8I:
case TextureInternalFormat::RG8UI:
case TextureInternalFormat::RG16:
case TextureInternalFormat::RG16Snorm:
case TextureInternalFormat::RG16I:
case TextureInternalFormat::RG16UI:
case TextureInternalFormat::RG16F:
case TextureInternalFormat::RG32F:
case TextureInternalFormat::RG32I:
case TextureInternalFormat::RG32UI:
return 2;
case TextureInternalFormat::RGB:
case TextureInternalFormat::RGB4:
case TextureInternalFormat::RGB5:
case TextureInternalFormat::RGB8:
case TextureInternalFormat::RGB8Snorm:
case TextureInternalFormat::RGB10:
case TextureInternalFormat::RGB12:
case TextureInternalFormat::RGB16:
case TextureInternalFormat::RGB16Snorm:
case TextureInternalFormat::SRGB8:
case TextureInternalFormat::RGB8I:
case TextureInternalFormat::RGB8UI:
case TextureInternalFormat::RGB16F:
case TextureInternalFormat::RGB32F:
case TextureInternalFormat::RGB16I:
case TextureInternalFormat::RGB16UI:
case TextureInternalFormat::RGB32I:
case TextureInternalFormat::RGB32UI:
case TextureInternalFormat::R11FG11FB10F:
case TextureInternalFormat::RGB9E5:
return 3;
case TextureInternalFormat::RGBA:
case TextureInternalFormat::RGBA2:
case TextureInternalFormat::RGBA4:
case TextureInternalFormat::RGBA8:
case TextureInternalFormat::RGBA8Snorm:
case TextureInternalFormat::RGBA8I:
case TextureInternalFormat::RGBA8UI:
case TextureInternalFormat::SRGB8Alpha8:
case TextureInternalFormat::RGBA12:
case TextureInternalFormat::RGBA16:
case TextureInternalFormat::RGBA16Snorm:
case TextureInternalFormat::RGBA16I:
case TextureInternalFormat::RGBA16UI:
case TextureInternalFormat::RGBA16F:
case TextureInternalFormat::RGBA32F:
case TextureInternalFormat::RGBA32I:
case TextureInternalFormat::RGBA32UI:
case TextureInternalFormat::RGB10A2:
case TextureInternalFormat::RGB10A2UI:
return 4;
default:
return 0;
}
}
SizeT GetSizedTexturePixelDataTypeSize(TexturePixelDataType type) {
switch (type) {
case TexturePixelDataType::UnsignedByte332:
case TexturePixelDataType::UnsignedByte233Rev:
return 1;
case TexturePixelDataType::UnsignedShort565:
case TexturePixelDataType::UnsignedShort565Rev:
case TexturePixelDataType::UnsignedShort4444:
case TexturePixelDataType::UnsignedShort4444Rev:
case TexturePixelDataType::UnsignedShort5551:
case TexturePixelDataType::UnsignedShort1555Rev:
return 2;
case TexturePixelDataType::UnsignedInt8888:
case TexturePixelDataType::UnsignedInt8888Rev:
case TexturePixelDataType::UnsignedInt1010102:
case TexturePixelDataType::UnsignedInt2101010Rev:
case TexturePixelDataType::UnsignedInt101111Rev:
case TexturePixelDataType::UnsignedInt5999Rev:
case TexturePixelDataType::UnsignedInt248:
return 4;
case TexturePixelDataType::Float32UnsignedInt248Rev:
// A 32-bit float depth word followed by a 32-bit word holding stencil.
return 8;
default:
return 0;
}
}
SizeT GetBaseTexturePixelDataTypeSize(TexturePixelDataType type) {
switch (type) {
case TexturePixelDataType::UnsignedByte:
case TexturePixelDataType::Byte:
return 1;
case TexturePixelDataType::UnsignedShort:
case TexturePixelDataType::HalfFloat:
case TexturePixelDataType::Short:
return 2;
case TexturePixelDataType::UnsignedInt:
case TexturePixelDataType::Float:
case TexturePixelDataType::Int:
return 4;
default:
return 0;
}
}
SizeT GetTexturePixelDataTypeSize(TexturePixelDataType type) {
SizeT sizedPixelFormatSize = GetSizedTexturePixelDataTypeSize(type);
if (sizedPixelFormatSize > 0) return sizedPixelFormatSize;
return GetBaseTexturePixelDataTypeSize(type);
}
SizeT GetInternalBytesPerPixel(TextureInternalFormat internalformat, TexturePixelDataType type) {
SizeT sizedTextureFormatSize = GetSizedInternalFormatSizeInBytes(internalformat);
if (sizedTextureFormatSize > 0) return sizedTextureFormatSize;
SizeT sizedPixelFormatSize = GetSizedTexturePixelDataTypeSize(type);
if (sizedPixelFormatSize > 0) return sizedPixelFormatSize;
SizeT chCount = GetBaseInternalFormatComponentCount(internalformat);
SizeT bytesPerChannel = GetBaseTexturePixelDataTypeSize(type);
return chCount * bytesPerChannel;
}
SizeT GetInputBytesPerPixel(TextureInputFormat inputFormat, TexturePixelDataType type) {
SizeT sizedPixelFormatSize = GetSizedTexturePixelDataTypeSize(type);
if (sizedPixelFormatSize > 0) return sizedPixelFormatSize;
SizeT bytesPerChannel = GetBaseTexturePixelDataTypeSize(type);
SizeT chCount = GetBaseInputFormatComponentCount(inputFormat);
return chCount * bytesPerChannel;
}
SizeT CalculateInputTextureImageSize(TextureInputFormat inputFormat, TexturePixelDataType pixelDataType,
IntVec3 size) {
return GetInputBytesPerPixel(inputFormat, pixelDataType) * size.x() * size.y() * size.z();
}
ComponentSizes GetComponentSizesForInternalFormat(TextureInternalFormat internal) {
ComponentSizes s = {};
switch (internal) {
case TextureInternalFormat::R8:
case TextureInternalFormat::Red:
case TextureInternalFormat::R8Snorm:
case TextureInternalFormat::R8I:
case TextureInternalFormat::R8UI:
s.Red = 8;
break;
case TextureInternalFormat::R16:
case TextureInternalFormat::R16Snorm:
case TextureInternalFormat::R16I:
case TextureInternalFormat::R16UI:
case TextureInternalFormat::R16F:
s.Red = 16;
break;
case TextureInternalFormat::RG8:
case TextureInternalFormat::RG8Snorm:
case TextureInternalFormat::RG8I:
case TextureInternalFormat::RG8UI:
case TextureInternalFormat::RG:
s.Red = 8;
s.Green = 8;
break;
case TextureInternalFormat::RG16:
case TextureInternalFormat::RG16Snorm:
case TextureInternalFormat::RG16I:
case TextureInternalFormat::RG16UI:
case TextureInternalFormat::RG16F:
s.Red = 16;
s.Green = 16;
break;
case TextureInternalFormat::RGB4:
case TextureInternalFormat::RGB5:
case TextureInternalFormat::RGB8:
case TextureInternalFormat::RGB8Snorm:
case TextureInternalFormat::SRGB8:
case TextureInternalFormat::RGB8I:
case TextureInternalFormat::RGB8UI:
case TextureInternalFormat::RGB:
s.Red = 8;
s.Green = 8;
s.Blue = 8;
break;
case TextureInternalFormat::RGB10:
s.Red = 10;
s.Green = 10;
s.Blue = 10;
break;
case TextureInternalFormat::RGB12:
s.Red = 12;
s.Green = 12;
s.Blue = 12;
break;
case TextureInternalFormat::RGB16:
case TextureInternalFormat::RGB16Snorm:
s.Red = 16;
s.Green = 16;
s.Blue = 16;
break;
case TextureInternalFormat::R3G3B2:
s.Red = 3;
s.Green = 3;
s.Blue = 2;
break;
case TextureInternalFormat::RGBA2:
s.Red = 2;
s.Green = 2;
s.Blue = 2;
s.Alpha = 2;
break;
case TextureInternalFormat::RGBA4:
s.Red = 4;
s.Green = 4;
s.Blue = 4;
s.Alpha = 4;
break;
case TextureInternalFormat::RGB5A1:
s.Red = 5;
s.Green = 5;
s.Blue = 5;
s.Alpha = 1;
break;
case TextureInternalFormat::RGBA8:
case TextureInternalFormat::RGBA8Snorm:
case TextureInternalFormat::RGBA8I:
case TextureInternalFormat::RGBA8UI:
case TextureInternalFormat::SRGB8Alpha8:
case TextureInternalFormat::RGBA:
s.Red = 8;
s.Green = 8;
s.Blue = 8;
s.Alpha = 8;
break;
case TextureInternalFormat::RGB10A2:
case TextureInternalFormat::RGB10A2UI:
s.Red = 10;
s.Green = 10;
s.Blue = 10;
s.Alpha = 2;
break;
case TextureInternalFormat::R32F:
case TextureInternalFormat::R32I:
case TextureInternalFormat::R32UI:
s.Red = 32;
break;
case TextureInternalFormat::RG32F:
case TextureInternalFormat::RG32I:
case TextureInternalFormat::RG32UI:
s.Red = 32;
s.Green = 32;
break;
case TextureInternalFormat::RGBA12:
s.Red = 12;
s.Green = 12;
s.Blue = 12;
s.Alpha = 12;
break;
case TextureInternalFormat::RGBA16:
case TextureInternalFormat::RGBA16Snorm:
case TextureInternalFormat::RGBA16I:
case TextureInternalFormat::RGBA16UI:
case TextureInternalFormat::RGBA16F:
s.Red = 16;
s.Green = 16;
s.Blue = 16;
s.Alpha = 16;
break;
case TextureInternalFormat::RGB16F:
case TextureInternalFormat::RGB16I:
case TextureInternalFormat::RGB16UI:
s.Red = 16;
s.Green = 16;
s.Blue = 16;
break;
case TextureInternalFormat::RGB32F:
case TextureInternalFormat::RGB32I:
case TextureInternalFormat::RGB32UI:
s.Red = 32;
s.Green = 32;
s.Blue = 32;
break;
case TextureInternalFormat::RGBA32F:
case TextureInternalFormat::RGBA32I:
case TextureInternalFormat::RGBA32UI:
s.Red = 32;
s.Green = 32;
s.Blue = 32;
s.Alpha = 32;
break;
case TextureInternalFormat::R11FG11FB10F:
s.Red = 11;
s.Green = 11;
s.Blue = 10;
break;
case TextureInternalFormat::RGB9E5:
s.Red = 9;
s.Green = 9;
s.Blue = 9;
break;
case TextureInternalFormat::DepthComponent:
case TextureInternalFormat::DepthComponent16:
s.Depth = 16;
break;
case TextureInternalFormat::DepthComponent24:
s.Depth = 24;
break;
case TextureInternalFormat::DepthComponent32:
case TextureInternalFormat::DepthComponent32F:
s.Depth = 32;
break;
case TextureInternalFormat::DepthStencil:
case TextureInternalFormat::Depth24Stencil8:
s.Depth = 24;
s.Stencil = 8;
break;
case TextureInternalFormat::Depth32FStencil8:
s.Depth = 32;
s.Stencil = 8;
break;
case TextureInternalFormat::StencilIndex8:
s.Stencil = 8;
break;
case TextureInternalFormat::Unknown:
// Queried for attachments that have no storage yet (e.g. framebuffer
// parameter queries on the initial state); every size stays 0.
break;
default:
MGLOG_W_ONCE("Unimplemented internal format in GetComponentSizesForInternalFormat: %d",
static_cast<Int>(internal));
break;
}
return s;
}
CompressedFormatInfo GetCompressedFormatInfo(GLenum internalFormat) {
// Every format here is 4x4-blocked; only the bytes per block differ (8 for the one- and
// two-channel RGTC/EAC and the 1-bit-alpha ETC2 forms, 16 for BPTC and the full-alpha
// ETC2/EAC and two-channel EAC forms). The generic compressed formats (GL_COMPRESSED_RGBA
// and friends) are deliberately absent: GL lets the implementation pick, MobileGL picks
// uncompressed, and glCompressedTexImage* must reject them because there is no defined
// block layout to hand it. The accepted set is exactly the set
// ConvertGLEnumToTextureInternalFormat can back with uncompressed storage, so this call
// can never accept a format whose texel shadow cannot be allocated.
switch (internalFormat) {
case GL_COMPRESSED_RED_RGTC1:
case GL_COMPRESSED_SIGNED_RED_RGTC1:
case GL_COMPRESSED_RGB8_ETC2:
case GL_COMPRESSED_SRGB8_ETC2:
case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2:
case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2:
case GL_COMPRESSED_R11_EAC:
case GL_COMPRESSED_SIGNED_R11_EAC:
return {4, 4, 8};
case GL_COMPRESSED_RG_RGTC2:
case GL_COMPRESSED_SIGNED_RG_RGTC2:
case GL_COMPRESSED_RGBA_BPTC_UNORM:
case GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM:
case GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT:
case GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT:
case GL_COMPRESSED_RGBA8_ETC2_EAC:
case GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC:
case GL_COMPRESSED_RG11_EAC:
case GL_COMPRESSED_SIGNED_RG11_EAC:
return {4, 4, 16};
default:
return {};
}
}
SizeT CalculateCompressedTextureImageSize(const CompressedFormatInfo& info, IntVec3 size) {
if (info.blockWidth == 0 || info.blockHeight == 0) return 0;
const SizeT width = static_cast<SizeT>(std::max<Int>(size.x(), 0));
const SizeT height = static_cast<SizeT>(std::max<Int>(size.y(), 0));
const SizeT depth = static_cast<SizeT>(std::max<Int>(size.z(), 1));
const SizeT blocksX = (width + info.blockWidth - 1) / info.blockWidth;
const SizeT blocksY = (height + info.blockHeight - 1) / info.blockHeight;
return blocksX * blocksY * depth * info.blockByteSize;
}
} // namespace MG_Util
} // namespace MobileGL