blob: f7bb5a4e7ab295deddad8e22036be6fde292bf41 [file]
/*
* 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