From f151351f47f3702c410b36e569c87cec7ef22537 Mon Sep 17 00:00:00 2001 From: Jim Van Verth Date: Fri, 25 Mar 2022 16:27:50 -0400 Subject: [PATCH] [graphite] Add format tables to MtlCaps Bug: skia:12845 Change-Id: If3ac2b6ba2c8e28328ee5805a29fc83353220364 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/524756 Reviewed-by: Greg Daniel Commit-Queue: Jim Van Verth --- experimental/graphite/src/Caps.h | 17 ++ experimental/graphite/src/mtl/MtlCaps.h | 45 ++++- experimental/graphite/src/mtl/MtlCaps.mm | 242 +++++++++++++++++++++-- include/core/SkColorType.h | 1 + tests/graphite/MtlBackendTextureTest.mm | 4 +- 5 files changed, 286 insertions(+), 23 deletions(-) diff --git a/experimental/graphite/src/Caps.h b/experimental/graphite/src/Caps.h index c7d7c608c9..9bb71a62ae 100644 --- a/experimental/graphite/src/Caps.h +++ b/experimental/graphite/src/Caps.h @@ -12,6 +12,7 @@ #include "include/core/SkImageInfo.h" #include "include/core/SkRefCnt.h" #include "src/gpu/ResourceKey.h" +#include "src/gpu/Swizzle.h" namespace SkSL { struct ShaderCaps; @@ -72,6 +73,22 @@ public: protected: Caps(); + // ColorTypeInfo for a specific format. + // Used in format tables. + struct ColorTypeInfo { + SkColorType fColorType = kUnknown_SkColorType; + enum { + kUploadData_Flag = 0x1, + // Does Graphite itself support rendering to this colorType & format pair. Renderability + // still additionally depends on if the format itself is renderable. + kRenderable_Flag = 0x2, + }; + uint32_t fFlags = 0; + + skgpu::Swizzle fReadSwizzle; + skgpu::Swizzle fWriteSwizzle; + }; + int fMaxTextureSize = 0; size_t fRequiredUniformBufferAlignment = 0; diff --git a/experimental/graphite/src/mtl/MtlCaps.h b/experimental/graphite/src/mtl/MtlCaps.h index 7d070bde9f..5be733fca8 100644 --- a/experimental/graphite/src/mtl/MtlCaps.h +++ b/experimental/graphite/src/mtl/MtlCaps.h @@ -8,10 +8,12 @@ #ifndef skgpu_MtlCaps_DEFINED #define skgpu_MtlCaps_DEFINED -#include "experimental/graphite/src/Caps.h" +#include #import +#include "experimental/graphite/src/Caps.h" + namespace skgpu::mtl { class Caps final : public skgpu::Caps { @@ -69,9 +71,50 @@ private: bool onIsTexturable(const skgpu::TextureInfo&) const override; bool isTexturable(MTLPixelFormat) const; bool isRenderable(MTLPixelFormat, uint32_t numSamples) const; + uint32_t maxRenderTargetSampleCount(MTLPixelFormat) const; size_t getTransferBufferAlignment(size_t bytesPerPixel) const override; + struct FormatInfo { + uint32_t colorTypeFlags(SkColorType colorType) const { + for (int i = 0; i < fColorTypeInfoCount; ++i) { + if (fColorTypeInfos[i].fColorType == colorType) { + return fColorTypeInfos[i].fFlags; + } + } + return 0; + } + + enum { + kTexturable_Flag = 0x1, + kRenderable_Flag = 0x2, // Color attachment and blendable + kMSAA_Flag = 0x4, + kResolve_Flag = 0x8, + }; + static const uint16_t kAllFlags = kTexturable_Flag | kRenderable_Flag | + kMSAA_Flag | kResolve_Flag; + + uint16_t fFlags = 0; + + std::unique_ptr fColorTypeInfos; + int fColorTypeInfoCount = 0; + }; + inline static constexpr size_t kNumMtlFormats = 8; + + static size_t GetFormatIndex(MTLPixelFormat); + FormatInfo fFormatTable[kNumMtlFormats]; + + const FormatInfo& getFormatInfo(const MTLPixelFormat pixelFormat) const { + size_t index = GetFormatIndex(pixelFormat); + return fFormatTable[index]; + } + + MTLPixelFormat fColorTypeToFormatTable[kSkColorTypeCnt]; + void setColorType(SkColorType, std::initializer_list formats); + + // A vector of the viable sample counts (e.g., { 1, 2, 4, 8 }). + std::vector fColorSampleCounts; + GPUFamily fGPUFamily; int fFamilyGroup; }; diff --git a/experimental/graphite/src/mtl/MtlCaps.mm b/experimental/graphite/src/mtl/MtlCaps.mm index a7145b4be7..f4654640c0 100644 --- a/experimental/graphite/src/mtl/MtlCaps.mm +++ b/experimental/graphite/src/mtl/MtlCaps.mm @@ -236,6 +236,16 @@ void Caps::initCaps(const id device) { } else { fClampToBorderSupport = false; } + + // Init sample counts. All devices support 1 (i.e. 0 in skia). + fColorSampleCounts.push_back(1); + if (@available(macOS 10.11, iOS 9.0, *)) { + for (auto sampleCnt : {2, 4, 8}) { + if ([device supportsTextureSampleCount:sampleCnt]) { + fColorSampleCounts.push_back(sampleCnt); + } + } + } } void Caps::initShaderCaps() { @@ -266,8 +276,192 @@ void Caps::initShaderCaps() { shaderCaps->fFloatIs32Bits = true; } +// These are all the valid MTLPixelFormats that we currently support in Skia. They are roughly +// ordered from most frequently used to least to improve look up times in arrays. +static constexpr MTLPixelFormat kMtlFormats[] = { + MTLPixelFormatRGBA8Unorm, + MTLPixelFormatR8Unorm, + MTLPixelFormatA8Unorm, + MTLPixelFormatBGRA8Unorm, + + MTLPixelFormatStencil8, + MTLPixelFormatDepth32Float, + MTLPixelFormatDepth32Float_Stencil8, + + MTLPixelFormatInvalid, +}; + +void Caps::setColorType(SkColorType colorType, std::initializer_list formats) { +#ifdef SK_DEBUG + for (size_t i = 0; i < kNumMtlFormats; ++i) { + const auto& formatInfo = fFormatTable[i]; + for (int j = 0; j < formatInfo.fColorTypeInfoCount; ++j) { + const auto& ctInfo = formatInfo.fColorTypeInfos[j]; + if (ctInfo.fColorType == colorType) { + bool found = false; + for (auto it = formats.begin(); it != formats.end(); ++it) { + if (kMtlFormats[i] == *it) { + found = true; + } + } + SkASSERT(found); + } + } + } +#endif + int idx = static_cast(colorType); + for (auto it = formats.begin(); it != formats.end(); ++it) { + const auto& info = this->getFormatInfo(*it); + for (int i = 0; i < info.fColorTypeInfoCount; ++i) { + if (info.fColorTypeInfos[i].fColorType == colorType) { + fColorTypeToFormatTable[idx] = *it; + return; + } + } + } +} + +size_t Caps::GetFormatIndex(MTLPixelFormat pixelFormat) { + static_assert(SK_ARRAY_COUNT(kMtlFormats) == Caps::kNumMtlFormats, + "Size of kMtlFormats array must match static value in header"); + for (size_t i = 0; i < Caps::kNumMtlFormats; ++i) { + if (kMtlFormats[i] == pixelFormat) { + return i; + } + } + return GetFormatIndex(MTLPixelFormatInvalid); +} + void Caps::initFormatTable() { - // TODO + FormatInfo* info; + + // Format: RGBA8Unorm + { + info = &fFormatTable[GetFormatIndex(MTLPixelFormatRGBA8Unorm)]; + info->fFlags = FormatInfo::kAllFlags; + info->fColorTypeInfoCount = 2; + info->fColorTypeInfos.reset(new ColorTypeInfo[info->fColorTypeInfoCount]()); + int ctIdx = 0; + // Format: RGBA8Unorm, Surface: kRGBA_8888 + { + auto& ctInfo = info->fColorTypeInfos[ctIdx++]; + ctInfo.fColorType = kRGBA_8888_SkColorType; + ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag; + } + // Format: RGBA8Unorm, Surface: kRGB_888x + { + auto& ctInfo = info->fColorTypeInfos[ctIdx++]; + ctInfo.fColorType = kRGB_888x_SkColorType; + ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag; + ctInfo.fReadSwizzle = skgpu::Swizzle::RGB1(); + } + } + + // Format: R8Unorm + { + info = &fFormatTable[GetFormatIndex(MTLPixelFormatR8Unorm)]; + info->fFlags = FormatInfo::kAllFlags; + info->fColorTypeInfoCount = 3; + info->fColorTypeInfos.reset(new ColorTypeInfo[info->fColorTypeInfoCount]()); + int ctIdx = 0; + // Format: R8Unorm, Surface: kR8_unorm + { + auto& ctInfo = info->fColorTypeInfos[ctIdx++]; + ctInfo.fColorType = kR8_unorm_SkColorType; + ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag; + } + // Format: R8Unorm, Surface: kAlpha_8 + { + auto& ctInfo = info->fColorTypeInfos[ctIdx++]; + ctInfo.fColorType = kAlpha_8_SkColorType; + ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag; + ctInfo.fReadSwizzle = skgpu::Swizzle("000r"); + ctInfo.fWriteSwizzle = skgpu::Swizzle("a000"); + } + // Format: R8Unorm, Surface: kGray_8 + { + auto& ctInfo = info->fColorTypeInfos[ctIdx++]; + ctInfo.fColorType = kGray_8_SkColorType; + ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag; + ctInfo.fReadSwizzle = skgpu::Swizzle("rrr1"); + } + } + + // Format: A8Unorm + { + info = &fFormatTable[GetFormatIndex(MTLPixelFormatA8Unorm)]; + info->fFlags = FormatInfo::kTexturable_Flag; + info->fColorTypeInfoCount = 1; + info->fColorTypeInfos.reset(new ColorTypeInfo[info->fColorTypeInfoCount]()); + int ctIdx = 0; + // Format: A8Unorm, Surface: kAlpha_8 + { + auto& ctInfo = info->fColorTypeInfos[ctIdx++]; + ctInfo.fColorType = kAlpha_8_SkColorType; + ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag; + } + } + + // Format: BGRA8Unorm + { + info = &fFormatTable[GetFormatIndex(MTLPixelFormatBGRA8Unorm)]; + info->fFlags = FormatInfo::kAllFlags; + info->fColorTypeInfoCount = 1; + info->fColorTypeInfos.reset(new ColorTypeInfo[info->fColorTypeInfoCount]()); + int ctIdx = 0; + // Format: BGRA8Unorm, Surface: kBGRA_8888 + { + auto& ctInfo = info->fColorTypeInfos[ctIdx++]; + ctInfo.fColorType = kBGRA_8888_SkColorType; + ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag; + } + } + + /* + * Non-color formats + */ + + // Format: Stencil8 + { + info = &fFormatTable[GetFormatIndex(MTLPixelFormatStencil8)]; + info->fFlags = FormatInfo::kMSAA_Flag; + info->fColorTypeInfoCount = 0; + } + + // Format: Depth32Float + { + info = &fFormatTable[GetFormatIndex(MTLPixelFormatDepth32Float)]; + info->fFlags = FormatInfo::kMSAA_Flag; + if (this->isMac() || fFamilyGroup >= 3) { + info->fFlags |= FormatInfo::kResolve_Flag; + } + info->fColorTypeInfoCount = 0; + } + + // Format: Depth32Float_Stencil8 + { + info = &fFormatTable[GetFormatIndex(MTLPixelFormatDepth32Float_Stencil8)]; + info->fFlags = FormatInfo::kMSAA_Flag; + if (this->isMac() || fFamilyGroup >= 3) { + info->fFlags |= FormatInfo::kResolve_Flag; + } + info->fColorTypeInfoCount = 0; + } + + //////////////////////////////////////////////////////////////////////////// + // Map SkColorTypes (used for creating SkSurfaces) to MTLPixelFormats. The order in which the + // formats are passed into the setColorType function indicates the priority in selecting which + // format we use for a given SkColorType. + + std::fill_n(fColorTypeToFormatTable, kSkColorTypeCnt, MTLPixelFormatInvalid); + + this->setColorType(kAlpha_8_SkColorType, { MTLPixelFormatR8Unorm, + MTLPixelFormatA8Unorm }); + this->setColorType(kRGBA_8888_SkColorType, { MTLPixelFormatRGBA8Unorm }); + this->setColorType(kRGB_888x_SkColorType, { MTLPixelFormatRGBA8Unorm }); + this->setColorType(kBGRA_8888_SkColorType, { MTLPixelFormatBGRA8Unorm }); + this->setColorType(kGray_8_SkColorType, { MTLPixelFormatR8Unorm }); + this->setColorType(kR8_unorm_SkColorType, { MTLPixelFormatR8Unorm }); } skgpu::TextureInfo Caps::getDefaultSampledTextureInfo(SkColorType colorType, @@ -356,9 +550,8 @@ bool Caps::onIsTexturable(const skgpu::TextureInfo& info) const { } bool Caps::isTexturable(MTLPixelFormat format) const { - // TODO: Fill out format table so that we can query all formats. For now we only support RGBA8 - // and BGRA8 which is supported everywhere. - return format == MTLPixelFormatRGBA8Unorm || format == MTLPixelFormatBGRA8Unorm; + const FormatInfo& formatInfo = this->getFormatInfo(format); + return SkToBool(FormatInfo::kTexturable_Flag && formatInfo.fFlags); } bool Caps::isRenderable(const skgpu::TextureInfo& info) const { @@ -366,24 +559,33 @@ bool Caps::isRenderable(const skgpu::TextureInfo& info) const { this->isRenderable((MTLPixelFormat)info.mtlTextureSpec().fFormat, info.numSamples()); } -bool Caps::isRenderable(MTLPixelFormat format, uint32_t numSamples) const { - // TODO: Fill out format table so that we can query all formats. For now we only support RGBA8 - // and BGRA8 with a sampleCount of 1 which is supported everywhere. - if ((format != MTLPixelFormatRGBA8Unorm && format != MTLPixelFormatBGRA8Unorm) || - numSamples != 1) { - return false; - } - return true; +bool Caps::isRenderable(MTLPixelFormat format, uint32_t sampleCount) const { + return sampleCount <= this->maxRenderTargetSampleCount(format); } -bool Caps::onAreColorTypeAndTextureInfoCompatible(SkColorType type, - const skgpu::TextureInfo& info) const { - // TODO: Fill out format table so that we can query all formats. For now we only support RGBA8 - // or BGRA8 for both the color type and format. - return (type == kRGBA_8888_SkColorType && - info.mtlTextureSpec().fFormat == MTLPixelFormatRGBA8Unorm) || - (type == kBGRA_8888_SkColorType && - info.mtlTextureSpec().fFormat == MTLPixelFormatBGRA8Unorm); +uint32_t Caps::maxRenderTargetSampleCount(MTLPixelFormat format) const { + const FormatInfo& formatInfo = this->getFormatInfo(format); + if (!SkToBool(formatInfo.fFlags & FormatInfo::kRenderable_Flag)) { + return 0; + } + if (SkToBool(formatInfo.fFlags & FormatInfo::kMSAA_Flag)) { + return fColorSampleCounts[fColorSampleCounts.size() - 1]; + } else { + return 1; + } +} + +bool Caps::onAreColorTypeAndTextureInfoCompatible(SkColorType ct, + const skgpu::TextureInfo& textureInfo) const { + MTLPixelFormat mtlFormat = static_cast(textureInfo.mtlTextureSpec().fFormat); + + const auto& info = this->getFormatInfo(mtlFormat); + for (int i = 0; i < info.fColorTypeInfoCount; ++i) { + if (info.fColorTypeInfos[i].fColorType == ct) { + return true; + } + } + return false; } size_t Caps::getTransferBufferAlignment(size_t bytesPerPixel) const { diff --git a/include/core/SkColorType.h b/include/core/SkColorType.h index ca57ff94c6..b51aa7b96f 100644 --- a/include/core/SkColorType.h +++ b/include/core/SkColorType.h @@ -61,5 +61,6 @@ enum SkColorType : int { #error "SK_*32_SHIFT values must correspond to BGRA or RGBA byte order" #endif }; +static constexpr int kSkColorTypeCnt = static_cast(kLastEnum_SkColorType) + 1; #endif diff --git a/tests/graphite/MtlBackendTextureTest.mm b/tests/graphite/MtlBackendTextureTest.mm index aa8c6ba78e..ed5d393adb 100644 --- a/tests/graphite/MtlBackendTextureTest.mm +++ b/tests/graphite/MtlBackendTextureTest.mm @@ -42,8 +42,8 @@ DEF_GRAPHITE_TEST_FOR_CONTEXTS(MtlBackendTextureTest, reporter, context) { REPORTER_ASSERT(reporter, beTexture.isValid()); context->deleteBackendTexture(beTexture); - // It should fail with a format that isn't rgba8 - textureInfo.fFormat = MTLPixelFormatR8Unorm; + // It should fail with a format that isn't one of our supported formats + textureInfo.fFormat = MTLPixelFormatRGB9E5Float; beTexture = context->createBackendTexture(kSize, textureInfo); REPORTER_ASSERT(reporter, !beTexture.isValid()); context->deleteBackendTexture(beTexture);