Add EAC / ETC2 and DXT compression formats to Metal backend (#1800)

This commit is contained in:
Ben Doherty
2019-11-05 09:21:41 -08:00
committed by GitHub
parent 5462cc0fcf
commit 7c0d3cdb10
6 changed files with 178 additions and 21 deletions

View File

@@ -8,6 +8,7 @@ A new header is inserted each time a *tag* is created.
- Fixed morph shapes not rendering in WebGL.
- Added support for the latest version of emscripten.
- gltfio: fixed blackness seen with default material.
- Added ETC2 and BC compressed texture support to Metal backend.
## v1.4.2

View File

@@ -22,9 +22,16 @@ namespace filament {
namespace backend {
/**
* Returns the number of bytes per pixel for the given format.
* Returns the number of bytes per pixel for the given format. For compressed texture formats,
* returns the number of bytes per block.
*/
size_t getFormatSize(TextureFormat format) noexcept;
/**
* For compressed texture formats, returns the number of horizontal pixels per block. Otherwise
* returns 0.
*/
size_t getBlockWidth(TextureFormat format) noexcept;
} // namespace backend
} // namespace filament

View File

@@ -99,10 +99,130 @@ size_t getFormatSize(TextureFormat format) noexcept {
case TextureFormat::RGBA32I:
return 16;
// Compressed formats ---------------------------------------------------------------------
case TextureFormat::EAC_RG11:
case TextureFormat::EAC_RG11_SIGNED:
case TextureFormat::ETC2_EAC_RGBA8:
case TextureFormat::ETC2_EAC_SRGBA8:
return 16;
case TextureFormat::EAC_R11:
case TextureFormat::EAC_R11_SIGNED:
case TextureFormat::ETC2_RGB8:
case TextureFormat::ETC2_SRGB8:
case TextureFormat::ETC2_RGB8_A1:
case TextureFormat::ETC2_SRGB8_A1:
return 8;
case TextureFormat::DXT1_RGB:
return 8;
case TextureFormat::DXT3_RGBA:
case TextureFormat::DXT5_RGBA:
return 16;
// The block size for ASTC compression is always 16 bytes.
case TextureFormat::RGBA_ASTC_4x4:
case TextureFormat::RGBA_ASTC_5x4:
case TextureFormat::RGBA_ASTC_5x5:
case TextureFormat::RGBA_ASTC_6x5:
case TextureFormat::RGBA_ASTC_6x6:
case TextureFormat::RGBA_ASTC_8x5:
case TextureFormat::RGBA_ASTC_8x6:
case TextureFormat::RGBA_ASTC_8x8:
case TextureFormat::RGBA_ASTC_10x5:
case TextureFormat::RGBA_ASTC_10x6:
case TextureFormat::RGBA_ASTC_10x8:
case TextureFormat::RGBA_ASTC_10x10:
case TextureFormat::RGBA_ASTC_12x10:
case TextureFormat::RGBA_ASTC_12x12:
case TextureFormat::SRGB8_ALPHA8_ASTC_4x4:
case TextureFormat::SRGB8_ALPHA8_ASTC_5x4:
case TextureFormat::SRGB8_ALPHA8_ASTC_5x5:
case TextureFormat::SRGB8_ALPHA8_ASTC_6x5:
case TextureFormat::SRGB8_ALPHA8_ASTC_6x6:
case TextureFormat::SRGB8_ALPHA8_ASTC_8x5:
case TextureFormat::SRGB8_ALPHA8_ASTC_8x6:
case TextureFormat::SRGB8_ALPHA8_ASTC_8x8:
case TextureFormat::SRGB8_ALPHA8_ASTC_10x5:
case TextureFormat::SRGB8_ALPHA8_ASTC_10x6:
case TextureFormat::SRGB8_ALPHA8_ASTC_10x8:
case TextureFormat::SRGB8_ALPHA8_ASTC_10x10:
case TextureFormat::SRGB8_ALPHA8_ASTC_12x10:
case TextureFormat::SRGB8_ALPHA8_ASTC_12x12:
return 16;
default:
return 0;
}
}
size_t getBlockWidth(TextureFormat format) noexcept {
switch (format) {
case TextureFormat::EAC_RG11:
case TextureFormat::EAC_RG11_SIGNED:
case TextureFormat::ETC2_EAC_RGBA8:
case TextureFormat::ETC2_EAC_SRGBA8:
case TextureFormat::EAC_R11:
case TextureFormat::EAC_R11_SIGNED:
case TextureFormat::ETC2_RGB8:
case TextureFormat::ETC2_SRGB8:
case TextureFormat::ETC2_RGB8_A1:
case TextureFormat::ETC2_SRGB8_A1:
return 4;
case TextureFormat::DXT1_RGB:
case TextureFormat::DXT3_RGBA:
case TextureFormat::DXT5_RGBA:
return 4;
case TextureFormat::RGBA_ASTC_4x4:
case TextureFormat::SRGB8_ALPHA8_ASTC_4x4:
return 4;
case TextureFormat::RGBA_ASTC_5x4:
case TextureFormat::RGBA_ASTC_5x5:
case TextureFormat::SRGB8_ALPHA8_ASTC_5x4:
case TextureFormat::SRGB8_ALPHA8_ASTC_5x5:
return 5;
return 5;
case TextureFormat::RGBA_ASTC_6x5:
case TextureFormat::RGBA_ASTC_6x6:
case TextureFormat::SRGB8_ALPHA8_ASTC_6x5:
case TextureFormat::SRGB8_ALPHA8_ASTC_6x6:
return 6;
case TextureFormat::RGBA_ASTC_8x5:
case TextureFormat::RGBA_ASTC_8x6:
case TextureFormat::RGBA_ASTC_8x8:
case TextureFormat::SRGB8_ALPHA8_ASTC_8x5:
case TextureFormat::SRGB8_ALPHA8_ASTC_8x6:
case TextureFormat::SRGB8_ALPHA8_ASTC_8x8:
return 8;
case TextureFormat::RGBA_ASTC_10x5:
case TextureFormat::RGBA_ASTC_10x6:
case TextureFormat::RGBA_ASTC_10x8:
case TextureFormat::RGBA_ASTC_10x10:
case TextureFormat::SRGB8_ALPHA8_ASTC_10x5:
case TextureFormat::SRGB8_ALPHA8_ASTC_10x6:
case TextureFormat::SRGB8_ALPHA8_ASTC_10x8:
case TextureFormat::SRGB8_ALPHA8_ASTC_10x10:
return 10;
case TextureFormat::RGBA_ASTC_12x10:
case TextureFormat::RGBA_ASTC_12x12:
case TextureFormat::SRGB8_ALPHA8_ASTC_12x10:
case TextureFormat::SRGB8_ALPHA8_ASTC_12x12:
return 12;
default:
return 0;
}
}
} // namespace backend
} // namespace filament

