1 /*
2  * Copyright 2020 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/ops/DrawAtlasPathOp.h"
9 
10 #include "src/gpu/BufferWriter.h"
11 #include "src/gpu/GrGeometryProcessor.h"
12 #include "src/gpu/GrOpFlushState.h"
13 #include "src/gpu/GrOpsRenderPass.h"
14 #include "src/gpu/GrProgramInfo.h"
15 #include "src/gpu/GrResourceProvider.h"
16 #include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
17 #include "src/gpu/glsl/GrGLSLVarying.h"
18 #include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
19 
20 namespace {
21 
22 class DrawAtlasPathShader : public GrGeometryProcessor {
23 public:
DrawAtlasPathShader(bool usesLocalCoords, const skgpu::v1::AtlasInstancedHelper* atlasHelper, const GrShaderCaps& shaderCaps)24     DrawAtlasPathShader(bool usesLocalCoords,
25                         const skgpu::v1::AtlasInstancedHelper* atlasHelper,
26                         const GrShaderCaps& shaderCaps)
27             : GrGeometryProcessor(kDrawAtlasPathShader_ClassID)
28             , fUsesLocalCoords(usesLocalCoords)
29             , fAtlasHelper(atlasHelper)
30             , fAtlasAccess(GrSamplerState::Filter::kNearest,
31                            fAtlasHelper->proxy()->backendFormat(),
32                            fAtlasHelper->atlasSwizzle()) {
33         if (!shaderCaps.vertexIDSupport()) {
34             constexpr static Attribute kUnitCoordAttrib(
35                     "unitCoord", kFloat2_GrVertexAttribType, kFloat2_GrSLType);
36             this->setVertexAttributes(&kUnitCoordAttrib, 1);
37         }
38         fAttribs.emplace_back("fillBounds", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
39         if (fUsesLocalCoords) {
40             fAttribs.emplace_back("affineMatrix", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
41             fAttribs.emplace_back("translate", kFloat2_GrVertexAttribType, kFloat2_GrSLType);
42         }
43         SkASSERT(fAttribs.count() == this->colorAttribIdx());
44         fAttribs.emplace_back("color", kFloat4_GrVertexAttribType, kHalf4_GrSLType);
45         fAtlasHelper->appendInstanceAttribs(&fAttribs);
46         SkASSERT(fAttribs.count() <= kMaxInstanceAttribs);
47         this->setInstanceAttributes(fAttribs.data(), fAttribs.count());
48         this->setTextureSamplerCnt(1);
49     }
50 
51 private:
52     class Impl;
53 
colorAttribIdx() const54     int colorAttribIdx() const { return fUsesLocalCoords ? 3 : 1; }
55     const char* name() const override { return "DrawAtlasPathShader"; }
56     SkString getShaderDfxInfo() const override {
57         SkString format;
58         format.printf("ShaderDfx_DrawAtlasPathShader_%d", fUsesLocalCoords);
59         return format;
60     }
61     void addToKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const override {
62         b->addBits(1, fUsesLocalCoords, "localCoords");
63         fAtlasHelper->getKeyBits(b);
64     }
65     const TextureSampler& onTextureSampler(int) const override { return fAtlasAccess; }
66     std::unique_ptr<ProgramImpl> makeProgramImpl(const GrShaderCaps&) const override;
67 
68     const bool fUsesLocalCoords;
69     const skgpu::v1::AtlasInstancedHelper* const fAtlasHelper;
70     TextureSampler fAtlasAccess;
71     constexpr static int kMaxInstanceAttribs = 6;
72     SkSTArray<kMaxInstanceAttribs, GrGeometryProcessor::Attribute> fAttribs;
73 };
74 
75 class DrawAtlasPathShader::Impl : public ProgramImpl {
76 public:
77     void setData(const GrGLSLProgramDataManager& pdman,
78                  const GrShaderCaps&,
79                  const GrGeometryProcessor& geomProc) override {
80         auto* atlasHelper = geomProc.cast<DrawAtlasPathShader>().fAtlasHelper;
81         atlasHelper->setUniformData(pdman, fAtlasAdjustUniform);
82     }
83 
84 private:
85     void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
86         const auto& shader = args.fGeomProc.cast<DrawAtlasPathShader>();
87         args.fVaryingHandler->emitAttributes(shader);
88 
89         if (args.fShaderCaps->vertexIDSupport()) {
90             // If we don't have sk_VertexID support then "unitCoord" already came in as a vertex
91             // attrib.
92             args.fVertBuilder->codeAppendf(R"(
93             float2 unitCoord = float2(sk_VertexID & 1, sk_VertexID >> 1);)");
94         }
95 
96         args.fVertBuilder->codeAppendf(R"(
97         float2 devCoord = mix(fillBounds.xy, fillBounds.zw, unitCoord);)");
98         gpArgs->fPositionVar.set(kFloat2_GrSLType, "devCoord");
99 
100         if (shader.fUsesLocalCoords) {
101             args.fVertBuilder->codeAppendf(R"(
102             float2x2 M = float2x2(affineMatrix);
103             float2 localCoord = inverse(M) * (devCoord - translate);)");
104             gpArgs->fLocalCoordVar.set(kFloat2_GrSLType, "localCoord");
105         }
106 
107         args.fFragBuilder->codeAppendf("half4 %s = half4(1);", args.fOutputCoverage);
108         shader.fAtlasHelper->injectShaderCode(args, gpArgs->fPositionVar, &fAtlasAdjustUniform);
109 
110         args.fFragBuilder->codeAppendf("half4 %s;", args.fOutputColor);
111         args.fVaryingHandler->addPassThroughAttribute(
112                 shader.fAttribs[shader.colorAttribIdx()].asShaderVar(),
113                 args.fOutputColor,
114                 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
115     }
116 
117     GrGLSLUniformHandler::UniformHandle fAtlasAdjustUniform;
118 };
119 
makeProgramImpl( const GrShaderCaps&) const120 std::unique_ptr<GrGeometryProcessor::ProgramImpl> DrawAtlasPathShader::makeProgramImpl(
121         const GrShaderCaps&) const {
122     return std::make_unique<Impl>();
123 }
124 
125 }  // anonymous namespace
126 
127 namespace skgpu::v1 {
128 
finalize(const GrCaps& caps, const GrAppliedClip* clip, GrClampType clampType)129 GrProcessorSet::Analysis DrawAtlasPathOp::finalize(const GrCaps& caps, const GrAppliedClip* clip,
130                                                    GrClampType clampType) {
131     const GrProcessorSet::Analysis& analysis = fProcessors.finalize(
132             fHeadInstance->fColor, GrProcessorAnalysisCoverage::kSingleChannel, clip,
133             &GrUserStencilSettings::kUnused, caps, clampType, &fHeadInstance->fColor);
134     fUsesLocalCoords = analysis.usesLocalCoords();
135     return analysis;
136 }
137 
onCombineIfPossible(GrOp* op, SkArenaAlloc*, const GrCaps&)138 GrOp::CombineResult DrawAtlasPathOp::onCombineIfPossible(GrOp* op, SkArenaAlloc*, const GrCaps&) {
139     auto that = op->cast<DrawAtlasPathOp>();
140 
141     if (!fAtlasHelper.isCompatible(that->fAtlasHelper) ||
142         fProcessors != that->fProcessors) {
143         return CombineResult::kCannotCombine;
144     }
145 
146     SkASSERT(fUsesLocalCoords == that->fUsesLocalCoords);
147     *fTailInstance = that->fHeadInstance;
148     fTailInstance = that->fTailInstance;
149     fInstanceCount += that->fInstanceCount;
150     return CombineResult::kMerged;
151 }
152 
prepareProgram(const GrCaps& caps, SkArenaAlloc* arena, const GrSurfaceProxyView& writeView, bool usesMSAASurface, GrAppliedClip&& appliedClip, const GrDstProxyView& dstProxyView, GrXferBarrierFlags renderPassXferBarriers, GrLoadOp colorLoadOp)153 void DrawAtlasPathOp::prepareProgram(const GrCaps& caps, SkArenaAlloc* arena,
154                                      const GrSurfaceProxyView& writeView, bool usesMSAASurface,
155                                      GrAppliedClip&& appliedClip,
156                                      const GrDstProxyView& dstProxyView,
157                                      GrXferBarrierFlags renderPassXferBarriers,
158                                      GrLoadOp colorLoadOp) {
159     SkASSERT(!fProgram);
160     GrPipeline::InitArgs initArgs;
161     initArgs.fCaps = &caps;
162     initArgs.fDstProxyView = dstProxyView;
163     initArgs.fWriteSwizzle = writeView.swizzle();
164     auto pipeline = arena->make<GrPipeline>(initArgs, std::move(fProcessors),
165                                             std::move(appliedClip));
166     auto shader = arena->make<DrawAtlasPathShader>(fUsesLocalCoords, &fAtlasHelper,
167                                                    *caps.shaderCaps());
168     fProgram = arena->make<GrProgramInfo>(caps, writeView, usesMSAASurface, pipeline,
169                                           &GrUserStencilSettings::kUnused, shader,
170                                           GrPrimitiveType::kTriangleStrip, 0,
171                                           renderPassXferBarriers, colorLoadOp);
172 }
173 
onPrePrepare(GrRecordingContext* rContext, const GrSurfaceProxyView& writeView, GrAppliedClip* appliedClip, const GrDstProxyView& dstProxyView, GrXferBarrierFlags renderPassXferBarriers, GrLoadOp colorLoadOp)174 void DrawAtlasPathOp::onPrePrepare(GrRecordingContext* rContext,
175                                    const GrSurfaceProxyView& writeView,
176                                    GrAppliedClip* appliedClip, const GrDstProxyView& dstProxyView,
177                                    GrXferBarrierFlags renderPassXferBarriers,
178                                    GrLoadOp colorLoadOp) {
179     // DMSAA is not supported on DDL.
180     bool usesMSAASurface = writeView.asRenderTargetProxy()->numSamples() > 1;
181     this->prepareProgram(*rContext->priv().caps(), rContext->priv().recordTimeAllocator(),
182                          writeView, usesMSAASurface, std::move(*appliedClip), dstProxyView,
183                          renderPassXferBarriers, colorLoadOp);
184     SkASSERT(fProgram);
185     rContext->priv().recordProgramInfo(fProgram);
186 }
187 
188 GR_DECLARE_STATIC_UNIQUE_KEY(gUnitQuadBufferKey);
189 
onPrepare(GrOpFlushState* flushState)190 void DrawAtlasPathOp::onPrepare(GrOpFlushState* flushState) {
191     if (!fProgram) {
192         this->prepareProgram(flushState->caps(), flushState->allocator(), flushState->writeView(),
193                              flushState->usesMSAASurface(), flushState->detachAppliedClip(),
194                              flushState->dstProxyView(), flushState->renderPassBarriers(),
195                              flushState->colorLoadOp());
196         SkASSERT(fProgram);
197     }
198 
199     if (VertexWriter instanceWriter = flushState->makeVertexSpace(
200                 fProgram->geomProc().instanceStride(), fInstanceCount, &fInstanceBuffer,
201                 &fBaseInstance)) {
202         for (const Instance* i = fHeadInstance; i; i = i->fNext) {
203             instanceWriter << SkRect::Make(i->fFillBounds)
204                            << VertexWriter::If(fUsesLocalCoords,
205                                                i->fLocalToDeviceIfUsingLocalCoords)
206                            << i->fColor;
207             fAtlasHelper.writeInstanceData(&instanceWriter, &i->fAtlasInstance);
208         }
209     }
210 
211     if (!flushState->caps().shaderCaps()->vertexIDSupport()) {
212         constexpr static SkPoint kUnitQuad[4] = {{0,0}, {0,1}, {1,0}, {1,1}};
213 
214         GR_DEFINE_STATIC_UNIQUE_KEY(gUnitQuadBufferKey);
215 
216         fVertexBufferIfNoIDSupport = flushState->resourceProvider()->findOrMakeStaticBuffer(
217                 GrGpuBufferType::kVertex, sizeof(kUnitQuad), kUnitQuad, gUnitQuadBufferKey);
218     }
219 }
220 
onExecute(GrOpFlushState* flushState, const SkRect& chainBounds)221 void DrawAtlasPathOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
222     if (fProgram->geomProc().hasVertexAttributes() && !fVertexBufferIfNoIDSupport) {
223         return;
224     }
225     flushState->bindPipelineAndScissorClip(*fProgram, this->bounds());
226     flushState->bindTextures(fProgram->geomProc(), *fAtlasHelper.proxy(), fProgram->pipeline());
227     flushState->bindBuffers(nullptr, std::move(fInstanceBuffer), fVertexBufferIfNoIDSupport);
228     flushState->drawInstanced(fInstanceCount, fBaseInstance, 4, 0);
229 }
230 
231 } // namespace skgpu::v1
232