| 1 | /* | 
|---|
| 2 | * Copyright 2019 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 "src/gpu/ccpr/GrStencilAtlasOp.h" | 
|---|
| 9 |  | 
|---|
| 10 | #include "include/private/GrRecordingContext.h" | 
|---|
| 11 | #include "src/gpu/GrOpFlushState.h" | 
|---|
| 12 | #include "src/gpu/GrOpsRenderPass.h" | 
|---|
| 13 | #include "src/gpu/GrProgramInfo.h" | 
|---|
| 14 | #include "src/gpu/GrRecordingContextPriv.h" | 
|---|
| 15 | #include "src/gpu/ccpr/GrCCPerFlushResources.h" | 
|---|
| 16 | #include "src/gpu/ccpr/GrSampleMaskProcessor.h" | 
|---|
| 17 | #include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h" | 
|---|
| 18 | #include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h" | 
|---|
| 19 |  | 
|---|
| 20 | namespace { | 
|---|
| 21 |  | 
|---|
| 22 | class StencilResolveProcessor : public GrGeometryProcessor { | 
|---|
| 23 | public: | 
|---|
| 24 | StencilResolveProcessor() : INHERITED(kStencilResolveProcessor_ClassID) { | 
|---|
| 25 | static constexpr Attribute kIBounds = { | 
|---|
| 26 | "ibounds", kShort4_GrVertexAttribType, kShort4_GrSLType}; | 
|---|
| 27 | this->setInstanceAttributes(&kIBounds, 1); | 
|---|
| 28 | SkASSERT(this->instanceStride() == sizeof(GrStencilAtlasOp::ResolveRectInstance)); | 
|---|
| 29 | } | 
|---|
| 30 |  | 
|---|
| 31 | private: | 
|---|
| 32 | const char* name() const final { return "StencilResolveProcessor"; } | 
|---|
| 33 | void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const final {} | 
|---|
| 34 | GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const final; | 
|---|
| 35 | class Impl; | 
|---|
| 36 |  | 
|---|
| 37 | typedef GrGeometryProcessor INHERITED; | 
|---|
| 38 | }; | 
|---|
| 39 |  | 
|---|
| 40 | // This processor draws pixel-aligned rectangles directly on top of every path in the atlas. | 
|---|
| 41 | // The caller should have set up the instance data such that "Nonzero" paths get clockwise | 
|---|
| 42 | // rectangles (l < r) and "even/odd" paths get counter-clockwise (r < l). Its purpose | 
|---|
| 43 | // is to convert winding counts in the stencil buffer to A8 coverage in the color buffer. | 
|---|
| 44 | class StencilResolveProcessor::Impl : public GrGLSLGeometryProcessor { | 
|---|
| 45 | void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override { | 
|---|
| 46 | args.fVaryingHandler->emitAttributes(args.fGP.cast<StencilResolveProcessor>()); | 
|---|
| 47 |  | 
|---|
| 48 | GrGLSLVertexBuilder* v = args.fVertBuilder; | 
|---|
| 49 | v->codeAppendf( "short2 devcoord;"); | 
|---|
| 50 | v->codeAppendf( "devcoord.x = (0 == (sk_VertexID & 1)) ? ibounds.x : ibounds.z;"); | 
|---|
| 51 | v->codeAppendf( "devcoord.y = (sk_VertexID < 2) ? ibounds.y : ibounds.w;"); | 
|---|
| 52 |  | 
|---|
| 53 | v->codeAppendf( "float2 atlascoord = float2(devcoord);"); | 
|---|
| 54 | gpArgs->fPositionVar.set(kFloat2_GrSLType, "atlascoord"); | 
|---|
| 55 |  | 
|---|
| 56 | // Just output "1" for coverage. This will be modulated by the MSAA stencil test. | 
|---|
| 57 | GrGLSLFPFragmentBuilder* f = args.fFragBuilder; | 
|---|
| 58 | f->codeAppendf( "%s = %s = half4(1);", args.fOutputColor, args.fOutputCoverage); | 
|---|
| 59 | } | 
|---|
| 60 |  | 
|---|
| 61 | void setData(const GrGLSLProgramDataManager&, const GrPrimitiveProcessor&, | 
|---|
| 62 | const CoordTransformRange&) override {} | 
|---|
| 63 | }; | 
|---|
| 64 |  | 
|---|
| 65 | GrGLSLPrimitiveProcessor* StencilResolveProcessor::createGLSLInstance(const GrShaderCaps&) const { | 
|---|
| 66 | return new Impl(); | 
|---|
| 67 | } | 
|---|
| 68 |  | 
|---|
| 69 | } | 
|---|
| 70 |  | 
|---|
| 71 | std::unique_ptr<GrDrawOp> GrStencilAtlasOp::Make( | 
|---|
| 72 | GrRecordingContext* context, sk_sp<const GrCCPerFlushResources> resources, | 
|---|
| 73 | FillBatchID fillBatchID, StrokeBatchID strokeBatchID, int baseStencilResolveInstance, | 
|---|
| 74 | int endStencilResolveInstance, const SkISize& drawBounds) { | 
|---|
| 75 | GrOpMemoryPool* pool = context->priv().opMemoryPool(); | 
|---|
| 76 |  | 
|---|
| 77 | return pool->allocate<GrStencilAtlasOp>( | 
|---|
| 78 | std::move(resources), fillBatchID, strokeBatchID, baseStencilResolveInstance, | 
|---|
| 79 | endStencilResolveInstance, drawBounds); | 
|---|
| 80 | } | 
|---|
| 81 |  | 
|---|
| 82 | // Increments clockwise triangles and decrements counterclockwise. We use the same incr/decr | 
|---|
| 83 | // settings regardless of fill rule; fill rule is accounted for during the resolve step. | 
|---|
| 84 | static constexpr GrUserStencilSettings kIncrDecrStencil( | 
|---|
| 85 | GrUserStencilSettings::StaticInitSeparate< | 
|---|
| 86 | 0x0000,                        0x0000, | 
|---|
| 87 | GrUserStencilTest::kNever,     GrUserStencilTest::kNever, | 
|---|
| 88 | 0xffff,                        0xffff, | 
|---|
| 89 | GrUserStencilOp::kIncWrap,     GrUserStencilOp::kDecWrap, | 
|---|
| 90 | GrUserStencilOp::kIncWrap,     GrUserStencilOp::kDecWrap, | 
|---|
| 91 | 0xffff,                        0xffff>() | 
|---|
| 92 | ); | 
|---|
| 93 |  | 
|---|
| 94 | // Resolves stencil winding counts to A8 coverage. Leaves stencil values untouched. | 
|---|
| 95 | // NOTE: For the CCW face we intentionally use "1 == (stencil & 1)" because the contrapositive logic | 
|---|
| 96 | // (i.e. 0 != ...) causes bugs on Adreno Vulkan. http://skbug.com/9643 | 
|---|
| 97 | static constexpr GrUserStencilSettings kResolveStencilCoverage( | 
|---|
| 98 | GrUserStencilSettings::StaticInitSeparate< | 
|---|
| 99 | 0x0000,                           0x0001, | 
|---|
| 100 | GrUserStencilTest::kNotEqual,     GrUserStencilTest::kEqual, | 
|---|
| 101 | 0xffff,                           0x0001, | 
|---|
| 102 | GrUserStencilOp::kKeep,           GrUserStencilOp::kKeep, | 
|---|
| 103 | GrUserStencilOp::kKeep,           GrUserStencilOp::kKeep, | 
|---|
| 104 | 0xffff,                           0xffff>() | 
|---|
| 105 | ); | 
|---|
| 106 |  | 
|---|
| 107 | // Same as above, but also resets stencil values to zero. This is better for non-tilers | 
|---|
| 108 | // where we prefer to not clear the stencil buffer at the beginning of every render pass. | 
|---|
| 109 | static constexpr GrUserStencilSettings kResolveStencilCoverageAndReset( | 
|---|
| 110 | GrUserStencilSettings::StaticInitSeparate< | 
|---|
| 111 | 0x0000,                           0x0000, | 
|---|
| 112 | GrUserStencilTest::kNotEqual,     GrUserStencilTest::kNotEqual, | 
|---|
| 113 | 0xffff,                           0x0001, | 
|---|
| 114 | GrUserStencilOp::kZero,           GrUserStencilOp::kZero, | 
|---|
| 115 | GrUserStencilOp::kKeep,           GrUserStencilOp::kKeep, | 
|---|
| 116 | 0xffff,                           0xffff>() | 
|---|
| 117 | ); | 
|---|
| 118 |  | 
|---|
| 119 | void GrStencilAtlasOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) { | 
|---|
| 120 | SkIRect drawBoundsRect = SkIRect::MakeWH(fDrawBounds.width(), fDrawBounds.height()); | 
|---|
| 121 |  | 
|---|
| 122 | GrPipeline pipeline(GrScissorTest::kEnabled, GrDisableColorXPFactory::MakeXferProcessor(), | 
|---|
| 123 | flushState->drawOpArgs().writeSwizzle(), | 
|---|
| 124 | GrPipeline::InputFlags::kHWAntialias, &kIncrDecrStencil); | 
|---|
| 125 |  | 
|---|
| 126 | GrSampleMaskProcessor sampleMaskProc; | 
|---|
| 127 |  | 
|---|
| 128 | fResources->filler().drawFills( | 
|---|
| 129 | flushState, &sampleMaskProc, pipeline, fFillBatchID, drawBoundsRect); | 
|---|
| 130 |  | 
|---|
| 131 | fResources->stroker().drawStrokes( | 
|---|
| 132 | flushState, &sampleMaskProc, fStrokeBatchID, drawBoundsRect); | 
|---|
| 133 |  | 
|---|
| 134 | // We resolve the stencil coverage to alpha by drawing pixel-aligned boxes. Fine raster is | 
|---|
| 135 | // not necessary, and will even cause artifacts if using mixed samples. | 
|---|
| 136 | constexpr auto noHWAA = GrPipeline::InputFlags::kNone; | 
|---|
| 137 |  | 
|---|
| 138 | const auto* stencilResolveSettings = (flushState->caps().discardStencilValuesAfterRenderPass()) | 
|---|
| 139 | // The next draw will be the final op in the renderTargetContext. So if Ganesh is | 
|---|
| 140 | // planning to discard the stencil values anyway, we don't actually need to reset them | 
|---|
| 141 | // back to zero. | 
|---|
| 142 | ? &kResolveStencilCoverage | 
|---|
| 143 | : &kResolveStencilCoverageAndReset; | 
|---|
| 144 |  | 
|---|
| 145 | GrPipeline resolvePipeline(GrScissorTest::kEnabled, SkBlendMode::kSrc, | 
|---|
| 146 | flushState->drawOpArgs().writeSwizzle(), noHWAA, | 
|---|
| 147 | stencilResolveSettings); | 
|---|
| 148 |  | 
|---|
| 149 | StencilResolveProcessor primProc; | 
|---|
| 150 |  | 
|---|
| 151 | GrProgramInfo programInfo(flushState->proxy()->numSamples(), | 
|---|
| 152 | flushState->proxy()->numStencilSamples(), | 
|---|
| 153 | flushState->proxy()->backendFormat(), | 
|---|
| 154 | flushState->writeView()->origin(), &resolvePipeline, &primProc, | 
|---|
| 155 | GrPrimitiveType::kTriangleStrip); | 
|---|
| 156 |  | 
|---|
| 157 | flushState->bindPipeline(programInfo, SkRect::Make(drawBoundsRect)); | 
|---|
| 158 | flushState->setScissorRect(drawBoundsRect); | 
|---|
| 159 | flushState->bindBuffers(nullptr, fResources->stencilResolveBuffer(), nullptr); | 
|---|
| 160 | flushState->drawInstanced(fEndStencilResolveInstance - fBaseStencilResolveInstance, | 
|---|
| 161 | fBaseStencilResolveInstance, 4, 0); | 
|---|
| 162 | } | 
|---|
| 163 |  | 
|---|