diff --git a/src/gpu/GrGpu.h b/src/gpu/GrGpu.h index dd63839db5..8b8c7b3809 100644 --- a/src/gpu/GrGpu.h +++ b/src/gpu/GrGpu.h @@ -479,9 +479,6 @@ public: // clears target's entire stencil buffer to 0 virtual void clearStencil(GrRenderTarget* target) = 0; - // draws an outline rectangle for debugging/visualization purposes. - virtual void drawDebugWireRect(GrRenderTarget*, const SkIRect&, GrColor) = 0; - // Determines whether a texture will need to be rescaled in order to be used with the // GrSamplerParams. This variation is called when the caller will create a new texture using the // resource provider from a non-texture src (cpu-backed image, ...). diff --git a/src/gpu/GrStencilSettings.cpp b/src/gpu/GrStencilSettings.cpp index 7790c5506b..689557eaf4 100644 --- a/src/gpu/GrStencilSettings.cpp +++ b/src/gpu/GrStencilSettings.cpp @@ -455,6 +455,20 @@ GrUserStencilSettings const* const* GrStencilSettings::GetClipPasses(SkRegion::O return gUserToClipTable[invertedFill][op]; } +static constexpr GrUserStencilSettings gZeroStencilClipBit( + GrUserStencilSettings::StaticInit< + 0x0000, + GrUserStencilTest::kAlways, + 0xffff, + GrUserStencilOp::kZeroClipBit, + GrUserStencilOp::kZeroClipBit, + 0x0000>() +); + +const GrUserStencilSettings* GrStencilSettings::SetClipBitSettings(bool setToInside) { + return setToInside ? &gReplaceClip : &gZeroStencilClipBit; +} + void GrStencilSettings::genKey(GrProcessorKeyBuilder* b) const { b->add32(fFlags); if (this->isDisabled()) { diff --git a/src/gpu/GrStencilSettings.h b/src/gpu/GrStencilSettings.h index 15a8cac12f..1fed6b8e63 100644 --- a/src/gpu/GrStencilSettings.h +++ b/src/gpu/GrStencilSettings.h @@ -109,6 +109,9 @@ public: bool invertedFill, bool* drawDirectToClip); + /** Gets the user stencil settings to directly set the clip bit. */ + static const GrUserStencilSettings* SetClipBitSettings(bool setToInside); + private: // Internal flag for backends to optionally mark their tracked stencil state as invalid. enum { kInvalid_PrivateFlag = (kLast_StencilFlag << 1) }; diff --git a/src/gpu/gl/GrGLCaps.cpp b/src/gpu/gl/GrGLCaps.cpp index c672cb9a59..44d25299ae 100644 --- a/src/gpu/gl/GrGLCaps.cpp +++ b/src/gpu/gl/GrGLCaps.cpp @@ -55,6 +55,7 @@ GrGLCaps::GrGLCaps(const GrContextOptions& contextOptions, fClearToBoundaryValuesIsBroken = false; fClearTextureSupport = false; fDrawArraysBaseVertexIsBroken = false; + fUseDrawToClearStencilClip = false; fBlitFramebufferFlags = kNoSupport_BlitFramebufferFlag; @@ -511,6 +512,7 @@ void GrGLCaps::init(const GrContextOptions& contextOptions, if (kAdreno4xx_GrGLRenderer == ctxInfo.renderer()) { fUseDrawInsteadOfPartialRenderTargetWrite = true; + fUseDrawToClearStencilClip = true; } // Texture uploads sometimes seem to be ignored to textures bound to FBOS on Tegra3. diff --git a/src/gpu/gl/GrGLCaps.h b/src/gpu/gl/GrGLCaps.h index b7273a08b1..efb324b064 100644 --- a/src/gpu/gl/GrGLCaps.h +++ b/src/gpu/gl/GrGLCaps.h @@ -368,6 +368,11 @@ public: // https://bugs.chromium.org/p/skia/issues/detail?id=6650 bool drawArraysBaseVertexIsBroken() const { return fDrawArraysBaseVertexIsBroken; } + /// Adreno 4xx devices experience an issue when there are a large number of stencil clip bit + /// clears. The minimal repro steps are not precisely known but drawing a rect with a stencil + /// op instead of using glClear seems to resolve the issue. + bool useDrawToClearStencilClip() const { return fUseDrawToClearStencilClip; } + bool initDescForDstCopy(const GrRenderTargetProxy* src, GrSurfaceDesc* desc, bool* rectsMustMatch, bool* disallowSubrect) const override; @@ -442,6 +447,7 @@ private: bool fClearToBoundaryValuesIsBroken : 1; bool fClearTextureSupport : 1; bool fDrawArraysBaseVertexIsBroken : 1; + bool fUseDrawToClearStencilClip : 1; uint32_t fBlitFramebufferFlags; diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp index fc629d60d0..3160ecaed5 100644 --- a/src/gpu/gl/GrGLGpu.cpp +++ b/src/gpu/gl/GrGLGpu.cpp @@ -220,7 +220,7 @@ GrGLGpu::GrGLGpu(GrGLContext* ctx, GrContext* context) for (size_t i = 0; i < SK_ARRAY_COUNT(fMipmapPrograms); ++i) { fMipmapPrograms[i].fProgram = 0; } - fWireRectProgram.fProgram = 0; + fStencilClipClearProgram = 0; SkASSERT(ctx); fCaps.reset(SkRef(ctx->caps())); @@ -277,7 +277,7 @@ GrGLGpu::~GrGLGpu() { fPathRendering.reset(); fCopyProgramArrayBuffer.reset(); fMipmapProgramArrayBuffer.reset(); - fWireRectArrayBuffer.reset(); + fStencilClipClearArrayBuffer.reset(); if (0 != fHWProgramID) { // detach the current program so there is no confusion on OpenGL's part @@ -307,8 +307,8 @@ GrGLGpu::~GrGLGpu() { } } - if (0 != fWireRectProgram.fProgram) { - GL_CALL(DeleteProgram(fWireRectProgram.fProgram)); + if (0 != fStencilClipClearProgram) { + GL_CALL(DeleteProgram(fStencilClipClearProgram)); } delete fProgramCache; @@ -339,8 +339,8 @@ void GrGLGpu::disconnect(DisconnectType type) { GL_CALL(DeleteProgram(fMipmapPrograms[i].fProgram)); } } - if (fWireRectProgram.fProgram) { - GL_CALL(DeleteProgram(fWireRectProgram.fProgram)); + if (fStencilClipClearProgram) { + GL_CALL(DeleteProgram(fStencilClipClearProgram)); } } else { if (fProgramCache) { @@ -363,8 +363,8 @@ void GrGLGpu::disconnect(DisconnectType type) { for (size_t i = 0; i < SK_ARRAY_COUNT(fMipmapPrograms); ++i) { fMipmapPrograms[i].fProgram = 0; } - fWireRectProgram.fProgram = 0; - fWireRectArrayBuffer.reset(); + fStencilClipClearProgram = 0; + fStencilClipClearArrayBuffer.reset(); if (this->glCaps().shaderCaps()->pathRenderingSupport()) { this->glPathRendering()->disconnect(type); } @@ -1943,6 +1943,11 @@ void GrGLGpu::clearStencilClip(const GrFixedClip& clip, SkASSERT(target); this->handleDirtyContext(); + if (this->glCaps().useDrawToClearStencilClip()) { + this->clearStencilClipAsDraw(clip, insideStencilMask, target); + return; + } + GrStencilAttachment* sb = target->renderTargetPriv().getStencilAttachment(); // this should only be called internally when we know we have a // stencil buffer. @@ -3611,64 +3616,43 @@ bool GrGLGpu::createMipmapProgram(int progIdx) { return true; } -bool GrGLGpu::createWireRectProgram() { - if (!fWireRectArrayBuffer) { - static const GrGLfloat vdata[] = { - 0, 0, - 0, 1, - 1, 1, - 1, 0 - }; - fWireRectArrayBuffer.reset(GrGLBuffer::Create(this, sizeof(vdata), kVertex_GrBufferType, - kStatic_GrAccessPattern, vdata)); - if (!fWireRectArrayBuffer) { +bool GrGLGpu::createStencilClipClearProgram() { + if (!fStencilClipClearArrayBuffer) { + static const GrGLfloat vdata[] = {-1, -1, 1, -1, -1, 1, 1, 1}; + fStencilClipClearArrayBuffer.reset(GrGLBuffer::Create( + this, sizeof(vdata), kVertex_GrBufferType, kStatic_GrAccessPattern, vdata)); + if (!fStencilClipClearArrayBuffer) { return false; } } - SkASSERT(!fWireRectProgram.fProgram); - GL_CALL_RET(fWireRectProgram.fProgram, CreateProgram()); - if (!fWireRectProgram.fProgram) { + SkASSERT(!fStencilClipClearProgram); + GL_CALL_RET(fStencilClipClearProgram, CreateProgram()); + if (!fStencilClipClearProgram) { return false; } - GrShaderVar uColor("u_color", kVec4f_GrSLType, GrShaderVar::kUniform_TypeModifier); - GrShaderVar uRect("u_rect", kVec4f_GrSLType, GrShaderVar::kUniform_TypeModifier); GrShaderVar aVertex("a_vertex", kVec2f_GrSLType, GrShaderVar::kIn_TypeModifier); const char* version = this->caps()->shaderCaps()->versionDeclString(); - // The rect uniform specifies the rectangle in NDC space as a vec4 (left,top,right,bottom). The - // program is used with a vbo containing the unit square. Vertices are computed from the rect - // uniform using the 4 vbo vertices. SkString vshaderTxt(version); aVertex.appendDecl(this->caps()->shaderCaps(), &vshaderTxt); vshaderTxt.append(";"); - uRect.appendDecl(this->caps()->shaderCaps(), &vshaderTxt); - vshaderTxt.append(";"); vshaderTxt.append( - "// Wire Rect Program VS\n" - "void main() {" - " gl_Position.x = u_rect.x + a_vertex.x * (u_rect.z - u_rect.x);" - " gl_Position.y = u_rect.y + a_vertex.y * (u_rect.w - u_rect.y);" - " gl_Position.zw = vec2(0, 1);" - "}" - ); - - GrShaderVar oFragColor("o_FragColor", kVec4f_GrSLType, GrShaderVar::kOut_TypeModifier); + "// Stencil Clip Clear Program VS\n" + "void main() {" + " gl_Position = vec4(a_vertex.x, a_vertex.y, 0, 1);" + "}"); SkString fshaderTxt(version); GrGLSLAppendDefaultFloatPrecisionDeclaration(kMedium_GrSLPrecision, *this->caps()->shaderCaps(), &fshaderTxt); - uColor.appendDecl(this->caps()->shaderCaps(), &fshaderTxt); - fshaderTxt.append(";"); fshaderTxt.appendf( - "// Write Rect Program FS\n" - "void main() {" - " sk_FragColor = %s;" - "}", - uColor.c_str() - ); + "// Stencil Clip Clear Program FS\n" + "void main() {" + " sk_FragColor = vec4(0);" + "}"); const char* str; GrGLint length; @@ -3678,25 +3662,21 @@ bool GrGLGpu::createWireRectProgram() { SkSL::Program::Settings settings; settings.fCaps = this->caps()->shaderCaps(); SkSL::Program::Inputs inputs; - GrGLuint vshader = GrGLCompileAndAttachShader(*fGLContext, fWireRectProgram.fProgram, - GR_GL_VERTEX_SHADER, &str, &length, 1, - &fStats, settings, &inputs); + GrGLuint vshader = + GrGLCompileAndAttachShader(*fGLContext, fStencilClipClearProgram, GR_GL_VERTEX_SHADER, + &str, &length, 1, &fStats, settings, &inputs); SkASSERT(inputs.isEmpty()); str = fshaderTxt.c_str(); length = SkToInt(fshaderTxt.size()); - GrGLuint fshader = GrGLCompileAndAttachShader(*fGLContext, fWireRectProgram.fProgram, - GR_GL_FRAGMENT_SHADER, &str, &length, 1, - &fStats, settings, &inputs); + GrGLuint fshader = + GrGLCompileAndAttachShader(*fGLContext, fStencilClipClearProgram, GR_GL_FRAGMENT_SHADER, + &str, &length, 1, &fStats, settings, &inputs); SkASSERT(inputs.isEmpty()); - GL_CALL(LinkProgram(fWireRectProgram.fProgram)); + GL_CALL(LinkProgram(fStencilClipClearProgram)); - GL_CALL_RET(fWireRectProgram.fColorUniform, - GetUniformLocation(fWireRectProgram.fProgram, "u_color")); - GL_CALL_RET(fWireRectProgram.fRectUniform, - GetUniformLocation(fWireRectProgram.fProgram, "u_rect")); - GL_CALL(BindAttribLocation(fWireRectProgram.fProgram, 0, "a_vertex")); + GL_CALL(BindAttribLocation(fStencilClipClearProgram, 0, "a_vertex")); GL_CALL(DeleteShader(vshader)); GL_CALL(DeleteShader(fshader)); @@ -3704,71 +3684,46 @@ bool GrGLGpu::createWireRectProgram() { return true; } -void GrGLGpu::drawDebugWireRect(GrRenderTarget* rt, const SkIRect& rect, GrColor color) { +void GrGLGpu::clearStencilClipAsDraw(const GrFixedClip& clip, bool insideStencilMask, + GrRenderTarget* rt) { // TODO: This should swizzle the output to match dst's config, though it is a debugging // visualization. this->handleDirtyContext(); - if (!fWireRectProgram.fProgram) { - if (!this->createWireRectProgram()) { - SkDebugf("Failed to create wire rect program.\n"); + if (!fStencilClipClearProgram) { + if (!this->createStencilClipClearProgram()) { + SkDebugf("Failed to create stencil clip clear program.\n"); return; } } - int w = rt->width(); - int h = rt->height(); - - // Compute the edges of the rectangle (top,left,right,bottom) in NDC space. Must consider - // whether the render target is flipped or not. - GrGLfloat edges[4]; - edges[0] = SkIntToScalar(rect.fLeft) + 0.5f; - edges[2] = SkIntToScalar(rect.fRight) - 0.5f; - if (kBottomLeft_GrSurfaceOrigin == rt->origin()) { - edges[1] = h - (SkIntToScalar(rect.fTop) + 0.5f); - edges[3] = h - (SkIntToScalar(rect.fBottom) - 0.5f); - } else { - edges[1] = SkIntToScalar(rect.fTop) + 0.5f; - edges[3] = SkIntToScalar(rect.fBottom) - 0.5f; - } - edges[0] = 2 * edges[0] / w - 1.0f; - edges[1] = 2 * edges[1] / h - 1.0f; - edges[2] = 2 * edges[2] / w - 1.0f; - edges[3] = 2 * edges[3] / h - 1.0f; - - GrGLfloat channels[4]; - static const GrGLfloat scale255 = 1.f / 255.f; - channels[0] = GrColorUnpackR(color) * scale255; - channels[1] = GrColorUnpackG(color) * scale255; - channels[2] = GrColorUnpackB(color) * scale255; - channels[3] = GrColorUnpackA(color) * scale255; - GrGLRenderTarget* glRT = static_cast(rt->asRenderTarget()); - this->flushRenderTarget(glRT, &rect); + this->flushRenderTarget(glRT, nullptr); - GL_CALL(UseProgram(fWireRectProgram.fProgram)); - fHWProgramID = fWireRectProgram.fProgram; + GL_CALL(UseProgram(fStencilClipClearProgram)); + fHWProgramID = fStencilClipClearProgram; fHWVertexArrayState.setVertexArrayID(this, 0); GrGLAttribArrayState* attribs = fHWVertexArrayState.bindInternalVertexArray(this); attribs->enableVertexArrays(this, 1); - attribs->set(this, 0, fWireRectArrayBuffer.get(), kVec2f_GrVertexAttribType, + attribs->set(this, 0, fStencilClipClearArrayBuffer.get(), kVec2f_GrVertexAttribType, 2 * sizeof(GrGLfloat), 0); - GL_CALL(Uniform4fv(fWireRectProgram.fRectUniform, 1, edges)); - GL_CALL(Uniform4fv(fWireRectProgram.fColorUniform, 1, channels)); - GrXferProcessor::BlendInfo blendInfo; blendInfo.reset(); this->flushBlend(blendInfo, GrSwizzle::RGBA()); - this->flushColorWrite(true); + this->flushColorWrite(false); this->flushHWAAState(glRT, false, false); - this->disableScissor(); - this->disableWindowRectangles(); - this->disableStencil(); - - GL_CALL(DrawArrays(GR_GL_LINE_LOOP, 0, 4)); + this->flushScissor(clip.scissorState(), glRT->getViewport(), glRT->origin()); + this->flushWindowRectangles(clip.windowRectsState(), glRT); + GrStencilAttachment* sb = rt->renderTargetPriv().getStencilAttachment(); + // This should only be called internally when we know we have a stencil buffer. + SkASSERT(sb); + GrStencilSettings settings = GrStencilSettings( + *GrStencilSettings::SetClipBitSettings(insideStencilMask), false, sb->bits()); + this->flushStencil(settings); + GL_CALL(DrawArrays(GR_GL_TRIANGLE_STRIP, 0, 4)); } diff --git a/src/gpu/gl/GrGLGpu.h b/src/gpu/gl/GrGLGpu.h index 770c357c5f..1785eae143 100644 --- a/src/gpu/gl/GrGLGpu.h +++ b/src/gpu/gl/GrGLGpu.h @@ -162,8 +162,6 @@ public: void resetShaderCacheForTesting() const override; - void drawDebugWireRect(GrRenderTarget*, const SkIRect&, GrColor) override; - GrFence SK_WARN_UNUSED_RESULT insertFence() override; bool waitFence(GrFence, uint64_t timeout) override; void deleteFence(GrFence) const override; @@ -296,6 +294,7 @@ private: const SkIRect& srcRect, const SkIPoint& dstPoint); bool generateMipmap(GrGLTexture* texture, bool gammaCorrect); + void clearStencilClipAsDraw(const GrFixedClip&, bool insideStencilMask, GrRenderTarget*); static bool BlendCoeffReferencesConstant(GrBlendCoeff coeff); @@ -349,11 +348,6 @@ private: void flushWindowRectangles(const GrWindowRectsState&, const GrGLRenderTarget*); void disableWindowRectangles(); - void initFSAASupport(); - - // determines valid stencil formats - void initStencilFormats(); - // sets a texture unit to use for texture operations other than binding a texture to a program. // ensures that such operations don't negatively interact with tracking bound textures. void setScratchTextureUnit(); @@ -407,7 +401,7 @@ private: bool createCopyProgram(GrTexture* srcTexture); bool createMipmapProgram(int progIdx); - bool createWireRectProgram(); + bool createStencilClipClearProgram(); // GL program-related state ProgramCache* fProgramCache; @@ -620,12 +614,8 @@ private: } fMipmapPrograms[4]; sk_sp fMipmapProgramArrayBuffer; - struct { - GrGLuint fProgram; - GrGLint fColorUniform; - GrGLint fRectUniform; - } fWireRectProgram; - sk_sp fWireRectArrayBuffer; + GrGLuint fStencilClipClearProgram; + sk_sp fStencilClipClearArrayBuffer; static int TextureToCopyProgramIdx(GrTexture* texture) { switch (texture->texturePriv().samplerType()) { diff --git a/src/gpu/vk/GrVkGpu.h b/src/gpu/vk/GrVkGpu.h index cce6883f8d..f81daea336 100644 --- a/src/gpu/vk/GrVkGpu.h +++ b/src/gpu/vk/GrVkGpu.h @@ -98,8 +98,6 @@ public: const GrGpuCommandBuffer::LoadAndStoreInfo& colorInfo, const GrGpuCommandBuffer::LoadAndStoreInfo& stencilInfo) override; - void drawDebugWireRect(GrRenderTarget*, const SkIRect&, GrColor) override {} - void addMemoryBarrier(VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask, bool byRegion, diff --git a/tools/gpu/GrTest.cpp b/tools/gpu/GrTest.cpp index bd85cff7ec..32f2d765f8 100644 --- a/tools/gpu/GrTest.cpp +++ b/tools/gpu/GrTest.cpp @@ -339,8 +339,6 @@ public: return nullptr; } - void drawDebugWireRect(GrRenderTarget*, const SkIRect&, GrColor) override {} - GrFence SK_WARN_UNUSED_RESULT insertFence() override { return 0; } bool waitFence(GrFence, uint64_t) override { return true; } void deleteFence(GrFence) const override {}