| // |
| // Copyright 2019 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. |
| // |
| // mtl_format_utils.mm: |
| // Implements Format conversion utilities classes that convert from angle formats |
| // to respective MTLPixelFormat and MTLVertexFormat. |
| // |
| |
| #include "libANGLE/renderer/metal/mtl_format_utils.h" |
| |
| #include "common/debug.h" |
| #include "libANGLE/renderer/Format.h" |
| #include "libANGLE/renderer/metal/DisplayMtl.h" |
| |
| namespace rx |
| { |
| namespace mtl |
| { |
| |
| namespace |
| { |
| |
| bool OverrideTextureCaps(const DisplayMtl *display, angle::FormatID formatId, gl::TextureCaps *caps) |
| { |
| // NOTE(hqle): Auto generate this. |
| switch (formatId) |
| { |
| case angle::FormatID::R8G8_UNORM: |
| case angle::FormatID::R8G8B8_UNORM: |
| case angle::FormatID::R8G8B8_UNORM_SRGB: |
| case angle::FormatID::R8G8B8A8_UNORM: |
| case angle::FormatID::R8G8B8A8_UNORM_SRGB: |
| case angle::FormatID::B8G8R8A8_UNORM: |
| case angle::FormatID::B8G8R8A8_UNORM_SRGB: |
| caps->texturable = caps->filterable = caps->textureAttachment = caps->renderbuffer = |
| true; |
| return true; |
| default: |
| // NOTE(hqle): Handle more cases |
| return false; |
| } |
| } |
| |
| void GenerateTextureCapsMap(const FormatTable &formatTable, |
| const DisplayMtl *display, |
| gl::TextureCapsMap *capsMapOut, |
| std::vector<GLenum> *compressedFormatsOut) |
| { |
| auto &textureCapsMap = *capsMapOut; |
| auto &compressedFormats = *compressedFormatsOut; |
| |
| compressedFormats.clear(); |
| |
| // Metal doesn't have programmatical way to determine texture format support. |
| // What is available is the online documents from Apple. What we can do here |
| // is manually set certain extension flag to true then let angle decide the supported formats. |
| // |
| // TODO(hqle): The proper way of doing this is creating a detailed "format support table" json |
| // file with info parsed from https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf. |
| // Then using that json file to generate a table in C++ file. |
| gl::Extensions tmpTextureExtensions; |
| |
| #if TARGET_OS_OSX || TARGET_OS_MACCATALYST |
| // https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf |
| // Requires depth24Stencil8PixelFormatSupported=YES for these extensions |
| bool packedDepthStencil24Support = |
| display->getMetalDevice().depth24Stencil8PixelFormatSupported; |
| tmpTextureExtensions.packedDepthStencil = true; // We support this reguardless |
| tmpTextureExtensions.colorBufferHalfFloat = packedDepthStencil24Support; |
| tmpTextureExtensions.colorBufferFloat = packedDepthStencil24Support; |
| tmpTextureExtensions.colorBufferFloatRGB = packedDepthStencil24Support; |
| tmpTextureExtensions.colorBufferFloatRGBA = packedDepthStencil24Support; |
| tmpTextureExtensions.textureHalfFloat = packedDepthStencil24Support; |
| tmpTextureExtensions.textureFloat = packedDepthStencil24Support; |
| tmpTextureExtensions.textureHalfFloatLinear = packedDepthStencil24Support; |
| tmpTextureExtensions.textureFloatLinear = packedDepthStencil24Support; |
| tmpTextureExtensions.textureRG = packedDepthStencil24Support; |
| tmpTextureExtensions.textureFormatBGRA8888 = packedDepthStencil24Support; |
| |
| tmpTextureExtensions.textureCompressionDXT3 = true; |
| tmpTextureExtensions.textureCompressionDXT5 = true; |
| |
| // We can only fully support DXT1 without alpha using texture swizzle support from MacOs 10.15 |
| tmpTextureExtensions.textureCompressionDXT1 = display->getFeatures().hasTextureSwizzle.enabled; |
| |
| tmpTextureExtensions.textureCompressionS3TCsRGB = tmpTextureExtensions.textureCompressionDXT1; |
| #else |
| tmpTextureExtensions.packedDepthStencil = true; // override to D32_FLOAT_S8X24_UINT |
| tmpTextureExtensions.colorBufferHalfFloat = true; |
| tmpTextureExtensions.colorBufferFloat = true; |
| tmpTextureExtensions.colorBufferFloatRGB = true; |
| tmpTextureExtensions.colorBufferFloatRGBA = true; |
| tmpTextureExtensions.textureHalfFloat = true; |
| tmpTextureExtensions.textureHalfFloatLinear = true; |
| tmpTextureExtensions.textureFloat = true; |
| tmpTextureExtensions.textureRG = true; |
| tmpTextureExtensions.textureFormatBGRA8888 = true; |
| if ([display->getMetalDevice() supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily1_v1]) |
| { |
| tmpTextureExtensions.compressedETC1RGB8Texture = true; |
| tmpTextureExtensions.compressedETC2RGB8Texture = true; |
| tmpTextureExtensions.compressedETC2sRGB8Texture = true; |
| tmpTextureExtensions.compressedETC2RGBA8Texture = true; |
| tmpTextureExtensions.compressedETC2sRGB8Alpha8Texture = true; |
| tmpTextureExtensions.compressedEACR11UnsignedTexture = true; |
| tmpTextureExtensions.compressedEACR11SignedTexture = true; |
| tmpTextureExtensions.compressedEACRG11UnsignedTexture = true; |
| tmpTextureExtensions.compressedEACRG11SignedTexture = true; |
| tmpTextureExtensions.compressedTexturePVRTC = true; |
| tmpTextureExtensions.compressedTexturePVRTCsRGB = true; |
| } |
| #endif |
| tmpTextureExtensions.sRGB = true; |
| tmpTextureExtensions.depth32 = true; |
| tmpTextureExtensions.depth24OES = true; |
| tmpTextureExtensions.rgb8rgba8 = true; |
| tmpTextureExtensions.textureStorage = true; |
| |
| auto formatVerifier = [&](const gl::InternalFormat &internalFormatInfo) { |
| angle::FormatID angleFormatId = |
| angle::Format::InternalFormatToID(internalFormatInfo.sizedInternalFormat); |
| const Format &mtlFormat = formatTable.getPixelFormat(angleFormatId); |
| |
| if (!mtlFormat.valid()) |
| { |
| return; |
| } |
| |
| const angle::Format &intendedAngleFormat = mtlFormat.intendedAngleFormat(); |
| gl::TextureCaps textureCaps; |
| |
| const auto &clientVersion = kMaxSupportedGLVersion; |
| |
| // First let check whether we can determine programmatically. |
| if (!OverrideTextureCaps(display, mtlFormat.intendedFormatId, &textureCaps)) |
| { |
| // Let angle decide based on extensions we enabled above. |
| textureCaps = gl::GenerateMinimumTextureCaps(internalFormatInfo.sizedInternalFormat, |
| clientVersion, tmpTextureExtensions); |
| } |
| |
| // NOTE(hqle): Support MSAA. |
| textureCaps.sampleCounts.clear(); |
| textureCaps.sampleCounts.insert(0); |
| textureCaps.sampleCounts.insert(1); |
| |
| if (textureCaps.filterable && mtlFormat.actualFormatId == angle::FormatID::D32_FLOAT) |
| { |
| // Only MacOS support filterable for D32_FLOAT texture |
| #if !TARGET_OS_OSX || TARGET_OS_MACCATALYST |
| textureCaps.filterable = false; |
| #endif |
| } |
| |
| textureCapsMap.set(mtlFormat.intendedFormatId, textureCaps); |
| |
| if (intendedAngleFormat.isBlock) |
| { |
| compressedFormats.push_back(intendedAngleFormat.glInternalFormat); |
| } |
| |
| // Verify implementation mismatch |
| ASSERT(!textureCaps.renderbuffer || mtl::Format::FormatRenderable(mtlFormat.metalFormat)); |
| ASSERT(!textureCaps.textureAttachment || |
| mtl::Format::FormatRenderable(mtlFormat.metalFormat)); |
| }; |
| |
| // Texture caps map. |
| const gl::FormatSet &internalFormats = gl::GetAllSizedInternalFormats(); |
| for (const auto internalFormat : internalFormats) |
| { |
| const gl::InternalFormat &internalFormatInfo = |
| gl::GetSizedInternalFormatInfo(internalFormat); |
| |
| formatVerifier(internalFormatInfo); |
| } |
| } |
| |
| } // namespace |
| |
| // FormatBase implementation |
| const angle::Format &FormatBase::actualAngleFormat() const |
| { |
| return angle::Format::Get(actualFormatId); |
| } |
| |
| const angle::Format &FormatBase::intendedAngleFormat() const |
| { |
| return angle::Format::Get(intendedFormatId); |
| } |
| |
| // Format implementation |
| /** static */ |
| bool Format::FormatRenderable(MTLPixelFormat format) |
| { |
| switch (format) |
| { |
| case MTLPixelFormatR8Unorm: |
| case MTLPixelFormatRG8Unorm: |
| case MTLPixelFormatR16Float: |
| case MTLPixelFormatRG16Float: |
| case MTLPixelFormatRGBA16Float: |
| case MTLPixelFormatR32Float: |
| case MTLPixelFormatRG32Float: |
| case MTLPixelFormatRGBA32Float: |
| case MTLPixelFormatBGRA8Unorm: |
| case MTLPixelFormatBGRA8Unorm_sRGB: |
| case MTLPixelFormatRGBA8Unorm: |
| case MTLPixelFormatRGBA8Unorm_sRGB: |
| case MTLPixelFormatDepth32Float: |
| case MTLPixelFormatStencil8: |
| case MTLPixelFormatDepth32Float_Stencil8: |
| #if TARGET_OS_OSX || TARGET_OS_MACCATALYST |
| case MTLPixelFormatDepth16Unorm: |
| case MTLPixelFormatDepth24Unorm_Stencil8: |
| #else |
| case MTLPixelFormatR8Unorm_sRGB: |
| case MTLPixelFormatRG8Unorm_sRGB: |
| case MTLPixelFormatB5G6R5Unorm: |
| case MTLPixelFormatA1BGR5Unorm: |
| case MTLPixelFormatABGR4Unorm: |
| case MTLPixelFormatBGR5A1Unorm: |
| #endif |
| // NOTE(hqle): we may add more formats support here in future. |
| return true; |
| default: |
| return false; |
| } |
| return false; |
| } |
| |
| /** static */ |
| bool Format::FormatCPUReadable(MTLPixelFormat format) |
| { |
| switch (format) |
| { |
| case MTLPixelFormatDepth32Float: |
| case MTLPixelFormatStencil8: |
| case MTLPixelFormatDepth32Float_Stencil8: |
| #if TARGET_OS_OSX || TARGET_OS_MACCATALYST |
| case MTLPixelFormatDepth16Unorm: |
| case MTLPixelFormatDepth24Unorm_Stencil8: |
| #endif |
| // NOTE(hqle): we may add more formats support here in future. |
| return false; |
| default: |
| return true; |
| } |
| } |
| |
| const gl::InternalFormat &Format::intendedInternalFormat() const |
| { |
| return gl::GetSizedInternalFormatInfo(intendedAngleFormat().glInternalFormat); |
| } |
| |
| // FormatTable implementation |
| angle::Result FormatTable::initialize(const DisplayMtl *display) |
| { |
| for (size_t i = 0; i < angle::kNumANGLEFormats; ++i) |
| { |
| const auto formatId = static_cast<angle::FormatID>(i); |
| |
| mPixelFormatTable[i].init(display, formatId); |
| mVertexFormatTables[0][i].init(formatId, false); |
| mVertexFormatTables[1][i].init(formatId, true); |
| } |
| |
| return angle::Result::Continue; |
| } |
| |
| void FormatTable::generateTextureCaps(const DisplayMtl *display, |
| gl::TextureCapsMap *capsMapOut, |
| std::vector<GLenum> *compressedFormatsOut) const |
| { |
| GenerateTextureCapsMap(*this, display, capsMapOut, compressedFormatsOut); |
| } |
| |
| const Format &FormatTable::getPixelFormat(angle::FormatID angleFormatId) const |
| { |
| return mPixelFormatTable[static_cast<size_t>(angleFormatId)]; |
| } |
| const VertexFormat &FormatTable::getVertexFormat(angle::FormatID angleFormatId, |
| bool tightlyPacked) const |
| { |
| auto tableIdx = tightlyPacked ? 1 : 0; |
| return mVertexFormatTables[tableIdx][static_cast<size_t>(angleFormatId)]; |
| } |
| |
| } // namespace mtl |
| } // namespace rx |