1 /*
2  * Copyright 2015 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 "GrLatticeOp.h"
9 #include "GrDefaultGeoProcFactory.h"
10 #include "GrDrawOpTest.h"
11 #include "GrGpu.h"
12 #include "GrMeshDrawOp.h"
13 #include "GrOpFlushState.h"
14 #include "GrResourceProvider.h"
15 #include "GrResourceProviderPriv.h"
16 #include "GrSimpleMeshDrawOpHelper.h"
17 #include "GrVertexWriter.h"
18 #include "SkBitmap.h"
19 #include "SkLatticeIter.h"
20 #include "SkMatrixPriv.h"
21 #include "SkRect.h"
22 #include "glsl/GrGLSLColorSpaceXformHelper.h"
23 #include "glsl/GrGLSLGeometryProcessor.h"
24 #include "glsl/GrGLSLVarying.h"
25 
26 namespace {
27 
28 class LatticeGP : public GrGeometryProcessor {
29 public:
Make(GrGpu * gpu,const GrTextureProxy * proxy,sk_sp<GrColorSpaceXform> csxf,GrSamplerState::Filter filter,bool wideColor)30     static sk_sp<GrGeometryProcessor> Make(GrGpu* gpu,
31                                            const GrTextureProxy* proxy,
32                                            sk_sp<GrColorSpaceXform> csxf,
33                                            GrSamplerState::Filter filter,
34                                            bool wideColor) {
35         return sk_sp<GrGeometryProcessor>(
36                 new LatticeGP(gpu, proxy, std::move(csxf), filter, wideColor));
37     }
38 
name() const39     const char* name() const override { return "LatticeGP"; }
40 
getGLSLProcessorKey(const GrShaderCaps &,GrProcessorKeyBuilder * b) const41     void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const override {
42         b->add32(GrColorSpaceXform::XformKey(fColorSpaceXform.get()));
43     }
44 
createGLSLInstance(const GrShaderCaps & caps) const45     GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps& caps) const override {
46         class GLSLProcessor : public GrGLSLGeometryProcessor {
47         public:
48             void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& proc,
49                          FPCoordTransformIter&& transformIter) override {
50                 const auto& latticeGP = proc.cast<LatticeGP>();
51                 this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
52                 fColorSpaceXformHelper.setData(pdman, latticeGP.fColorSpaceXform.get());
53             }
54 
55         private:
56             void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
57                 using Interpolation = GrGLSLVaryingHandler::Interpolation;
58                 const auto& latticeGP = args.fGP.cast<LatticeGP>();
59                 fColorSpaceXformHelper.emitCode(args.fUniformHandler,
60                                                 latticeGP.fColorSpaceXform.get());
61 
62                 args.fVaryingHandler->emitAttributes(latticeGP);
63                 this->writeOutputPosition(args.fVertBuilder, gpArgs, latticeGP.fInPosition.name());
64                 this->emitTransforms(args.fVertBuilder,
65                                      args.fVaryingHandler,
66                                      args.fUniformHandler,
67                                      latticeGP.fInTextureCoords.asShaderVar(),
68                                      args.fFPCoordTransformHandler);
69                 args.fFragBuilder->codeAppend("float2 textureCoords;");
70                 args.fVaryingHandler->addPassThroughAttribute(latticeGP.fInTextureCoords,
71                                                               "textureCoords");
72                 args.fFragBuilder->codeAppend("float4 textureDomain;");
73                 args.fVaryingHandler->addPassThroughAttribute(
74                         latticeGP.fInTextureDomain, "textureDomain", Interpolation::kCanBeFlat);
75                 args.fVaryingHandler->addPassThroughAttribute(latticeGP.fInColor,
76                                                               args.fOutputColor,
77                                                               Interpolation::kCanBeFlat);
78                 args.fFragBuilder->codeAppendf("%s = ", args.fOutputColor);
79                 args.fFragBuilder->appendTextureLookupAndModulate(
80                         args.fOutputColor,
81                         args.fTexSamplers[0],
82                         "clamp(textureCoords, textureDomain.xy, textureDomain.zw)",
83                         kFloat2_GrSLType,
84                         &fColorSpaceXformHelper);
85                 args.fFragBuilder->codeAppend(";");
86                 args.fFragBuilder->codeAppendf("%s = half4(1);", args.fOutputCoverage);
87             }
88             GrGLSLColorSpaceXformHelper fColorSpaceXformHelper;
89         };
90         return new GLSLProcessor;
91     }
92 
93 private:
LatticeGP(GrGpu * gpu,const GrTextureProxy * proxy,sk_sp<GrColorSpaceXform> csxf,GrSamplerState::Filter filter,bool wideColor)94     LatticeGP(GrGpu* gpu, const GrTextureProxy* proxy, sk_sp<GrColorSpaceXform> csxf,
95               GrSamplerState::Filter filter, bool wideColor)
96             : INHERITED(kLatticeGP_ClassID), fColorSpaceXform(std::move(csxf)) {
97 
98         GrSamplerState samplerState = GrSamplerState(GrSamplerState::WrapMode::kClamp,
99                                                      filter);
100         uint32_t extraSamplerKey = gpu->getExtraSamplerKeyForProgram(samplerState,
101                                                                      proxy->backendFormat());
102 
103         fSampler.reset(proxy->textureType(), proxy->config(), samplerState,
104                        extraSamplerKey);
105         this->setTextureSamplerCnt(1);
106         fInPosition = {"position", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
107         fInTextureCoords = {"textureCoords", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
108         fInTextureDomain = {"textureDomain", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
109         fInColor = MakeColorAttribute("color", wideColor);
110         this->setVertexAttributes(&fInPosition, 4);
111     }
112 
onTextureSampler(int) const113     const TextureSampler& onTextureSampler(int) const override { return fSampler; }
114 
115     Attribute fInPosition;
116     Attribute fInTextureCoords;
117     Attribute fInTextureDomain;
118     Attribute fInColor;
119 
120     sk_sp<GrColorSpaceXform> fColorSpaceXform;
121     TextureSampler fSampler;
122 
123     typedef GrGeometryProcessor INHERITED;
124 };
125 
126 class NonAALatticeOp final : public GrMeshDrawOp {
127 private:
128     using Helper = GrSimpleMeshDrawOpHelper;
129 
130 public:
131     DEFINE_OP_CLASS_ID
132 
133     static const int kVertsPerRect = 4;
134     static const int kIndicesPerRect = 6;
135 
Make(GrContext * context,GrPaint && paint,const SkMatrix & viewMatrix,sk_sp<GrTextureProxy> proxy,sk_sp<GrColorSpaceXform> colorSpaceXForm,GrSamplerState::Filter filter,std::unique_ptr<SkLatticeIter> iter,const SkRect & dst)136     static std::unique_ptr<GrDrawOp> Make(GrContext* context,
137                                           GrPaint&& paint,
138                                           const SkMatrix& viewMatrix,
139                                           sk_sp<GrTextureProxy> proxy,
140                                           sk_sp<GrColorSpaceXform> colorSpaceXForm,
141                                           GrSamplerState::Filter filter,
142                                           std::unique_ptr<SkLatticeIter> iter,
143                                           const SkRect& dst) {
144         SkASSERT(proxy);
145         return Helper::FactoryHelper<NonAALatticeOp>(context, std::move(paint), viewMatrix,
146                                                      std::move(proxy),
147                                                      std::move(colorSpaceXForm), filter,
148                                                      std::move(iter), dst);
149     }
150 
NonAALatticeOp(Helper::MakeArgs & helperArgs,const SkPMColor4f & color,const SkMatrix & viewMatrix,sk_sp<GrTextureProxy> proxy,sk_sp<GrColorSpaceXform> colorSpaceXform,GrSamplerState::Filter filter,std::unique_ptr<SkLatticeIter> iter,const SkRect & dst)151     NonAALatticeOp(Helper::MakeArgs& helperArgs, const SkPMColor4f& color,
152                    const SkMatrix& viewMatrix, sk_sp<GrTextureProxy> proxy,
153                    sk_sp<GrColorSpaceXform> colorSpaceXform, GrSamplerState::Filter filter,
154                    std::unique_ptr<SkLatticeIter> iter, const SkRect& dst)
155             : INHERITED(ClassID())
156             , fHelper(helperArgs, GrAAType::kNone)
157             , fProxy(std::move(proxy))
158             , fColorSpaceXform(std::move(colorSpaceXform))
159             , fFilter(filter) {
160         Patch& patch = fPatches.push_back();
161         patch.fViewMatrix = viewMatrix;
162         patch.fColor = color;
163         patch.fIter = std::move(iter);
164         patch.fDst = dst;
165 
166         // setup bounds
167         this->setTransformedBounds(patch.fDst, viewMatrix, HasAABloat::kNo, IsZeroArea::kNo);
168         fWideColor = !SkPMColor4fFitsInBytes(color);
169     }
170 
name() const171     const char* name() const override { return "NonAALatticeOp"; }
172 
visitProxies(const VisitProxyFunc & func,VisitorType) const173     void visitProxies(const VisitProxyFunc& func, VisitorType) const override {
174         func(fProxy.get());
175         fHelper.visitProxies(func);
176     }
177 
178 #ifdef SK_DEBUG
dumpInfo() const179     SkString dumpInfo() const override {
180         SkString str;
181 
182         for (int i = 0; i < fPatches.count(); ++i) {
183             str.appendf("%d: Color: 0x%08x Dst [L: %.2f, T: %.2f, R: %.2f, B: %.2f]\n", i,
184                         fPatches[i].fColor.toBytes_RGBA(), fPatches[i].fDst.fLeft,
185                         fPatches[i].fDst.fTop, fPatches[i].fDst.fRight, fPatches[i].fDst.fBottom);
186         }
187 
188         str += fHelper.dumpInfo();
189         str += INHERITED::dumpInfo();
190         return str;
191     }
192 #endif
193 
fixedFunctionFlags() const194     FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
195 
finalize(const GrCaps & caps,const GrAppliedClip * clip)196     GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip) override {
197         auto opaque = fPatches[0].fColor.isOpaque() && GrPixelConfigIsOpaque(fProxy->config())
198                               ? GrProcessorAnalysisColor::Opaque::kYes
199                               : GrProcessorAnalysisColor::Opaque::kNo;
200         auto analysisColor = GrProcessorAnalysisColor(opaque);
201         auto result = fHelper.finalizeProcessors(
202                 caps, clip, GrProcessorAnalysisCoverage::kNone, &analysisColor);
203         analysisColor.isConstant(&fPatches[0].fColor);
204         return result;
205     }
206 
207 private:
onPrepareDraws(Target * target)208     void onPrepareDraws(Target* target) override {
209         GrGpu* gpu = target->resourceProvider()->priv().gpu();
210         auto gp = LatticeGP::Make(gpu, fProxy.get(), fColorSpaceXform, fFilter, fWideColor);
211         if (!gp) {
212             SkDebugf("Couldn't create GrGeometryProcessor\n");
213             return;
214         }
215 
216         int patchCnt = fPatches.count();
217         int numRects = 0;
218         for (int i = 0; i < patchCnt; i++) {
219             numRects += fPatches[i].fIter->numRectsToDraw();
220         }
221 
222         if (!numRects) {
223             return;
224         }
225 
226         const size_t kVertexStride = gp->vertexStride();
227         sk_sp<const GrBuffer> indexBuffer = target->resourceProvider()->refQuadIndexBuffer();
228         if (!indexBuffer) {
229             SkDebugf("Could not allocate indices\n");
230             return;
231         }
232         PatternHelper helper(target, GrPrimitiveType::kTriangles, kVertexStride,
233                              std::move(indexBuffer), kVertsPerRect, kIndicesPerRect, numRects);
234         GrVertexWriter vertices{helper.vertices()};
235         if (!vertices.fPtr) {
236             SkDebugf("Could not allocate vertices\n");
237             return;
238         }
239 
240         for (int i = 0; i < patchCnt; i++) {
241             const Patch& patch = fPatches[i];
242 
243             GrVertexColor patchColor(patch.fColor, fWideColor);
244 
245             // Apply the view matrix here if it is scale-translate.  Otherwise, we need to
246             // wait until we've created the dst rects.
247             bool isScaleTranslate = patch.fViewMatrix.isScaleTranslate();
248             if (isScaleTranslate) {
249                 patch.fIter->mapDstScaleTranslate(patch.fViewMatrix);
250             }
251 
252             SkIRect srcR;
253             SkRect dstR;
254             SkPoint* patchPositions = reinterpret_cast<SkPoint*>(vertices.fPtr);
255             Sk4f scales(1.f / fProxy->width(), 1.f / fProxy->height(),
256                         1.f / fProxy->width(), 1.f / fProxy->height());
257             static const Sk4f kDomainOffsets(0.5f, 0.5f, -0.5f, -0.5f);
258             static const Sk4f kFlipOffsets(0.f, 1.f, 0.f, 1.f);
259             static const Sk4f kFlipMuls(1.f, -1.f, 1.f, -1.f);
260             while (patch.fIter->next(&srcR, &dstR)) {
261                 Sk4f coords(SkIntToScalar(srcR.fLeft), SkIntToScalar(srcR.fTop),
262                             SkIntToScalar(srcR.fRight), SkIntToScalar(srcR.fBottom));
263                 Sk4f domain = coords + kDomainOffsets;
264                 coords *= scales;
265                 domain *= scales;
266                 if (fProxy->origin() == kBottomLeft_GrSurfaceOrigin) {
267                     coords = kFlipMuls * coords + kFlipOffsets;
268                     domain = SkNx_shuffle<0, 3, 2, 1>(kFlipMuls * domain + kFlipOffsets);
269                 }
270                 SkRect texDomain;
271                 SkRect texCoords;
272                 domain.store(&texDomain);
273                 coords.store(&texCoords);
274 
275                 vertices.writeQuad(GrVertexWriter::TriStripFromRect(dstR),
276                                    GrVertexWriter::TriStripFromRect(texCoords),
277                                    texDomain,
278                                    patchColor);
279             }
280 
281             // If we didn't handle it above, apply the matrix here.
282             if (!isScaleTranslate) {
283                 SkMatrixPriv::MapPointsWithStride(patch.fViewMatrix, patchPositions, kVertexStride,
284                                                   kVertsPerRect * patch.fIter->numRectsToDraw());
285             }
286         }
287         auto pipe = fHelper.makePipeline(target, 1);
288         pipe.fFixedDynamicState->fPrimitiveProcessorTextures[0] = fProxy.get();
289         helper.recordDraw(target, std::move(gp), pipe.fPipeline, pipe.fFixedDynamicState);
290     }
291 
onCombineIfPossible(GrOp * t,const GrCaps & caps)292     CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
293         NonAALatticeOp* that = t->cast<NonAALatticeOp>();
294         if (fProxy != that->fProxy) {
295             return CombineResult::kCannotCombine;
296         }
297         if (fFilter != that->fFilter) {
298             return CombineResult::kCannotCombine;
299         }
300         if (GrColorSpaceXform::Equals(fColorSpaceXform.get(), that->fColorSpaceXform.get())) {
301             return CombineResult::kCannotCombine;
302         }
303         if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
304             return CombineResult::kCannotCombine;
305         }
306 
307         fPatches.move_back_n(that->fPatches.count(), that->fPatches.begin());
308         fWideColor |= that->fWideColor;
309         return CombineResult::kMerged;
310     }
311 
312     struct Patch {
313         SkMatrix fViewMatrix;
314         std::unique_ptr<SkLatticeIter> fIter;
315         SkRect fDst;
316         SkPMColor4f fColor;
317     };
318 
319     Helper fHelper;
320     SkSTArray<1, Patch, true> fPatches;
321     sk_sp<GrTextureProxy> fProxy;
322     sk_sp<GrColorSpaceXform> fColorSpaceXform;
323     GrSamplerState::Filter fFilter;
324     bool fWideColor;
325 
326     typedef GrMeshDrawOp INHERITED;
327 };
328 
329 }  // anonymous namespace
330 
331 namespace GrLatticeOp {
MakeNonAA(GrContext * context,GrPaint && paint,const SkMatrix & viewMatrix,sk_sp<GrTextureProxy> proxy,sk_sp<GrColorSpaceXform> colorSpaceXform,GrSamplerState::Filter filter,std::unique_ptr<SkLatticeIter> iter,const SkRect & dst)332 std::unique_ptr<GrDrawOp> MakeNonAA(GrContext* context,
333                                     GrPaint&& paint,
334                                     const SkMatrix& viewMatrix,
335                                     sk_sp<GrTextureProxy> proxy,
336                                     sk_sp<GrColorSpaceXform> colorSpaceXform,
337                                     GrSamplerState::Filter filter,
338                                     std::unique_ptr<SkLatticeIter> iter,
339                                     const SkRect& dst) {
340     return NonAALatticeOp::Make(context, std::move(paint), viewMatrix, std::move(proxy),
341                                 std::move(colorSpaceXform), filter, std::move(iter), dst);
342 }
343 };
344 
345 #if GR_TEST_UTILS
346 #include "GrContextPriv.h"
347 #include "GrProxyProvider.h"
348 
349 /** Randomly divides subset into count divs. */
init_random_divs(int divs[],int count,int subsetStart,int subsetStop,SkRandom * random)350 static void init_random_divs(int divs[], int count, int subsetStart, int subsetStop,
351                              SkRandom* random) {
352     // Rules for lattice divs: Must be strictly increasing and in the range
353     // [subsetStart, subsetStop).
354     // Not terribly efficient alg for generating random divs:
355     // 1) Start with minimum legal pixels between each div.
356     // 2) Randomly assign the remaining pixels of the subset to divs.
357     // 3) Convert from pixel counts to div offsets.
358 
359     // 1) Initially each divs[i] represents the number of pixels between
360     // div i-1 and i. The initial div is allowed to be at subsetStart. There
361     // must be one pixel spacing between subsequent divs.
362     divs[0] = 0;
363     for (int i = 1; i < count; ++i) {
364         divs[i] = 1;
365     }
366     // 2) Assign the remaining subset pixels to fall
367     int subsetLength = subsetStop - subsetStart;
368     for (int i = 0; i < subsetLength - count; ++i) {
369         // +1 because count divs means count+1 intervals.
370         int entry = random->nextULessThan(count + 1);
371         // We don't have an entry to  to store the count after the last div
372         if (entry < count) {
373             divs[entry]++;
374         }
375     }
376     // 3) Now convert the counts between divs to pixel indices, incorporating the subset's offset.
377     int offset = subsetStart;
378     for (int i = 0; i < count; ++i) {
379         divs[i] += offset;
380         offset = divs[i];
381     }
382 }
383 
GR_DRAW_OP_TEST_DEFINE(NonAALatticeOp)384 GR_DRAW_OP_TEST_DEFINE(NonAALatticeOp) {
385     SkCanvas::Lattice lattice;
386     // We loop because our random lattice code can produce an invalid lattice in the case where
387     // there is a single div separator in both x and y and both are aligned with the left and top
388     // edge of the image subset, respectively.
389     std::unique_ptr<int[]> xdivs;
390     std::unique_ptr<int[]> ydivs;
391     std::unique_ptr<SkCanvas::Lattice::RectType[]> flags;
392     std::unique_ptr<SkColor[]> colors;
393     SkIRect subset;
394     GrSurfaceDesc desc;
395     desc.fConfig = kRGBA_8888_GrPixelConfig;
396     desc.fWidth = random->nextRangeU(1, 1000);
397     desc.fHeight = random->nextRangeU(1, 1000);
398     GrSurfaceOrigin origin =
399             random->nextBool() ? kTopLeft_GrSurfaceOrigin : kBottomLeft_GrSurfaceOrigin;
400     const GrBackendFormat format =
401             context->contextPriv().caps()->getBackendFormatFromColorType(kRGBA_8888_SkColorType);
402     auto proxy = context->contextPriv().proxyProvider()->createProxy(
403             format, desc, origin, SkBackingFit::kExact, SkBudgeted::kYes);
404 
405     do {
406         if (random->nextBool()) {
407             subset.fLeft = random->nextULessThan(desc.fWidth);
408             subset.fRight = random->nextRangeU(subset.fLeft + 1, desc.fWidth);
409             subset.fTop = random->nextULessThan(desc.fHeight);
410             subset.fBottom = random->nextRangeU(subset.fTop + 1, desc.fHeight);
411         } else {
412             subset.setXYWH(0, 0, desc.fWidth, desc.fHeight);
413         }
414         // SkCanvas::Lattice allows bounds to be null. However, SkCanvas creates a temp Lattice with
415         // a non-null bounds before creating a SkLatticeIter since SkLatticeIter requires a bounds.
416         lattice.fBounds = &subset;
417         lattice.fXCount = random->nextRangeU(1, subset.width());
418         lattice.fYCount = random->nextRangeU(1, subset.height());
419         xdivs.reset(new int[lattice.fXCount]);
420         ydivs.reset(new int[lattice.fYCount]);
421         init_random_divs(xdivs.get(), lattice.fXCount, subset.fLeft, subset.fRight, random);
422         init_random_divs(ydivs.get(), lattice.fYCount, subset.fTop, subset.fBottom, random);
423         lattice.fXDivs = xdivs.get();
424         lattice.fYDivs = ydivs.get();
425         bool hasFlags = random->nextBool();
426         if (hasFlags) {
427             int n = (lattice.fXCount + 1) * (lattice.fYCount + 1);
428             flags.reset(new SkCanvas::Lattice::RectType[n]);
429             colors.reset(new SkColor[n]);
430             for (int i = 0; i < n; ++i) {
431                 flags[i] = random->nextBool() ? SkCanvas::Lattice::kTransparent
432                                               : SkCanvas::Lattice::kDefault;
433             }
434             lattice.fRectTypes = flags.get();
435             lattice.fColors = colors.get();
436         } else {
437             lattice.fRectTypes = nullptr;
438             lattice.fColors = nullptr;
439         }
440     } while (!SkLatticeIter::Valid(desc.fWidth, desc.fHeight, lattice));
441     SkRect dst;
442     dst.fLeft = random->nextRangeScalar(-2000.5f, 1000.f);
443     dst.fTop = random->nextRangeScalar(-2000.5f, 1000.f);
444     dst.fRight = dst.fLeft + random->nextRangeScalar(0.5f, 1000.f);
445     dst.fBottom = dst.fTop + random->nextRangeScalar(0.5f, 1000.f);
446     std::unique_ptr<SkLatticeIter> iter(new SkLatticeIter(lattice, dst));
447     SkMatrix viewMatrix = GrTest::TestMatrixPreservesRightAngles(random);
448     auto csxf = GrTest::TestColorXform(random);
449     GrSamplerState::Filter filter =
450             random->nextBool() ? GrSamplerState::Filter::kNearest : GrSamplerState::Filter::kBilerp;
451     return NonAALatticeOp::Make(context, std::move(paint), viewMatrix, std::move(proxy),
452                                 std::move(csxf), filter, std::move(iter), dst);
453 }
454 
455 #endif
456