Add EAC / ETC2 and DXT compression formats to Metal backend (#1800)
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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),
|
||||
|
||||
Reference in New Issue
Block a user