From cc13419addb1ca867a992f96ff4455e02cb829be Mon Sep 17 00:00:00 2001 From: Eric Karl Date: Tue, 16 May 2017 10:10:40 -0700 Subject: [PATCH] Add flag to avoid stencil buffers in Skia Certain systems experience a leak in the GL driver associated with stencil buffers. Attempts to avoid the leak (while still using stencil buffers) dind't succeed. This patch adds a GrContextOption fAvoidStencilBuffers. This disables certain path rendering modes, as well as stencil based masking/clipping. NOTREECHECKS=true NOTRY=true NOPRESUBMIT=true Bug: 713854 Change-Id: Ifa6c0f2bd5ee395547bda9165d6c79d197ae8b8b Reviewed-On: https://skia-review.googlesource.com/15253 Commit-Queue: Eric Karl Reviewed-By: Eric Karl Reviewed-By: Brian Salomon Reviewed-on: https://skia-review.googlesource.com/17081 Commit-Queue: Eric Karl --- include/gpu/GrCaps.h | 3 + include/gpu/GrContextOptions.h | 6 ++ src/gpu/GrCaps.cpp | 2 + src/gpu/GrClipStackClip.cpp | 24 ++++-- src/gpu/GrGpu.cpp | 11 ++- src/gpu/GrPathRenderer.h | 8 +- src/gpu/GrReducedClip.cpp | 2 +- src/gpu/GrRenderTargetContext.cpp | 5 +- src/gpu/GrSoftwarePathRenderer.cpp | 4 +- src/gpu/gl/GrGLCaps.cpp | 104 +++++++++++++------------- src/gpu/gl/GrGLCaps.h | 5 +- src/gpu/gl/GrGLRenderTarget.cpp | 5 ++ src/gpu/gl/GrGLTextureRenderTarget.cpp | 5 +- src/gpu/ops/GrAAConvexPathRenderer.cpp | 6 +- src/gpu/ops/GrAAHairLinePathRenderer.cpp | 2 +- src/gpu/ops/GrDefaultPathRenderer.cpp | 8 +- src/gpu/ops/GrMSAAPathRenderer.cpp | 4 + src/gpu/ops/GrSmallPathRenderer.cpp | 2 +- src/gpu/ops/GrStencilAndCoverPathRenderer.cpp | 2 +- tests/GLProgramsTest.cpp | 8 +- tests/GpuSampleLocationsTest.cpp | 5 ++ tests/ResourceCacheTest.cpp | 3 + tests/SurfaceTest.cpp | 3 + tests/TestConfigParsing.cpp | 2 +- tools/flags/SkCommonFlagsConfig.cpp | 15 +++- tools/flags/SkCommonFlagsConfig.h | 3 +- tools/gpu/GrContextFactory.cpp | 3 + tools/gpu/GrContextFactory.h | 5 +- 28 files changed, 166 insertions(+), 89 deletions(-) diff --git a/include/gpu/GrCaps.h b/include/gpu/GrCaps.h index 0d05d47..5702db6 100644 --- a/include/gpu/GrCaps.h +++ b/include/gpu/GrCaps.h @@ -83,6 +83,8 @@ public: bool avoidInstancedDrawsToFPTargets() const { return fAvoidInstancedDrawsToFPTargets; } + bool avoidStencilBuffers() const { return fAvoidStencilBuffers; } + /** * Indicates the capabilities of the fixed function blend unit. */ @@ -229,6 +231,7 @@ protected: bool fUseDrawInsteadOfPartialRenderTargetWrite : 1; bool fUseDrawInsteadOfAllRenderTargetWrites : 1; bool fAvoidInstancedDrawsToFPTargets : 1; + bool fAvoidStencilBuffers : 1; // ANGLE workaround bool fPreferVRAMUseOverFlushes : 1; diff --git a/include/gpu/GrContextOptions.h b/include/gpu/GrContextOptions.h index 8b61c9c..1f34f23 100644 --- a/include/gpu/GrContextOptions.h +++ b/include/gpu/GrContextOptions.h @@ -105,6 +105,12 @@ struct GrContextOptions { }; GpuPathRenderers fGpuPathRenderers = GpuPathRenderers::kAll; + + /** + * Bugs on certain drivers cause stencil buffers to leak. This flag causes Skia to avoid + * allocating stencil buffers and use alternate rasterization paths, avoiding the leak. + */ + bool fAvoidStencilBuffers = false; }; GR_MAKE_BITFIELD_CLASS_OPS(GrContextOptions::GpuPathRenderers) diff --git a/src/gpu/GrCaps.cpp b/src/gpu/GrCaps.cpp index ade64d6..26065b6 100644 --- a/src/gpu/GrCaps.cpp +++ b/src/gpu/GrCaps.cpp @@ -78,6 +78,7 @@ GrCaps::GrCaps(const GrContextOptions& options) { fUseDrawInsteadOfPartialRenderTargetWrite = options.fUseDrawInsteadOfPartialRenderTargetWrite; fUseDrawInsteadOfAllRenderTargetWrites = false; fAvoidInstancedDrawsToFPTargets = false; + fAvoidStencilBuffers = false; fPreferVRAMUseOverFlushes = true; } @@ -96,6 +97,7 @@ void GrCaps::applyOptionsOverrides(const GrContextOptions& options) { GrWindowRectangles::kMaxWindows, fMaxWindowRectangles); fMaxWindowRectangles = GrWindowRectangles::kMaxWindows; } + fAvoidStencilBuffers = options.fAvoidStencilBuffers; } static SkString map_flags_to_string(uint32_t flags) { diff --git a/src/gpu/GrClipStackClip.cpp b/src/gpu/GrClipStackClip.cpp index b010981..702d7a1 100644 --- a/src/gpu/GrClipStackClip.cpp +++ b/src/gpu/GrClipStackClip.cpp @@ -115,7 +115,7 @@ bool GrClipStackClip::PathNeedsSWRenderer(GrContext* context, GrShape shape(path, GrStyle::SimpleFill()); GrPathRenderer::CanDrawPathArgs canDrawArgs; - canDrawArgs.fShaderCaps = context->caps()->shaderCaps(); + canDrawArgs.fCaps = context->caps(); canDrawArgs.fViewMatrix = &viewMatrix; canDrawArgs.fShape = &shape; if (!element->isAA()) { @@ -152,6 +152,10 @@ bool GrClipStackClip::UseSWOnlyPath(GrContext* context, // a clip gets complex enough it can just be done in SW regardless // of whether it would invoke the GrSoftwarePathRenderer. + // If we're avoiding stencils, always use SW: + if (context->caps()->avoidStencilBuffers()) + return true; + // Set the matrix so that rendered clip elements are transformed to mask space from clip // space. SkMatrix translate; @@ -283,6 +287,8 @@ bool GrClipStackClip::apply(GrContext* context, GrRenderTargetContext* renderTar SkASSERT(rtIBounds.contains(clipIBounds)); // Mask shouldn't be larger than the RT. #endif + bool avoidStencilBuffers = context->caps()->avoidStencilBuffers(); + // An element count of 4 was chosen because of the common pattern in Blink of: // isect RR // diff RR @@ -294,7 +300,8 @@ bool GrClipStackClip::apply(GrContext* context, GrRenderTargetContext* renderTar if (reducedClip.elements().count() <= kMaxAnalyticElements) { // When there are multiple samples we want to do per-sample clipping, not compute a // fractional pixel coverage. - bool disallowAnalyticAA = renderTargetContext->isStencilBufferMultisampled(); + bool disallowAnalyticAA = renderTargetContext->isStencilBufferMultisampled() && + !avoidStencilBuffers; if (disallowAnalyticAA && !renderTargetContext->numColorSamples()) { // With a single color sample, any coverage info is lost from color once it hits the // color buffer anyway, so we may as well use coverage AA if nothing else in the pipe @@ -302,7 +309,7 @@ bool GrClipStackClip::apply(GrContext* context, GrRenderTargetContext* renderTar disallowAnalyticAA = useHWAA || hasUserStencilSettings; } sk_sp clipFP; - if (reducedClip.requiresAA() && + if ((reducedClip.requiresAA() || avoidStencilBuffers) && get_analytic_clip_processor(reducedClip.elements(), disallowAnalyticAA, devBounds, &clipFP)) { out->addCoverageFP(std::move(clipFP)); @@ -311,7 +318,8 @@ bool GrClipStackClip::apply(GrContext* context, GrRenderTargetContext* renderTar } // If the stencil buffer is multisampled we can use it to do everything. - if (!renderTargetContext->isStencilBufferMultisampled() && reducedClip.requiresAA()) { + if ((!renderTargetContext->isStencilBufferMultisampled() && reducedClip.requiresAA()) || + avoidStencilBuffers) { sk_sp result; if (UseSWOnlyPath(context, hasUserStencilSettings, renderTargetContext, reducedClip)) { // The clip geometry is complex enough that it will be more efficient to create it @@ -328,7 +336,13 @@ bool GrClipStackClip::apply(GrContext* context, GrRenderTargetContext* renderTar reducedClip.ibounds())); return true; } - // if alpha clip mask creation fails fall through to the non-AA code paths + + // If alpha or software clip mask creation fails, fall through to the stencil code paths, + // unless stencils are disallowed. + if (context->caps()->avoidStencilBuffers()) { + SkDebugf("WARNING: Clip mask requires stencil, but stencil unavailable. Clip will be ignored.\n"); + return false; + } } GrRenderTarget* rt = renderTargetContext->accessRenderTarget(); diff --git a/src/gpu/GrGpu.cpp b/src/gpu/GrGpu.cpp index b1560f9..149db11 100644 --- a/src/gpu/GrGpu.cpp +++ b/src/gpu/GrGpu.cpp @@ -210,10 +210,13 @@ sk_sp GrGpu::wrapBackendTexture(const GrBackendTextureDesc& desc, if (!tex) { return nullptr; } - // TODO: defer this and attach dynamically - GrRenderTarget* tgt = tex->asRenderTarget(); - if (tgt && !fContext->resourceProvider()->attachStencilAttachment(tgt)) { - return nullptr; + + if (!this->caps()->avoidStencilBuffers()) { + // TODO: defer this and attach dynamically + GrRenderTarget* tgt = tex->asRenderTarget(); + if (tgt && !fContext->resourceProvider()->attachStencilAttachment(tgt)) { + return nullptr; + } } return tex; } diff --git a/src/gpu/GrPathRenderer.h b/src/gpu/GrPathRenderer.h index 05673d5..0845bc0 100644 --- a/src/gpu/GrPathRenderer.h +++ b/src/gpu/GrPathRenderer.h @@ -74,14 +74,14 @@ public: /** Args to canDrawPath() * - * fShaderCaps The shader caps + * fCaps The context caps * fPipelineBuilder The pipelineBuilder * fViewMatrix The viewMatrix * fShape The shape to draw * fAntiAlias The type of anti aliasing required. */ struct CanDrawPathArgs { - const GrShaderCaps* fShaderCaps; + const GrCaps* fCaps; const SkMatrix* fViewMatrix; const GrShape* fShape; GrAAType fAAType; @@ -91,7 +91,7 @@ public: #ifdef SK_DEBUG void validate() const { - SkASSERT(fShaderCaps); + SkASSERT(fCaps); SkASSERT(fViewMatrix); SkASSERT(fShape); } @@ -153,7 +153,7 @@ public: SkDEBUGCODE(args.validate();) #ifdef SK_DEBUG CanDrawPathArgs canArgs; - canArgs.fShaderCaps = args.fContext->caps()->shaderCaps(); + canArgs.fCaps = args.fContext->caps(); canArgs.fViewMatrix = args.fViewMatrix; canArgs.fShape = args.fShape; canArgs.fAAType = args.fAAType; diff --git a/src/gpu/GrReducedClip.cpp b/src/gpu/GrReducedClip.cpp index 4b7e69b..28ba47c 100644 --- a/src/gpu/GrReducedClip.cpp +++ b/src/gpu/GrReducedClip.cpp @@ -730,7 +730,7 @@ bool GrReducedClip::drawStencilClipMask(GrContext* context, GrShape shape(clipPath, GrStyle::SimpleFill()); GrPathRenderer::CanDrawPathArgs canDrawArgs; - canDrawArgs.fShaderCaps = context->caps()->shaderCaps(); + canDrawArgs.fCaps = context->caps(); canDrawArgs.fViewMatrix = &SkMatrix::I(); canDrawArgs.fShape = &shape; canDrawArgs.fAAType = aaType; diff --git a/src/gpu/GrRenderTargetContext.cpp b/src/gpu/GrRenderTargetContext.cpp index be3ad8b..9024269 100644 --- a/src/gpu/GrRenderTargetContext.cpp +++ b/src/gpu/GrRenderTargetContext.cpp @@ -1416,8 +1416,7 @@ bool GrRenderTargetContextPriv::drawAndStencilPath(const GrClip& clip, GrShape shape(path, GrStyle::SimpleFill()); GrPathRenderer::CanDrawPathArgs canDrawArgs; - canDrawArgs.fShaderCaps = - fRenderTargetContext->drawingManager()->getContext()->caps()->shaderCaps(); + canDrawArgs.fCaps = fRenderTargetContext->drawingManager()->getContext()->caps(); canDrawArgs.fViewMatrix = &viewMatrix; canDrawArgs.fShape = &shape; canDrawArgs.fAAType = aaType; @@ -1478,7 +1477,7 @@ void GrRenderTargetContext::internalDrawPath(const GrClip& clip, aaType = GrAAType::kCoverage; } GrPathRenderer::CanDrawPathArgs canDrawArgs; - canDrawArgs.fShaderCaps = this->drawingManager()->getContext()->caps()->shaderCaps(); + canDrawArgs.fCaps = this->drawingManager()->getContext()->caps(); canDrawArgs.fViewMatrix = &viewMatrix; canDrawArgs.fShape = &shape; canDrawArgs.fHasUserStencilSettings = false; diff --git a/src/gpu/GrSoftwarePathRenderer.cpp b/src/gpu/GrSoftwarePathRenderer.cpp index 0e7cecd..a9613b4 100644 --- a/src/gpu/GrSoftwarePathRenderer.cpp +++ b/src/gpu/GrSoftwarePathRenderer.cpp @@ -136,7 +136,9 @@ bool GrSoftwarePathRenderer::onDrawPath(const DrawPathArgs& args) { bool inverseFilled = false; SkTLazy tmpShape; SkASSERT(!args.fShape->style().applies()); - inverseFilled = args.fShape->inverseFilled(); + // If the path is hairline, ignore inverse fill. + inverseFilled = args.fShape->inverseFilled() && + !IsStrokeHairlineOrEquivalent(args.fShape->style(), *args.fViewMatrix, nullptr); SkIRect unclippedDevShapeBounds, clippedDevShapeBounds, devClipBounds; // To prevent overloading the cache with entries during animations we limit the cache of masks diff --git a/src/gpu/gl/GrGLCaps.cpp b/src/gpu/gl/GrGLCaps.cpp index bc894c3..41c3706 100644 --- a/src/gpu/gl/GrGLCaps.cpp +++ b/src/gpu/gl/GrGLCaps.cpp @@ -344,23 +344,6 @@ void GrGLCaps::init(const GrContextOptions& contextOptions, * GrCaps fields **************************************************************************/ - // We need dual source blending and the ability to disable multisample in order to support mixed - // samples in every corner case. We only use mixed samples if the stencil-and-cover path - // renderer is available and enabled; no other path renderers support this feature. - if (fMultisampleDisableSupport && - shaderCaps->dualSourceBlendingSupport() && - fShaderCaps->pathRenderingSupport() && - (contextOptions.fGpuPathRenderers & GrContextOptions::GpuPathRenderers::kStencilAndCover)) { - fUsesMixedSamples = ctxInfo.hasExtension("GL_NV_framebuffer_mixed_samples") || - ctxInfo.hasExtension("GL_CHROMIUM_framebuffer_mixed_samples"); - // Workaround NVIDIA bug related to glInvalidateFramebuffer and mixed samples. - if (fUsesMixedSamples && (kNVIDIA_GrGLDriver == ctxInfo.driver() || - kChromium_GrGLDriver == ctxInfo.driver())) { - fDiscardRenderTargetSupport = false; - fInvalidateFBType = kNone_InvalidateFBType; - } - } - // SGX and Mali GPUs that are based on a tiled-deferred architecture that have trouble with // frequently changing VBOs. We've measured a performance increase using non-VBO vertex // data for dynamic content on these GPUs. Perhaps we should read the renderer string and @@ -373,24 +356,12 @@ void GrGLCaps::init(const GrContextOptions& contextOptions, fPreferClientSideDynamicBuffers = true; } - // fUsesMixedSamples must be set before calling initFSAASupport. - this->initFSAASupport(ctxInfo, gli); - this->initBlendEqationSupport(ctxInfo); - this->initStencilFormats(ctxInfo); - - if (kGL_GrGLStandard == standard) { - // we could also look for GL_ATI_separate_stencil extension or - // GL_EXT_stencil_two_side but they use different function signatures - // than GL2.0+ (and than each other). - fTwoSidedStencilSupport = (ctxInfo.version() >= GR_GL_VER(2,0)); - // supported on GL 1.4 and higher or by extension - fStencilWrapOpsSupport = (ctxInfo.version() >= GR_GL_VER(1,4)) || - ctxInfo.hasExtension("GL_EXT_stencil_wrap"); - } else { - // ES 2 has two sided stencil and stencil wrap - fTwoSidedStencilSupport = true; - fStencilWrapOpsSupport = true; + if (!contextOptions.fAvoidStencilBuffers) { + // To reduce surface area, if we avoid stencil buffers, we also disable MSAA. + this->initFSAASupport(contextOptions, ctxInfo, gli); + this->initStencilSupport(ctxInfo); } + this->initBlendEqationSupport(ctxInfo); if (kGL_GrGLStandard == standard) { fMapBufferFlags = kCanMap_MapFlag; // we require VBO support and the desktop VBO @@ -479,21 +450,6 @@ void GrGLCaps::init(const GrContextOptions& contextOptions, kQualcomm_GrGLVendor != ctxInfo.vendor(); #endif - // initFSAASupport() must have been called before this point - if (GrGLCaps::kES_IMG_MsToTexture_MSFBOType == fMSFBOType) { - GR_GL_GetIntegerv(gli, GR_GL_MAX_SAMPLES_IMG, &fMaxStencilSampleCount); - } else if (GrGLCaps::kNone_MSFBOType != fMSFBOType) { - GR_GL_GetIntegerv(gli, GR_GL_MAX_SAMPLES, &fMaxStencilSampleCount); - } - // We only have a use for raster multisample if there is coverage modulation from mixed samples. - if (fUsesMixedSamples && ctxInfo.hasExtension("GL_EXT_raster_multisample")) { - GR_GL_GetIntegerv(gli, GR_GL_MAX_RASTER_SAMPLES, &fMaxRasterSamples); - // This is to guard against platforms that may not support as many samples for - // glRasterSamples as they do for framebuffers. - fMaxStencilSampleCount = SkTMin(fMaxStencilSampleCount, fMaxRasterSamples); - } - fMaxColorSampleCount = fMaxStencilSampleCount; - if (ctxInfo.hasExtension("GL_EXT_window_rectangles")) { GR_GL_GetIntegerv(gli, GR_GL_MAX_WINDOW_RECTANGLES, &fMaxWindowRectangles); } @@ -997,7 +953,25 @@ bool GrGLCaps::readPixelsSupported(GrPixelConfig surfaceConfig, fConfigTable[surfaceConfig].fSecondReadPixelsFormat.fType == readType; } -void GrGLCaps::initFSAASupport(const GrGLContextInfo& ctxInfo, const GrGLInterface* gli) { +void GrGLCaps::initFSAASupport(const GrContextOptions& contextOptions, const GrGLContextInfo& ctxInfo, + const GrGLInterface* gli) { + // We need dual source blending and the ability to disable multisample in order to support mixed + // samples in every corner case. We only use mixed samples if the stencil-and-cover path + // renderer is available and enabled; no other path renderers support this feature. + if (fMultisampleDisableSupport && + this->shaderCaps()->dualSourceBlendingSupport() && + this->shaderCaps()->pathRenderingSupport() && + (contextOptions.fGpuPathRenderers & GrContextOptions::GpuPathRenderers::kStencilAndCover)) { + fUsesMixedSamples = ctxInfo.hasExtension("GL_NV_framebuffer_mixed_samples") || + ctxInfo.hasExtension("GL_CHROMIUM_framebuffer_mixed_samples"); + // Workaround NVIDIA bug related to glInvalidateFramebuffer and mixed samples. + if (fUsesMixedSamples && (kNVIDIA_GrGLDriver == ctxInfo.driver() || + kChromium_GrGLDriver == ctxInfo.driver())) { + fDiscardRenderTargetSupport = false; + fInvalidateFBType = kNone_InvalidateFBType; + } + } + if (kGL_GrGLStandard != ctxInfo.standard()) { // We prefer the EXT/IMG extension over ES3 MSAA because we've observed // ES3 driver bugs on at least one device with a tiled GPU (N10). @@ -1045,6 +1019,20 @@ void GrGLCaps::initFSAASupport(const GrGLContextInfo& ctxInfo, const GrGLInterfa fBlitFramebufferFlags = 0; } } + + if (GrGLCaps::kES_IMG_MsToTexture_MSFBOType == fMSFBOType) { + GR_GL_GetIntegerv(gli, GR_GL_MAX_SAMPLES_IMG, &fMaxStencilSampleCount); + } else if (GrGLCaps::kNone_MSFBOType != fMSFBOType) { + GR_GL_GetIntegerv(gli, GR_GL_MAX_SAMPLES, &fMaxStencilSampleCount); + } + // We only have a use for raster multisample if there is coverage modulation from mixed samples. + if (fUsesMixedSamples && ctxInfo.hasExtension("GL_EXT_raster_multisample")) { + GR_GL_GetIntegerv(gli, GR_GL_MAX_RASTER_SAMPLES, &fMaxRasterSamples); + // This is to guard against platforms that may not support as many samples for + // glRasterSamples as they do for framebuffers. + fMaxStencilSampleCount = SkTMin(fMaxStencilSampleCount, fMaxRasterSamples); + } + fMaxColorSampleCount = fMaxStencilSampleCount; } void GrGLCaps::initBlendEqationSupport(const GrGLContextInfo& ctxInfo) { @@ -1099,7 +1087,7 @@ namespace { const GrGLuint kUnknownBitCount = GrGLStencilAttachment::kUnknownBitCount; } -void GrGLCaps::initStencilFormats(const GrGLContextInfo& ctxInfo) { +void GrGLCaps::initStencilSupport(const GrGLContextInfo& ctxInfo) { // Build up list of legal stencil formats (though perhaps not supported on // the particular gpu/driver) from most preferred to least. @@ -1149,6 +1137,20 @@ void GrGLCaps::initStencilFormats(const GrGLContextInfo& ctxInfo) { fStencilFormats.push_back() = gS4; } } + // Set up additional stencil properties. + if (kGL_GrGLStandard == ctxInfo.standard()) { + // we could also look for GL_ATI_separate_stencil extension or + // GL_EXT_stencil_two_side but they use different function signatures + // than GL2.0+ (and than each other). + fTwoSidedStencilSupport = (ctxInfo.version() >= GR_GL_VER(2,0)); + // supported on GL 1.4 and higher or by extension + fStencilWrapOpsSupport = (ctxInfo.version() >= GR_GL_VER(1,4)) || + ctxInfo.hasExtension("GL_EXT_stencil_wrap"); + } else { + // ES 2 has two sided stencil and stencil wrap + fTwoSidedStencilSupport = true; + fStencilWrapOpsSupport = true; + } } SkString GrGLCaps::dump() const { diff --git a/src/gpu/gl/GrGLCaps.h b/src/gpu/gl/GrGLCaps.h index 7439711..c32e419 100644 --- a/src/gpu/gl/GrGLCaps.h +++ b/src/gpu/gl/GrGLCaps.h @@ -380,9 +380,10 @@ private: void onApplyOptionsOverrides(const GrContextOptions& options) override; - void initFSAASupport(const GrGLContextInfo&, const GrGLInterface*); + void initFSAASupport(const GrContextOptions& contextOptions, const GrGLContextInfo&, + const GrGLInterface*); void initBlendEqationSupport(const GrGLContextInfo&); - void initStencilFormats(const GrGLContextInfo&); + void initStencilSupport(const GrGLContextInfo&); // This must be called after initFSAASupport(). void initConfigTable(const GrContextOptions&, const GrGLContextInfo&, const GrGLInterface*, GrShaderCaps*); diff --git a/src/gpu/gl/GrGLRenderTarget.cpp b/src/gpu/gl/GrGLRenderTarget.cpp index 45efba2..09c2851 100644 --- a/src/gpu/gl/GrGLRenderTarget.cpp +++ b/src/gpu/gl/GrGLRenderTarget.cpp @@ -7,6 +7,7 @@ #include "GrGLRenderTarget.h" +#include "GrContext.h" #include "GrGLGpu.h" #include "GrGLUtil.h" #include "GrGpuResourcePriv.h" @@ -170,6 +171,10 @@ GrGLGpu* GrGLRenderTarget::getGLGpu() const { } bool GrGLRenderTarget::canAttemptStencilAttachment() const { + if (this->getGpu()->getContext()->caps()->avoidStencilBuffers()) { + return false; + } + // Only modify the FBO's attachments if we have created the FBO. Public APIs do not currently // allow for borrowed FBO ownership, so we can safely assume that if an object is owned, // Skia created it. diff --git a/src/gpu/gl/GrGLTextureRenderTarget.cpp b/src/gpu/gl/GrGLTextureRenderTarget.cpp index 0abeb8e..eac29b6 100644 --- a/src/gpu/gl/GrGLTextureRenderTarget.cpp +++ b/src/gpu/gl/GrGLTextureRenderTarget.cpp @@ -7,6 +7,7 @@ #include "GrGLTextureRenderTarget.h" +#include "GrContext.h" #include "SkTraceMemoryDump.h" // GrGLTextureRenderTarget must dump both of its superclasses. @@ -40,8 +41,8 @@ void GrGLTextureRenderTarget::dumpMemoryStatistics( bool GrGLTextureRenderTarget::canAttemptStencilAttachment() const { // The RT FBO of GrGLTextureRenderTarget is never created from a - // wrapped FBO. - return true; + // wrapped FBO, so we only care about the flag. + return !this->getGpu()->getContext()->caps()->avoidStencilBuffers(); } sk_sp GrGLTextureRenderTarget::MakeWrapped( diff --git a/src/gpu/ops/GrAAConvexPathRenderer.cpp b/src/gpu/ops/GrAAConvexPathRenderer.cpp index efc6622..05a7265 100644 --- a/src/gpu/ops/GrAAConvexPathRenderer.cpp +++ b/src/gpu/ops/GrAAConvexPathRenderer.cpp @@ -667,9 +667,9 @@ sk_sp QuadEdgeEffect::TestCreate(GrProcessorTestData* d) { /////////////////////////////////////////////////////////////////////////////// bool GrAAConvexPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const { - return (args.fShaderCaps->shaderDerivativeSupport() && (GrAAType::kCoverage == args.fAAType) && - args.fShape->style().isSimpleFill() && !args.fShape->inverseFilled() && - args.fShape->knownToBeConvex()); + return (args.fCaps->shaderCaps()->shaderDerivativeSupport() && + (GrAAType::kCoverage == args.fAAType) && args.fShape->style().isSimpleFill() && + !args.fShape->inverseFilled() && args.fShape->knownToBeConvex()); } // extract the result vertices and indices from the GrAAConvexTessellator diff --git a/src/gpu/ops/GrAAHairLinePathRenderer.cpp b/src/gpu/ops/GrAAHairLinePathRenderer.cpp index 274e308..610c3a2 100644 --- a/src/gpu/ops/GrAAHairLinePathRenderer.cpp +++ b/src/gpu/ops/GrAAHairLinePathRenderer.cpp @@ -624,7 +624,7 @@ bool GrAAHairLinePathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const } if (SkPath::kLine_SegmentMask == args.fShape->segmentMask() || - args.fShaderCaps->shaderDerivativeSupport()) { + args.fCaps->shaderCaps()->shaderDerivativeSupport()) { return true; } diff --git a/src/gpu/ops/GrDefaultPathRenderer.cpp b/src/gpu/ops/GrDefaultPathRenderer.cpp index 762084c..8c264b5 100644 --- a/src/gpu/ops/GrDefaultPathRenderer.cpp +++ b/src/gpu/ops/GrDefaultPathRenderer.cpp @@ -576,10 +576,14 @@ bool GrDefaultPathRenderer::internalDrawPath(GrRenderTargetContext* renderTarget } bool GrDefaultPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const { + bool isHairline = IsStrokeHairlineOrEquivalent(args.fShape->style(), *args.fViewMatrix, nullptr); + // If we aren't a single_pass_shape or hairline, we require stencil buffers. + if (!(single_pass_shape(*args.fShape) || isHairline) && args.fCaps->avoidStencilBuffers()) { + return false; + } // This can draw any path with any simple fill style but doesn't do coverage-based antialiasing. return GrAAType::kCoverage != args.fAAType && - (args.fShape->style().isSimpleFill() || - IsStrokeHairlineOrEquivalent(args.fShape->style(), *args.fViewMatrix, nullptr)); + (args.fShape->style().isSimpleFill() || isHairline); } bool GrDefaultPathRenderer::onDrawPath(const DrawPathArgs& args) { diff --git a/src/gpu/ops/GrMSAAPathRenderer.cpp b/src/gpu/ops/GrMSAAPathRenderer.cpp index 8e32006..d9050f2 100644 --- a/src/gpu/ops/GrMSAAPathRenderer.cpp +++ b/src/gpu/ops/GrMSAAPathRenderer.cpp @@ -675,6 +675,10 @@ bool GrMSAAPathRenderer::internalDrawPath(GrRenderTargetContext* renderTargetCon } bool GrMSAAPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const { + // If we aren't a single_pass_shape, we require stencil buffers. + if (!single_pass_shape(*args.fShape) && args.fCaps->avoidStencilBuffers()) { + return false; + } // This path renderer only fills and relies on MSAA for antialiasing. Stroked shapes are // handled by passing on the original shape and letting the caller compute the stroked shape // which will have a fill style. diff --git a/src/gpu/ops/GrSmallPathRenderer.cpp b/src/gpu/ops/GrSmallPathRenderer.cpp index 0986a09..e1f2d18 100644 --- a/src/gpu/ops/GrSmallPathRenderer.cpp +++ b/src/gpu/ops/GrSmallPathRenderer.cpp @@ -87,7 +87,7 @@ GrSmallPathRenderer::~GrSmallPathRenderer() { //////////////////////////////////////////////////////////////////////////////// bool GrSmallPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const { - if (!args.fShaderCaps->shaderDerivativeSupport()) { + if (!args.fCaps->shaderCaps()->shaderDerivativeSupport()) { return false; } // If the shape has no key then we won't get any reuse. diff --git a/src/gpu/ops/GrStencilAndCoverPathRenderer.cpp b/src/gpu/ops/GrStencilAndCoverPathRenderer.cpp index bd5c297..9832359 100644 --- a/src/gpu/ops/GrStencilAndCoverPathRenderer.cpp +++ b/src/gpu/ops/GrStencilAndCoverPathRenderer.cpp @@ -22,7 +22,7 @@ GrPathRenderer* GrStencilAndCoverPathRenderer::Create(GrResourceProvider* resourceProvider, const GrCaps& caps) { - if (caps.shaderCaps()->pathRenderingSupport()) { + if (caps.shaderCaps()->pathRenderingSupport() && !caps.avoidStencilBuffers()) { return new GrStencilAndCoverPathRenderer(resourceProvider); } else { return nullptr; diff --git a/tests/GLProgramsTest.cpp b/tests/GLProgramsTest.cpp index 5400d92..6d15e9a 100644 --- a/tests/GLProgramsTest.cpp +++ b/tests/GLProgramsTest.cpp @@ -251,7 +251,11 @@ static bool set_random_state(GrPaint* paint, SkRandom* random) { } // right now, the only thing we seem to care about in drawState's stencil is 'doesWrite()' -static const GrUserStencilSettings* get_random_stencil(SkRandom* random) { +static const GrUserStencilSettings* get_random_stencil(SkRandom* random, GrContext* context) { + if (context->caps()->avoidStencilBuffers()) { + return &GrUserStencilSettings::kUnused; + } + static constexpr GrUserStencilSettings kDoesWriteStencil( GrUserStencilSettings::StaticInit< 0xffff, @@ -332,7 +336,7 @@ bool GrDrawingManager::ProgramUnitTest(GrContext* context, int maxStages) { set_random_color_coverage_stages(&grPaint, &ptd, maxStages); set_random_xpf(&grPaint, &ptd); bool snapToCenters = set_random_state(&grPaint, &random); - const GrUserStencilSettings* uss = get_random_stencil(&random); + const GrUserStencilSettings* uss = get_random_stencil(&random, context); // We don't use kHW because we will hit an assertion if the render target is not // multisampled static constexpr GrAAType kAATypes[] = {GrAAType::kNone, GrAAType::kCoverage}; diff --git a/tests/GpuSampleLocationsTest.cpp b/tests/GpuSampleLocationsTest.cpp index 26d7e70..835d5bc 100644 --- a/tests/GpuSampleLocationsTest.cpp +++ b/tests/GpuSampleLocationsTest.cpp @@ -190,6 +190,11 @@ private: DEF_GPUTEST(GLSampleLocations, reporter, /*factory*/) { GLTestSampleLocationsInterface testInterface; sk_sp ctx(GrContext::Create(kOpenGL_GrBackend, testInterface)); + + // This test relies on at least 2 samples. + if (ctx->caps()->maxSampleCount() < 2) { + return; + } test_sampleLocations(reporter, &testInterface, ctx.get()); } diff --git a/tests/ResourceCacheTest.cpp b/tests/ResourceCacheTest.cpp index fcf3fe8..5138e2f 100644 --- a/tests/ResourceCacheTest.cpp +++ b/tests/ResourceCacheTest.cpp @@ -100,6 +100,9 @@ DEF_GPUTEST_FOR_CONTEXTS(ResourceCacheStencilBuffers, &is_rendering_and_not_angl smallDesc.fHeight = 4; smallDesc.fSampleCnt = 0; + if (context->caps()->avoidStencilBuffers()) { + return; + } GrResourceProvider* resourceProvider = context->resourceProvider(); // Test that two budgeted RTs with the same desc share a stencil buffer. sk_sp smallRT0(resourceProvider->createTexture(smallDesc, SkBudgeted::kYes)); diff --git a/tests/SurfaceTest.cpp b/tests/SurfaceTest.cpp index f620cff..672ef61 100644 --- a/tests/SurfaceTest.cpp +++ b/tests/SurfaceTest.cpp @@ -774,6 +774,9 @@ DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(SurfaceAttachStencil_Gpu, reporter, ctxInf if (!gpu) { return; } + if (gpu->caps()->avoidStencilBuffers()) { + return; + } static const uint32_t kOrigColor = 0xFFAABBCC; for (auto& surfaceFunc : {&create_gpu_surface_backend_texture, diff --git a/tests/TestConfigParsing.cpp b/tests/TestConfigParsing.cpp index 7dfda4f..959fdbe 100644 --- a/tests/TestConfigParsing.cpp +++ b/tests/TestConfigParsing.cpp @@ -80,7 +80,7 @@ DEF_TEST(ParseConfigs_DefaultConfigs, reporter) { "pdf", "skp", "svg", "xps", "angle_d3d11_es2", "angle_gl_es2", "commandbuffer", "mesa", "hwui", "glf16", "glessrgb", "gl", "glnvpr4", "glnvprdit4", "glsrgb", "glmsaa4", "vk", "glinst", "glinst4", "glinstdit4", "glinst8", "glinstdit8", "glesinst", "glesinst4", - "glesinstdit4", "glwide", "glnarrow" + "glesinstdit4", "glwide", "glnarrow", "glnostencils" }); SkCommandLineConfigArray configs; diff --git a/tools/flags/SkCommonFlagsConfig.cpp b/tools/flags/SkCommonFlagsConfig.cpp index 1e980d2..458962e 100644 --- a/tools/flags/SkCommonFlagsConfig.cpp +++ b/tools/flags/SkCommonFlagsConfig.cpp @@ -64,6 +64,7 @@ static const struct { { "glsrgb", "gpu", "api=gl,color=srgb" }, { "glwide", "gpu", "api=gl,color=f16_wide" }, { "glnarrow", "gpu", "api=gl,color=f16_narrow" }, + { "glnostencils", "gpu", "api=gl,stencils=false" }, { "glessrgb", "gpu", "api=gles,color=srgb" }, { "gleswide", "gpu", "api=gles,color=f16_wide" }, { "glesnarrow", "gpu", "api=gles,color=f16_narrow" }, @@ -151,6 +152,8 @@ static const char configExtendedHelp[] = "\t Use NV_path_rendering OpenGL and OpenGL ES extension.\n" "\tsamples\ttype: int\tdefault: 0.\n" "\t Use multisampling with N samples.\n" + "\tstencils\ttype: bool\tdefault: true.\n" + "\t Allow the use of stencil buffers.\n" "\n" "Predefined configs:\n\n" // Help text for pre-defined configs is auto-generated from gPredefinedConfigs @@ -181,7 +184,7 @@ SkCommandLineConfig::~SkCommandLineConfig() { SkCommandLineConfigGpu::SkCommandLineConfigGpu( const SkString& tag, const SkTArray& viaParts, ContextType contextType, bool useNVPR, bool useInstanced, bool useDIText, int samples, SkColorType colorType, - sk_sp colorSpace) + sk_sp colorSpace, bool useStencilBuffers) : SkCommandLineConfig(tag, SkString("gpu"), viaParts) , fContextType(contextType) , fContextOverrides(ContextOverrides::kNone) @@ -210,6 +213,9 @@ SkCommandLineConfigGpu::SkCommandLineConfigGpu( fContextOverrides |= ContextOverrides::kRequireSRGBSupport; fContextOverrides |= ContextOverrides::kAllowSRGBWithoutDecodeControl; } + if (!useStencilBuffers) { + fContextOverrides |= ContextOverrides::kAvoidStencilBuffers; + } } static bool parse_option_int(const SkString& value, int* outInt) { if (value.isEmpty()) { @@ -370,6 +376,8 @@ SkCommandLineConfigGpu* parse_command_line_config_gpu(const SkString& tag, bool seenColor = false; SkColorType colorType = kRGBA_8888_SkColorType; sk_sp colorSpace = nullptr; + bool seenUseStencils = false; + bool useStencils = true; SkTArray optionParts; SkStrSplit(options.c_str(), ",", kStrict_SkStrSplitMode, &optionParts); @@ -400,6 +408,9 @@ SkCommandLineConfigGpu* parse_command_line_config_gpu(const SkString& tag, } else if (key.equals("color") && !seenColor) { valueOk = parse_option_gpu_color(value, &colorType, &colorSpace); seenColor = true; + } else if (key.equals("stencils") && !seenUseStencils) { + valueOk = parse_option_bool(value, &useStencils); + seenUseStencils = true; } if (!valueOk) { return nullptr; @@ -409,7 +420,7 @@ SkCommandLineConfigGpu* parse_command_line_config_gpu(const SkString& tag, return nullptr; } return new SkCommandLineConfigGpu(tag, vias, contextType, useNVPR, useInstanced, useDIText, - samples, colorType, colorSpace); + samples, colorType, colorSpace, useStencils); } #endif diff --git a/tools/flags/SkCommonFlagsConfig.h b/tools/flags/SkCommonFlagsConfig.h index e6ebefd..a3c3b38 100644 --- a/tools/flags/SkCommonFlagsConfig.h +++ b/tools/flags/SkCommonFlagsConfig.h @@ -55,7 +55,8 @@ class SkCommandLineConfigGpu : public SkCommandLineConfig { typedef sk_gpu_test::GrContextFactory::ContextOverrides ContextOverrides; SkCommandLineConfigGpu(const SkString& tag, const SkTArray& viaParts, ContextType contextType, bool useNVPR, bool useInstanced, bool useDIText, - int samples, SkColorType colorType, sk_sp colorSpace); + int samples, SkColorType colorType, sk_sp colorSpace, + bool useStencilBuffers); const SkCommandLineConfigGpu* asConfigGpu() const override { return this; } ContextType getContextType() const { return fContextType; } ContextOverrides getContextOverrides() const { return fContextOverrides; } diff --git a/tools/gpu/GrContextFactory.cpp b/tools/gpu/GrContextFactory.cpp index 6ed8954..e02bc1b 100644 --- a/tools/gpu/GrContextFactory.cpp +++ b/tools/gpu/GrContextFactory.cpp @@ -230,6 +230,9 @@ ContextInfo GrContextFactory::getContextInfoInternal(ContextType type, ContextOv if (ContextOverrides::kAllowSRGBWithoutDecodeControl & overrides) { grOptions.fRequireDecodeDisableForSRGB = false; } + if (ContextOverrides::kAvoidStencilBuffers & overrides) { + grOptions.fAvoidStencilBuffers = true; + } sk_sp grCtx(GrContext::Create(backend, backendContext, grOptions)); if (!grCtx.get()) { return ContextInfo(); diff --git a/tools/gpu/GrContextFactory.h b/tools/gpu/GrContextFactory.h index 0067e52..eb54b50 100644 --- a/tools/gpu/GrContextFactory.h +++ b/tools/gpu/GrContextFactory.h @@ -94,9 +94,10 @@ public: kDisableNVPR = 0x1, kUseInstanced = 0x2, kAllowSRGBWithoutDecodeControl = 0x4, + kAvoidStencilBuffers = 0x8, - kRequireNVPRSupport = 0x8, - kRequireSRGBSupport = 0x10 + kRequireNVPRSupport = 0x10, + kRequireSRGBSupport = 0x20, }; static bool IsRenderingContext(ContextType type) { -- 2.7.4