Add a cap for when dual sided stencil refs and masks must match
Change-Id: Ice00a00cd7185131578669e9c1ca865341f0ed0f
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/283274
Reviewed-by: Greg Daniel <egdaniel@google.com>
Commit-Queue: Chris Dalton <csmartdalton@google.com>
diff --git a/src/gpu/GrCaps.cpp b/src/gpu/GrCaps.cpp
index 530e384..43b8996 100644
--- a/src/gpu/GrCaps.cpp
+++ b/src/gpu/GrCaps.cpp
@@ -33,6 +33,7 @@
fUsePrimitiveRestart = false;
fPreferClientSideDynamicBuffers = false;
fPreferFullscreenClears = false;
+ fTwoSidedStencilRefsAndMasksMustMatch = false;
fMustClearUploadedBufferData = false;
fShouldInitializeTextures = false;
fSupportsAHardwareBufferImages = false;
@@ -206,6 +207,8 @@
writer->appendBool("Use primitive restart", fUsePrimitiveRestart);
writer->appendBool("Prefer client-side dynamic buffers", fPreferClientSideDynamicBuffers);
writer->appendBool("Prefer fullscreen clears (and stencil discard)", fPreferFullscreenClears);
+ writer->appendBool("Two-sided Stencil Refs And Masks Must Match",
+ fTwoSidedStencilRefsAndMasksMustMatch);
writer->appendBool("Must clear buffer memory", fMustClearUploadedBufferData);
writer->appendBool("Should initialize textures", fShouldInitializeTextures);
writer->appendBool("Supports importing AHardwareBuffers", fSupportsAHardwareBufferImages);
diff --git a/src/gpu/GrCaps.h b/src/gpu/GrCaps.h
index 5ddd661..3ef0c61 100644
--- a/src/gpu/GrCaps.h
+++ b/src/gpu/GrCaps.h
@@ -83,6 +83,11 @@
return this->preferFullscreenClears();
}
+ // D3D does not allow the refs or masks to differ on a two-sided stencil draw.
+ bool twoSidedStencilRefsAndMasksMustMatch() const {
+ return fTwoSidedStencilRefsAndMasksMustMatch;
+ }
+
bool preferVRAMUseOverFlushes() const { return fPreferVRAMUseOverFlushes; }
bool preferTrianglesOverSampleMask() const { return fPreferTrianglesOverSampleMask; }
@@ -473,6 +478,7 @@
bool fUsePrimitiveRestart : 1;
bool fPreferClientSideDynamicBuffers : 1;
bool fPreferFullscreenClears : 1;
+ bool fTwoSidedStencilRefsAndMasksMustMatch : 1;
bool fMustClearUploadedBufferData : 1;
bool fShouldInitializeTextures : 1;
bool fSupportsAHardwareBufferImages : 1;
diff --git a/src/gpu/GrOpsRenderPass.cpp b/src/gpu/GrOpsRenderPass.cpp
index 060b9fc..66e346b 100644
--- a/src/gpu/GrOpsRenderPass.cpp
+++ b/src/gpu/GrOpsRenderPass.cpp
@@ -77,6 +77,15 @@
if (programInfo.pipeline().isWireframe()) {
SkASSERT(this->gpu()->caps()->wireframeSupport());
}
+ if (this->gpu()->caps()->twoSidedStencilRefsAndMasksMustMatch() &&
+ programInfo.pipeline().isStencilEnabled()) {
+ const GrUserStencilSettings* stencil = programInfo.pipeline().getUserStencil();
+ if (stencil->isTwoSided(programInfo.pipeline().hasStencilClip())) {
+ SkASSERT(stencil->fCCWFace.fRef == stencil->fCWFace.fRef);
+ SkASSERT(stencil->fCCWFace.fTestMask == stencil->fCWFace.fTestMask);
+ SkASSERT(stencil->fCCWFace.fWriteMask == stencil->fCWFace.fWriteMask);
+ }
+ }
if (GrPrimitiveType::kPatches == programInfo.primitiveType()) {
SkASSERT(this->gpu()->caps()->shaderCaps()->tessellationSupport());
}
diff --git a/src/gpu/ccpr/GrStencilAtlasOp.cpp b/src/gpu/ccpr/GrStencilAtlasOp.cpp
index 1f8f777..3422592 100644
--- a/src/gpu/ccpr/GrStencilAtlasOp.cpp
+++ b/src/gpu/ccpr/GrStencilAtlasOp.cpp
@@ -116,6 +116,28 @@
0xffff, 0xffff>()
);
+// Same as above, but done in two passes for D3D, which doesn't support mismatched refs or masks on
+// dual sided stencil settings.
+static constexpr GrUserStencilSettings kResolveWindingCoverageAndReset(
+ GrUserStencilSettings::StaticInitSeparate<
+ 0x0000, 0x0000,
+ GrUserStencilTest::kNotEqual, GrUserStencilTest::kNever,
+ 0xffff, 0xffff,
+ GrUserStencilOp::kZero, GrUserStencilOp::kKeep,
+ GrUserStencilOp::kKeep, GrUserStencilOp::kKeep,
+ 0xffff, 0xffff>()
+);
+static constexpr GrUserStencilSettings kResolveEvenOddCoverageAndReset(
+ GrUserStencilSettings::StaticInitSeparate<
+ 0x0000, 0x0000,
+ GrUserStencilTest::kNever, GrUserStencilTest::kNotEqual,
+ 0x0001, 0x0001,
+ GrUserStencilOp::kKeep, GrUserStencilOp::kZero,
+ GrUserStencilOp::kKeep, GrUserStencilOp::kZero,
+ 0xffff, 0xffff>()
+);
+
+
void GrStencilAtlasOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
SkIRect drawBoundsRect = SkIRect::MakeWH(fDrawBounds.width(), fDrawBounds.height());
@@ -135,27 +157,41 @@
// not necessary, and will even cause artifacts if using mixed samples.
constexpr auto noHWAA = GrPipeline::InputFlags::kNone;
- const auto* stencilResolveSettings = (flushState->caps().discardStencilValuesAfterRenderPass())
- // The next draw will be the final op in the renderTargetContext. So if Ganesh is
- // planning to discard the stencil values anyway, we don't actually need to reset them
- // back to zero.
- ? &kResolveStencilCoverage
- : &kResolveStencilCoverageAndReset;
-
GrPipeline resolvePipeline(GrScissorTest::kEnabled, SkBlendMode::kSrc,
- flushState->drawOpArgs().writeSwizzle(), noHWAA,
- stencilResolveSettings);
-
+ flushState->drawOpArgs().writeSwizzle(), noHWAA);
StencilResolveProcessor primProc;
+ if (!flushState->caps().twoSidedStencilRefsAndMasksMustMatch()) {
+ if (flushState->caps().discardStencilValuesAfterRenderPass()) {
+ resolvePipeline.setUserStencil(&kResolveStencilCoverage);
+ } else {
+ resolvePipeline.setUserStencil(&kResolveStencilCoverageAndReset);
+ }
+ this->drawResolve(flushState, resolvePipeline, primProc, drawBoundsRect);
+ return;
+ }
+
+ // If this ever becomes true then we should add new per-fill-type stencil settings that also
+ // don't reset back to zero.
+ SkASSERT(!flushState->caps().discardStencilValuesAfterRenderPass());
+
+ resolvePipeline.setUserStencil(&kResolveWindingCoverageAndReset);
+ this->drawResolve(flushState, resolvePipeline, primProc, drawBoundsRect);
+
+ resolvePipeline.setUserStencil(&kResolveEvenOddCoverageAndReset);
+ this->drawResolve(flushState, resolvePipeline, primProc, drawBoundsRect);
+}
+
+void GrStencilAtlasOp::drawResolve(GrOpFlushState* flushState, const GrPipeline& resolvePipeline,
+ const GrPrimitiveProcessor& primProc,
+ const SkIRect& drawBounds) const {
GrProgramInfo programInfo(flushState->proxy()->numSamples(),
flushState->proxy()->numStencilSamples(),
flushState->proxy()->backendFormat(),
flushState->writeView()->origin(), &resolvePipeline, &primProc,
GrPrimitiveType::kTriangleStrip);
-
- flushState->bindPipeline(programInfo, SkRect::Make(drawBoundsRect));
- flushState->setScissorRect(drawBoundsRect);
+ flushState->bindPipeline(programInfo, SkRect::Make(drawBounds));
+ flushState->setScissorRect(drawBounds);
flushState->bindBuffers(nullptr, fResources->stencilResolveBuffer(), nullptr);
flushState->drawInstanced(fEndStencilResolveInstance - fBaseStencilResolveInstance,
fBaseStencilResolveInstance, 4, 0);
diff --git a/src/gpu/ccpr/GrStencilAtlasOp.h b/src/gpu/ccpr/GrStencilAtlasOp.h
index 6981fed..7cce205 100644
--- a/src/gpu/ccpr/GrStencilAtlasOp.h
+++ b/src/gpu/ccpr/GrStencilAtlasOp.h
@@ -59,6 +59,8 @@
const GrXferProcessor::DstProxyView&) override {}
void onPrepare(GrOpFlushState*) override {}
void onExecute(GrOpFlushState*, const SkRect& chainBounds) override;
+ void drawResolve(GrOpFlushState*, const GrPipeline&, const GrPrimitiveProcessor&,
+ const SkIRect& drawBounds) const;
friend class ::GrOpMemoryPool; // for ctor