Implement generic draw-as-clear fallback for color and stencil
This will likely have a perf hit for GL devices that trigger the clear-as-draw fallback since
the fillrectop cannot be as optimized as the direct GLSL shader. However, since the Metal
backend now needs to perform something very similar for scissored clears, I think this
improves code maintainability and is worth it.
Bug: skia:
Change-Id: Id87513784e5892c7ff3dc988115da1d39a46d8e0
Reviewed-on: https://skia-review.googlesource.com/c/182971
Reviewed-by: Chris Dalton <csmartdalton@google.com>
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
diff --git a/src/gpu/GrCaps.h b/src/gpu/GrCaps.h
index 633e066..5a87c88 100644
--- a/src/gpu/GrCaps.h
+++ b/src/gpu/GrCaps.h
@@ -252,11 +252,10 @@
return fDynamicStateArrayGeometryProcessorTextureSupport;
}
- // Not all backends support clearing with a scissor test (e.g. Metal).
- // FIXME(michaelludwig): This should always return true if performColorClearsAsDraws() returns
- // true, but the current partial-clear code doesn't handle transparent clear colors correctly
+ // Not all backends support clearing with a scissor test (e.g. Metal), this will always
+ // return true if performColorClearsAsDraws() returns true.
bool performPartialClearsAsDraws() const {
- return fPerformPartialClearsAsDraws;
+ return fPerformColorClearsAsDraws || fPerformPartialClearsAsDraws;
}
// Many drivers have issues with color clears.
diff --git a/src/gpu/GrGpuCommandBuffer.cpp b/src/gpu/GrGpuCommandBuffer.cpp
index a6a2f84..e0923a2 100644
--- a/src/gpu/GrGpuCommandBuffer.cpp
+++ b/src/gpu/GrGpuCommandBuffer.cpp
@@ -18,11 +18,16 @@
void GrGpuRTCommandBuffer::clear(const GrFixedClip& clip, const SkPMColor4f& color) {
SkASSERT(fRenderTarget);
-
+ // A clear at this level will always be a true clear, so make sure clears were not supposed to
+ // be redirected to draws instead
+ SkASSERT(!this->gpu()->caps()->performColorClearsAsDraws());
+ SkASSERT(!clip.scissorEnabled() || !this->gpu()->caps()->performPartialClearsAsDraws());
this->onClear(clip, color);
}
void GrGpuRTCommandBuffer::clearStencilClip(const GrFixedClip& clip, bool insideStencilMask) {
+ // As above, make sure the stencil clear wasn't supposed to be a draw rect with stencil settings
+ SkASSERT(!this->gpu()->caps()->performStencilClearsAsDraws());
this->onClearStencilClip(clip, insideStencilMask);
}
diff --git a/src/gpu/GrRenderTargetContext.cpp b/src/gpu/GrRenderTargetContext.cpp
index ec40e48..12b1b31 100644
--- a/src/gpu/GrRenderTargetContext.cpp
+++ b/src/gpu/GrRenderTargetContext.cpp
@@ -277,6 +277,89 @@
canClearFullscreen);
}
+void GrRenderTargetContextPriv::clear(const GrFixedClip& clip,
+ const SkPMColor4f& color,
+ CanClearFullscreen canClearFullscreen) {
+ ASSERT_SINGLE_OWNER_PRIV
+ RETURN_IF_ABANDONED_PRIV
+ SkDEBUGCODE(fRenderTargetContext->validate();)
+ GR_CREATE_TRACE_MARKER_CONTEXT("GrRenderTargetContextPriv", "clear",
+ fRenderTargetContext->fContext);
+
+ AutoCheckFlush acf(fRenderTargetContext->drawingManager());
+ fRenderTargetContext->internalClear(clip, color, canClearFullscreen);
+}
+
+static void clear_to_grpaint(const SkPMColor4f& color, GrPaint* paint) {
+ paint->setColor4f(color);
+ if (color.isOpaque()) {
+ // Can just rely on the src-over blend mode to do the right thing
+ paint->setPorterDuffXPFactory(SkBlendMode::kSrcOver);
+ } else {
+ // A clear overwrites the prior color, so even if it's transparent, it behaves as if it
+ // were src blended
+ paint->setPorterDuffXPFactory(SkBlendMode::kSrc);
+ }
+}
+
+void GrRenderTargetContext::internalClear(const GrFixedClip& clip,
+ const SkPMColor4f& color,
+ CanClearFullscreen canClearFullscreen) {
+ bool isFull = false;
+ if (!clip.hasWindowRectangles()) {
+ isFull = !clip.scissorEnabled() ||
+ (CanClearFullscreen::kYes == canClearFullscreen &&
+ this->caps()->preferFullscreenClears()) ||
+ clip.scissorRect().contains(SkIRect::MakeWH(this->width(), this->height()));
+ }
+
+ if (isFull) {
+ if (this->getRTOpList()->resetForFullscreenClear() &&
+ !this->caps()->performColorClearsAsDraws()) {
+ // The op list was emptied and native clears are allowed, so just use the load op
+ this->getRTOpList()->setColorLoadOp(GrLoadOp::kClear, color);
+ return;
+ } else {
+ // Will use an op for the clear, reset the load op to discard since the op will
+ // blow away the color buffer contents
+ this->getRTOpList()->setColorLoadOp(GrLoadOp::kDiscard);
+ }
+
+ // Must add an op to the list (either because we couldn't use a load op, or because the
+ // clear load op isn't supported)
+ if (this->caps()->performColorClearsAsDraws()) {
+ SkRect rtRect = SkRect::MakeWH(this->width(), this->height());
+ GrPaint paint;
+ clear_to_grpaint(color, &paint);
+ this->addDrawOp(GrFixedClip::Disabled(),
+ GrFillRectOp::Make(fContext, std::move(paint),
+ GrAAType::kNone, SkMatrix::I(), rtRect));
+ } else {
+ this->getRTOpList()->addOp(GrClearOp::Make(fContext, SkIRect::MakeEmpty(), color,
+ /* fullscreen */ true), *this->caps());
+ }
+ } else {
+ if (this->caps()->performPartialClearsAsDraws()) {
+ // performPartialClearsAsDraws() also returns true if any clear has to be a draw.
+ SkRect scissor = SkRect::Make(clip.scissorRect());
+ GrPaint paint;
+ clear_to_grpaint(color, &paint);
+
+ this->addDrawOp(clip, GrFillRectOp::Make(fContext, std::move(paint), GrAAType::kNone,
+ SkMatrix::I(), scissor));
+ } else {
+ std::unique_ptr<GrOp> op(GrClearOp::Make(fContext, clip, color,
+ this->asSurfaceProxy()));
+ // This version of the clear op factory can return null if the clip doesn't intersect
+ // with the surface proxy's boundary
+ if (!op) {
+ return;
+ }
+ this->getRTOpList()->addOp(std::move(op), *this->caps());
+ }
+ }
+}
+
void GrRenderTargetContextPriv::absClear(const SkIRect* clearRect, const SkPMColor4f& color) {
ASSERT_SINGLE_OWNER_PRIV
RETURN_IF_ABANDONED_PRIV
@@ -300,79 +383,54 @@
}
// TODO: in a post-MDB world this should be handled at the OpList level.
- // An op-list that is initially cleared and has no other ops should receive an
- // extra draw.
- // This path doesn't handle coalescing of full screen clears b.c. it
- // has to clear the entire render target - not just the content area.
- // It could be done but will take more finagling.
- if (clearRect && fRenderTargetContext->caps()->performPartialClearsAsDraws()) {
- GrPaint paint;
- paint.setColor4f(color);
- SkRect scissor = SkRect::Make(rtRect);
- std::unique_ptr<GrDrawOp> op(GrFillRectOp::Make(fRenderTargetContext->fContext,
- std::move(paint), GrAAType::kNone,
- SkMatrix::I(), scissor));
- if (!op) {
- return;
- }
- fRenderTargetContext->addDrawOp(GrFixedClip(), std::move(op));
- }
- else {
- std::unique_ptr<GrOp> op(GrClearOp::Make(fRenderTargetContext->fContext, rtRect,
- color, !clearRect));
- if (!op) {
- return;
- }
- fRenderTargetContext->getRTOpList()->addOp(std::move(op), *fRenderTargetContext->caps());
- }
-}
-
-void GrRenderTargetContextPriv::clear(const GrFixedClip& clip,
- const SkPMColor4f& color,
- CanClearFullscreen canClearFullscreen) {
- ASSERT_SINGLE_OWNER_PRIV
- RETURN_IF_ABANDONED_PRIV
- SkDEBUGCODE(fRenderTargetContext->validate();)
- GR_CREATE_TRACE_MARKER_CONTEXT("GrRenderTargetContextPriv", "clear",
- fRenderTargetContext->fContext);
-
- AutoCheckFlush acf(fRenderTargetContext->drawingManager());
- fRenderTargetContext->internalClear(clip, color, canClearFullscreen);
-}
-
-void GrRenderTargetContext::internalClear(const GrFixedClip& clip,
- const SkPMColor4f& color,
- CanClearFullscreen canClearFullscreen) {
- bool isFull = false;
- if (!clip.hasWindowRectangles()) {
- isFull = !clip.scissorEnabled() ||
- (CanClearFullscreen::kYes == canClearFullscreen &&
- this->caps()->preferFullscreenClears()) ||
- clip.scissorRect().contains(SkIRect::MakeWH(this->width(), this->height()));
- }
-
- if (isFull) {
- this->getRTOpList()->fullClear(fContext, color);
- } else {
- if (this->caps()->performPartialClearsAsDraws()) {
+ // This makes sure to always add an op to the list, instead of marking the clear as a load op.
+ // This code follows very similar logic to internalClear() below, but critical differences are
+ // highlighted in line related to absClear()'s unique behavior.
+ if (clearRect) {
+ if (fRenderTargetContext->caps()->performPartialClearsAsDraws()) {
GrPaint paint;
- paint.setColor4f(color);
- SkRect scissor = SkRect::Make(clip.scissorRect());
- std::unique_ptr<GrDrawOp> op(GrFillRectOp::Make(fContext, std::move(paint),
- GrAAType::kNone, SkMatrix::I(),
- scissor));
- if (!op) {
- return;
- }
- this->addDrawOp(clip, std::move(op));
+ clear_to_grpaint(color, &paint);
+
+ // Use the disabled clip; the rect geometry already matches the clear rectangle and
+ // if it were added to a scissor, that would be intersected with the logical surface
+ // bounds and not the worst case dimensions required here.
+ fRenderTargetContext->addDrawOp(GrFixedClip::Disabled(),
+ GrFillRectOp::Make(fRenderTargetContext->fContext,
+ std::move(paint),
+ GrAAType::kNone,
+ SkMatrix::I(),
+ SkRect::Make(rtRect)));
+ } else {
+ // Must use the ClearOp factory that takes a boolean (false) instead of a surface
+ // proxy. The surface proxy variant would intersect the clip rect with its logical
+ // bounds, which is not desired in this special case.
+ fRenderTargetContext->getRTOpList()->addOp(
+ GrClearOp::Make(fRenderTargetContext->fContext, rtRect, color,
+ /* fullscreen */ false),
+ *fRenderTargetContext->caps());
}
- else {
- std::unique_ptr<GrOp> op(GrClearOp::Make(fContext, clip, color,
- this->asSurfaceProxy()));
- if (!op) {
- return;
- }
- this->getRTOpList()->addOp(std::move(op), *this->caps());
+ } else {
+ // Reset the oplist like in internalClear(), but do not rely on a load op for the clear
+ fRenderTargetContext->getRTOpList()->resetForFullscreenClear();
+ fRenderTargetContext->getRTOpList()->setColorLoadOp(GrLoadOp::kDiscard);
+
+ if (fRenderTargetContext->caps()->performColorClearsAsDraws()) {
+ // This draws a quad covering the worst case dimensions instead of just the logical
+ // width and height like in internalClear().
+ GrPaint paint;
+ clear_to_grpaint(color, &paint);
+ fRenderTargetContext->addDrawOp(GrFixedClip::Disabled(),
+ GrFillRectOp::Make(fRenderTargetContext->fContext,
+ std::move(paint),
+ GrAAType::kNone,
+ SkMatrix::I(),
+ SkRect::Make(rtRect)));
+ } else {
+ // Nothing special about this path in absClear compared to internalClear()
+ fRenderTargetContext->getRTOpList()->addOp(
+ GrClearOp::Make(fRenderTargetContext->fContext, SkIRect::MakeEmpty(), color,
+ /* fullscreen */ true),
+ *fRenderTargetContext->caps());
}
}
}
@@ -652,13 +710,32 @@
AutoCheckFlush acf(fRenderTargetContext->drawingManager());
- GrRenderTargetProxy* rtProxy = fRenderTargetContext->fRenderTargetProxy.get();
- std::unique_ptr<GrOp> op(GrClearStencilClipOp::Make(fRenderTargetContext->fContext,
- clip, insideStencilMask, rtProxy));
- if (!op) {
- return;
+ fRenderTargetContext->internalStencilClear(clip, insideStencilMask);
+}
+
+void GrRenderTargetContext::internalStencilClear(const GrFixedClip& clip, bool insideStencilMask) {
+ if (this->caps()->performStencilClearsAsDraws()) {
+ const GrUserStencilSettings* ss = GrStencilSettings::SetClipBitSettings(insideStencilMask);
+ SkRect rtRect = SkRect::MakeWH(this->width(), this->height());
+
+ // Configure the paint to have no impact on the color buffer
+ GrPaint paint;
+ paint.setColor4f({0.f, 0.f, 0.f, 0.f});
+ paint.setPorterDuffXPFactory(SkBlendMode::kSrcOver);
+
+ // Mark stencil usage here before addDrawOp() so that it doesn't try to re-call
+ // internalStencilClear() just because the op has stencil settings.
+ this->setNeedsStencil();
+ this->addDrawOp(clip, GrFillRectOp::Make(fContext, std::move(paint),
+ GrAAType::kNone, SkMatrix::I(), rtRect, ss));
+ } else {
+ std::unique_ptr<GrOp> op(GrClearStencilClipOp::Make(fContext, clip, insideStencilMask,
+ fRenderTargetProxy.get()));
+ if (!op) {
+ return;
+ }
+ this->getRTOpList()->addOp(std::move(op), *this->caps());
}
- fRenderTargetContext->getRTOpList()->addOp(std::move(op), *fRenderTargetContext->caps());
}
void GrRenderTargetContextPriv::stencilPath(const GrHardClip& clip,
@@ -1763,7 +1840,19 @@
if (fixedFunctionFlags & GrDrawOp::FixedFunctionFlags::kUsesStencil ||
appliedClip.hasStencilClip()) {
- this->getOpList()->setStencilLoadOp(GrLoadOp::kClear);
+ if (this->caps()->performStencilClearsAsDraws()) {
+ // Must use an op to perform the clear of the stencil buffer before this op, but only
+ // have to clear the first time any draw needs it (this also ensures we don't loop
+ // forever when the internal stencil clear adds a draw op that has stencil settings).
+ if (!fRenderTargetProxy->needsStencil()) {
+ // Send false so that the stencil buffer is fully cleared to 0
+ this->internalStencilClear(GrFixedClip::Disabled(), /* inside mask */ false);
+ }
+ } else {
+ // Just make sure the stencil buffer is cleared before the draw op, easy to do it as
+ // a load at the start
+ this->getRTOpList()->setStencilLoadOp(GrLoadOp::kClear);
+ }
this->setNeedsStencil();
}
diff --git a/src/gpu/GrRenderTargetContext.h b/src/gpu/GrRenderTargetContext.h
index f66acc5..ff6593d 100644
--- a/src/gpu/GrRenderTargetContext.h
+++ b/src/gpu/GrRenderTargetContext.h
@@ -453,6 +453,7 @@
sk_sp<GrTextureProxy>);
void internalClear(const GrFixedClip&, const SkPMColor4f&, CanClearFullscreen);
+ void internalStencilClear(const GrFixedClip&, bool insideStencilMask);
// Only consumes the GrPaint if successful.
bool drawFilledDRRect(const GrClip& clip,
diff --git a/src/gpu/GrRenderTargetOpList.cpp b/src/gpu/GrRenderTargetOpList.cpp
index 5e4db11..606fe37 100644
--- a/src/gpu/GrRenderTargetOpList.cpp
+++ b/src/gpu/GrRenderTargetOpList.cpp
@@ -458,6 +458,12 @@
// TODO: at the very least, we want the stencil store op to always be discard (at this
// level). In Vulkan, sub-command buffers would still need to load & store the stencil buffer.
+
+ // Make sure load ops are not kClear if the GPU needs to use draws for clears
+ SkASSERT(fColorLoadOp != GrLoadOp::kClear ||
+ !flushState->gpu()->caps()->performColorClearsAsDraws());
+ SkASSERT(fStencilLoadOp != GrLoadOp::kClear ||
+ !flushState->gpu()->caps()->performStencilClearsAsDraws());
GrGpuRTCommandBuffer* commandBuffer = create_command_buffer(
flushState->gpu(),
fTarget.get()->peekRenderTarget(),
@@ -513,13 +519,28 @@
}
}
-void GrRenderTargetOpList::fullClear(GrContext* context, const SkPMColor4f& color) {
+void GrRenderTargetOpList::setStencilLoadOp(GrLoadOp op) {
+ fStencilLoadOp = op;
+}
- // This is conservative. If the opList is marked as needing a stencil buffer then there
- // may be a prior op that writes to the stencil buffer. Although the clear will ignore the
- // stencil buffer, following draw ops may not so we can't get rid of all the preceding ops.
- // Beware! If we ever add any ops that have a side effect beyond modifying the stencil
- // buffer we will need a more elaborate tracking system (skbug.com/7002).
+void GrRenderTargetOpList::setColorLoadOp(GrLoadOp op, const SkPMColor4f& color) {
+ fColorLoadOp = op;
+ fLoadClearColor = color;
+}
+
+bool GrRenderTargetOpList::resetForFullscreenClear() {
+ // Mark the color load op as discard (this may be followed by a clearColorOnLoad call to make
+ // the load op kClear, or it may be followed by an explicit op). In the event of an absClear()
+ // after a regular clear(), we could end up with a clear load op and a real clear op in the list
+ // if the load op were not reset here.
+ fColorLoadOp = GrLoadOp::kDiscard;
+
+ // Regardless of how the clear is implemented (native clear or a fullscreen quad), all prior ops
+ // would be overwritten, so discard them entirely. The one exception is if the opList is marked
+ // as needing a stencil buffer then there may be a prior op that writes to the stencil buffer.
+ // Although the clear will ignore the stencil buffer, following draw ops may not so we can't get
+ // rid of all the preceding ops. Beware! If we ever add any ops that have a side effect beyond
+ // modifying the stencil buffer we will need a more elaborate tracking system (skbug.com/7002).
if (this->isEmpty() || !fTarget.get()->asRenderTargetProxy()->needsStencil()) {
this->deleteOps();
fDeferredProxies.reset();
@@ -527,20 +548,11 @@
// If the opList is using a render target which wraps a vulkan command buffer, we can't do a
// clear load since we cannot change the render pass that we are using. Thus we fall back to
// making a clear op in this case.
- if (!fTarget.get()->asRenderTargetProxy()->wrapsVkSecondaryCB()) {
- fColorLoadOp = GrLoadOp::kClear;
- fLoadClearColor = color;
- return;
- }
+ return !fTarget.get()->asRenderTargetProxy()->wrapsVkSecondaryCB();
}
- std::unique_ptr<GrClearOp> op(GrClearOp::Make(context, GrFixedClip::Disabled(),
- color, fTarget.get()));
- if (!op) {
- return;
- }
-
- this->recordOp(std::move(op), *context->contextPriv().caps());
+ // Could not empty the list, so an op must be added to handle the clear
+ return false;
}
////////////////////////////////////////////////////////////////////////////////
diff --git a/src/gpu/GrRenderTargetOpList.h b/src/gpu/GrRenderTargetOpList.h
index 6eb19ec..5d58068 100644
--- a/src/gpu/GrRenderTargetOpList.h
+++ b/src/gpu/GrRenderTargetOpList.h
@@ -88,9 +88,6 @@
void discard();
- /** Clears the entire render target */
- void fullClear(GrContext*, const SkPMColor4f& color);
-
/**
* Copies a pixel rectangle from one surface to another. This call may finalize
* reserved vertex/index data (as though a draw call was made). The src pixels
@@ -116,6 +113,27 @@
private:
friend class GrRenderTargetContextPriv; // for stencil clip state. TODO: this is invasive
+ // The RTC and RTOpList have to work together to handle buffer clears. In most cases, buffer
+ // clearing can be done natively, in which case the op list's load ops are sufficient. In other
+ // cases, draw ops must be used, which makes the RTC the best place for those decisions. This,
+ // however, requires that the RTC be able to coordinate with the op list to achieve similar ends
+ friend class GrRenderTargetContext;
+
+ // Must only be called if native stencil buffer clearing is enabled
+ void setStencilLoadOp(GrLoadOp op);
+ // Must only be called if native color buffer clearing is enabled.
+ void setColorLoadOp(GrLoadOp op, const SkPMColor4f& color);
+ // Sets the clear color to transparent black
+ void setColorLoadOp(GrLoadOp op) {
+ static const SkPMColor4f kDefaultClearColor = {0.f, 0.f, 0.f, 0.f};
+ this->setColorLoadOp(op, kDefaultClearColor);
+ }
+
+ // Perform book-keeping for a fullscreen clear, regardless of how the clear is implemented later
+ // (i.e. setColorLoadOp(), adding a ClearOp, or adding a GrFillRectOp that covers the device).
+ // Returns true if the clear can be converted into a load op (barring device caps).
+ bool resetForFullscreenClear();
+
void deleteOps();
class OpChain {
diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp
index d0aadd3..2c8b602 100644
--- a/src/gpu/gl/GrGLGpu.cpp
+++ b/src/gpu/gl/GrGLGpu.cpp
@@ -2157,14 +2157,11 @@
GrRenderTarget* target, GrSurfaceOrigin origin) {
// parent class should never let us get here with no RT
SkASSERT(target);
+ SkASSERT(!this->caps()->performColorClearsAsDraws());
+ SkASSERT(!clip.scissorEnabled() || !this->caps()->performPartialClearsAsDraws());
this->handleDirtyContext();
- if (this->caps()->performColorClearsAsDraws()) {
- this->clearColorAsDraw(clip, color, target, origin);
- return;
- }
-
GrGLRenderTarget* glRT = static_cast<GrGLRenderTarget*>(target);
if (clip.scissorEnabled()) {
@@ -2191,6 +2188,8 @@
}
void GrGLGpu::clearStencil(GrRenderTarget* target, int clearValue) {
+ SkASSERT(!this->caps()->performStencilClearsAsDraws());
+
if (!target) {
return;
}
@@ -2219,13 +2218,9 @@
bool insideStencilMask,
GrRenderTarget* target, GrSurfaceOrigin origin) {
SkASSERT(target);
+ SkASSERT(!this->caps()->performStencilClearsAsDraws());
this->handleDirtyContext();
- if (this->caps()->performStencilClearsAsDraws()) {
- this->clearStencilClipAsDraw(clip, insideStencilMask, target, origin);
- return;
- }
-
GrStencilAttachment* sb = target->renderTargetPriv().getStencilAttachment();
// this should only be called internally when we know we have a
// stencil buffer.
@@ -3596,234 +3591,6 @@
return true;
}
-bool GrGLGpu::createStencilClipClearProgram() {
- TRACE_EVENT0("skia", TRACE_FUNC);
-
- 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(!fStencilClipClearProgram);
- GL_CALL_RET(fStencilClipClearProgram, CreateProgram());
- if (!fStencilClipClearProgram) {
- return false;
- }
-
- GrShaderVar aVertex("a_vertex", kHalf2_GrSLType, GrShaderVar::kIn_TypeModifier);
- const char* version = this->caps()->shaderCaps()->versionDeclString();
-
- SkString vshaderTxt(version);
- aVertex.appendDecl(this->caps()->shaderCaps(), &vshaderTxt);
- vshaderTxt.append(";");
- vshaderTxt.append(
- "// Stencil Clip Clear Program VS\n"
- "void main() {"
- " sk_Position = float4(a_vertex.x, a_vertex.y, 0, 1);"
- "}");
-
- SkString fshaderTxt(version);
- fshaderTxt.appendf(
- "// Stencil Clip Clear Program FS\n"
- "void main() {"
- " sk_FragColor = half4(0);"
- "}");
-
- const char* str;
- GrGLint length;
-
- str = vshaderTxt.c_str();
- length = SkToInt(vshaderTxt.size());
- SkSL::Program::Settings settings;
- settings.fCaps = this->caps()->shaderCaps();
- SkSL::String glsl;
- std::unique_ptr<SkSL::Program> program = GrSkSLtoGLSL(*fGLContext, GR_GL_VERTEX_SHADER,
- &str, &length, 1, settings, &glsl);
- GrGLuint vshader = GrGLCompileAndAttachShader(*fGLContext, fStencilClipClearProgram,
- GR_GL_VERTEX_SHADER, glsl.c_str(), glsl.size(),
- &fStats, settings);
- SkASSERT(program->fInputs.isEmpty());
-
- str = fshaderTxt.c_str();
- length = SkToInt(fshaderTxt.size());
- program = GrSkSLtoGLSL(*fGLContext, GR_GL_FRAGMENT_SHADER, &str, &length, 1, settings, &glsl);
- GrGLuint fshader = GrGLCompileAndAttachShader(*fGLContext, fStencilClipClearProgram,
- GR_GL_FRAGMENT_SHADER, glsl.c_str(), glsl.size(),
- &fStats, settings);
- SkASSERT(program->fInputs.isEmpty());
-
- GL_CALL(LinkProgram(fStencilClipClearProgram));
-
- GL_CALL(BindAttribLocation(fStencilClipClearProgram, 0, "a_vertex"));
-
- GL_CALL(DeleteShader(vshader));
- GL_CALL(DeleteShader(fshader));
-
- return true;
-}
-
-void GrGLGpu::clearStencilClipAsDraw(const GrFixedClip& clip, bool insideStencilMask,
- GrRenderTarget* rt, GrSurfaceOrigin origin) {
- // TODO: This should swizzle the output to match dst's config, though it is a debugging
- // visualization.
-
- this->handleDirtyContext();
- if (!fStencilClipClearProgram) {
- if (!this->createStencilClipClearProgram()) {
- SkDebugf("Failed to create stencil clip clear program.\n");
- return;
- }
- }
-
- GrGLRenderTarget* glRT = static_cast<GrGLRenderTarget*>(rt->asRenderTarget());
- this->flushRenderTarget(glRT);
-
- this->flushProgram(fStencilClipClearProgram);
-
- fHWVertexArrayState.setVertexArrayID(this, 0);
-
- GrGLAttribArrayState* attribs = fHWVertexArrayState.bindInternalVertexArray(this);
- attribs->enableVertexArrays(this, 1);
- attribs->set(this, 0, fStencilClipClearArrayBuffer.get(), kFloat2_GrVertexAttribType,
- kFloat2_GrSLType, 2 * sizeof(GrGLfloat), 0);
-
- GrXferProcessor::BlendInfo blendInfo;
- blendInfo.reset();
- this->flushBlend(blendInfo, GrSwizzle::RGBA());
- this->flushColorWrite(false);
- this->flushHWAAState(glRT, false, false);
- this->flushScissor(clip.scissorState(), glRT->getViewport(), origin);
- this->flushWindowRectangles(clip.windowRectsState(), glRT, origin);
- 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));
-}
-
-bool GrGLGpu::createClearColorProgram() {
- TRACE_EVENT0("skia", TRACE_FUNC);
-
- if (!fClearProgramArrayBuffer) {
- static const GrGLfloat vdata[] = {-1, -1, 1, -1, -1, 1, 1, 1};
- fClearProgramArrayBuffer.reset(GrGLBuffer::Create(this, sizeof(vdata), kVertex_GrBufferType,
- kStatic_GrAccessPattern, vdata));
- if (!fClearProgramArrayBuffer) {
- return false;
- }
- }
-
- SkASSERT(!fClearColorProgram.fProgram);
- GL_CALL_RET(fClearColorProgram.fProgram, CreateProgram());
- if (!fClearColorProgram.fProgram) {
- return false;
- }
-
- GrShaderVar aVertex("a_vertex", kHalf2_GrSLType, GrShaderVar::kIn_TypeModifier);
- const char* version = this->caps()->shaderCaps()->versionDeclString();
-
- SkString vshaderTxt(version);
- aVertex.appendDecl(this->caps()->shaderCaps(), &vshaderTxt);
- vshaderTxt.append(";");
- vshaderTxt.append(R"(
- // Clear Color Program VS
- void main() {
- sk_Position = float4(a_vertex.x, a_vertex.y, 0, 1);
- })");
-
- GrShaderVar uColor("u_color", kHalf4_GrSLType, GrShaderVar::kUniform_TypeModifier);
- SkString fshaderTxt(version);
- uColor.appendDecl(this->caps()->shaderCaps(), &fshaderTxt);
- fshaderTxt.append(";");
- fshaderTxt.appendf(R"(
- // Clear Color Program FS
- void main() {
- sk_FragColor = u_color;
- })");
-
- const char* str;
- GrGLint length;
-
- str = vshaderTxt.c_str();
- length = SkToInt(vshaderTxt.size());
- SkSL::Program::Settings settings;
- settings.fCaps = this->caps()->shaderCaps();
- SkSL::String glsl;
- GrSkSLtoGLSL(*fGLContext, GR_GL_VERTEX_SHADER, &str, &length, 1, settings, &glsl);
- GrGLuint vshader = GrGLCompileAndAttachShader(*fGLContext, fClearColorProgram.fProgram,
- GR_GL_VERTEX_SHADER, glsl.c_str(), glsl.size(),
- &fStats, settings);
-
- str = fshaderTxt.c_str();
- length = SkToInt(fshaderTxt.size());
- GrSkSLtoGLSL(*fGLContext, GR_GL_FRAGMENT_SHADER, &str, &length, 1, settings, &glsl);
- GrGLuint fshader = GrGLCompileAndAttachShader(*fGLContext, fClearColorProgram.fProgram,
- GR_GL_FRAGMENT_SHADER, glsl.c_str(), glsl.size(),
- &fStats, settings);
-
- GL_CALL(LinkProgram(fClearColorProgram.fProgram));
-
- GL_CALL(BindAttribLocation(fClearColorProgram.fProgram, 0, "a_vertex"));
-
- GL_CALL_RET(fClearColorProgram.fColorUniform,
- GetUniformLocation(fClearColorProgram.fProgram, "u_color"));
-
- GL_CALL(DeleteShader(vshader));
- GL_CALL(DeleteShader(fshader));
-
- return true;
-}
-
-void GrGLGpu::clearColorAsDraw(const GrFixedClip& clip, const SkPMColor4f& color,
- GrRenderTarget* dst, GrSurfaceOrigin origin) {
- if (!fClearColorProgram.fProgram) {
- if (!this->createClearColorProgram()) {
- SkDebugf("Failed to create clear color program.\n");
- return;
- }
- }
-
- GrGLIRect dstVP;
- this->bindSurfaceFBOForPixelOps(dst, GR_GL_FRAMEBUFFER, &dstVP, kDst_TempFBOTarget);
- this->flushViewport(dstVP);
- fHWBoundRenderTargetUniqueID.makeInvalid();
-
- this->flushProgram(fClearColorProgram.fProgram);
-
- fHWVertexArrayState.setVertexArrayID(this, 0);
-
- GrGLAttribArrayState* attribs = fHWVertexArrayState.bindInternalVertexArray(this);
- attribs->enableVertexArrays(this, 1);
- attribs->set(this, 0, fClearProgramArrayBuffer.get(), kFloat2_GrVertexAttribType,
- kFloat2_GrSLType, 2 * sizeof(GrGLfloat), 0);
-
- GrGLRenderTarget* glrt = static_cast<GrGLRenderTarget*>(dst);
- this->flushScissor(clip.scissorState(), glrt->getViewport(), origin);
- this->flushWindowRectangles(clip.windowRectsState(), glrt, origin);
-
- GL_CALL(Uniform4f(fClearColorProgram.fColorUniform, color.fR, color.fG, color.fB, color.fA));
-
- GrXferProcessor::BlendInfo blendInfo;
- blendInfo.reset();
- this->flushBlend(blendInfo, GrSwizzle::RGBA());
- this->flushColorWrite(true);
- this->flushHWAAState(nullptr, false, false);
- this->disableStencil();
- if (this->glCaps().srgbWriteControl()) {
- this->flushFramebufferSRGB(true);
- }
-
- GL_CALL(DrawArrays(GR_GL_TRIANGLE_STRIP, 0, 4));
- this->unbindTextureFBOForPixelOps(GR_GL_FRAMEBUFFER, dst);
- this->didWriteToSurface(dst, origin, clip.scissorEnabled() ? &clip.scissorRect() : nullptr);
-}
-
bool GrGLGpu::copySurfaceAsDraw(GrSurface* dst, GrSurfaceOrigin dstOrigin,
GrSurface* src, GrSurfaceOrigin srcOrigin,
const SkIRect& srcRect,
diff --git a/src/gpu/gl/GrGLGpu.h b/src/gpu/gl/GrGLGpu.h
index f6524b0..75466cf 100644
--- a/src/gpu/gl/GrGLGpu.h
+++ b/src/gpu/gl/GrGLGpu.h
@@ -109,8 +109,6 @@
// Thus this is the implementation of the clear call for the corresponding passthrough function
// on GrGLGpuRTCommandBuffer.
void clear(const GrFixedClip&, const SkPMColor4f&, GrRenderTarget*, GrSurfaceOrigin);
- void clearColorAsDraw(const GrFixedClip&, const SkPMColor4f& color, GrRenderTarget*,
- GrSurfaceOrigin);
// The GrGLGpuRTCommandBuffer does not buffer up draws before submitting them to the gpu.
// Thus this is the implementation of the clearStencil call for the corresponding passthrough
@@ -118,6 +116,8 @@
void clearStencilClip(const GrFixedClip&, bool insideStencilMask,
GrRenderTarget*, GrSurfaceOrigin);
+ // FIXME (michaelludwig): Can this go away and just use clearStencilClip() + marking the
+ // stencil buffer as not dirty?
void clearStencil(GrRenderTarget*, int clearValue);
GrGpuRTCommandBuffer* getCommandBuffer(
@@ -298,8 +298,6 @@
bool copySurfaceAsBlitFramebuffer(GrSurface* dst, GrSurfaceOrigin dstOrigin,
GrSurface* src, GrSurfaceOrigin srcOrigin,
const SkIRect& srcRect, const SkIPoint& dstPoint);
- void clearStencilClipAsDraw(const GrFixedClip&, bool insideStencilMask,
- GrRenderTarget*, GrSurfaceOrigin);
static bool BlendCoeffReferencesConstant(GrBlendCoeff coeff);
@@ -417,8 +415,6 @@
bool createCopyProgram(GrTexture* srcTexture);
bool createMipmapProgram(int progIdx);
- bool createStencilClipClearProgram();
- bool createClearColorProgram();
std::unique_ptr<GrGLContext> fGLContext;