View File

@@ -215,22 +215,29 @@ constexpr inline MTLPixelFormat getMetalFormat(TextureFormat format) noexcept {
case TextureFormat::RGBA32UI: return MTLPixelFormatRGBA32Uint;
case TextureFormat::RGBA32I: return MTLPixelFormatRGBA32Sint;
// TODO: add compressed formats
case TextureFormat::EAC_R11:
case TextureFormat::EAC_R11_SIGNED:
case TextureFormat::EAC_RG11:
case TextureFormat::EAC_RG11_SIGNED:
case TextureFormat::ETC2_RGB8:
case TextureFormat::ETC2_SRGB8:
case TextureFormat::ETC2_RGB8_A1:
case TextureFormat::ETC2_SRGB8_A1:
case TextureFormat::ETC2_EAC_RGBA8:
case TextureFormat::ETC2_EAC_SRGBA8:
#if defined(IOS)
// EAC / ETC2 formats are only available on iPhone.
case TextureFormat::EAC_R11: return MTLPixelFormatEAC_R11Unorm;
case TextureFormat::EAC_R11_SIGNED: return MTLPixelFormatEAC_R11Snorm;
case TextureFormat::EAC_RG11: return MTLPixelFormatEAC_RG11Unorm;
case TextureFormat::EAC_RG11_SIGNED: return MTLPixelFormatEAC_RG11Snorm;
case TextureFormat::ETC2_RGB8: return MTLPixelFormatETC2_RGB8;
case TextureFormat::ETC2_SRGB8: return MTLPixelFormatETC2_RGB8_sRGB;
case TextureFormat::ETC2_RGB8_A1: return MTLPixelFormatETC2_RGB8A1;
case TextureFormat::ETC2_SRGB8_A1: return MTLPixelFormatETC2_RGB8A1_sRGB;
case TextureFormat::ETC2_EAC_RGBA8: return MTLPixelFormatEAC_RGBA8;
case TextureFormat::ETC2_EAC_SRGBA8: return MTLPixelFormatEAC_RGBA8_sRGB;
#endif
case TextureFormat::DXT1_RGB:
case TextureFormat::DXT1_RGBA:
case TextureFormat::DXT3_RGBA:
case TextureFormat::DXT5_RGBA:
#if !defined(IOS)
// DXT (BC) formats are only available on macOS desktkop.
// See https://en.wikipedia.org/wiki/S3_Texture_Compression#S3TC_format_comparison
case TextureFormat::DXT1_RGBA: return MTLPixelFormatBC1_RGBA;
case TextureFormat::DXT3_RGBA: return MTLPixelFormatBC2_RGBA;
case TextureFormat::DXT5_RGBA: return MTLPixelFormatBC3_RGBA;
case TextureFormat::DXT1_RGB: return MTLPixelFormatInvalid;
#endif
case TextureFormat::RGBA_ASTC_4x4:
case TextureFormat::RGBA_ASTC_5x4:

View File

@@ -129,10 +129,13 @@ struct MetalTexture : public HwTexture {
void loadCubeImage(const PixelBufferDescriptor& data, const FaceOffsets& faceOffsets,
int miplevel);
NSUInteger getBytesPerRow(PixelDataType type, NSUInteger width) const noexcept;
MetalContext& context;
MetalExternalImage externalImage;
id<MTLTexture> texture = nil;
uint8_t bytesPerPixel;
uint8_t bytesPerElement; // The number of bytes per pixel, or block (for compressed texture formats).
uint8_t blockWidth; // The number of horizontal pixels per block (only for compressed texture formats).
TextureReshaper reshaper;
};

View File

@@ -23,6 +23,8 @@
#include <utils/Panic.h>
#include <utils/trap.h>
#include <math.h>
namespace filament {
namespace backend {
namespace metal {
@@ -289,7 +291,9 @@ MetalTexture::MetalTexture(MetalContext& context, backend::SamplerType target, u
const TextureFormat reshapedFormat = reshaper.getReshapedFormat();
const MTLPixelFormat pixelFormat = decidePixelFormat(context.device, reshapedFormat);
bytesPerPixel = static_cast<uint8_t>(getFormatSize(reshapedFormat));
bytesPerElement = static_cast<uint8_t>(getFormatSize(reshapedFormat));
assert(bytesPerElement > 0);
blockWidth = static_cast<uint8_t>(getBlockWidth(reshapedFormat));
ASSERT_POSTCONDITION(pixelFormat != MTLPixelFormatInvalid, "Pixel format not supported.");
@@ -349,7 +353,7 @@ void MetalTexture::load2DImage(uint32_t level, uint32_t xoffset, uint32_t yoffse
.depth = 1
}
};
NSUInteger bytesPerRow = bytesPerPixel * width;
const NSUInteger bytesPerRow = getBytesPerRow(data.type, width);
[texture replaceRegion:region
mipmapLevel:level
slice:0
@@ -362,8 +366,9 @@ void MetalTexture::load2DImage(uint32_t level, uint32_t xoffset, uint32_t yoffse
void MetalTexture::loadCubeImage(const PixelBufferDescriptor& data, const FaceOffsets& faceOffsets,
int miplevel) {
NSUInteger faceWidth = width >> miplevel;
NSUInteger bytesPerRow = bytesPerPixel * faceWidth;
const NSUInteger faceWidth = width >> miplevel;
const NSUInteger bytesPerRow = getBytesPerRow(data.type, faceWidth);
MTLRegion region = MTLRegionMake2D(0, 0, faceWidth, faceWidth);
for (NSUInteger slice = 0; slice < 6; slice++) {
FaceOffsets::size_type faceOffset = faceOffsets.offsets[slice];
@@ -376,6 +381,20 @@ void MetalTexture::loadCubeImage(const PixelBufferDescriptor& data, const FaceOf
}
}
NSUInteger MetalTexture::getBytesPerRow(PixelDataType type, NSUInteger width) const noexcept {
// From https://developer.apple.com/documentation/metal/mtltexture/1515464-replaceregion:
// For an ordinary or packed pixel format, the stride, in bytes, between rows of source data.
// For a compressed pixel format, the stride is the number of bytes from the beginning of one
// row of blocks to the beginning of the next.
if (type == PixelDataType::COMPRESSED) {
assert(blockWidth > 0);
const NSUInteger blocksPerRow = std::ceil(width / (float) blockWidth);
return bytesPerElement * blocksPerRow;
} else {
return bytesPerElement * width;
}
}
MetalRenderTarget::MetalRenderTarget(MetalContext* context, uint32_t width, uint32_t height,
uint8_t samples, id<MTLTexture> color, id<MTLTexture> depth, uint8_t colorLevel,
uint8_t depthLevel) : HwRenderTarget(width, height), context(context), samples(samples),