| /* |
| * Copyright 2025 Google LLC |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "src/gpu/graphite/TextureFormat.h" |
| |
| #include "include/core/SkColor.h" |
| #include "include/core/SkColorType.h" |
| #include "src/core/SkImageInfoPriv.h" |
| |
| namespace skgpu::graphite { |
| |
| using TF = TextureFormat; // for brevity in this file |
| |
| const char* TextureFormatName(TextureFormat format) { |
| switch (format) { |
| case TF::kUnsupported: return "Unsupported"; |
| case TF::kR8: return "R8"; |
| case TF::kR16: return "R16"; |
| case TF::kR16F: return "R16F"; |
| case TF::kR32F: return "R32F"; |
| case TF::kA8: return "A8"; |
| case TF::kRG8: return "RG8"; |
| case TF::kRG16: return "RG16"; |
| case TF::kRG16F: return "RG16F"; |
| case TF::kRG32F: return "RG32F"; |
| case TF::kRGB8: return "RGB8"; |
| case TF::kBGR8: return "BGR8"; |
| case TF::kB5_G6_R5: return "B5_G6_R5"; |
| case TF::kR5_G6_B5: return "R5_G6_B5"; |
| case TF::kRGB16: return "RGB16"; |
| case TF::kRGB16F: return "RGB16F"; |
| case TF::kRGB32F: return "RGB32F"; |
| case TF::kRGB8_sRGB: return "RGB8_sRGB"; |
| case TF::kBGR10_XR: return "BGR10_XR"; |
| case TF::kRGBA8: return "RGBA8"; |
| case TF::kRGBA16: return "RBGA16"; |
| case TF::kRGBA16F: return "RGBA16F"; |
| case TF::kRGBA32F: return "RGBA32F"; |
| case TF::kRGB10_A2: return "RGB10_A2"; |
| case TF::kRGBA10x6: return "RGBA10x6"; |
| case TF::kRGBA8_sRGB: return "RGBA8_sRGB"; |
| case TF::kBGRA8: return "BGRA8"; |
| case TF::kBGR10_A2: return "BGR10_A2"; |
| case TF::kBGRA8_sRGB: return "BGRA8_sRGB"; |
| case TF::kABGR4: return "ABGR4"; |
| case TF::kARGB4: return "ARGB4"; |
| case TF::kBGRA10x6_XR: return "BGRA10x6_XR"; |
| case TF::kRGB8_ETC2: return "RGB8_ETC2"; |
| case TF::kRGB8_ETC2_sRGB: return "RGB8_ETC2_sRGB"; |
| case TF::kRGB8_BC1: return "RGB8_BC1"; |
| case TF::kRGBA8_BC1: return "RGBA8_BC1"; |
| case TF::kRGBA8_BC1_sRGB: return "RGBA8_BC1_sRGB"; |
| case TF::kYUV8_P2_420: return "YUV8_P2_420"; |
| case TF::kYUV8_P3_420: return "YUV8_P3_420"; |
| case TF::kYUV10x6_P2_420: return "YUV10x6_P2_420"; |
| case TF::kExternal: return "External"; |
| case TF::kS8: return "S8"; |
| case TF::kD16: return "D16"; |
| case TF::kD32F: return "D32F"; |
| case TF::kD24_S8: return "D24_S8"; |
| case TF::kD32F_S8: return "D32F_S8"; |
| } |
| SkUNREACHABLE; |
| } |
| |
| SkTextureCompressionType TextureFormatCompressionType(TextureFormat format) { |
| switch (format) { |
| case TF::kRGB8_ETC2: [[fallthrough]]; |
| case TF::kRGB8_ETC2_sRGB: return SkTextureCompressionType::kETC2_RGB8_UNORM; |
| case TF::kRGB8_BC1: return SkTextureCompressionType::kBC1_RGB8_UNORM; |
| case TF::kRGBA8_BC1: [[fallthrough]]; |
| case TF::kRGBA8_BC1_sRGB: return SkTextureCompressionType::kBC1_RGBA8_UNORM; |
| default: return SkTextureCompressionType::kNone; |
| } |
| } |
| |
| TextureFormat CompressionTypeToTextureFormat(SkTextureCompressionType type) { |
| switch (type) { |
| case SkTextureCompressionType::kBC1_RGB8_UNORM: return TF::kRGB8_BC1; |
| case SkTextureCompressionType::kBC1_RGBA8_UNORM: return TF::kRGBA8_BC1; |
| case SkTextureCompressionType::kETC2_RGB8_UNORM: return TF::kRGB8_ETC2; |
| default: return TF::kUnsupported; |
| } |
| } |
| |
| int TextureFormatBytesPerBlock(TextureFormat format) { |
| switch (format) { |
| case TF::kUnsupported: return 0; |
| case TF::kR8: return 1; |
| case TF::kR16: return 2; |
| case TF::kR16F: return 2; |
| case TF::kR32F: return 4; |
| case TF::kA8: return 1; |
| case TF::kRG8: return 2; |
| case TF::kRG16: return 4; |
| case TF::kRG16F: return 4; |
| case TF::kRG32F: return 8; |
| case TF::kRGB8: return 3; |
| case TF::kBGR8: return 3; |
| case TF::kB5_G6_R5: return 2; |
| case TF::kR5_G6_B5: return 2; |
| case TF::kRGB16: return 6; |
| case TF::kRGB16F: return 6; |
| case TF::kRGB32F: return 12; |
| case TF::kRGB8_sRGB: return 3; |
| case TF::kBGR10_XR: return 4; |
| case TF::kRGBA8: return 4; |
| case TF::kRGBA16: return 8; |
| case TF::kRGBA16F: return 8; |
| case TF::kRGBA32F: return 16; |
| case TF::kRGB10_A2: return 4; |
| case TF::kRGBA10x6: return 8; |
| case TF::kRGBA8_sRGB: return 4; |
| case TF::kBGRA8: return 4; |
| case TF::kBGR10_A2: return 4; |
| case TF::kBGRA8_sRGB: return 4; |
| case TF::kABGR4: return 2; |
| case TF::kARGB4: return 2; |
| case TF::kBGRA10x6_XR: return 8; |
| case TF::kS8: return 1; |
| case TF::kD16: return 2; |
| case TF::kD32F: return 4; |
| case TF::kD24_S8: return 4; |
| case TF::kD32F_S8: return 5; // Assuming it's multiplanar |
| |
| // NOTE: For compressed formats, the block size refers to an actual compressed block of |
| // multiple texels, whereas with other formats the block size represents a single pixel. |
| case TF::kRGB8_ETC2: |
| case TF::kRGB8_ETC2_sRGB: |
| case TF::kRGB8_BC1: |
| case TF::kRGBA8_BC1: |
| case TF::kRGBA8_BC1_sRGB: |
| return 8; |
| // NOTE: We don't actually know the size of external formats, so this is an arbitrary value. |
| // We will see external formats only in wrapped SkImages, so this won't impact Skia's |
| // internal budgeting. |
| case TF::kExternal: |
| return 4; |
| // TODO(b/401016699): We are just over estimating this value to be used in gpu size |
| // calculations even though the actually size is probably less. We should instead treat |
| // planar formats similar to compressed textures that go through their own special query for |
| // calculating size. |
| case TF::kYUV8_P2_420: |
| case TF::kYUV8_P3_420: |
| return 3; |
| case TF::kYUV10x6_P2_420: |
| return 6; |
| } |
| SkUNREACHABLE; |
| } |
| |
| uint32_t TextureFormatChannelMask(TextureFormat format) { |
| switch (format) { |
| case TF::kA8: return kAlpha_SkColorChannelFlag; |
| |
| case TF::kR8: [[fallthrough]]; |
| case TF::kR16: |
| case TF::kR16F: |
| case TF::kR32F: return kRed_SkColorChannelFlag; |
| |
| case TF::kRG8: [[fallthrough]]; |
| case TF::kRG16: |
| case TF::kRG16F: |
| case TF::kRG32F: return kRG_SkColorChannelFlags; |
| |
| case TF::kRGB8: [[fallthrough]]; |
| case TF::kBGR8: |
| case TF::kB5_G6_R5: |
| case TF::kR5_G6_B5: |
| case TF::kRGB16: |
| case TF::kRGB16F: |
| case TF::kRGB32F: |
| case TF::kRGB8_sRGB: |
| case TF::kBGR10_XR: |
| case TF::kRGB8_ETC2: |
| case TF::kRGB8_ETC2_sRGB: |
| case TF::kRGB8_BC1: |
| case TF::kYUV8_P2_420: |
| case TF::kYUV8_P3_420: |
| case TF::kYUV10x6_P2_420: return kRGB_SkColorChannelFlags; |
| |
| case TF::kRGBA8: [[fallthrough]]; |
| case TF::kRGBA16: |
| case TF::kRGBA16F: |
| case TF::kRGBA32F: |
| case TF::kRGB10_A2: |
| case TF::kRGBA10x6: |
| case TF::kRGBA8_sRGB: |
| case TF::kBGRA8: |
| case TF::kBGR10_A2: |
| case TF::kBGRA8_sRGB: |
| case TF::kABGR4: |
| case TF::kARGB4: |
| case TF::kBGRA10x6_XR: |
| case TF::kRGBA8_BC1: |
| case TF::kRGBA8_BC1_sRGB: |
| case TF::kExternal: return kRGBA_SkColorChannelFlags; |
| |
| case TF::kS8: [[fallthrough]]; |
| case TF::kD16: |
| case TF::kD32F: |
| case TF::kD24_S8: |
| case TF::kD32F_S8: |
| case TF::kUnsupported: return 0; |
| } |
| SkUNREACHABLE; |
| } |
| |
| bool TextureFormatAutoClamps(TextureFormat format) { |
| // Floating point formats, extended range formats, and non-normalized integer formats do not |
| // auto-clamp. Everything behaves like an unsigned normalized number. |
| return !(TextureFormatIsFloatingPoint(format) || |
| format == TF::kBGR10_XR || |
| format == TF::kBGRA10x6_XR || |
| format == TF::kS8); |
| } |
| |
| bool TextureFormatIsFloatingPoint(TextureFormat format) { |
| switch (format) { |
| // Floating point formats |
| case TF::kR16F: [[fallthrough]]; |
| case TF::kR32F: |
| case TF::kRG16F: |
| case TF::kRG32F: |
| case TF::kRGB16F: |
| case TF::kRGB32F: |
| case TF::kRGBA16F: |
| case TF::kRGBA32F: |
| case TF::kD32F: |
| case TF::kD32F_S8: return true; |
| |
| // Everything else is unorm, unorm-srgb, fixed point, or integral |
| case TF::kUnsupported: [[fallthrough]]; |
| case TF::kR8: |
| case TF::kR16: |
| case TF::kA8: |
| case TF::kRG8: |
| case TF::kRG16: |
| case TF::kRGB8: |
| case TF::kBGR8: |
| case TF::kB5_G6_R5: |
| case TF::kR5_G6_B5: |
| case TF::kRGB16: |
| case TF::kRGB8_sRGB: |
| case TF::kBGR10_XR: |
| case TF::kRGBA8: |
| case TF::kRGBA16: |
| case TF::kRGB10_A2: |
| case TF::kRGBA10x6: |
| case TF::kRGBA8_sRGB: |
| case TF::kBGRA8: |
| case TF::kBGR10_A2: |
| case TF::kBGRA8_sRGB: |
| case TF::kABGR4: |
| case TF::kARGB4: |
| case TF::kBGRA10x6_XR: |
| case TF::kRGB8_ETC2: |
| case TF::kRGB8_ETC2_sRGB: |
| case TF::kRGB8_BC1: |
| case TF::kRGBA8_BC1: |
| case TF::kRGBA8_BC1_sRGB: |
| case TF::kYUV8_P2_420: |
| case TF::kYUV8_P3_420: |
| case TF::kYUV10x6_P2_420: |
| case TF::kExternal: |
| case TF::kS8: |
| case TF::kD16: |
| case TF::kD24_S8: return false; |
| } |
| SkUNREACHABLE; |
| } |
| |
| bool TextureFormatIsDepthOrStencil(TextureFormat format) { |
| switch (format) { |
| case TF::kS8: [[fallthrough]]; |
| case TF::kD16: |
| case TF::kD32F: |
| case TF::kD24_S8: |
| case TF::kD32F_S8: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| bool TextureFormatHasDepth(TextureFormat format) { |
| switch (format) { |
| case TF::kD16: [[fallthrough]]; |
| case TF::kD32F: |
| case TF::kD24_S8: |
| case TF::kD32F_S8: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| bool TextureFormatHasStencil(TextureFormat format) { |
| switch (format) { |
| case TF::kS8: [[fallthrough]]; |
| case TF::kD24_S8: |
| case TF::kD32F_S8: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| bool TextureFormatIsMultiplanar(TextureFormat format) { |
| switch (format) { |
| case TF::kYUV8_P2_420: [[fallthrough]]; |
| case TF::kYUV8_P3_420: |
| case TF::kYUV10x6_P2_420: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| // Supporting implementation details for TextureFormat and SkColorType conversions |
| // ------------------------------------------------------------------------------------------------ |
| |
| Swizzle ReadSwizzleForColorType(SkColorType ct, TextureFormat format) { |
| // TODO(b/390473370): When data transfers can apply an RG swizzle outside of the |
| // SkColorType representation, we should instead apply the swizzle on upload and |
| // preserve the expected order for any GPU use. |
| if (ct == kARGB_4444_SkColorType && format == TextureFormat::kARGB4) { |
| return Swizzle::BGRA(); |
| } |
| |
| uint32_t colorChannels = SkColorTypeChannelFlags(ct); |
| uint32_t formatChannels = TextureFormatChannelMask(format); |
| |
| // Read swizzles only have to handle a few semantics around the sampled values, as any sort of |
| // channel ordering for RGB vs BGR is handled by hardware. All we have to handle is mapping to |
| // "gray", red-vs-alpha, and forcing to opaque. |
| if (SkColorTypeIsAlphaOnly(ct)) { |
| // If the format isn't just an alpha channel (e.g. TF::kA8), we need to adjust |
| if (formatChannels != kAlpha_SkColorChannelFlag) { |
| // If the format has an alpha channel, mask every other channel to 0 |
| if (formatChannels & kAlpha_SkColorChannelFlag) { |
| return Swizzle("000a"); |
| } else { |
| // Otherwise move the red channel to alpha |
| SkASSERT(formatChannels & kRed_SkColorChannelFlag); |
| return Swizzle("000r"); |
| } |
| } else { |
| // otherwise leave as "rgba" and let hardware do the right thing |
| return Swizzle::RGBA(); |
| } |
| } else { |
| // First map gray to rrra; if this is just gray and not gray+alpha, it will also be forced |
| // to opaque below and become rrr1. |
| Swizzle swizzle; |
| if (colorChannels & kGray_SkColorChannelFlag) { |
| SkASSERT(formatChannels & kRed_SkColorChannelFlag); |
| swizzle = Swizzle::RRRA(); |
| } else { |
| swizzle = Swizzle::RGBA(); |
| } |
| |
| // Last, force the alpha to opaque if the color type masks it off but is present in the |
| // texture format. |
| if (!(colorChannels & kAlpha_SkColorChannelFlag) && |
| (formatChannels & kAlpha_SkColorChannelFlag)) { |
| swizzle = Swizzle::Concat(swizzle, Swizzle::RGB1()); |
| } |
| |
| return swizzle; |
| } |
| } |
| |
| std::optional<skgpu::Swizzle> WriteSwizzleForColorType(SkColorType ct, TextureFormat format) { |
| // D/S, compressed, external, and multiplanar formats aren't renderable with a color type. |
| // Format support would mean we never really try to get here in practice, but keep consistent. |
| if (format == TextureFormat::kExternal || |
| TextureFormatIsDepthOrStencil(format) || |
| TextureFormatIsMultiplanar(format) || |
| TextureFormatCompressionType(format) != SkTextureCompressionType::kNone) { |
| return std::nullopt; |
| } |
| |
| // TODO(b/390473370): When data transfers can apply an RG swizzle outside of the |
| // SkColorType representation, we should instead apply the swizzle on upload and |
| // preserve the expected order for any GPU use. |
| if (ct == kARGB_4444_SkColorType && format == TextureFormat::kARGB4) { |
| return Swizzle::BGRA(); |
| } |
| |
| uint32_t colorChannels = SkColorTypeChannelFlags(ct); |
| uint32_t formatChannels = TextureFormatChannelMask(format); |
| |
| // Write swizzles only have to handle a few semantics around the sampled values, as any sort of |
| // channel ordering for RGB vs BGR is handled by hardware. This reduces to just handling red |
| // vs alpha. The other cases for read swizzles do not apply: |
| // - We disallow gray since computing luminance is beyond a swizzle. |
| // - We disallow forcing to opaque since in all cases where we'd do that we have no guarantee |
| // of what the dst pixel's alpha was. In the future, we could support forced-opaque |
| // rendering by always using shader-based blending or by guaranteeing a one-time initialize |
| // draw that forced any alpha channel to 1 (b/489785214). |
| if (SkColorTypeIsAlphaOnly(ct)) { |
| // If the format isn't just an alpha channel (e.g. TF::kA8), we need to adjust |
| if (formatChannels != kAlpha_SkColorChannelFlag) { |
| // If the format has an alpha channel, mask every other channel to 0 |
| if (formatChannels & kAlpha_SkColorChannelFlag) { |
| return Swizzle("000a"); |
| } else { |
| // Otherwise move the alpha channel to red |
| SkASSERT(formatChannels & kRed_SkColorChannelFlag); |
| return Swizzle("a000"); |
| } |
| } else { |
| // otherwise leave as "rgba" and let hardware do the right thing |
| return Swizzle::RGBA(); |
| } |
| } else { |
| if (((colorChannels & formatChannels) != formatChannels) || |
| (colorChannels & kGray_SkColorChannelFlag)) { |
| return std::nullopt; |
| } |
| return Swizzle::RGBA(); |
| } |
| } |
| |
| SkSpan<const TextureFormat> PreferredTextureFormats(SkColorType ct) { |
| #define N(...) std::size({__VA_ARGS__}) |
| #define CASE(C, ...) case C: { \ |
| static const std::array<TextureFormat, N(__VA_ARGS__)> kFormats{{__VA_ARGS__}}; \ |
| return SkSpan(kFormats); } |
| |
| switch (ct) { |
| case kUnknown_SkColorType: return {}; |
| // NOTE: Not all backends support all TextureFormats. Some of the more advanced formats |
| // may not be supported at all and have no viable fallback. For color types that have |
| // equivalent texture formats differing only in RGB vs. BGR swizzle, we allow both |
| // format variations to maximize color types that have some format. For alpha-only color |
| // types, we only match to red-channel formats as they have the broadest support. |
| |
| // SkColorType | TextureFormat(s)... |
| CASE(kAlpha_8_SkColorType, TF::kR8) |
| // NOTE: kRGB_565_SkColorType is misnamed and natively matches B5_G6_R5 |
| CASE(kRGB_565_SkColorType, TF::kB5_G6_R5, TF::kR5_G6_B5) |
| // NOTE: kARGB_4444_SkColorType is misnamed and natively matches ABGR4 |
| CASE(kARGB_4444_SkColorType, TF::kABGR4, TF::kARGB4) |
| CASE(kRGBA_8888_SkColorType, TF::kRGBA8, TF::kBGRA8) |
| CASE(kRGB_888x_SkColorType, TF::kRGB8, TF::kRGBA8, TF::kBGRA8) |
| CASE(kBGRA_8888_SkColorType, TF::kBGRA8, TF::kRGBA8) |
| CASE(kRGBA_1010102_SkColorType, TF::kRGB10_A2, TF::kBGR10_A2) |
| CASE(kBGRA_1010102_SkColorType, TF::kBGR10_A2, TF::kRGB10_A2) |
| CASE(kRGB_101010x_SkColorType, TF::kRGB10_A2, TF::kBGR10_A2) |
| CASE(kBGR_101010x_SkColorType, TF::kBGR10_A2, TF::kRGB10_A2) |
| CASE(kBGR_101010x_XR_SkColorType, TF::kBGR10_XR) |
| CASE(kBGRA_10101010_XR_SkColorType, TF::kBGRA10x6_XR) |
| CASE(kRGBA_10x6_SkColorType, TF::kRGBA10x6) |
| CASE(kGray_8_SkColorType, TF::kR8) |
| CASE(kRGBA_F16Norm_SkColorType, TF::kRGBA16F) |
| CASE(kRGBA_F16_SkColorType, TF::kRGBA16F) |
| CASE(kRGB_F16F16F16x_SkColorType, TF::kRGBA16F) |
| CASE(kRGBA_F32_SkColorType, TF::kRGBA32F) |
| CASE(kR8G8_unorm_SkColorType, TF::kRG8) |
| CASE(kA16_float_SkColorType, TF::kR16F) |
| CASE(kR16_float_SkColorType, TF::kR16F) |
| CASE(kR16G16_float_SkColorType, TF::kRG16F) |
| CASE(kA16_unorm_SkColorType, TF::kR16) |
| CASE(kR16_unorm_SkColorType, TF::kR16) |
| CASE(kR16G16_unorm_SkColorType, TF::kRG16) |
| CASE(kR16G16B16A16_unorm_SkColorType, TF::kRGBA16) |
| CASE(kSRGBA_8888_SkColorType, TF::kRGBA8_sRGB, |
| TF::kBGRA8_sRGB) |
| CASE(kR8_unorm_SkColorType, TF::kR8) |
| } |
| |
| SkUNREACHABLE; |
| #undef CASE |
| #undef N |
| } |
| |
| std::pair<SkColorType, SkEnumBitMask<FormatXferOp>> |
| TextureFormatColorTypeInfo(TextureFormat format) { |
| #define CASE(TF, CT, Ops) case TF: return {CT, Ops}; |
| using X = FormatXferOp; |
| |
| switch (format) { |
| // TextureFormat | SkColorType | FormatXferOp(s) |
| CASE(TF::kUnsupported, kUnknown_SkColorType, X::kDisabled) |
| |
| CASE(TF::kR8, kR8_unorm_SkColorType, X::kIdentity) |
| CASE(TF::kR16, kR16_unorm_SkColorType, X::kIdentity) |
| CASE(TF::kR16F, kR16_float_SkColorType, X::kIdentity) |
| CASE(TF::kR32F, kR16_float_SkColorType, X::kDisabled) |
| CASE(TF::kA8, kAlpha_8_SkColorType, X::kIdentity) |
| CASE(TF::kRG8, kR8G8_unorm_SkColorType, X::kIdentity) |
| CASE(TF::kRG16, kR16G16_unorm_SkColorType, X::kIdentity) |
| CASE(TF::kRG16F, kR16G16_float_SkColorType, X::kIdentity) |
| CASE(TF::kRG32F, kR16G16_float_SkColorType, X::kDisabled) |
| CASE(TF::kRGB8, kRGB_888x_SkColorType, X::kDropAlpha) |
| CASE(TF::kBGR8, kRGB_888x_SkColorType, X::kSwapRB | X::kDropAlpha) |
| // NOTE: kRGB_565_SkColorType is misnamed and natively matches TextureFormat::kB5_G6_R5 |
| CASE(TF::kB5_G6_R5, kRGB_565_SkColorType, X::kIdentity) |
| CASE(TF::kR5_G6_B5, kRGB_565_SkColorType, X::kSwapRB) |
| CASE(TF::kRGB16, kR16G16B16A16_unorm_SkColorType, X::kDropAlpha) |
| CASE(TF::kRGB16F, kRGB_F16F16F16x_SkColorType, X::kDropAlpha) |
| CASE(TF::kRGB32F, kRGBA_F32_SkColorType, X::kDropAlpha) |
| CASE(TF::kRGB8_sRGB, kSRGBA_8888_SkColorType, X::kDropAlpha) |
| CASE(TF::kBGR10_XR, kBGR_101010x_XR_SkColorType, X::kIdentity) |
| CASE(TF::kRGBA8, kRGBA_8888_SkColorType, X::kIdentity) |
| CASE(TF::kRGBA16, kR16G16B16A16_unorm_SkColorType, X::kIdentity) |
| CASE(TF::kRGBA16F, kRGBA_F16_SkColorType, X::kIdentity) |
| CASE(TF::kRGBA32F, kRGBA_F32_SkColorType, X::kIdentity) |
| CASE(TF::kRGB10_A2, kRGBA_1010102_SkColorType, X::kIdentity) |
| CASE(TF::kRGBA10x6, kRGBA_10x6_SkColorType, X::kIdentity) |
| CASE(TF::kRGBA8_sRGB, kSRGBA_8888_SkColorType, X::kIdentity) |
| CASE(TF::kBGRA8, kBGRA_8888_SkColorType, X::kIdentity) |
| CASE(TF::kBGR10_A2, kBGRA_1010102_SkColorType, X::kIdentity) |
| CASE(TF::kBGRA8_sRGB, kSRGBA_8888_SkColorType, X::kSwapRB) |
| // NOTE: kARGB_4444_SkColorType is misnamed and natively matches TextureFormat::kABGR4 |
| CASE(TF::kABGR4, kARGB_4444_SkColorType, X::kIdentity) |
| CASE(TF::kARGB4, kARGB_4444_SkColorType, X::kSwapRB) |
| CASE(TF::kBGRA10x6_XR, kBGRA_10101010_XR_SkColorType, X::kIdentity) |
| |
| // Compressed, multi-planar, and external formats can't exactly describe their data as |
| // an SkColorType (although transfers with specialized data could be allowed). |
| CASE(TF::kRGB8_ETC2, kRGB_888x_SkColorType, X::kDisabled) |
| CASE(TF::kRGB8_ETC2_sRGB, kSRGBA_8888_SkColorType, X::kDisabled) |
| CASE(TF::kRGB8_BC1, kRGB_888x_SkColorType, X::kDisabled) |
| CASE(TF::kRGBA8_BC1, kRGBA_8888_SkColorType, X::kDisabled) |
| CASE(TF::kRGBA8_BC1_sRGB, kSRGBA_8888_SkColorType, X::kDisabled) |
| CASE(TF::kYUV8_P2_420, kRGB_888x_SkColorType, X::kDisabled) |
| CASE(TF::kYUV8_P3_420, kRGB_888x_SkColorType, X::kDisabled) |
| CASE(TF::kYUV10x6_P2_420, kRGBA_10x6_SkColorType, X::kDisabled) |
| CASE(TF::kExternal, kRGBA_8888_SkColorType, X::kDisabled) |
| |
| // Non color texture formats can't be used with SkColorType |
| CASE(TF::kS8, kUnknown_SkColorType, X::kDisabled) |
| CASE(TF::kD16, kUnknown_SkColorType, X::kDisabled) |
| CASE(TF::kD32F, kUnknown_SkColorType, X::kDisabled) |
| CASE(TF::kD24_S8, kUnknown_SkColorType, X::kDisabled) |
| CASE(TF::kD32F_S8, kUnknown_SkColorType, X::kDisabled) |
| } |
| |
| SkUNREACHABLE; |
| #undef CASE |
| } |
| |
| bool AreColorTypeAndFormatCompatible(SkColorType targetColorType, TextureFormat format) { |
| // If the format maps to the color type, they are compatible |
| auto [baseColorType, _] = TextureFormatColorTypeInfo(format); |
| if (baseColorType != kUnknown_SkColorType && baseColorType == targetColorType) { |
| return true; // shortcut |
| } |
| |
| // If the color type could map to the format, they are compatible |
| for (TextureFormat preferred : PreferredTextureFormats(targetColorType)) { |
| if (preferred == format) { |
| return true; |
| } |
| } |
| |
| // Also allow kRGB_888x if kRGBA_8888 is compatible since RGBx is just a swizzle. This is almost |
| // always handled by the combination of base color type and preferred formats, but for external |
| // and compressed formats those two functions aren't quite descriptive enough. |
| if (targetColorType == kRGB_888x_SkColorType && |
| AreColorTypeAndFormatCompatible(kRGBA_8888_SkColorType, format)) { |
| return true; |
| } |
| |
| // Otherwise consider them incompatible |
| return false; |
| } |
| |
| } // namespace skgpu::graphite |