| 1 | /* |
| 2 | * Copyright 2015 Google Inc. |
| 3 | * |
| 4 | * Use of this source code is governed by a BSD-style license that can be |
| 5 | * found in the LICENSE file. |
| 6 | */ |
| 7 | |
| 8 | #include "include/gpu/GrBackendSurface.h" |
| 9 | #include "include/gpu/GrContextOptions.h" |
| 10 | #include "include/private/GrTypesPriv.h" |
| 11 | #include "src/gpu/GrBackendUtils.h" |
| 12 | #include "src/gpu/GrCaps.h" |
| 13 | #include "src/gpu/GrSurface.h" |
| 14 | #include "src/gpu/GrSurfaceProxy.h" |
| 15 | #include "src/gpu/GrWindowRectangles.h" |
| 16 | #include "src/utils/SkJSONWriter.h" |
| 17 | |
| 18 | GrCaps::GrCaps(const GrContextOptions& options) { |
| 19 | fMipmapSupport = false; |
| 20 | fNPOTTextureTileSupport = false; |
| 21 | fReuseScratchTextures = true; |
| 22 | fReuseScratchBuffers = true; |
| 23 | fGpuTracingSupport = false; |
| 24 | fOversizedStencilSupport = false; |
| 25 | fTextureBarrierSupport = false; |
| 26 | fSampleLocationsSupport = false; |
| 27 | fMultisampleDisableSupport = false; |
| 28 | fDrawInstancedSupport = false; |
| 29 | fNativeDrawIndirectSupport = false; |
| 30 | fUseClientSideIndirectBuffers = false; |
| 31 | fMixedSamplesSupport = false; |
| 32 | fConservativeRasterSupport = false; |
| 33 | fWireframeSupport = false; |
| 34 | fMSAAResolvesAutomatically = false; |
| 35 | fUsePrimitiveRestart = false; |
| 36 | fPreferClientSideDynamicBuffers = false; |
| 37 | fPreferFullscreenClears = false; |
| 38 | fTwoSidedStencilRefsAndMasksMustMatch = false; |
| 39 | fMustClearUploadedBufferData = false; |
| 40 | fShouldInitializeTextures = false; |
| 41 | fSupportsAHardwareBufferImages = false; |
| 42 | fFenceSyncSupport = false; |
| 43 | fSemaphoreSupport = false; |
| 44 | fCrossContextTextureSupport = false; |
| 45 | fHalfFloatVertexAttributeSupport = false; |
| 46 | fDynamicStateArrayGeometryProcessorTextureSupport = false; |
| 47 | fPerformPartialClearsAsDraws = false; |
| 48 | fPerformColorClearsAsDraws = false; |
| 49 | fAvoidLargeIndexBufferDraws = false; |
| 50 | fPerformStencilClearsAsDraws = false; |
| 51 | fAllowCoverageCounting = false; |
| 52 | fTransferFromBufferToTextureSupport = false; |
| 53 | fTransferFromSurfaceToBufferSupport = false; |
| 54 | fWritePixelsRowBytesSupport = false; |
| 55 | fReadPixelsRowBytesSupport = false; |
| 56 | fShouldCollapseSrcOverToSrcWhenAble = false; |
| 57 | fDriverDisableCCPR = false; |
| 58 | fDriverDisableMSAACCPR = false; |
| 59 | |
| 60 | fBlendEquationSupport = kBasic_BlendEquationSupport; |
| 61 | fAdvBlendEqDisableFlags = 0; |
| 62 | |
| 63 | fMapBufferFlags = kNone_MapFlags; |
| 64 | |
| 65 | fMaxVertexAttributes = 0; |
| 66 | fMaxRenderTargetSize = 1; |
| 67 | fMaxPreferredRenderTargetSize = 1; |
| 68 | fMaxTextureSize = 1; |
| 69 | fMaxWindowRectangles = 0; |
| 70 | fInternalMultisampleCount = 0; |
| 71 | |
| 72 | fSuppressPrints = options.fSuppressPrints; |
| 73 | #if GR_TEST_UTILS |
| 74 | fWireframeMode = options.fWireframeMode; |
| 75 | #else |
| 76 | fWireframeMode = false; |
| 77 | #endif |
| 78 | fBufferMapThreshold = options.fBufferMapThreshold; |
| 79 | fAvoidStencilBuffers = false; |
| 80 | fAvoidWritePixelsFastPath = false; |
| 81 | fRequiresManualFBBarrierAfterTessellatedStencilDraw = false; |
| 82 | fNativeDrawIndexedIndirectIsBroken = false; |
| 83 | |
| 84 | fPreferVRAMUseOverFlushes = true; |
| 85 | |
| 86 | fPreferTrianglesOverSampleMask = false; |
| 87 | |
| 88 | // Default to true, allow older versions of OpenGL to disable explicitly |
| 89 | fClampToBorderSupport = true; |
| 90 | |
| 91 | fDriverBugWorkarounds = options.fDriverBugWorkarounds; |
| 92 | } |
| 93 | |
| 94 | void GrCaps::finishInitialization(const GrContextOptions& options) { |
| 95 | if (fMixedSamplesSupport) { |
| 96 | // We need multisample disable and dual source blending in order to support mixed samples. |
| 97 | fMixedSamplesSupport = this->multisampleDisableSupport() && |
| 98 | this->shaderCaps()->dualSourceBlendingSupport(); |
| 99 | } |
| 100 | |
| 101 | if (!fNativeDrawIndirectSupport) { |
| 102 | // We will implement indirect draws with a polyfill, so the commands need to reside in CPU |
| 103 | // memory. |
| 104 | fUseClientSideIndirectBuffers = true; |
| 105 | } |
| 106 | |
| 107 | // Overrides happen last. |
| 108 | this->applyOptionsOverrides(options); |
| 109 | } |
| 110 | |
| 111 | void GrCaps::applyOptionsOverrides(const GrContextOptions& options) { |
| 112 | fShaderCaps->applyOptionsOverrides(options); |
| 113 | this->onApplyOptionsOverrides(options); |
| 114 | if (options.fDisableDriverCorrectnessWorkarounds) { |
| 115 | SkASSERT(!fDriverDisableCCPR); |
| 116 | SkASSERT(!fDriverDisableMSAACCPR); |
| 117 | SkASSERT(!fAvoidStencilBuffers); |
| 118 | SkASSERT(!fAvoidWritePixelsFastPath); |
| 119 | SkASSERT(!fRequiresManualFBBarrierAfterTessellatedStencilDraw); |
| 120 | SkASSERT(!fNativeDrawIndexedIndirectIsBroken); |
| 121 | SkASSERT(!fAdvBlendEqDisableFlags); |
| 122 | SkASSERT(!fPerformColorClearsAsDraws); |
| 123 | SkASSERT(!fPerformStencilClearsAsDraws); |
| 124 | // Don't check the partial-clear workaround, since that is a backend limitation, not a |
| 125 | // driver workaround (it just so happens the fallbacks are the same). |
| 126 | } |
| 127 | if (GrContextOptions::Enable::kNo == options.fUseDrawInsteadOfClear) { |
| 128 | fPerformColorClearsAsDraws = false; |
| 129 | fPerformStencilClearsAsDraws = false; |
| 130 | } else if (GrContextOptions::Enable::kYes == options.fUseDrawInsteadOfClear) { |
| 131 | fPerformColorClearsAsDraws = true; |
| 132 | fPerformStencilClearsAsDraws = true; |
| 133 | } |
| 134 | |
| 135 | fAllowCoverageCounting = !options.fDisableCoverageCountingPaths; |
| 136 | |
| 137 | fMaxTextureSize = std::min(fMaxTextureSize, options.fMaxTextureSizeOverride); |
| 138 | fMaxTileSize = fMaxTextureSize; |
| 139 | #if GR_TEST_UTILS |
| 140 | // If the max tile override is zero, it means we should use the max texture size. |
| 141 | if (options.fMaxTileSizeOverride && options.fMaxTileSizeOverride < fMaxTextureSize) { |
| 142 | fMaxTileSize = options.fMaxTileSizeOverride; |
| 143 | } |
| 144 | if (options.fSuppressDualSourceBlending) { |
| 145 | // GrShaderCaps::applyOptionsOverrides already handled the rest; here we just need to make |
| 146 | // sure mixed samples gets disabled if dual source blending is suppressed. |
| 147 | fMixedSamplesSupport = false; |
| 148 | } |
| 149 | if (options.fClearAllTextures) { |
| 150 | fShouldInitializeTextures = true; |
| 151 | } |
| 152 | #endif |
| 153 | |
| 154 | if (fMaxWindowRectangles > GrWindowRectangles::kMaxWindows) { |
| 155 | SkDebugf("WARNING: capping window rectangles at %i. HW advertises support for %i.\n" , |
| 156 | GrWindowRectangles::kMaxWindows, fMaxWindowRectangles); |
| 157 | fMaxWindowRectangles = GrWindowRectangles::kMaxWindows; |
| 158 | } |
| 159 | |
| 160 | fInternalMultisampleCount = options.fInternalMultisampleCount; |
| 161 | |
| 162 | fAvoidStencilBuffers = options.fAvoidStencilBuffers; |
| 163 | |
| 164 | fDriverBugWorkarounds.applyOverrides(options.fDriverBugWorkarounds); |
| 165 | } |
| 166 | |
| 167 | |
| 168 | #ifdef SK_ENABLE_DUMP_GPU |
| 169 | #include "src/gpu/GrTestUtils.h" |
| 170 | |
| 171 | static SkString map_flags_to_string(uint32_t flags) { |
| 172 | SkString str; |
| 173 | if (GrCaps::kNone_MapFlags == flags) { |
| 174 | str = "none" ; |
| 175 | } else { |
| 176 | SkASSERT(GrCaps::kCanMap_MapFlag & flags); |
| 177 | SkDEBUGCODE(flags &= ~GrCaps::kCanMap_MapFlag); |
| 178 | str = "can_map" ; |
| 179 | |
| 180 | if (GrCaps::kSubset_MapFlag & flags) { |
| 181 | str.append(" partial" ); |
| 182 | } else { |
| 183 | str.append(" full" ); |
| 184 | } |
| 185 | SkDEBUGCODE(flags &= ~GrCaps::kSubset_MapFlag); |
| 186 | if (GrCaps::kAsyncRead_MapFlag & flags) { |
| 187 | str.append(" async_read" ); |
| 188 | } else { |
| 189 | str.append(" sync_read" ); |
| 190 | } |
| 191 | SkDEBUGCODE(flags &= ~GrCaps::kAsyncRead_MapFlag); |
| 192 | } |
| 193 | SkASSERT(0 == flags); // Make sure we handled all the flags. |
| 194 | return str; |
| 195 | } |
| 196 | |
| 197 | void GrCaps::dumpJSON(SkJSONWriter* writer) const { |
| 198 | writer->beginObject(); |
| 199 | |
| 200 | writer->appendBool("MIP Map Support" , fMipmapSupport); |
| 201 | writer->appendBool("NPOT Texture Tile Support" , fNPOTTextureTileSupport); |
| 202 | writer->appendBool("Reuse Scratch Textures" , fReuseScratchTextures); |
| 203 | writer->appendBool("Reuse Scratch Buffers" , fReuseScratchBuffers); |
| 204 | writer->appendBool("Gpu Tracing Support" , fGpuTracingSupport); |
| 205 | writer->appendBool("Oversized Stencil Support" , fOversizedStencilSupport); |
| 206 | writer->appendBool("Texture Barrier Support" , fTextureBarrierSupport); |
| 207 | writer->appendBool("Sample Locations Support" , fSampleLocationsSupport); |
| 208 | writer->appendBool("Multisample disable support" , fMultisampleDisableSupport); |
| 209 | writer->appendBool("Draw Instanced Support" , fDrawInstancedSupport); |
| 210 | writer->appendBool("Native Draw Indirect Support" , fNativeDrawIndirectSupport); |
| 211 | writer->appendBool("Use client side indirect buffers" , fUseClientSideIndirectBuffers); |
| 212 | writer->appendBool("Mixed Samples Support" , fMixedSamplesSupport); |
| 213 | writer->appendBool("Conservative Raster Support" , fConservativeRasterSupport); |
| 214 | writer->appendBool("Wireframe Support" , fWireframeSupport); |
| 215 | writer->appendBool("MSAA Resolves Automatically" , fMSAAResolvesAutomatically); |
| 216 | writer->appendBool("Use primitive restart" , fUsePrimitiveRestart); |
| 217 | writer->appendBool("Prefer client-side dynamic buffers" , fPreferClientSideDynamicBuffers); |
| 218 | writer->appendBool("Prefer fullscreen clears (and stencil discard)" , fPreferFullscreenClears); |
| 219 | writer->appendBool("Two-sided Stencil Refs And Masks Must Match" , |
| 220 | fTwoSidedStencilRefsAndMasksMustMatch); |
| 221 | writer->appendBool("Must clear buffer memory" , fMustClearUploadedBufferData); |
| 222 | writer->appendBool("Should initialize textures" , fShouldInitializeTextures); |
| 223 | writer->appendBool("Supports importing AHardwareBuffers" , fSupportsAHardwareBufferImages); |
| 224 | writer->appendBool("Fence sync support" , fFenceSyncSupport); |
| 225 | writer->appendBool("Semaphore support" , fSemaphoreSupport); |
| 226 | writer->appendBool("Cross context texture support" , fCrossContextTextureSupport); |
| 227 | writer->appendBool("Half float vertex attribute support" , fHalfFloatVertexAttributeSupport); |
| 228 | writer->appendBool("Specify GeometryProcessor textures as a dynamic state array" , |
| 229 | fDynamicStateArrayGeometryProcessorTextureSupport); |
| 230 | writer->appendBool("Use draws for partial clears" , fPerformPartialClearsAsDraws); |
| 231 | writer->appendBool("Use draws for color clears" , fPerformColorClearsAsDraws); |
| 232 | writer->appendBool("Avoid Large IndexBuffer Draws" , fAvoidLargeIndexBufferDraws); |
| 233 | writer->appendBool("Use draws for stencil clip clears" , fPerformStencilClearsAsDraws); |
| 234 | writer->appendBool("Allow coverage counting shortcuts" , fAllowCoverageCounting); |
| 235 | writer->appendBool("Supports transfers from buffers to textures" , |
| 236 | fTransferFromBufferToTextureSupport); |
| 237 | writer->appendBool("Supports transfers from textures to buffers" , |
| 238 | fTransferFromSurfaceToBufferSupport); |
| 239 | writer->appendBool("Write pixels row bytes support" , fWritePixelsRowBytesSupport); |
| 240 | writer->appendBool("Read pixels row bytes support" , fReadPixelsRowBytesSupport); |
| 241 | writer->appendBool("Disable CCPR on current driver [workaround]" , fDriverDisableCCPR); |
| 242 | writer->appendBool("Disable MSAA version of CCPR on current driver [workaround]" , |
| 243 | fDriverDisableMSAACCPR); |
| 244 | writer->appendBool("Clamp-to-border" , fClampToBorderSupport); |
| 245 | |
| 246 | writer->appendBool("Prefer VRAM Use over flushes [workaround]" , fPreferVRAMUseOverFlushes); |
| 247 | writer->appendBool("Prefer more triangles over sample mask [MSAA only]" , |
| 248 | fPreferTrianglesOverSampleMask); |
| 249 | writer->appendBool("Avoid stencil buffers [workaround]" , fAvoidStencilBuffers); |
| 250 | writer->appendBool("Avoid writePixels fast path [workaround]" , fAvoidWritePixelsFastPath); |
| 251 | writer->appendBool("Requires manual FB barrier after tessellated stencilDraw [workaround]" , |
| 252 | fRequiresManualFBBarrierAfterTessellatedStencilDraw); |
| 253 | writer->appendBool("Native draw indexed indirect is broken [workaround]" , |
| 254 | fNativeDrawIndexedIndirectIsBroken); |
| 255 | |
| 256 | if (this->advancedBlendEquationSupport()) { |
| 257 | writer->appendHexU32("Advanced Blend Equation Disable Flags" , fAdvBlendEqDisableFlags); |
| 258 | } |
| 259 | |
| 260 | writer->appendS32("Max Vertex Attributes" , fMaxVertexAttributes); |
| 261 | writer->appendS32("Max Texture Size" , fMaxTextureSize); |
| 262 | writer->appendS32("Max Render Target Size" , fMaxRenderTargetSize); |
| 263 | writer->appendS32("Max Preferred Render Target Size" , fMaxPreferredRenderTargetSize); |
| 264 | writer->appendS32("Max Window Rectangles" , fMaxWindowRectangles); |
| 265 | writer->appendS32("Preferred Sample Count for Internal MSAA and Mixed Samples" , |
| 266 | fInternalMultisampleCount); |
| 267 | |
| 268 | static const char* kBlendEquationSupportNames[] = { |
| 269 | "Basic" , |
| 270 | "Advanced" , |
| 271 | "Advanced Coherent" , |
| 272 | }; |
| 273 | static_assert(0 == kBasic_BlendEquationSupport); |
| 274 | static_assert(1 == kAdvanced_BlendEquationSupport); |
| 275 | static_assert(2 == kAdvancedCoherent_BlendEquationSupport); |
| 276 | static_assert(SK_ARRAY_COUNT(kBlendEquationSupportNames) == kLast_BlendEquationSupport + 1); |
| 277 | |
| 278 | writer->appendString("Blend Equation Support" , |
| 279 | kBlendEquationSupportNames[fBlendEquationSupport]); |
| 280 | writer->appendString("Map Buffer Support" , map_flags_to_string(fMapBufferFlags).c_str()); |
| 281 | |
| 282 | this->onDumpJSON(writer); |
| 283 | |
| 284 | writer->appendName("shaderCaps" ); |
| 285 | this->shaderCaps()->dumpJSON(writer); |
| 286 | |
| 287 | writer->endObject(); |
| 288 | } |
| 289 | #else |
| 290 | void GrCaps::dumpJSON(SkJSONWriter* writer) const { } |
| 291 | #endif |
| 292 | |
| 293 | bool GrCaps::surfaceSupportsWritePixels(const GrSurface* surface) const { |
| 294 | return surface->readOnly() ? false : this->onSurfaceSupportsWritePixels(surface); |
| 295 | } |
| 296 | |
| 297 | bool GrCaps::canCopySurface(const GrSurfaceProxy* dst, const GrSurfaceProxy* src, |
| 298 | const SkIRect& srcRect, const SkIPoint& dstPoint) const { |
| 299 | if (dst->readOnly()) { |
| 300 | return false; |
| 301 | } |
| 302 | |
| 303 | if (dst->backendFormat() != src->backendFormat()) { |
| 304 | return false; |
| 305 | } |
| 306 | return this->onCanCopySurface(dst, src, srcRect, dstPoint); |
| 307 | } |
| 308 | |
| 309 | bool GrCaps::validateSurfaceParams(const SkISize& dimensions, const GrBackendFormat& format, |
| 310 | GrRenderable renderable, int renderTargetSampleCnt, |
| 311 | GrMipmapped mipped) const { |
| 312 | if (!this->isFormatTexturable(format)) { |
| 313 | return false; |
| 314 | } |
| 315 | |
| 316 | if (GrMipmapped::kYes == mipped && !this->mipmapSupport()) { |
| 317 | return false; |
| 318 | } |
| 319 | |
| 320 | if (dimensions.width() < 1 || dimensions.height() < 1) { |
| 321 | return false; |
| 322 | } |
| 323 | |
| 324 | if (renderable == GrRenderable::kYes) { |
| 325 | if (!this->isFormatRenderable(format, renderTargetSampleCnt)) { |
| 326 | return false; |
| 327 | } |
| 328 | int maxRTSize = this->maxRenderTargetSize(); |
| 329 | if (dimensions.width() > maxRTSize || dimensions.height() > maxRTSize) { |
| 330 | return false; |
| 331 | } |
| 332 | } else { |
| 333 | // We currently do not support multisampled textures |
| 334 | if (renderTargetSampleCnt != 1) { |
| 335 | return false; |
| 336 | } |
| 337 | int maxSize = this->maxTextureSize(); |
| 338 | if (dimensions.width() > maxSize || dimensions.height() > maxSize) { |
| 339 | return false; |
| 340 | } |
| 341 | } |
| 342 | |
| 343 | return true; |
| 344 | } |
| 345 | |
| 346 | GrCaps::SupportedRead GrCaps::supportedReadPixelsColorType(GrColorType srcColorType, |
| 347 | const GrBackendFormat& srcFormat, |
| 348 | GrColorType dstColorType) const { |
| 349 | SupportedRead read = this->onSupportedReadPixelsColorType(srcColorType, srcFormat, |
| 350 | dstColorType); |
| 351 | |
| 352 | // There are known problems with 24 vs 32 bit BPP with this color type. Just fail for now if |
| 353 | // using a transfer buffer. |
| 354 | if (GrColorType::kRGB_888x == read.fColorType) { |
| 355 | read.fOffsetAlignmentForTransferBuffer = 0; |
| 356 | } |
| 357 | // It's very convenient to access 1 byte-per-channel 32 bit color types as uint32_t on the CPU. |
| 358 | // Make those aligned reads out of the buffer even if the underlying API doesn't require it. |
| 359 | auto channelFlags = GrColorTypeChannelFlags(read.fColorType); |
| 360 | if ((channelFlags == kRGBA_SkColorChannelFlags || channelFlags == kRGB_SkColorChannelFlags || |
| 361 | channelFlags == kAlpha_SkColorChannelFlag || channelFlags == kGray_SkColorChannelFlag) && |
| 362 | GrColorTypeBytesPerPixel(read.fColorType) == 4) { |
| 363 | switch (read.fOffsetAlignmentForTransferBuffer & 0b11) { |
| 364 | // offset alignment already a multiple of 4 |
| 365 | case 0: |
| 366 | break; |
| 367 | // offset alignment is a multiple of 2 but not 4. |
| 368 | case 2: |
| 369 | read.fOffsetAlignmentForTransferBuffer *= 2; |
| 370 | break; |
| 371 | // offset alignment is not a multiple of 2. |
| 372 | default: |
| 373 | read.fOffsetAlignmentForTransferBuffer *= 4; |
| 374 | break; |
| 375 | } |
| 376 | } |
| 377 | return read; |
| 378 | } |
| 379 | |
| 380 | GrBackendFormat GrCaps::getDefaultBackendFormat(GrColorType colorType, |
| 381 | GrRenderable renderable) const { |
| 382 | auto format = this->onGetDefaultBackendFormat(colorType); |
| 383 | if (!this->isFormatTexturable(format)) { |
| 384 | return {}; |
| 385 | } |
| 386 | if (!this->areColorTypeAndFormatCompatible(colorType, format)) { |
| 387 | return {}; |
| 388 | } |
| 389 | // Currently we require that it be possible to write pixels into the "default" format. Perhaps, |
| 390 | // that could be a separate requirement from the caller. It seems less necessary if |
| 391 | // renderability was requested. |
| 392 | if (this->supportedWritePixelsColorType(colorType, format, colorType).fColorType == |
| 393 | GrColorType::kUnknown) { |
| 394 | return {}; |
| 395 | } |
| 396 | if (renderable == GrRenderable::kYes && |
| 397 | !this->isFormatAsColorTypeRenderable(colorType, format)) { |
| 398 | return {}; |
| 399 | } |
| 400 | return format; |
| 401 | } |
| 402 | |
| 403 | bool GrCaps::areColorTypeAndFormatCompatible(GrColorType grCT, |
| 404 | const GrBackendFormat& format) const { |
| 405 | if (GrColorType::kUnknown == grCT) { |
| 406 | return false; |
| 407 | } |
| 408 | |
| 409 | SkImage::CompressionType compression = GrBackendFormatToCompressionType(format); |
| 410 | if (compression != SkImage::CompressionType::kNone) { |
| 411 | return grCT == (SkCompressionTypeIsOpaque(compression) ? GrColorType::kRGB_888x |
| 412 | : GrColorType::kRGBA_8888); |
| 413 | } |
| 414 | |
| 415 | return this->onAreColorTypeAndFormatCompatible(grCT, format); |
| 416 | } |
| 417 | |
| 418 | GrSwizzle GrCaps::getReadSwizzle(const GrBackendFormat& format, GrColorType colorType) const { |
| 419 | SkImage::CompressionType compression = GrBackendFormatToCompressionType(format); |
| 420 | if (compression != SkImage::CompressionType::kNone) { |
| 421 | if (colorType == GrColorType::kRGB_888x || colorType == GrColorType::kRGBA_8888) { |
| 422 | return GrSwizzle::RGBA(); |
| 423 | } |
| 424 | SkDEBUGFAILF("Illegal color type (%d) and compressed format (%d) combination." , colorType, |
| 425 | compression); |
| 426 | return {}; |
| 427 | } |
| 428 | |
| 429 | return this->onGetReadSwizzle(format, colorType); |
| 430 | } |
| 431 | |
| 432 | bool GrCaps::isFormatCompressed(const GrBackendFormat& format) const { |
| 433 | return GrBackendFormatToCompressionType(format) != SkImage::CompressionType::kNone; |
| 434 | } |
| 435 | |
| 436 | |