| /* | |
| * Copyright 2015 Google Inc. | |
| * | |
| * Use of this source code is governed by a BSD-style license that can be | |
| * found in the LICENSE file. | |
| */ | |
| #include "src/gpu/ganesh/GrCaps.h" | |
| #include "include/core/SkColor.h" | |
| #include "include/core/SkRect.h" | |
| #include "include/core/SkSize.h" | |
| #include "include/core/SkTextureCompressionType.h" | |
| #include "include/gpu/GpuTypes.h" | |
| #include "include/gpu/ganesh/GrBackendSurface.h" | |
| #include "include/gpu/ganesh/GrContextOptions.h" | |
| #include "include/private/base/SkDebug.h" | |
| #include "include/private/gpu/ganesh/GrTypesPriv.h" | |
| #include "src/core/SkCompressedDataUtils.h" | |
| #include "src/gpu/ganesh/GrBackendUtils.h" | |
| #include "src/gpu/ganesh/GrRenderTargetProxy.h" | |
| #include "src/gpu/ganesh/GrSurface.h" | |
| #include "src/gpu/ganesh/GrSurfaceProxy.h" | |
| #include "src/gpu/ganesh/GrWindowRectangles.h" | |
| GrCaps::GrCaps(const GrContextOptions& options) { | |
| fNPOTTextureTileSupport = false; | |
| fMipmapSupport = false; | |
| fAnisoSupport = false; | |
| fReuseScratchTextures = true; | |
| fReuseScratchBuffers = true; | |
| fGpuTracingSupport = false; | |
| fOversizedStencilSupport = false; | |
| fTextureBarrierSupport = false; | |
| fSampleLocationsSupport = false; | |
| fDrawInstancedSupport = false; | |
| fNativeDrawIndirectSupport = false; | |
| fUseClientSideIndirectBuffers = false; | |
| fConservativeRasterSupport = false; | |
| fWireframeSupport = false; | |
| fMSAAResolvesAutomatically = false; | |
| fPreferDiscardableMSAAAttachment = false; | |
| fUsePrimitiveRestart = false; | |
| fPreferClientSideDynamicBuffers = false; | |
| fPreferFullscreenClears = false; | |
| fTwoSidedStencilRefsAndMasksMustMatch = false; | |
| fMustClearUploadedBufferData = false; | |
| fShouldInitializeTextures = false; | |
| fBuffersAreInitiallyZero = false; | |
| fSupportsAHardwareBufferImages = false; | |
| fSemaphoreSupport = false; | |
| fBackendSemaphoreSupport = false; | |
| fFinishedProcAsyncCallbackSupport = false; | |
| fCrossContextTextureSupport = false; | |
| fHalfFloatVertexAttributeSupport = false; | |
| fDynamicStateArrayGeometryProcessorTextureSupport = false; | |
| fSupportsProtectedContent = false; | |
| fPerformPartialClearsAsDraws = false; | |
| fPerformColorClearsAsDraws = false; | |
| fAvoidLargeIndexBufferDraws = false; | |
| fPerformStencilClearsAsDraws = false; | |
| fTransferFromBufferToTextureSupport = false; | |
| fTransferFromSurfaceToBufferSupport = false; | |
| fTransferFromBufferToBufferSupport = false; | |
| fWritePixelsRowBytesSupport = false; | |
| fTransferPixelsToRowBytesSupport = false; | |
| fReadPixelsRowBytesSupport = false; | |
| fShouldCollapseSrcOverToSrcWhenAble = false; | |
| fMustSyncGpuDuringAbandon = true; | |
| fDisableTessellationPathRenderer = false; | |
| fBlendEquationSupport = kBasic_BlendEquationSupport; | |
| fAdvBlendEqDisableFlags = 0; | |
| fMapBufferFlags = kNone_MapFlags; | |
| fMaxVertexAttributes = 0; | |
| fMaxRenderTargetSize = 1; | |
| fMaxPreferredRenderTargetSize = 1; | |
| fMaxTextureSize = 1; | |
| fMaxWindowRectangles = 0; | |
| fInternalMultisampleCount = 0; | |
| fSuppressPrints = options.fSuppressPrints; | |
| #if defined(GPU_TEST_UTILS) | |
| fWireframeMode = options.fWireframeMode; | |
| #else | |
| fWireframeMode = false; | |
| #endif | |
| fBufferMapThreshold = options.fBufferMapThreshold; | |
| fAvoidStencilBuffers = false; | |
| fAvoidWritePixelsFastPath = false; | |
| fNativeDrawIndexedIndirectIsBroken = false; | |
| fAvoidReorderingRenderTasks = false; | |
| fAvoidDithering = false; | |
| fAvoidLineDraws = false; | |
| fDisablePerspectiveSDFText = false; | |
| fPreferVRAMUseOverFlushes = true; | |
| // Default to true, allow older versions of OpenGL to disable explicitly | |
| fClampToBorderSupport = true; | |
| fDriverBugWorkarounds = options.fDriverBugWorkarounds; | |
| } | |
| void GrCaps::finishInitialization(const GrContextOptions& options) { | |
| if (!fNativeDrawIndirectSupport) { | |
| // We will implement indirect draws with a polyfill, so the commands need to reside in CPU | |
| // memory. | |
| fUseClientSideIndirectBuffers = true; | |
| } | |
| this->applyOptionsOverrides(options); | |
| // Our render targets are always created with textures as the color attachment, hence this min: | |
| fMaxRenderTargetSize = std::min(fMaxRenderTargetSize, fMaxTextureSize); | |
| fMaxPreferredRenderTargetSize = std::min(fMaxPreferredRenderTargetSize, fMaxRenderTargetSize); | |
| this->initSkCaps(this->shaderCaps()); | |
| } | |
| void GrCaps::applyOptionsOverrides(const GrContextOptions& options) { | |
| fShaderCaps->applyOptionsOverrides(options); | |
| this->onApplyOptionsOverrides(options); | |
| if (options.fDisableDriverCorrectnessWorkarounds) { | |
| SkASSERT(!fDisableTessellationPathRenderer); | |
| SkASSERT(!fAvoidStencilBuffers); | |
| SkASSERT(!fAvoidWritePixelsFastPath); | |
| SkASSERT(!fNativeDrawIndexedIndirectIsBroken); | |
| SkASSERT(!fAdvBlendEqDisableFlags); | |
| SkASSERT(!fPerformColorClearsAsDraws); | |
| SkASSERT(!fPerformStencilClearsAsDraws); | |
| // Don't check the partial-clear workaround, since that is a backend limitation, not a | |
| // driver workaround (it just so happens the fallbacks are the same). | |
| } | |
| if (GrContextOptions::Enable::kNo == options.fUseDrawInsteadOfClear) { | |
| fPerformColorClearsAsDraws = false; | |
| fPerformStencilClearsAsDraws = false; | |
| } else if (GrContextOptions::Enable::kYes == options.fUseDrawInsteadOfClear) { | |
| fPerformColorClearsAsDraws = true; | |
| fPerformStencilClearsAsDraws = true; | |
| } | |
| fMaxTextureSize = std::min(fMaxTextureSize, options.fMaxTextureSizeOverride); | |
| #if defined(GPU_TEST_UTILS) | |
| if (options.fSuppressAdvancedBlendEquations) { | |
| fBlendEquationSupport = kBasic_BlendEquationSupport; | |
| } | |
| if (options.fClearAllTextures) { | |
| fShouldInitializeTextures = true; | |
| } | |
| if (options.fDisallowWriteAndTransferPixelRowBytes) { | |
| fWritePixelsRowBytesSupport = false; | |
| fTransferPixelsToRowBytesSupport = false; | |
| } | |
| #endif | |
| if (options.fSuppressMipmapSupport) { | |
| fMipmapSupport = false; | |
| } | |
| if (fMaxWindowRectangles > GrWindowRectangles::kMaxWindows) { | |
| SkDebugf("WARNING: capping window rectangles at %i. HW advertises support for %i.\n", | |
| GrWindowRectangles::kMaxWindows, fMaxWindowRectangles); | |
| fMaxWindowRectangles = GrWindowRectangles::kMaxWindows; | |
| } | |
| fInternalMultisampleCount = options.fInternalMultisampleCount; | |
| fAvoidStencilBuffers = options.fAvoidStencilBuffers; | |
| fDriverBugWorkarounds.applyOverrides(options.fDriverBugWorkarounds); | |
| if (options.fDisableTessellationPathRenderer) { | |
| fDisableTessellationPathRenderer = true; | |
| } | |
| } | |
| #ifdef SK_ENABLE_DUMP_GPU | |
| #include "src/gpu/ganesh/GrTestUtils.h" | |
| #include "src/utils/SkJSONWriter.h" | |
| static SkString map_flags_to_string(uint32_t flags) { | |
| SkString str; | |
| if (GrCaps::kNone_MapFlags == flags) { | |
| str = "none"; | |
| } else { | |
| SkASSERT(GrCaps::kCanMap_MapFlag & flags); | |
| SkDEBUGCODE(flags &= ~GrCaps::kCanMap_MapFlag); | |
| str = "can_map"; | |
| if (GrCaps::kSubset_MapFlag & flags) { | |
| str.append(" partial"); | |
| } else { | |
| str.append(" full"); | |
| } | |
| SkDEBUGCODE(flags &= ~GrCaps::kSubset_MapFlag); | |
| if (GrCaps::kAsyncRead_MapFlag & flags) { | |
| str.append(" async_read"); | |
| } else { | |
| str.append(" sync_read"); | |
| } | |
| SkDEBUGCODE(flags &= ~GrCaps::kAsyncRead_MapFlag); | |
| } | |
| SkASSERT(0 == flags); // Make sure we handled all the flags. | |
| return str; | |
| } | |
| void GrCaps::dumpJSON(SkJSONWriter* writer) const { | |
| writer->beginObject(); | |
| writer->appendBool("NPOT Texture Tile Support", fNPOTTextureTileSupport); | |
| writer->appendBool("MIP Map Support", fMipmapSupport); | |
| writer->appendBool("Aniso Support", fAnisoSupport); | |
| writer->appendBool("Reuse Scratch Textures", fReuseScratchTextures); | |
| writer->appendBool("Reuse Scratch Buffers", fReuseScratchBuffers); | |
| writer->appendBool("Gpu Tracing Support", fGpuTracingSupport); | |
| writer->appendBool("Oversized Stencil Support", fOversizedStencilSupport); | |
| writer->appendBool("Texture Barrier Support", fTextureBarrierSupport); | |
| writer->appendBool("Sample Locations Support", fSampleLocationsSupport); | |
| writer->appendBool("Draw Instanced Support", fDrawInstancedSupport); | |
| writer->appendBool("Native Draw Indirect Support", fNativeDrawIndirectSupport); | |
| writer->appendBool("Use client side indirect buffers", fUseClientSideIndirectBuffers); | |
| writer->appendBool("Conservative Raster Support", fConservativeRasterSupport); | |
| writer->appendBool("Wireframe Support", fWireframeSupport); | |
| writer->appendBool("MSAA Resolves Automatically", fMSAAResolvesAutomatically); | |
| writer->appendBool("Use primitive restart", fUsePrimitiveRestart); | |
| writer->appendBool("Prefer client-side dynamic buffers", fPreferClientSideDynamicBuffers); | |
| writer->appendBool("Prefer fullscreen clears (and stencil discard)", fPreferFullscreenClears); | |
| writer->appendBool("Two-sided Stencil Refs And Masks Must Match", | |
| fTwoSidedStencilRefsAndMasksMustMatch); | |
| writer->appendBool("Must clear buffer memory", fMustClearUploadedBufferData); | |
| writer->appendBool("Should initialize textures", fShouldInitializeTextures); | |
| writer->appendBool("Buffers are initially zero", fBuffersAreInitiallyZero); | |
| writer->appendBool("Supports importing AHardwareBuffers", fSupportsAHardwareBufferImages); | |
| writer->appendBool("Semaphore support", fSemaphoreSupport); | |
| writer->appendBool("Backend Semaphore support", fBackendSemaphoreSupport); | |
| writer->appendBool("FinishedProc async callback support", fFinishedProcAsyncCallbackSupport); | |
| writer->appendBool("Cross context texture support", fCrossContextTextureSupport); | |
| writer->appendBool("Half float vertex attribute support", fHalfFloatVertexAttributeSupport); | |
| writer->appendBool("Specify GeometryProcessor textures as a dynamic state array", | |
| fDynamicStateArrayGeometryProcessorTextureSupport); | |
| writer->appendBool("Supports Protected content", fSupportsProtectedContent); | |
| writer->appendBool("Use draws for partial clears", fPerformPartialClearsAsDraws); | |
| writer->appendBool("Use draws for color clears", fPerformColorClearsAsDraws); | |
| writer->appendBool("Avoid Large IndexBuffer Draws", fAvoidLargeIndexBufferDraws); | |
| writer->appendBool("Use draws for stencil clip clears", fPerformStencilClearsAsDraws); | |
| writer->appendBool("Supports transfers from buffers to textures", | |
| fTransferFromBufferToTextureSupport); | |
| writer->appendBool("Supports transfers from textures to buffers", | |
| fTransferFromSurfaceToBufferSupport); | |
| writer->appendBool("Write pixels row bytes support", fWritePixelsRowBytesSupport); | |
| writer->appendBool("Transfer pixels to row bytes support", fTransferPixelsToRowBytesSupport); | |
| writer->appendBool("Read pixels row bytes support", fReadPixelsRowBytesSupport); | |
| writer->appendBool("Disable TessellationPathRenderer current driver [workaround]", | |
| fDisableTessellationPathRenderer); | |
| writer->appendBool("Clamp-to-border", fClampToBorderSupport); | |
| writer->appendBool("Prefer VRAM Use over flushes [workaround]", fPreferVRAMUseOverFlushes); | |
| writer->appendBool("Avoid stencil buffers [workaround]", fAvoidStencilBuffers); | |
| writer->appendBool("Avoid writePixels fast path [workaround]", fAvoidWritePixelsFastPath); | |
| writer->appendBool("Native draw indexed indirect is broken [workaround]", | |
| fNativeDrawIndexedIndirectIsBroken); | |
| writer->appendBool("Avoid DAG reordering [workaround]", fAvoidReorderingRenderTasks); | |
| writer->appendBool("Avoid Dithering [workaround]", fAvoidDithering); | |
| writer->appendBool("Disable perspective SDF Text [workaround]", fDisablePerspectiveSDFText); | |
| if (this->advancedBlendEquationSupport()) { | |
| writer->appendHexU32("Advanced Blend Equation Disable Flags", fAdvBlendEqDisableFlags); | |
| } | |
| writer->appendS32("Max Vertex Attributes", fMaxVertexAttributes); | |
| writer->appendS32("Max Texture Size", fMaxTextureSize); | |
| writer->appendS32("Max Render Target Size", fMaxRenderTargetSize); | |
| writer->appendS32("Max Preferred Render Target Size", fMaxPreferredRenderTargetSize); | |
| writer->appendS32("Max Window Rectangles", fMaxWindowRectangles); | |
| writer->appendS32("Sample Count for Internal MSAA", fInternalMultisampleCount); | |
| static const char* kBlendEquationSupportNames[] = { | |
| "Basic", | |
| "Advanced", | |
| "Advanced Coherent", | |
| }; | |
| static_assert(0 == kBasic_BlendEquationSupport); | |
| static_assert(1 == kAdvanced_BlendEquationSupport); | |
| static_assert(2 == kAdvancedCoherent_BlendEquationSupport); | |
| static_assert(std::size(kBlendEquationSupportNames) == kLast_BlendEquationSupport + 1); | |
| writer->appendCString("Blend Equation Support", | |
| kBlendEquationSupportNames[fBlendEquationSupport]); | |
| writer->appendString("Map Buffer Support", map_flags_to_string(fMapBufferFlags)); | |
| this->onDumpJSON(writer); | |
| writer->appendName("shaderCaps"); | |
| this->shaderCaps()->dumpJSON(writer); | |
| writer->endObject(); | |
| } | |
| #else | |
| void GrCaps::dumpJSON(SkJSONWriter* writer) const { } | |
| #endif | |
| bool GrCaps::surfaceSupportsWritePixels(const GrSurface* surface) const { | |
| return surface->readOnly() ? false : this->onSurfaceSupportsWritePixels(surface); | |
| } | |
| bool GrCaps::canCopySurface(const GrSurfaceProxy* dst, const SkIRect& dstRect, | |
| const GrSurfaceProxy* src, const SkIRect& srcRect) const { | |
| if (dst->readOnly()) { | |
| return false; | |
| } | |
| if (dst->backendFormat() != src->backendFormat()) { | |
| return false; | |
| } | |
| // For simplicity, all GrGpu::copySurface() calls can assume that srcRect and dstRect | |
| // are already contained within their respective surfaces. | |
| if (!SkIRect::MakeSize(dst->dimensions()).contains(dstRect) || | |
| !SkIRect::MakeSize(src->dimensions()).contains(srcRect)) { | |
| return false; | |
| } | |
| return this->onCanCopySurface(dst, dstRect, src, srcRect); | |
| } | |
| bool GrCaps::validateSurfaceParams(const SkISize& dimensions, const GrBackendFormat& format, | |
| GrRenderable renderable, int renderTargetSampleCnt, | |
| skgpu::Mipmapped mipped, GrTextureType textureType) const { | |
| if (textureType != GrTextureType::kNone) { | |
| if (!this->isFormatTexturable(format, textureType)) { | |
| return false; | |
| } | |
| } | |
| if (skgpu::Mipmapped::kYes == mipped && !this->mipmapSupport()) { | |
| return false; | |
| } | |
| if (dimensions.width() < 1 || dimensions.height() < 1) { | |
| return false; | |
| } | |
| if (renderable == GrRenderable::kYes) { | |
| if (!this->isFormatRenderable(format, renderTargetSampleCnt)) { | |
| return false; | |
| } | |
| int maxRTSize = this->maxRenderTargetSize(); | |
| if (dimensions.width() > maxRTSize || dimensions.height() > maxRTSize) { | |
| return false; | |
| } | |
| } else { | |
| // We currently do not support multisampled textures | |
| if (renderTargetSampleCnt != 1) { | |
| return false; | |
| } | |
| int maxSize = this->maxTextureSize(); | |
| if (dimensions.width() > maxSize || dimensions.height() > maxSize) { | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| GrCaps::SupportedRead GrCaps::supportedReadPixelsColorType(GrColorType srcColorType, | |
| const GrBackendFormat& srcFormat, | |
| GrColorType dstColorType) const { | |
| SupportedRead read = this->onSupportedReadPixelsColorType(srcColorType, srcFormat, | |
| dstColorType); | |
| // There are known problems with 24 vs 32 bit BPP with this color type. Just fail for now if | |
| // using a transfer buffer. | |
| if (GrColorType::kRGB_888x == read.fColorType) { | |
| read.fOffsetAlignmentForTransferBuffer = 0; | |
| } | |
| // It's very convenient to access 1 byte-per-channel 32 bit color types as uint32_t on the CPU. | |
| // Make those aligned reads out of the buffer even if the underlying API doesn't require it. | |
| auto channelFlags = GrColorTypeChannelFlags(read.fColorType); | |
| if ((channelFlags == kRGBA_SkColorChannelFlags || channelFlags == kRGB_SkColorChannelFlags || | |
| channelFlags == kAlpha_SkColorChannelFlag || channelFlags == kGray_SkColorChannelFlag) && | |
| GrColorTypeBytesPerPixel(read.fColorType) == 4) { | |
| switch (read.fOffsetAlignmentForTransferBuffer & 0b11) { | |
| // offset alignment already a multiple of 4 | |
| case 0: | |
| break; | |
| // offset alignment is a multiple of 2 but not 4. | |
| case 2: | |
| read.fOffsetAlignmentForTransferBuffer *= 2; | |
| break; | |
| // offset alignment is not a multiple of 2. | |
| default: | |
| read.fOffsetAlignmentForTransferBuffer *= 4; | |
| break; | |
| } | |
| } | |
| return read; | |
| } | |
| GrBackendFormat GrCaps::getDefaultBackendFormat(GrColorType colorType, | |
| GrRenderable renderable) const { | |
| // Unknown color types are always an invalid format, so early out before calling virtual. | |
| if (colorType == GrColorType::kUnknown) { | |
| return {}; | |
| } | |
| auto format = this->onGetDefaultBackendFormat(colorType); | |
| if (!this->isFormatTexturable(format, GrTextureType::k2D)) { | |
| return {}; | |
| } | |
| if (!this->areColorTypeAndFormatCompatible(colorType, format)) { | |
| return {}; | |
| } | |
| // Currently we require that it be possible to write pixels into the "default" format. Perhaps, | |
| // that could be a separate requirement from the caller. It seems less necessary if | |
| // renderability was requested. | |
| if (this->supportedWritePixelsColorType(colorType, format, colorType).fColorType == | |
| GrColorType::kUnknown) { | |
| return {}; | |
| } | |
| if (renderable == GrRenderable::kYes && | |
| !this->isFormatAsColorTypeRenderable(colorType, format)) { | |
| return {}; | |
| } | |
| return format; | |
| } | |
| bool GrCaps::areColorTypeAndFormatCompatible(GrColorType grCT, | |
| const GrBackendFormat& format) const { | |
| if (GrColorType::kUnknown == grCT) { | |
| return false; | |
| } | |
| SkTextureCompressionType compression = GrBackendFormatToCompressionType(format); | |
| if (compression != SkTextureCompressionType::kNone) { | |
| return grCT == (SkTextureCompressionTypeIsOpaque(compression) ? GrColorType::kRGB_888x | |
| : GrColorType::kRGBA_8888); | |
| } | |
| return this->onAreColorTypeAndFormatCompatible(grCT, format); | |
| } | |
| skgpu::Swizzle GrCaps::getReadSwizzle(const GrBackendFormat& format, GrColorType colorType) const { | |
| SkTextureCompressionType compression = GrBackendFormatToCompressionType(format); | |
| if (compression != SkTextureCompressionType::kNone) { | |
| if (colorType == GrColorType::kRGB_888x || colorType == GrColorType::kRGBA_8888) { | |
| return skgpu::Swizzle::RGBA(); | |
| } | |
| SkDEBUGFAILF("Illegal color type (%d) and compressed format (%d) combination.", | |
| (int)colorType, (int)compression); | |
| return {}; | |
| } | |
| return this->onGetReadSwizzle(format, colorType); | |
| } | |
| bool GrCaps::isFormatCompressed(const GrBackendFormat& format) const { | |
| return GrBackendFormatToCompressionType(format) != SkTextureCompressionType::kNone; | |
| } | |
| GrDstSampleFlags GrCaps::getDstSampleFlagsForProxy(const GrRenderTargetProxy* rt, | |
| bool drawUsesMSAA) const { | |
| SkASSERT(rt); | |
| if (this->textureBarrierSupport() && (!drawUsesMSAA || this->msaaResolvesAutomatically())) { | |
| return this->onGetDstSampleFlagsForProxy(rt); | |
| } | |
| return GrDstSampleFlags::kNone; | |
| } | |
| bool GrCaps::supportsDynamicMSAA(const GrRenderTargetProxy* rtProxy) const { | |
| return rtProxy->numSamples() == 1 && | |
| this->internalMultisampleCount(rtProxy->backendFormat()) > 1 && | |
| this->onSupportsDynamicMSAA(rtProxy); | |
| } | |
| static inline GrColorType color_type_fallback(GrColorType ct) { | |
| switch (ct) { | |
| // kRGBA_8888 is our default fallback for many color types that may not have renderable | |
| // backend formats. | |
| case GrColorType::kAlpha_8: | |
| case GrColorType::kBGR_565: | |
| case GrColorType::kRGB_565: | |
| case GrColorType::kABGR_4444: | |
| case GrColorType::kBGRA_8888: | |
| case GrColorType::kRGBA_1010102: | |
| case GrColorType::kBGRA_1010102: | |
| case GrColorType::kRGBA_F16: | |
| case GrColorType::kRGBA_F16_Clamped: | |
| return GrColorType::kRGBA_8888; | |
| case GrColorType::kAlpha_F16: | |
| return GrColorType::kRGBA_F16; | |
| case GrColorType::kGray_8: | |
| case GrColorType::kRGB_F16F16F16x: | |
| case GrColorType::kRGB_101010x: | |
| return GrColorType::kRGB_888x; | |
| default: | |
| return GrColorType::kUnknown; | |
| } | |
| } | |
| std::tuple<GrColorType, GrBackendFormat> GrCaps::getFallbackColorTypeAndFormat( | |
| GrColorType ct, | |
| int sampleCnt) const { | |
| do { | |
| auto format = this->getDefaultBackendFormat(ct, GrRenderable::kYes); | |
| // We continue to the fallback color type if there no default renderable format or we | |
| // requested msaa and the format doesn't support msaa. | |
| if (format.isValid() && this->isFormatRenderable(format, sampleCnt)) { | |
| return {ct, format}; | |
| } | |
| ct = color_type_fallback(ct); | |
| } while (ct != GrColorType::kUnknown); | |
| return {GrColorType::kUnknown, {}}; | |
| } |