Commit f37f1dcb by Le Hoang Quyen Committed by Commit Bot

Metal: Init format table using Metal-Feature-Set-Tables.pdf

- Format table is now initialized using informations from https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf. Previously, it was setup using gl::GenerateMinimumTextureCaps(). - This CL also adds InitializeTextureDataFunction and LoadFunctionMap to mtl::Format. They are needed to properly initialize/convert textures with non-normalized formats. - This CL is prerequisite for integer & floating point format supports. - New test: DXT1CompressedTextureTest.DXT1Alpha (this test was added in the past but was reverted for some reasons). Bug: angleproject:2634 Change-Id: I5eaad812909a49c4c138d0f65fd21a6a199fcb22 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2332144 Commit-Queue: Le Hoang Quyen <le.hoang.q@gmail.com> Reviewed-by: 's avatarJonah Ryan-Davis <jonahr@google.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org>
parent 23335ac0
......@@ -34,6 +34,14 @@ struct FeaturesMtl : FeatureSetBase
Feature hasTextureSwizzle = {"has_texture_swizzle", FeatureCategory::MetalFeatures,
"The renderer supports texture swizzle", &members};
Feature hasDepthAutoResolve = {
"has_msaa_depth_auto_resolve", FeatureCategory::MetalFeatures,
"The renderer supports MSAA depth auto resolve at the end of render pass", &members};
Feature hasStencilAutoResolve = {
"has_msaa_stencil_auto_resolve", FeatureCategory::MetalFeatures,
"The renderer supports MSAA stencil auto resolve at the end of render pass", &members};
// On macos, separate depth & stencil buffers are not supproted. However, on iOS devices,
// they are supproted:
Feature allowSeparatedDepthStencilBuffers = {
......
{
"src/libANGLE/renderer/angle_format.py":
"32ba71942c0fd00e6807104f1bb80a3c",
"src/libANGLE/renderer/angle_format_map.json":
"aa4a0d3463b76858a75787b9cdec8e98",
"src/libANGLE/renderer/metal/gen_mtl_format_table.py":
"780f56abea19db610d2b829ba817fdc6",
"27769e4e9cce3b7af18b69d41351c3ed",
"src/libANGLE/renderer/metal/mtl_format_map.json":
"7aad5b3ed806e0d932cbbfe6d3b8a834",
"b202d7a0349006e2bbd58c603d9cdc28",
"src/libANGLE/renderer/metal/mtl_format_table_autogen.mm":
"efd031ead828c19f5476413b2b743087"
"7a0ee6139ca1e7ec8b8b0a7de9180ef7"
}
\ No newline at end of file
......@@ -80,6 +80,7 @@ angle_source_set("angle_metal_backend") {
public_deps = [
"${angle_root}:angle_glslang_wrapper",
"${angle_root}:angle_image_util",
"${angle_root}:libANGLE_headers",
]
......
......@@ -278,6 +278,7 @@ class ContextMtl : public ContextImpl, public mtl::Context
bool getDepthMask() const;
const mtl::Format &getPixelFormat(angle::FormatID angleFormatId) const;
const mtl::FormatCaps &getNativeFormatCaps(MTLPixelFormat mtlFormat) const;
// See mtl::FormatTable::getVertexFormat()
const mtl::VertexFormat &getVertexFormat(angle::FormatID angleFormatId,
bool tightlyPacked) const;
......
......@@ -1118,6 +1118,11 @@ const mtl::VertexFormat &ContextMtl::getVertexFormat(angle::FormatID angleFormat
return getDisplay()->getVertexFormat(angleFormatId, tightlyPacked);
}
const mtl::FormatCaps &ContextMtl::getNativeFormatCaps(MTLPixelFormat mtlFormat) const
{
return getDisplay()->getNativeFormatCaps(mtlFormat);
}
angle::Result ContextMtl::getIncompleteTexture(const gl::Context *context,
gl::TextureType type,
gl::Texture **textureOut)
......
......@@ -107,6 +107,11 @@ class DisplayMtl : public DisplayImpl
const gl::Limitations &getNativeLimitations() const { return mNativeLimitations; }
const angle::FeaturesMtl &getFeatures() const { return mFeatures; }
// Check whether either of the specified iOS or Mac GPU family is supported
bool supportsEitherGPUFamily(uint8_t iOSFamily, uint8_t macFamily) const;
bool supportsIOSGPUFamily(uint8_t iOSFamily) const;
bool supportsMacGPUFamily(uint8_t macFamily) const;
id<MTLDevice> getMetalDevice() const { return mMetalDevice; }
mtl::CommandQueue &cmdQueue() { return mCmdQueue; }
......@@ -129,6 +134,10 @@ class DisplayMtl : public DisplayImpl
{
return mFormatTable.getPixelFormat(angleFormatId);
}
const mtl::FormatCaps &getNativeFormatCaps(MTLPixelFormat mtlFormat) const
{
return mFormatTable.getNativeFormatCaps(mtlFormat);
}
// See mtl::FormatTable::getVertexFormat()
const mtl::VertexFormat &getVertexFormat(angle::FormatID angleFormatId,
......@@ -154,7 +163,7 @@ class DisplayMtl : public DisplayImpl
mtl::CommandQueue mCmdQueue;
mtl::FormatTable mFormatTable;
mutable mtl::FormatTable mFormatTable;
mtl::StateCache mStateCache;
mtl::RenderUtils mUtils;
......
......@@ -452,7 +452,7 @@ void DisplayMtl::ensureCapsInitialized() const
mNativeCaps.maxVaryingVectors = 31;
mNativeCaps.maxVertexOutputComponents = 124;
#else
if ([getMetalDevice() supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily3_v1])
if (supportsIOSGPUFamily(3))
{
mNativeCaps.max2DTextureSize = 16384;
mNativeCaps.maxVertexOutputComponents = 124;
......@@ -662,6 +662,12 @@ void DisplayMtl::initializeTextureCaps() const
void DisplayMtl::initializeFeatures()
{
bool isMetal2_2 = false;
if (ANGLE_APPLE_AVAILABLE_XCI(10.15, 13.0, 13.0))
{
isMetal2_2 = true;
}
// default values:
mFeatures.hasBaseVertexInstancedDraw.enabled = true;
mFeatures.hasDepthTextureFiltering.enabled = false;
......@@ -669,33 +675,25 @@ void DisplayMtl::initializeFeatures()
mFeatures.hasTextureSwizzle.enabled = false;
mFeatures.allowSeparatedDepthStencilBuffers.enabled = false;
#if TARGET_OS_OSX || TARGET_OS_MACCATALYST
mFeatures.hasDepthTextureFiltering.enabled = true;
mFeatures.allowMultisampleStoreAndResolve.enabled = true;
ANGLE_FEATURE_CONDITION((&mFeatures), hasDepthTextureFiltering,
TARGET_OS_OSX || TARGET_OS_MACCATALYST);
ANGLE_FEATURE_CONDITION((&mFeatures), hasDepthAutoResolve, supportsEitherGPUFamily(3, 2));
ANGLE_FEATURE_CONDITION((&mFeatures), hasStencilAutoResolve, supportsEitherGPUFamily(5, 2));
ANGLE_FEATURE_CONDITION((&mFeatures), allowMultisampleStoreAndResolve,
supportsEitherGPUFamily(3, 1));
// Texture swizzle is only supported if macos sdk 10.15 is present
# if defined(__MAC_10_15)
if (ANGLE_APPLE_AVAILABLE_XC(10.15, 13.0))
{
// The runtime OS must be MacOS 10.15+ or Mac Catalyst for this to be supported:
ANGLE_FEATURE_CONDITION((&mFeatures), hasTextureSwizzle,
[getMetalDevice() supportsFamily:MTLGPUFamilyMac2]);
}
# endif
#elif TARGET_OS_IOS
ANGLE_FEATURE_CONDITION((&mFeatures), hasTextureSwizzle,
isMetal2_2 && supportsEitherGPUFamily(1, 2));
#if !TARGET_OS_MACCATALYST && (TARGET_OS_IOS || TARGET_OS_TV)
// Base Vertex drawing is only supported since GPU family 3.
ANGLE_FEATURE_CONDITION((&mFeatures), hasBaseVertexInstancedDraw,
[getMetalDevice() supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily3_v1]);
ANGLE_FEATURE_CONDITION((&mFeatures), hasBaseVertexInstancedDraw, supportsIOSGPUFamily(3));
ANGLE_FEATURE_CONDITION((&mFeatures), hasNonUniformDispatch,
[getMetalDevice() supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily4_v1]);
TARGET_OS_IOS && supportsIOSGPUFamily(4));
ANGLE_FEATURE_CONDITION((&mFeatures), allowMultisampleStoreAndResolve,
[getMetalDevice() supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily3_v1]);
ANGLE_FEATURE_CONDITION((&mFeatures), allowSeparatedDepthStencilBuffers, !TARGET_OS_SIMULATOR);
# if !TARGET_OS_SIMULATOR
mFeatures.allowSeparatedDepthStencilBuffers.enabled = true;
# endif
#endif
angle::PlatformMethods *platform = ANGLEPlatformCurrent();
......@@ -715,8 +713,8 @@ angle::Result DisplayMtl::initializeShaderLibrary()
compiled_shader_binary = compiled_default_metallib_debug;
compiled_shader_binary_len = compiled_default_metallib_debug_len;
#else
compiled_shader_binary = compiled_default_metallib;
compiled_shader_binary_len = compiled_default_metallib_len;
compiled_shader_binary = compiled_default_metallib;
compiled_shader_binary_len = compiled_default_metallib_len;
#endif
mDefaultShaders = CreateShaderLibraryFromBinary(getMetalDevice(), compiled_shader_binary,
......@@ -734,4 +732,151 @@ angle::Result DisplayMtl::initializeShaderLibrary()
return angle::Result::Continue;
}
bool DisplayMtl::supportsIOSGPUFamily(uint8_t iOSFamily) const
{
#if (!TARGET_OS_IOS && !TARGET_OS_TV) || TARGET_OS_MACCATALYST
return false;
#else
# if (__IPHONE_OS_VERSION_MAX_ALLOWED >= 130000) || (__TV_OS_VERSION_MAX_ALLOWED >= 130000)
// If device supports [MTLDevice supportsFamily:], then use it.
if (ANGLE_APPLE_AVAILABLE_I(13.0))
{
MTLGPUFamily family;
switch (iOSFamily)
{
case 1:
family = MTLGPUFamilyApple1;
break;
case 2:
family = MTLGPUFamilyApple2;
break;
case 3:
family = MTLGPUFamilyApple3;
break;
case 4:
family = MTLGPUFamilyApple4;
break;
case 5:
family = MTLGPUFamilyApple5;
break;
# if TARGET_OS_IOS
case 6:
family = MTLGPUFamilyApple6;
break;
# endif
default:
return false;
}
return [getMetalDevice() supportsFamily:family];
} // Metal 2.2
# endif // __IPHONE_OS_VERSION_MAX_ALLOWED
// If device doesn't support [MTLDevice supportsFamily:], then use
// [MTLDevice supportsFeatureSet:].
MTLFeatureSet featureSet;
switch (iOSFamily)
{
# if TARGET_OS_IOS
case 1:
featureSet = MTLFeatureSet_iOS_GPUFamily1_v1;
break;
case 2:
featureSet = MTLFeatureSet_iOS_GPUFamily2_v1;
break;
case 3:
featureSet = MTLFeatureSet_iOS_GPUFamily3_v1;
break;
case 4:
featureSet = MTLFeatureSet_iOS_GPUFamily4_v1;
break;
# if __IPHONE_OS_VERSION_MAX_ALLOWED >= 120000
case 5:
featureSet = MTLFeatureSet_iOS_GPUFamily5_v1;
break;
# endif // __IPHONE_OS_VERSION_MAX_ALLOWED
# elif TARGET_OS_TV
case 1:
case 2:
featureSet = MTLFeatureSet_tvOS_GPUFamily1_v1;
break;
case 3:
featureSet = MTLFeatureSet_tvOS_GPUFamily2_v1;
break;
# endif // TARGET_OS_IOS
default:
return false;
}
return [getMetalDevice() supportsFeatureSet:featureSet];
#endif // TARGET_OS_IOS || TARGET_OS_TV
}
bool DisplayMtl::supportsMacGPUFamily(uint8_t macFamily) const
{
#if TARGET_OS_OSX || TARGET_OS_MACCATALYST
# if defined(__MAC_10_15)
// If device supports [MTLDevice supportsFamily:], then use it.
if (ANGLE_APPLE_AVAILABLE_XC(10.15, 13.0))
{
MTLGPUFamily family;
switch (macFamily)
{
# if TARGET_OS_MACCATALYST
case 1:
family = MTLGPUFamilyMacCatalyst1;
break;
case 2:
family = MTLGPUFamilyMacCatalyst2;
break;
# else // TARGET_OS_MACCATALYST
case 1:
family = MTLGPUFamilyMac1;
break;
case 2:
family = MTLGPUFamilyMac2;
break;
# endif // TARGET_OS_MACCATALYST
default:
return false;
}
return [getMetalDevice() supportsFamily:family];
} // Metal 2.2
# endif
// If device doesn't support [MTLDevice supportsFamily:], then use
// [MTLDevice supportsFeatureSet:].
# if TARGET_OS_MACCATALYST
UNREACHABLE();
return false;
# else
MTLFeatureSet featureSet;
switch (macFamily)
{
case 1:
featureSet = MTLFeatureSet_macOS_GPUFamily1_v1;
break;
# if defined(__MAC_10_14)
case 2:
featureSet = MTLFeatureSet_macOS_GPUFamily2_v1;
break;
# endif
default:
return false;
}
return [getMetalDevice() supportsFeatureSet:featureSet];
# endif // TARGET_OS_MACCATALYST
#else // #if TARGET_OS_OSX || TARGET_OS_MACCATALYST
return false;
#endif
}
bool DisplayMtl::supportsEitherGPUFamily(uint8_t iOSFamily, uint8_t macFamily) const
{
return supportsIOSGPUFamily(iOSFamily) || supportsMacGPUFamily(macFamily);
}
} // namespace rx
......@@ -67,8 +67,7 @@ angle::Result RenderbufferMtl::setStorageImpl(const gl::Context *context,
// For emulated channels that GL texture intends to not have,
// we need to initialize their content.
bool emulatedChannels;
mTexture->setColorWritableMask(mtl::GetEmulatedColorWriteMask(mFormat, &emulatedChannels));
bool emulatedChannels = mtl::IsFormatEmulated(mFormat);
if (emulatedChannels)
{
gl::ImageIndex index;
......
......@@ -255,6 +255,7 @@ class TextureMtl : public TextureImpl
const gl::ImageIndex &index,
const MTLRegion &mtlArea,
const gl::InternalFormat &internalFormat,
GLenum type,
const angle::Format &pixelsFormat,
size_t pixelsRowPitch,
const uint8_t *pixels);
......
......@@ -1007,9 +1007,9 @@ angle::Result TextureMtl::setSubImageImpl(const gl::Context *context,
angle::Format::Get(angle::Format::InternalFormatToID(formatInfo.sizedInternalFormat));
// If source pixels are luminance or RGB8, we need to convert them to RGBA
if (mFormat.actualFormatId != srcAngleFormat.id)
if (mFormat.needConversion(srcAngleFormat.id))
{
return convertAndSetSubImage(context, index, mtlRegion, formatInfo, srcAngleFormat,
return convertAndSetSubImage(context, index, mtlRegion, formatInfo, type, srcAngleFormat,
sourceRowPitch, pixels);
}
......@@ -1024,6 +1024,7 @@ angle::Result TextureMtl::convertAndSetSubImage(const gl::Context *context,
const gl::ImageIndex &index,
const MTLRegion &mtlArea,
const gl::InternalFormat &internalFormat,
GLenum type,
const angle::Format &pixelsFormat,
size_t pixelsRowPitch,
const uint8_t *pixels)
......@@ -1034,29 +1035,63 @@ angle::Result TextureMtl::convertAndSetSubImage(const gl::Context *context,
ContextMtl *contextMtl = mtl::GetImpl(context);
// Create scratch buffer
LoadImageFunctionInfo loadFunctionInfo =
mFormat.textureLoadFunctions ? mFormat.textureLoadFunctions(type) : LoadImageFunctionInfo();
const angle::Format &dstFormat = angle::Format::Get(mFormat.actualFormatId);
angle::MemoryBuffer conversionRow;
const size_t dstRowPitch = dstFormat.pixelBytes * mtlArea.size.width;
ANGLE_CHECK_GL_ALLOC(contextMtl, conversionRow.resize(dstRowPitch));
const size_t dstRowPitch = dstFormat.pixelBytes * mtlArea.size.width;
MTLRegion mtlRow = mtlArea;
mtlRow.size.height = 1;
const uint8_t *psrc = pixels;
for (NSUInteger r = 0; r < mtlArea.size.height; ++r, psrc += pixelsRowPitch)
// Check if original image data is compressed:
if (mFormat.intendedAngleFormat().isBlock)
{
mtlRow.origin.y = mtlArea.origin.y + r;
ASSERT(loadFunctionInfo.loadFunction);
// Need to create a buffer to hold entire decompressed image.
const size_t dstDepthPitch = dstRowPitch * mtlArea.size.height;
angle::MemoryBuffer decompressBuf;
ANGLE_CHECK_GL_ALLOC(contextMtl, decompressBuf.resize(dstDepthPitch * mtlArea.size.depth));
// Convert pixels
CopyImageCHROMIUM(psrc, pixelsRowPitch, pixelsFormat.pixelBytes, 0,
pixelsFormat.pixelReadFunction, conversionRow.data(), dstRowPitch,
dstFormat.pixelBytes, 0, dstFormat.pixelWriteFunction,
internalFormat.format, dstFormat.componentType, mtlRow.size.width, 1, 1,
false, false, false);
// Decompress
loadFunctionInfo.loadFunction(mtlArea.size.width, mtlArea.size.height, mtlArea.size.depth,
pixels, pixelsRowPitch, 0, decompressBuf.data(), dstRowPitch,
dstDepthPitch);
// Upload to texture
ANGLE_TRY(UploadTextureContents(context, image, dstFormat, mtlRow, 0, 0,
conversionRow.data(), dstRowPitch));
ANGLE_TRY(UploadTextureContents(context, image, dstFormat, mtlArea, 0, 0,
decompressBuf.data(), dstRowPitch));
} // if (mFormat.intendedAngleFormat().isBlock)
else
{
// Create scratch buffer
angle::MemoryBuffer conversionRow;
ANGLE_CHECK_GL_ALLOC(contextMtl, conversionRow.resize(dstRowPitch));
MTLRegion mtlRow = mtlArea;
mtlRow.size.height = 1;
const uint8_t *psrc = pixels;
for (NSUInteger r = 0; r < mtlArea.size.height; ++r, psrc += pixelsRowPitch)
{
mtlRow.origin.y = mtlArea.origin.y + r;
// Convert pixels
if (loadFunctionInfo.loadFunction)
{
loadFunctionInfo.loadFunction(mtlRow.size.width, 1, 1, psrc, pixelsRowPitch, 0,
conversionRow.data(), dstRowPitch, 0);
}
else
{
CopyImageCHROMIUM(psrc, pixelsRowPitch, pixelsFormat.pixelBytes, 0,
pixelsFormat.pixelReadFunction, conversionRow.data(), dstRowPitch,
dstFormat.pixelBytes, 0, dstFormat.pixelWriteFunction,
internalFormat.format, dstFormat.componentType, mtlRow.size.width,
1, 1, false, false, false);
}
// Upload to texture
ANGLE_TRY(UploadTextureContents(context, image, dstFormat, mtlRow, 0, 0,
conversionRow.data(), dstRowPitch));
}
}
return angle::Result::Continue;
......@@ -1066,10 +1101,7 @@ angle::Result TextureMtl::checkForEmulatedChannels(const gl::Context *context,
const mtl::Format &mtlFormat,
const mtl::TextureRef &texture)
{
bool emulatedChannels = false;
MTLColorWriteMask colorWritableMask =
mtl::GetEmulatedColorWriteMask(mtlFormat, &emulatedChannels);
texture->setColorWritableMask(colorWritableMask);
bool emulatedChannels = mtl::IsFormatEmulated(mtlFormat);
// For emulated channels that GL texture intends to not have,
// we need to initialize their content.
......@@ -1135,7 +1167,7 @@ angle::Result TextureMtl::copySubImageImpl(const gl::Context *context,
ANGLE_TRY(ensureImageCreated(context, index));
if (!mtl::Format::FormatRenderable(mFormat.metalFormat))
if (!mFormat.getCaps().isRenderable())
{
return copySubImageCPU(context, index, modifiedDestOffset, clippedSourceArea,
internalFormat, source);
......@@ -1265,7 +1297,7 @@ angle::Result TextureMtl::copySubTextureImpl(const gl::Context *context,
const mtl::Format &dstFormat = contextMtl->getPixelFormat(
angle::Format::InternalFormatToID(internalFormat.sizedInternalFormat));
if (!mtl::Format::FormatRenderable(dstFormat.metalFormat))
if (!dstFormat.getCaps().isRenderable())
{
return copySubTextureCPU(context, index, destOffset, internalFormat, 0, sourceBox,
srcAngleFormat, unpackFlipY, unpackPremultiplyAlpha,
......
......@@ -13,10 +13,13 @@
#import <Metal/Metal.h>
#include <unordered_map>
#include "common/angleutils.h"
#include "libANGLE/Caps.h"
#include "libANGLE/formatutils.h"
#include "libANGLE/renderer/copyvertex.h"
#include "libANGLE/renderer/renderer_utils.h"
namespace rx
{
......@@ -41,6 +44,19 @@ struct FormatBase
angle::FormatID intendedFormatId = angle::FormatID::NONE;
};
struct FormatCaps
{
bool isRenderable() const { return colorRenderable || depthRenderable; }
bool filterable = false;
bool writable = false;
bool colorRenderable = false;
bool depthRenderable = false;
bool blendable = false;
bool multisample = false; // can be used as MSAA target
bool resolve = false; // Can be used as resolve target
};
// Pixel format
struct Format : public FormatBase
{
......@@ -49,12 +65,31 @@ struct Format : public FormatBase
const gl::InternalFormat &intendedInternalFormat() const;
bool valid() const { return metalFormat != MTLPixelFormatInvalid; }
bool hasDepthAndStencilBits() const
{
return actualAngleFormat().depthBits && actualAngleFormat().stencilBits;
}
bool hasDepthOrStencilBits() const
{
return actualAngleFormat().depthBits || actualAngleFormat().stencilBits;
}
bool isPVRTC() const;
static bool FormatRenderable(MTLPixelFormat format);
static bool FormatCPUReadable(MTLPixelFormat format);
const FormatCaps &getCaps() const { return *caps; }
// Need conversion between source format and this format?
bool needConversion(angle::FormatID srcFormatId) const;
MTLPixelFormat metalFormat = MTLPixelFormatInvalid;
LoadFunctionMap textureLoadFunctions = nullptr;
InitializeTextureDataFunction initFunction = nullptr;
const FormatCaps *caps = nullptr;
bool swizzled = false;
std::array<GLenum, 4> swizzle;
private:
void init(const DisplayMtl *display, angle::FormatID intendedFormatId);
......@@ -70,6 +105,11 @@ struct VertexFormat : public FormatBase
VertexCopyFunction vertexLoadFunction = nullptr;
uint32_t defaultAlpha = 0;
// Intended and actual format have same GL type, and possibly only differ in number of
// components?
bool actualSameGLType = true;
private:
void init(angle::FormatID angleFormatId, bool tightlyPacked = false);
......@@ -86,9 +126,10 @@ class FormatTable final : angle::NonCopyable
void generateTextureCaps(const DisplayMtl *display,
gl::TextureCapsMap *capsMapOut,
std::vector<GLenum> *compressedFormatsOut) const;
std::vector<GLenum> *compressedFormatsOut);
const Format &getPixelFormat(angle::FormatID angleFormatId) const;
const FormatCaps &getNativeFormatCaps(MTLPixelFormat mtlFormat) const;
// tightlyPacked means this format will be used in a tightly packed vertex buffer.
// In that case, it's easier to just convert everything to float to ensure
......@@ -96,10 +137,34 @@ class FormatTable final : angle::NonCopyable
// of how many components each element has.
const VertexFormat &getVertexFormat(angle::FormatID angleFormatId, bool tightlyPacked) const;
uint32_t getMaxSamples() const { return mMaxSamples; }
private:
void initNativeFormatCaps(const DisplayMtl *display);
void setFormatCaps(MTLPixelFormat formatId,
bool filterable,
bool writable,
bool blendable,
bool multisample,
bool resolve,
bool colorRenderable);
void setFormatCaps(MTLPixelFormat formatId,
bool filterable,
bool writable,
bool blendable,
bool multisample,
bool resolve,
bool colorRenderable,
bool depthRenderable);
void setCompressedFormatCaps(MTLPixelFormat formatId, bool filterable);
std::array<Format, angle::kNumANGLEFormats> mPixelFormatTable;
std::unordered_map<MTLPixelFormat, FormatCaps> mNativePixelFormatCapsTable;
// One for tightly packed buffers, one for general cases.
std::array<VertexFormat, angle::kNumANGLEFormats> mVertexFormatTables[2];
uint32_t mMaxSamples;
};
} // namespace mtl
......
......@@ -100,7 +100,7 @@ class Texture final : public Resource,
uint32_t height,
uint32_t mips /** use zero to create full mipmaps chain */,
bool renderTargetOnly,
bool allowTextureView,
bool allowFormatView,
TextureRef *refOut);
static angle::Result MakeCubeTexture(ContextMtl *context,
......@@ -108,7 +108,7 @@ class Texture final : public Resource,
uint32_t size,
uint32_t mips /** use zero to create full mipmaps chain */,
bool renderTargetOnly,
bool allowTextureView,
bool allowFormatView,
TextureRef *refOut);
static angle::Result Make2DMSTexture(ContextMtl *context,
......@@ -117,7 +117,7 @@ class Texture final : public Resource,
uint32_t height,
uint32_t samples,
bool renderTargetOnly,
bool allowTextureView,
bool allowFormatView,
TextureRef *refOut);
static TextureRef MakeFromMetal(id<MTLTexture> metalTexture);
......@@ -125,6 +125,8 @@ class Texture final : public Resource,
// Allow CPU to read & write data directly to this texture?
bool isCPUAccessible() const;
bool supportFormatView() const;
void replaceRegion(ContextMtl *context,
MTLRegion region,
uint32_t mipmapLevel,
......@@ -145,6 +147,9 @@ class Texture final : public Resource,
TextureRef createSliceMipView(uint32_t slice, uint32_t level);
// Create a view with different format
TextureRef createViewWithDifferentFormat(MTLPixelFormat format);
// Same as above but the target format must be compatible, for example sRGB to linear. In this
// case texture doesn't need format view usage flag.
TextureRef createViewWithCompatibleFormat(MTLPixelFormat format);
MTLTextureType textureType() const;
MTLPixelFormat pixelFormat() const;
......@@ -175,12 +180,20 @@ class Texture final : public Resource,
private:
using ParentClass = WrappedObject<id<MTLTexture>>;
static angle::Result MakeTexture(ContextMtl *context,
const Format &mtlFormat,
MTLTextureDescriptor *desc,
uint32_t mips,
bool renderTargetOnly,
bool allowFormatView,
TextureRef *refOut);
Texture(id<MTLTexture> metalTexture);
Texture(ContextMtl *context,
MTLTextureDescriptor *desc,
uint32_t mips,
bool renderTargetOnly,
bool supportTextureView);
bool allowFormatView);
// Create a texture view
Texture(Texture *original, MTLPixelFormat format);
......
......@@ -18,6 +18,7 @@
#include "libANGLE/renderer/metal/DisplayMtl.h"
#include "libANGLE/renderer/metal/mtl_command_buffer.h"
#include "libANGLE/renderer/metal/mtl_format_utils.h"
#include "libANGLE/renderer/metal/mtl_utils.h"
namespace rx
{
......@@ -35,21 +36,12 @@ void SetTextureSwizzle(ContextMtl *context,
MTLTextureDescriptor *textureDescOut)
{
// Texture swizzle functions's declarations are only available if macos 10.15 sdk is present
#if defined(__MAC_10_15)
if (context->getDisplay()->getFeatures().hasTextureSwizzle.enabled)
#if defined(__IPHONE_13_0) || defined(__MAC_10_15)
if (context->getDisplay()->getFeatures().hasTextureSwizzle.enabled && format.swizzled)
{
// Work around Metal doesn't have native support for DXT1 without alpha.
switch (format.intendedFormatId)
{
case angle::FormatID::BC1_RGB_UNORM_BLOCK:
case angle::FormatID::BC1_RGB_UNORM_SRGB_BLOCK:
textureDescOut.swizzle =
MTLTextureSwizzleChannelsMake(MTLTextureSwizzleRed, MTLTextureSwizzleGreen,
MTLTextureSwizzleBlue, MTLTextureSwizzleOne);
break;
default:
break;
}
textureDescOut.swizzle = MTLTextureSwizzleChannelsMake(
GetTextureSwizzle(format.swizzle[0]), GetTextureSwizzle(format.swizzle[1]),
GetTextureSwizzle(format.swizzle[2]), GetTextureSwizzle(format.swizzle[3]));
}
#endif
}
......@@ -97,7 +89,7 @@ angle::Result Texture::Make2DTexture(ContextMtl *context,
uint32_t height,
uint32_t mips,
bool renderTargetOnly,
bool allowTextureView,
bool allowFormatView,
TextureRef *refOut)
{
ANGLE_MTL_OBJC_SCOPE
......@@ -108,16 +100,8 @@ angle::Result Texture::Make2DTexture(ContextMtl *context,
height:height
mipmapped:mips == 0 || mips > 1];
SetTextureSwizzle(context, format, desc);
refOut->reset(new Texture(context, desc, mips, renderTargetOnly, allowTextureView));
return MakeTexture(context, format, desc, mips, renderTargetOnly, allowFormatView, refOut);
} // ANGLE_MTL_OBJC_SCOPE
if (!refOut || !refOut->get())
{
ANGLE_MTL_CHECK(context, false, GL_OUT_OF_MEMORY);
}
return angle::Result::Continue;
}
/** static */
......@@ -126,7 +110,7 @@ angle::Result Texture::MakeCubeTexture(ContextMtl *context,
uint32_t size,
uint32_t mips,
bool renderTargetOnly,
bool allowTextureView,
bool allowFormatView,
TextureRef *refOut)
{
ANGLE_MTL_OBJC_SCOPE
......@@ -135,16 +119,9 @@ angle::Result Texture::MakeCubeTexture(ContextMtl *context,
[MTLTextureDescriptor textureCubeDescriptorWithPixelFormat:format.metalFormat
size:size
mipmapped:mips == 0 || mips > 1];
SetTextureSwizzle(context, format, desc);
refOut->reset(new Texture(context, desc, mips, renderTargetOnly, allowTextureView));
} // ANGLE_MTL_OBJC_SCOPE
if (!refOut || !refOut->get())
{
ANGLE_MTL_CHECK(context, false, GL_OUT_OF_MEMORY);
}
return angle::Result::Continue;
return MakeTexture(context, format, desc, mips, renderTargetOnly, allowFormatView, refOut);
} // ANGLE_MTL_OBJC_SCOPE
}
/** static */
......@@ -154,7 +131,7 @@ angle::Result Texture::Make2DMSTexture(ContextMtl *context,
uint32_t height,
uint32_t samples,
bool renderTargetOnly,
bool allowTextureView,
bool allowFormatView,
TextureRef *refOut)
{
ANGLE_MTL_OBJC_SCOPE
......@@ -167,14 +144,31 @@ angle::Result Texture::Make2DMSTexture(ContextMtl *context,
desc.mipmapLevelCount = 1;
desc.sampleCount = samples;
SetTextureSwizzle(context, format, desc);
refOut->reset(new Texture(context, desc, 1, renderTargetOnly, allowTextureView));
return MakeTexture(context, format, desc, 1, renderTargetOnly, allowFormatView, refOut);
} // ANGLE_MTL_OBJC_SCOPE
}
/** static */
angle::Result Texture::MakeTexture(ContextMtl *context,
const Format &mtlFormat,
MTLTextureDescriptor *desc,
uint32_t mips,
bool renderTargetOnly,
bool allowFormatView,
TextureRef *refOut)
{
SetTextureSwizzle(context, mtlFormat, desc);
refOut->reset(new Texture(context, desc, mips, renderTargetOnly, allowFormatView));
if (!refOut || !refOut->get())
{
ANGLE_MTL_CHECK(context, false, GL_OUT_OF_MEMORY);
}
if (!mtlFormat.hasDepthAndStencilBits())
{
refOut->get()->setColorWritableMask(GetEmulatedColorWriteMask(mtlFormat));
}
return angle::Result::Continue;
}
......@@ -195,7 +189,7 @@ Texture::Texture(ContextMtl *context,
MTLTextureDescriptor *desc,
uint32_t mips,
bool renderTargetOnly,
bool supportTextureView)
bool allowFormatView)
: mColorWritableMask(std::make_shared<MTLColorWriteMask>(MTLColorWriteMaskAll))
{
ANGLE_MTL_OBJC_SCOPE
......@@ -210,14 +204,15 @@ Texture::Texture(ContextMtl *context,
// Every texture will support being rendered for now
desc.usage = 0;
if (Format::FormatRenderable(desc.pixelFormat))
if (context->getNativeFormatCaps(desc.pixelFormat).isRenderable())
{
desc.usage |= MTLTextureUsageRenderTarget;
}
if (!Format::FormatCPUReadable(desc.pixelFormat) ||
if (context->getNativeFormatCaps(desc.pixelFormat).depthRenderable ||
desc.textureType == MTLTextureType2DMultisample)
{
// Metal doesn't support host access to depth stencil texture's data
desc.resourceOptions = MTLResourceStorageModePrivate;
}
......@@ -226,7 +221,7 @@ Texture::Texture(ContextMtl *context,
desc.usage = desc.usage | MTLTextureUsageShaderRead;
}
if (supportTextureView)
if (allowFormatView)
{
desc.usage = desc.usage | MTLTextureUsagePixelFormatView;
}
......@@ -299,6 +294,11 @@ bool Texture::isCPUAccessible() const
return get().storageMode == MTLStorageModeShared;
}
bool Texture::supportFormatView() const
{
return get().usage & MTLTextureUsagePixelFormatView;
}
void Texture::replaceRegion(ContextMtl *context,
MTLRegion region,
uint32_t mipmapLevel,
......@@ -391,6 +391,12 @@ TextureRef Texture::createSliceMipView(uint32_t slice, uint32_t level)
TextureRef Texture::createViewWithDifferentFormat(MTLPixelFormat format)
{
ASSERT(supportFormatView());
return TextureRef(new Texture(this, format));
}
TextureRef Texture::createViewWithCompatibleFormat(MTLPixelFormat format)
{
// No need for ASSERT(supportFormatView());
return TextureRef(new Texture(this, format));
}
......@@ -453,10 +459,10 @@ TextureRef Texture::getLinearColorView()
switch (pixelFormat())
{
case MTLPixelFormatRGBA8Unorm_sRGB:
mLinearColorView = createViewWithDifferentFormat(MTLPixelFormatRGBA8Unorm);
mLinearColorView = createViewWithCompatibleFormat(MTLPixelFormatRGBA8Unorm);
break;
case MTLPixelFormatBGRA8Unorm_sRGB:
mLinearColorView = createViewWithDifferentFormat(MTLPixelFormatBGRA8Unorm);
mLinearColorView = createViewWithCompatibleFormat(MTLPixelFormatBGRA8Unorm);
break;
default:
// NOTE(hqle): Not all sRGB formats are supported yet.
......
......@@ -99,6 +99,10 @@ PrimitiveTopologyClass GetPrimitiveTopologyClass(gl::PrimitiveMode mode);
MTLPrimitiveType GetPrimitiveType(gl::PrimitiveMode mode);
MTLIndexType GetIndexType(gl::DrawElementsType type);
#if defined(__IPHONE_13_0) || defined(__MAC_10_15)
MTLTextureSwizzle GetTextureSwizzle(GLenum swizzle);
#endif
// Get color write mask for a specified format. Some formats such as RGB565 doesn't have alpha
// channel but is emulated by a RGBA8 format, we need to disable alpha write for this format.
// - isFormatEmulated: if the format is emulated, this pointer will store a true value.
......
......@@ -44,36 +44,52 @@ angle::Result InitializeTextureContents(const gl::Context *context,
gl::Extents size = texture->size(index);
// Intialize the content to black
const angle::Format &srcFormat =
angle::Format::Get(intendedInternalFormat.alphaBits > 0 ? angle::FormatID::R8G8B8A8_UNORM
: angle::FormatID::R8G8B8_UNORM);
const size_t srcRowPitch = srcFormat.pixelBytes * size.width;
angle::MemoryBuffer srcRow;
ANGLE_CHECK_GL_ALLOC(contextMtl, srcRow.resize(srcRowPitch));
memset(srcRow.data(), 0, srcRowPitch);
const angle::Format &dstFormat = angle::Format::Get(textureObjFormat.actualFormatId);
const size_t dstRowPitch = dstFormat.pixelBytes * size.width;
angle::MemoryBuffer conversionRow;
ANGLE_CHECK_GL_ALLOC(contextMtl, conversionRow.resize(dstRowPitch));
CopyImageCHROMIUM(srcRow.data(), srcRowPitch, srcFormat.pixelBytes, 0,
srcFormat.pixelReadFunction, conversionRow.data(), dstRowPitch,
dstFormat.pixelBytes, 0, dstFormat.pixelWriteFunction,
intendedInternalFormat.format, dstFormat.componentType, size.width, 1, 1,
false, false, false);
auto mtlRowRegion = MTLRegionMake2D(0, 0, size.width, 1);
for (NSUInteger r = 0; r < static_cast<NSUInteger>(size.height); ++r)
// Initialize the content to black
if (texture->isCPUAccessible() && index.getType() != gl::TextureType::_2DMultisample &&
index.getType() != gl::TextureType::_2DMultisampleArray)
{
mtlRowRegion.origin.y = r;
// Upload to texture
texture->replaceRegion(contextMtl, mtlRowRegion, index.getLevelIndex(),
index.hasLayer() ? index.cubeMapFaceIndex() : 0,
conversionRow.data(), dstRowPitch);
const angle::Format &dstFormat = angle::Format::Get(textureObjFormat.actualFormatId);
const size_t dstRowPitch = dstFormat.pixelBytes * size.width;
angle::MemoryBuffer conversionRow;
ANGLE_CHECK_GL_ALLOC(contextMtl, conversionRow.resize(dstRowPitch));
if (textureObjFormat.initFunction)
{
textureObjFormat.initFunction(size.width, 1, 1, conversionRow.data(), dstRowPitch, 0);
}
else
{
const angle::Format &srcFormat = angle::Format::Get(
intendedInternalFormat.alphaBits > 0 ? angle::FormatID::R8G8B8A8_UNORM
: angle::FormatID::R8G8B8_UNORM);
const size_t srcRowPitch = srcFormat.pixelBytes * size.width;
angle::MemoryBuffer srcRow;
ANGLE_CHECK_GL_ALLOC(contextMtl, srcRow.resize(srcRowPitch));
memset(srcRow.data(), 0, srcRowPitch);
CopyImageCHROMIUM(srcRow.data(), srcRowPitch, srcFormat.pixelBytes, 0,
srcFormat.pixelReadFunction, conversionRow.data(), dstRowPitch,
dstFormat.pixelBytes, 0, dstFormat.pixelWriteFunction,
intendedInternalFormat.format, dstFormat.componentType, size.width, 1,
1, false, false, false);
}
auto mtlRowRegion = MTLRegionMake2D(0, 0, size.width, 1);
for (NSUInteger r = 0; r < static_cast<NSUInteger>(size.height); ++r)
{
mtlRowRegion.origin.y = r;
// Upload to texture
texture->replaceRegion(contextMtl, mtlRowRegion, index.getLevelIndex(),
index.hasLayer() ? index.cubeMapFaceIndex() : 0,
conversionRow.data(), dstRowPitch);
}
}
else
{
ANGLE_TRY(InitializeTextureContentsGPU(context, texture, index, MTLColorWriteMaskAll));
}
return angle::Result::Continue;
......@@ -476,6 +492,30 @@ MTLIndexType GetIndexType(gl::DrawElementsType type)
}
}
#if defined(__IPHONE_13_0) || defined(__MAC_10_15)
MTLTextureSwizzle GetTextureSwizzle(GLenum swizzle)
{
switch (swizzle)
{
case GL_RED:
return MTLTextureSwizzleRed;
case GL_GREEN:
return MTLTextureSwizzleGreen;
case GL_BLUE:
return MTLTextureSwizzleBlue;
case GL_ALPHA:
return MTLTextureSwizzleAlpha;
case GL_ZERO:
return MTLTextureSwizzleZero;
case GL_ONE:
return MTLTextureSwizzleOne;
default:
UNREACHABLE();
return MTLTextureSwizzleZero;
}
}
#endif
MTLColorWriteMask GetEmulatedColorWriteMask(const mtl::Format &mtlFormat, bool *isEmulatedOut)
{
const angle::Format &intendedFormat = mtlFormat.intendedAngleFormat();
......
......@@ -110,6 +110,47 @@ TEST_P(DXT1CompressedTextureTest, CompressedTexImage)
EXPECT_GL_NO_ERROR();
}
// Verify that DXT1 RGB textures have 1.0 alpha when sampled
TEST_P(DXT1CompressedTextureTest, DXT1Alpha)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_texture_compression_dxt1"));
// http://anglebug.com/4917
ANGLE_SKIP_TEST_IF(IsD3D());
GLTexture texture;
glBindTexture(GL_TEXTURE_2D, texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
// Image using pixels with the code for transparent black:
// "BLACK, if color0 <= color1 and code(x,y) == 3"
constexpr uint8_t CompressedImageDXT1[] = {0, 0, 0, 0, 51, 204, 51, 204};
glCompressedTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGB_S3TC_DXT1_EXT, 4, 4, 0,
sizeof(CompressedImageDXT1), CompressedImageDXT1);
EXPECT_GL_NO_ERROR();
glUseProgram(mTextureProgram);
glUniform1i(mTextureUniformLocation, 0);
constexpr GLint kDrawSize = 4;
// The image is one 4x4 block, make the viewport only 4x4.
glViewport(0, 0, kDrawSize, kDrawSize);
drawQuad(mTextureProgram, "position", 0.5f);
EXPECT_GL_NO_ERROR();
for (GLint y = 0; y < kDrawSize; y++)
{
for (GLint x = 0; x < kDrawSize; x++)
{
EXPECT_PIXEL_EQ(x, y, 0, 0, 0, 255) << "at (" << x << ", " << y << ")";
}
}
}
TEST_P(DXT1CompressedTextureTest, CompressedTexStorage)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_texture_compression_dxt1"));
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment