1 /*
2  * Copyright 2017 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 "GrTextureOp.h"
9 #include <new>
10 #include "GrAppliedClip.h"
11 #include "GrCaps.h"
12 #include "GrDrawOpTest.h"
13 #include "GrGeometryProcessor.h"
14 #include "GrGpu.h"
15 #include "GrMemoryPool.h"
16 #include "GrMeshDrawOp.h"
17 #include "GrOpFlushState.h"
18 #include "GrQuad.h"
19 #include "GrQuadPerEdgeAA.h"
20 #include "GrRecordingContext.h"
21 #include "GrRecordingContextPriv.h"
22 #include "GrResourceProvider.h"
23 #include "GrResourceProviderPriv.h"
24 #include "GrShaderCaps.h"
25 #include "GrTexture.h"
26 #include "GrTexturePriv.h"
27 #include "GrTextureProxy.h"
28 #include "SkGr.h"
29 #include "SkMathPriv.h"
30 #include "SkMatrixPriv.h"
31 #include "SkPoint.h"
32 #include "SkPoint3.h"
33 #include "SkRectPriv.h"
34 #include "SkTo.h"
35 #include "glsl/GrGLSLVarying.h"
36 
37 namespace {
38 
39 using Domain = GrQuadPerEdgeAA::Domain;
40 using VertexSpec = GrQuadPerEdgeAA::VertexSpec;
41 using ColorType = GrQuadPerEdgeAA::ColorType;
42 
43 // if normalizing the domain then pass 1/width, 1/height, 1 for iw, ih, h. Otherwise pass
44 // 1, 1, and height.
compute_domain(Domain domain,GrSamplerState::Filter filter,GrSurfaceOrigin origin,const SkRect & srcRect,float iw,float ih,float h)45 static SkRect compute_domain(Domain domain, GrSamplerState::Filter filter, GrSurfaceOrigin origin,
46                              const SkRect& srcRect, float iw, float ih, float h) {
47     static constexpr SkRect kLargeRect = {-100000, -100000, 1000000, 1000000};
48     if (domain == Domain::kNo) {
49         // Either the quad has no domain constraint and is batched with a domain constrained op
50         // (in which case we want a domain that doesn't restrict normalized tex coords), or the
51         // entire op doesn't use the domain, in which case the returned value is ignored.
52         return kLargeRect;
53     }
54 
55     auto ltrb = Sk4f::Load(&srcRect);
56     if (filter == GrSamplerState::Filter::kBilerp) {
57         auto rblt = SkNx_shuffle<2, 3, 0, 1>(ltrb);
58         auto whwh = (rblt - ltrb).abs();
59         auto c = (rblt + ltrb) * 0.5f;
60         static const Sk4f kOffsets = {0.5f, 0.5f, -0.5f, -0.5f};
61         ltrb = (whwh < 1.f).thenElse(c, ltrb + kOffsets);
62     }
63     ltrb *= Sk4f(iw, ih, iw, ih);
64     if (origin == kBottomLeft_GrSurfaceOrigin) {
65         static const Sk4f kMul = {1.f, -1.f, 1.f, -1.f};
66         const Sk4f kAdd = {0.f, h, 0.f, h};
67         ltrb = SkNx_shuffle<0, 3, 2, 1>(kMul * ltrb + kAdd);
68     }
69 
70     SkRect domainRect;
71     ltrb.store(&domainRect);
72     return domainRect;
73 }
74 
75 // If normalizing the src quad then pass 1/width, 1/height, 1 for iw, ih, h. Otherwise pass
76 // 1, 1, and height.
compute_src_quad_from_rect(GrSurfaceOrigin origin,const SkRect & srcRect,float iw,float ih,float h)77 static GrPerspQuad compute_src_quad_from_rect(GrSurfaceOrigin origin, const SkRect& srcRect,
78                                               float iw, float ih, float h) {
79     // Convert the pixel-space src rectangle into normalized texture coordinates
80     SkRect texRect = {
81         iw * srcRect.fLeft,
82         ih * srcRect.fTop,
83         iw * srcRect.fRight,
84         ih * srcRect.fBottom
85     };
86     if (origin == kBottomLeft_GrSurfaceOrigin) {
87         texRect.fTop = h - texRect.fTop;
88         texRect.fBottom = h - texRect.fBottom;
89     }
90     return GrPerspQuad(texRect);
91 }
92 // Normalizes logical src coords and corrects for origin
compute_src_quad(GrSurfaceOrigin origin,const GrPerspQuad & srcQuad,float iw,float ih,float h)93 static GrPerspQuad compute_src_quad(GrSurfaceOrigin origin, const GrPerspQuad& srcQuad,
94                                     float iw, float ih, float h) {
95     // The src quad should not have any perspective
96     SkASSERT(!srcQuad.hasPerspective());
97     Sk4f xs = srcQuad.x4f() * iw;
98     Sk4f ys = srcQuad.y4f() * ih;
99     if (origin == kBottomLeft_GrSurfaceOrigin) {
100         ys = h - ys;
101     }
102     return GrPerspQuad(xs, ys);
103 }
104 
105 /**
106  * Op that implements GrTextureOp::Make. It draws textured quads. Each quad can modulate against a
107  * the texture by color. The blend with the destination is always src-over. The edges are non-AA.
108  */
109 class TextureOp final : public GrMeshDrawOp {
110 public:
Make(GrRecordingContext * context,sk_sp<GrTextureProxy> proxy,GrSamplerState::Filter filter,const SkPMColor4f & color,const SkRect & srcRect,const SkRect & dstRect,GrAAType aaType,GrQuadAAFlags aaFlags,SkCanvas::SrcRectConstraint constraint,const SkMatrix & viewMatrix,sk_sp<GrColorSpaceXform> textureColorSpaceXform)111     static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
112                                           sk_sp<GrTextureProxy> proxy,
113                                           GrSamplerState::Filter filter,
114                                           const SkPMColor4f& color,
115                                           const SkRect& srcRect,
116                                           const SkRect& dstRect,
117                                           GrAAType aaType,
118                                           GrQuadAAFlags aaFlags,
119                                           SkCanvas::SrcRectConstraint constraint,
120                                           const SkMatrix& viewMatrix,
121                                           sk_sp<GrColorSpaceXform> textureColorSpaceXform) {
122         GrPerspQuad dstQuad = GrPerspQuad::MakeFromRect(dstRect, viewMatrix);
123         GrQuadType dstQuadType = GrQuadTypeForTransformedRect(viewMatrix);
124 
125         if (dstQuadType == GrQuadType::kRect) {
126             // Disable filtering if possible (note AA optimizations for rects are automatically
127             // handled above in GrResolveAATypeForQuad).
128             if (filter != GrSamplerState::Filter::kNearest &&
129                 !GrTextureOp::GetFilterHasEffect(viewMatrix, srcRect, dstRect)) {
130                 filter = GrSamplerState::Filter::kNearest;
131             }
132         }
133 
134         GrOpMemoryPool* pool = context->priv().opMemoryPool();
135         // srcRect provides both local coords and domain (if needed), so use nullptr for srcQuad
136         return pool->allocate<TextureOp>(
137                 std::move(proxy), filter, color, dstQuad, dstQuadType, srcRect, constraint,
138                 nullptr, GrQuadType::kRect, aaType, aaFlags, std::move(textureColorSpaceXform));
139     }
Make(GrRecordingContext * context,sk_sp<GrTextureProxy> proxy,GrSamplerState::Filter filter,const SkPMColor4f & color,const SkPoint srcQuad[4],const SkPoint dstQuad[4],GrAAType aaType,GrQuadAAFlags aaFlags,const SkRect * domain,const SkMatrix & viewMatrix,sk_sp<GrColorSpaceXform> textureColorSpaceXform)140     static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
141                                           sk_sp<GrTextureProxy> proxy,
142                                           GrSamplerState::Filter filter,
143                                           const SkPMColor4f& color,
144                                           const SkPoint srcQuad[4],
145                                           const SkPoint dstQuad[4],
146                                           GrAAType aaType,
147                                           GrQuadAAFlags aaFlags,
148                                           const SkRect* domain,
149                                           const SkMatrix& viewMatrix,
150                                           sk_sp<GrColorSpaceXform> textureColorSpaceXform) {
151         GrPerspQuad grDstQuad = GrPerspQuad::MakeFromSkQuad(dstQuad, viewMatrix);
152         GrQuadType dstQuadType = viewMatrix.hasPerspective() ? GrQuadType::kPerspective
153                                                              : GrQuadType::kStandard;
154         GrPerspQuad grSrcQuad = GrPerspQuad::MakeFromSkQuad(srcQuad, SkMatrix::I());
155 
156         // If constraint remains fast, the value in srcRect will be ignored since srcQuads provides
157         // the local coordinates and a domain won't be used.
158         SkRect srcRect = SkRect::MakeEmpty();
159         SkCanvas::SrcRectConstraint constraint = SkCanvas::kFast_SrcRectConstraint;
160         if (domain) {
161             srcRect = *domain;
162             constraint = SkCanvas::kStrict_SrcRectConstraint;
163         }
164 
165         GrOpMemoryPool* pool = context->priv().opMemoryPool();
166         // Pass domain as srcRect if provided, but send srcQuad as a GrPerspQuad for local coords
167         return pool->allocate<TextureOp>(
168                 std::move(proxy), filter, color, grDstQuad, dstQuadType, srcRect, constraint,
169                 &grSrcQuad, GrQuadType::kStandard, aaType, aaFlags,
170                 std::move(textureColorSpaceXform));
171     }
Make(GrRecordingContext * context,const GrRenderTargetContext::TextureSetEntry set[],int cnt,GrSamplerState::Filter filter,GrAAType aaType,const SkMatrix & viewMatrix,sk_sp<GrColorSpaceXform> textureColorSpaceXform)172     static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
173                                           const GrRenderTargetContext::TextureSetEntry set[],
174                                           int cnt, GrSamplerState::Filter filter, GrAAType aaType,
175                                           const SkMatrix& viewMatrix,
176                                           sk_sp<GrColorSpaceXform> textureColorSpaceXform) {
177         size_t size = sizeof(TextureOp) + sizeof(Proxy) * (cnt - 1);
178         GrOpMemoryPool* pool = context->priv().opMemoryPool();
179         void* mem = pool->allocate(size);
180         return std::unique_ptr<GrDrawOp>(new (mem) TextureOp(set, cnt, filter, aaType, viewMatrix,
181                                                              std::move(textureColorSpaceXform)));
182     }
183 
~TextureOp()184     ~TextureOp() override {
185         for (unsigned p = 0; p < fProxyCnt; ++p) {
186             if (fFinalized) {
187                 fProxies[p].fProxy->completedRead();
188             } else {
189                 fProxies[p].fProxy->unref();
190             }
191         }
192     }
193 
name() const194     const char* name() const override { return "TextureOp"; }
195 
visitProxies(const VisitProxyFunc & func,VisitorType visitor) const196     void visitProxies(const VisitProxyFunc& func, VisitorType visitor) const override {
197         if (visitor == VisitorType::kAllocatorGather && fCanSkipAllocatorGather) {
198             return;
199         }
200         for (unsigned p = 0; p < fProxyCnt; ++p) {
201             func(fProxies[p].fProxy);
202         }
203     }
204 
205 #ifdef SK_DEBUG
dumpInfo() const206     SkString dumpInfo() const override {
207         SkString str;
208         str.appendf("# draws: %d\n", fQuads.count());
209         int q = 0;
210         for (unsigned p = 0; p < fProxyCnt; ++p) {
211             str.appendf("Proxy ID: %d, Filter: %d\n", fProxies[p].fProxy->uniqueID().asUInt(),
212                         static_cast<int>(fFilter));
213             for (int i = 0; i < fProxies[p].fQuadCnt; ++i, ++q) {
214                 GrPerspQuad quad = fQuads[q];
215                 const ColorDomainAndAA& info = fQuads.metadata(i);
216                 str.appendf(
217                         "%d: Color: 0x%08x, TexRect [L: %.2f, T: %.2f, R: %.2f, B: %.2f] "
218                         "Quad [(%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f)]\n",
219                         i, info.fColor.toBytes_RGBA(), info.fSrcRect.fLeft, info.fSrcRect.fTop,
220                         info.fSrcRect.fRight, info.fSrcRect.fBottom, quad.point(0).fX,
221                         quad.point(0).fY, quad.point(1).fX, quad.point(1).fY,
222                         quad.point(2).fX, quad.point(2).fY, quad.point(3).fX,
223                         quad.point(3).fY);
224             }
225         }
226         str += INHERITED::dumpInfo();
227         return str;
228     }
229 #endif
230 
finalize(const GrCaps &,const GrAppliedClip *,GrFSAAType,GrClampType)231     GrProcessorSet::Analysis finalize(
232             const GrCaps&, const GrAppliedClip*, GrFSAAType, GrClampType) override {
233         SkASSERT(!fFinalized);
234         fFinalized = true;
235         for (unsigned p = 0; p < fProxyCnt; ++p) {
236             fProxies[p].fProxy->addPendingRead();
237             fProxies[p].fProxy->unref();
238         }
239         return GrProcessorSet::EmptySetAnalysis();
240     }
241 
fixedFunctionFlags() const242     FixedFunctionFlags fixedFunctionFlags() const override {
243         return this->aaType() == GrAAType::kMSAA ? FixedFunctionFlags::kUsesHWAA
244                                                  : FixedFunctionFlags::kNone;
245     }
246 
247     DEFINE_OP_CLASS_ID
248 
249 private:
250     friend class ::GrOpMemoryPool;
251 
252     // dstQuad and dstQuadType should be the geometry transformed by the view matrix.
253     // srcRect represents original src rect and will be used as the domain when constraint is strict
254     // If srcQuad is provided, it will be used for the local coords instead of srcRect, although
255     // srcRect will still specify the domain constraint if needed.
TextureOp(sk_sp<GrTextureProxy> proxy,GrSamplerState::Filter filter,const SkPMColor4f & color,const GrPerspQuad & dstQuad,GrQuadType dstQuadType,const SkRect & srcRect,SkCanvas::SrcRectConstraint constraint,const GrPerspQuad * srcQuad,GrQuadType srcQuadType,GrAAType aaType,GrQuadAAFlags aaFlags,sk_sp<GrColorSpaceXform> textureColorSpaceXform)256     TextureOp(sk_sp<GrTextureProxy> proxy, GrSamplerState::Filter filter, const SkPMColor4f& color,
257               const GrPerspQuad& dstQuad, GrQuadType dstQuadType,
258               const SkRect& srcRect, SkCanvas::SrcRectConstraint constraint,
259               const GrPerspQuad* srcQuad, GrQuadType srcQuadType, GrAAType aaType,
260               GrQuadAAFlags aaFlags, sk_sp<GrColorSpaceXform> textureColorSpaceXform)
261             : INHERITED(ClassID())
262             , fTextureColorSpaceXform(std::move(textureColorSpaceXform))
263             , fFilter(static_cast<unsigned>(filter))
264             , fFinalized(0) {
265         // Clean up disparities between the overall aa type and edge configuration and apply
266         // optimizations based on the rect and matrix when appropriate
267         GrResolveAATypeForQuad(aaType, aaFlags, dstQuad, dstQuadType, &aaType, &aaFlags);
268         fAAType = static_cast<unsigned>(aaType);
269 
270         // We expect our caller to have already caught this optimization.
271         SkASSERT(!srcRect.contains(proxy->getWorstCaseBoundsRect()) ||
272                  constraint == SkCanvas::kFast_SrcRectConstraint);
273 
274         // We may have had a strict constraint with nearest filter solely due to possible AA bloat.
275         // If we don't have (or determined we don't need) coverage AA then we can skip using a
276         // domain.
277         if (constraint == SkCanvas::kStrict_SrcRectConstraint &&
278             this->filter() == GrSamplerState::Filter::kNearest &&
279             aaType != GrAAType::kCoverage) {
280             constraint = SkCanvas::kFast_SrcRectConstraint;
281         }
282 
283         Domain domain = constraint == SkCanvas::kStrict_SrcRectConstraint ? Domain::kYes
284                                                                           : Domain::kNo;
285         // Initially, if srcQuad is provided it will always be at index 0 of fSrcQuads
286         fQuads.push_back(dstQuad, dstQuadType, {color, srcRect, srcQuad ? 0 : -1, domain, aaFlags});
287         if (srcQuad) {
288             fSrcQuads.push_back(*srcQuad, srcQuadType);
289         }
290         fProxyCnt = 1;
291         fProxies[0] = {proxy.release(), 1};
292         auto bounds = dstQuad.bounds(dstQuadType);
293         this->setBounds(bounds, HasAABloat(aaType == GrAAType::kCoverage), IsZeroArea::kNo);
294         fDomain = static_cast<unsigned>(domain);
295         fColorType = static_cast<unsigned>(GrQuadPerEdgeAA::MinColorType(color));
296         fCanSkipAllocatorGather =
297                 static_cast<unsigned>(fProxies[0].fProxy->canSkipResourceAllocator());
298     }
TextureOp(const GrRenderTargetContext::TextureSetEntry set[],int cnt,GrSamplerState::Filter filter,GrAAType aaType,const SkMatrix & viewMatrix,sk_sp<GrColorSpaceXform> textureColorSpaceXform)299     TextureOp(const GrRenderTargetContext::TextureSetEntry set[], int cnt,
300               GrSamplerState::Filter filter, GrAAType aaType, const SkMatrix& viewMatrix,
301               sk_sp<GrColorSpaceXform> textureColorSpaceXform)
302             : INHERITED(ClassID())
303             , fTextureColorSpaceXform(std::move(textureColorSpaceXform))
304             , fFilter(static_cast<unsigned>(filter))
305             , fFinalized(0) {
306         fProxyCnt = SkToUInt(cnt);
307         SkRect bounds = SkRectPriv::MakeLargestInverted();
308         GrAAType overallAAType = GrAAType::kNone; // aa type maximally compatible with all dst rects
309         bool mustFilter = false;
310         fCanSkipAllocatorGather = static_cast<unsigned>(true);
311         // Most dst rects are transformed by the same view matrix, so their quad types start
312         // identical, unless an entry provides a dstClip or additional transform that changes it.
313         // The quad list will automatically adapt to that.
314         fQuads.reserve(cnt, GrQuadTypeForTransformedRect(viewMatrix));
315         bool allOpaque = true;
316         for (unsigned p = 0; p < fProxyCnt; ++p) {
317             fProxies[p].fProxy = SkRef(set[p].fProxy.get());
318             fProxies[p].fQuadCnt = 1;
319             SkASSERT(fProxies[p].fProxy->textureType() == fProxies[0].fProxy->textureType());
320             SkASSERT(fProxies[p].fProxy->config() == fProxies[0].fProxy->config());
321             if (!fProxies[p].fProxy->canSkipResourceAllocator()) {
322                 fCanSkipAllocatorGather = static_cast<unsigned>(false);
323             }
324 
325             SkMatrix ctm = viewMatrix;
326             if (set[p].fPreViewMatrix) {
327                 ctm.preConcat(*set[p].fPreViewMatrix);
328             }
329 
330             // Use dstRect unless dstClip is provided, which is assumed to be a quad
331             auto quad = set[p].fDstClipQuad == nullptr ?
332                     GrPerspQuad::MakeFromRect(set[p].fDstRect, ctm) :
333                     GrPerspQuad::MakeFromSkQuad(set[p].fDstClipQuad, ctm);
334             GrQuadType quadType = GrQuadTypeForTransformedRect(ctm);
335             if (set[p].fDstClipQuad && quadType != GrQuadType::kPerspective) {
336                 quadType = GrQuadType::kStandard;
337             }
338 
339             bounds.joinPossiblyEmptyRect(quad.bounds(quadType));
340             GrQuadAAFlags aaFlags;
341             // Don't update the overall aaType, might be inappropriate for some of the quads
342             GrAAType aaForQuad;
343             GrResolveAATypeForQuad(aaType, set[p].fAAFlags, quad, quadType, &aaForQuad, &aaFlags);
344             // Resolve sets aaForQuad to aaType or None, there is never a change between aa methods
345             SkASSERT(aaForQuad == GrAAType::kNone || aaForQuad == aaType);
346             if (overallAAType == GrAAType::kNone && aaForQuad != GrAAType::kNone) {
347                 overallAAType = aaType;
348             }
349             if (!mustFilter && this->filter() != GrSamplerState::Filter::kNearest) {
350                 mustFilter = quadType != GrQuadType::kRect ||
351                              GrTextureOp::GetFilterHasEffect(ctm, set[p].fSrcRect,
352                                                              set[p].fDstRect);
353             }
354             float alpha = SkTPin(set[p].fAlpha, 0.f, 1.f);
355             allOpaque &= (1.f == alpha);
356             SkPMColor4f color{alpha, alpha, alpha, alpha};
357             int srcQuadIndex = -1;
358             if (set[p].fDstClipQuad) {
359                 // Derive new source coordinates that match dstClip's relative locations in dstRect,
360                 // but with respect to srcRect
361                 SkPoint srcQuad[4];
362                 GrMapRectPoints(set[p].fDstRect, set[p].fSrcRect, set[p].fDstClipQuad, srcQuad, 4);
363                 fSrcQuads.push_back(GrPerspQuad::MakeFromSkQuad(srcQuad, SkMatrix::I()),
364                                     GrQuadType::kStandard);
365                 srcQuadIndex = fSrcQuads.count() - 1;
366             }
367             fQuads.push_back(quad, quadType,
368                              {color, set[p].fSrcRect, srcQuadIndex, Domain::kNo, aaFlags});
369         }
370         fAAType = static_cast<unsigned>(overallAAType);
371         if (!mustFilter) {
372             fFilter = static_cast<unsigned>(GrSamplerState::Filter::kNearest);
373         }
374         this->setBounds(bounds, HasAABloat(this->aaType() == GrAAType::kCoverage), IsZeroArea::kNo);
375         fDomain = static_cast<unsigned>(false);
376         fColorType = static_cast<unsigned>(allOpaque ? ColorType::kNone : ColorType::kByte);
377     }
378 
tess(void * v,const VertexSpec & spec,const GrTextureProxy * proxy,int start,int cnt) const379     void tess(void* v, const VertexSpec& spec, const GrTextureProxy* proxy, int start,
380               int cnt) const {
381         TRACE_EVENT0("skia", TRACE_FUNC);
382         auto origin = proxy->origin();
383         const auto* texture = proxy->peekTexture();
384         float iw, ih, h;
385         if (proxy->textureType() == GrTextureType::kRectangle) {
386             iw = ih = 1.f;
387             h = texture->height();
388         } else {
389             iw = 1.f / texture->width();
390             ih = 1.f / texture->height();
391             h = 1.f;
392         }
393 
394         for (int i = start; i < start + cnt; ++i) {
395             const GrPerspQuad& device = fQuads[i];
396             const ColorDomainAndAA& info = fQuads.metadata(i);
397 
398             GrPerspQuad srcQuad = info.fSrcQuadIndex >= 0 ?
399                     compute_src_quad(origin, fSrcQuads[info.fSrcQuadIndex], iw, ih, h) :
400                     compute_src_quad_from_rect(origin, info.fSrcRect, iw, ih, h);
401             SkRect domain =
402                     compute_domain(info.domain(), this->filter(), origin, info.fSrcRect, iw, ih, h);
403             v = GrQuadPerEdgeAA::Tessellate(v, spec, device, info.fColor, srcQuad, domain,
404                                             info.aaFlags());
405         }
406     }
407 
onPrepareDraws(Target * target)408     void onPrepareDraws(Target* target) override {
409         TRACE_EVENT0("skia", TRACE_FUNC);
410         GrQuadType quadType = GrQuadType::kRect;
411         GrQuadType srcQuadType = GrQuadType::kRect;
412         Domain domain = Domain::kNo;
413         ColorType colorType = ColorType::kNone;
414         int numProxies = 0;
415         int numTotalQuads = 0;
416         auto textureType = fProxies[0].fProxy->textureType();
417         auto config = fProxies[0].fProxy->config();
418         GrAAType aaType = this->aaType();
419         for (const auto& op : ChainRange<TextureOp>(this)) {
420             if (op.fQuads.quadType() > quadType) {
421                 quadType = op.fQuads.quadType();
422             }
423             if (op.fSrcQuads.quadType() > srcQuadType) {
424                 // Should only become more general if there are quads to use instead of fSrcRect
425                 SkASSERT(op.fSrcQuads.count() > 0);
426                 srcQuadType = op.fSrcQuads.quadType();
427             }
428             if (op.fDomain) {
429                 domain = Domain::kYes;
430             }
431             colorType = SkTMax(colorType, static_cast<ColorType>(op.fColorType));
432             numProxies += op.fProxyCnt;
433             for (unsigned p = 0; p < op.fProxyCnt; ++p) {
434                 numTotalQuads += op.fProxies[p].fQuadCnt;
435                 auto* proxy = op.fProxies[p].fProxy;
436                 if (!proxy->instantiate(target->resourceProvider())) {
437                     return;
438                 }
439                 SkASSERT(proxy->config() == config);
440                 SkASSERT(proxy->textureType() == textureType);
441             }
442             if (op.aaType() == GrAAType::kCoverage) {
443                 SkASSERT(aaType == GrAAType::kCoverage || aaType == GrAAType::kNone);
444                 aaType = GrAAType::kCoverage;
445             }
446         }
447 
448         VertexSpec vertexSpec(quadType, colorType, srcQuadType, /* hasLocal */ true, domain, aaType,
449                               /* alpha as coverage */ true);
450 
451         GrSamplerState samplerState = GrSamplerState(GrSamplerState::WrapMode::kClamp,
452                                                      this->filter());
453         GrGpu* gpu = target->resourceProvider()->priv().gpu();
454         uint32_t extraSamplerKey = gpu->getExtraSamplerKeyForProgram(
455                 samplerState, fProxies[0].fProxy->backendFormat());
456 
457         sk_sp<GrGeometryProcessor> gp = GrQuadPerEdgeAA::MakeTexturedProcessor(
458                 vertexSpec, *target->caps().shaderCaps(),
459                 textureType, config, samplerState, extraSamplerKey,
460                 std::move(fTextureColorSpaceXform));
461 
462         // We'll use a dynamic state array for the GP textures when there are multiple ops.
463         // Otherwise, we use fixed dynamic state to specify the single op's proxy.
464         GrPipeline::DynamicStateArrays* dynamicStateArrays = nullptr;
465         GrPipeline::FixedDynamicState* fixedDynamicState;
466         if (numProxies > 1) {
467             dynamicStateArrays = target->allocDynamicStateArrays(numProxies, 1, false);
468             fixedDynamicState = target->makeFixedDynamicState(0);
469         } else {
470             fixedDynamicState = target->makeFixedDynamicState(1);
471             fixedDynamicState->fPrimitiveProcessorTextures[0] = fProxies[0].fProxy;
472         }
473 
474         size_t vertexSize = gp->vertexStride();
475 
476         GrMesh* meshes = target->allocMeshes(numProxies);
477         sk_sp<const GrBuffer> vbuffer;
478         int vertexOffsetInBuffer = 0;
479         int numQuadVerticesLeft = numTotalQuads * vertexSpec.verticesPerQuad();
480         int numAllocatedVertices = 0;
481         void* vdata = nullptr;
482 
483         int m = 0;
484         for (const auto& op : ChainRange<TextureOp>(this)) {
485             int q = 0;
486             for (unsigned p = 0; p < op.fProxyCnt; ++p) {
487                 int quadCnt = op.fProxies[p].fQuadCnt;
488                 auto* proxy = op.fProxies[p].fProxy;
489                 int meshVertexCnt = quadCnt * vertexSpec.verticesPerQuad();
490                 if (numAllocatedVertices < meshVertexCnt) {
491                     vdata = target->makeVertexSpaceAtLeast(
492                             vertexSize, meshVertexCnt, numQuadVerticesLeft, &vbuffer,
493                             &vertexOffsetInBuffer, &numAllocatedVertices);
494                     SkASSERT(numAllocatedVertices <= numQuadVerticesLeft);
495                     if (!vdata) {
496                         SkDebugf("Could not allocate vertices\n");
497                         return;
498                     }
499                 }
500                 SkASSERT(numAllocatedVertices >= meshVertexCnt);
501 
502                 op.tess(vdata, vertexSpec, proxy, q, quadCnt);
503 
504                 if (!GrQuadPerEdgeAA::ConfigureMeshIndices(target, &(meshes[m]), vertexSpec,
505                                                            quadCnt)) {
506                     SkDebugf("Could not allocate indices");
507                     return;
508                 }
509                 meshes[m].setVertexData(vbuffer, vertexOffsetInBuffer);
510                 if (dynamicStateArrays) {
511                     dynamicStateArrays->fPrimitiveProcessorTextures[m] = proxy;
512                 }
513                 ++m;
514                 numAllocatedVertices -= meshVertexCnt;
515                 numQuadVerticesLeft -= meshVertexCnt;
516                 vertexOffsetInBuffer += meshVertexCnt;
517                 vdata = reinterpret_cast<char*>(vdata) + vertexSize * meshVertexCnt;
518                 q += quadCnt;
519             }
520         }
521         SkASSERT(!numQuadVerticesLeft);
522         SkASSERT(!numAllocatedVertices);
523         target->recordDraw(
524                 std::move(gp), meshes, numProxies, fixedDynamicState, dynamicStateArrays);
525     }
526 
onExecute(GrOpFlushState * flushState,const SkRect & chainBounds)527     void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
528         auto pipelineFlags = (GrAAType::kMSAA == this->aaType())
529                 ? GrPipeline::kHWAntialias_Flag
530                 : 0;
531         flushState->executeDrawsAndUploadsForMeshDrawOp(
532                 this, chainBounds, GrProcessorSet::MakeEmptySet(), pipelineFlags);
533     }
534 
onCombineIfPossible(GrOp * t,const GrCaps & caps)535     CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
536         TRACE_EVENT0("skia", TRACE_FUNC);
537         const auto* that = t->cast<TextureOp>();
538         if (!GrColorSpaceXform::Equals(fTextureColorSpaceXform.get(),
539                                        that->fTextureColorSpaceXform.get())) {
540             return CombineResult::kCannotCombine;
541         }
542         bool upgradeToCoverageAAOnMerge = false;
543         if (this->aaType() != that->aaType()) {
544             if (!((this->aaType() == GrAAType::kCoverage && that->aaType() == GrAAType::kNone) ||
545                   (that->aaType() == GrAAType::kCoverage && this->aaType() == GrAAType::kNone))) {
546                 return CombineResult::kCannotCombine;
547             }
548             upgradeToCoverageAAOnMerge = true;
549         }
550         if (fFilter != that->fFilter) {
551             return CombineResult::kCannotCombine;
552         }
553         auto thisProxy = fProxies[0].fProxy;
554         auto thatProxy = that->fProxies[0].fProxy;
555         if (fProxyCnt > 1 || that->fProxyCnt > 1 ||
556             thisProxy->uniqueID() != thatProxy->uniqueID()) {
557             // We can't merge across different proxies. Check if 'this' can be chained with 'that'.
558             if (GrTextureProxy::ProxiesAreCompatibleAsDynamicState(thisProxy, thatProxy) &&
559                 caps.dynamicStateArrayGeometryProcessorTextureSupport()) {
560                 return CombineResult::kMayChain;
561             }
562             return CombineResult::kCannotCombine;
563         }
564 
565         fDomain |= that->fDomain;
566         fColorType = SkTMax(fColorType, that->fColorType);
567         if (upgradeToCoverageAAOnMerge) {
568             fAAType = static_cast<unsigned>(GrAAType::kCoverage);
569         }
570 
571         // Concatenate quad lists together, updating the fSrcQuadIndex in the appended quads
572         // to account for the new starting index in fSrcQuads
573         int srcQuadOffset = fSrcQuads.count();
574         int oldQuadCount = fQuads.count();
575 
576         fSrcQuads.concat(that->fSrcQuads);
577         fQuads.concat(that->fQuads);
578         fProxies[0].fQuadCnt += that->fQuads.count();
579 
580         if (that->fSrcQuads.count() > 0) {
581             // Some of the concatenated quads pointed to fSrcQuads, so adjust the indices for the
582             // newly appended quads
583             for (int i = oldQuadCount; i < fQuads.count(); ++i) {
584                 if (fQuads.metadata(i).fSrcQuadIndex >= 0) {
585                     fQuads.metadata(i).fSrcQuadIndex += srcQuadOffset;
586                 }
587             }
588         }
589 
590         // Confirm all tracked state makes sense when in debug builds
591 #ifdef SK_DEBUG
592         SkASSERT(fSrcQuads.count() <= fQuads.count());
593         for (int i = 0; i < fQuads.count(); ++i) {
594             int srcIndex = fQuads.metadata(i).fSrcQuadIndex;
595             if (srcIndex >= 0) {
596                 // Make sure it points to a valid index, in the right region of the list
597                 SkASSERT(srcIndex < fSrcQuads.count());
598                 SkASSERT((i < oldQuadCount && srcIndex < srcQuadOffset) ||
599                          (i >= oldQuadCount && srcIndex >= srcQuadOffset));
600             }
601         }
602 #endif
603         return CombineResult::kMerged;
604     }
605 
aaType() const606     GrAAType aaType() const { return static_cast<GrAAType>(fAAType); }
filter() const607     GrSamplerState::Filter filter() const { return static_cast<GrSamplerState::Filter>(fFilter); }
608 
609     struct ColorDomainAndAA {
610         // Special constructor to convert enums into the packed bits, which should not delete
611         // the implicit move constructor (but it does require us to declare an empty ctor for
612         // use with the GrTQuadList).
ColorDomainAndAA__anon6e73ecb50111::TextureOp::ColorDomainAndAA613         ColorDomainAndAA(const SkPMColor4f& color, const SkRect& srcRect, int srcQuadIndex,
614                          Domain hasDomain, GrQuadAAFlags aaFlags)
615                 : fColor(color)
616                 , fSrcRect(srcRect)
617                 , fSrcQuadIndex(srcQuadIndex)
618                 , fHasDomain(static_cast<unsigned>(hasDomain))
619                 , fAAFlags(static_cast<unsigned>(aaFlags)) {
620             SkASSERT(fHasDomain == static_cast<unsigned>(hasDomain));
621             SkASSERT(fAAFlags == static_cast<unsigned>(aaFlags));
622         }
623         ColorDomainAndAA() = default;
624 
625         SkPMColor4f fColor;
626         // Even if fSrcQuadIndex provides source coords, use fSrcRect for domain constraint
627         SkRect fSrcRect;
628         // If >= 0, use to access fSrcQuads instead of fSrcRect for the source coordinates
629         int fSrcQuadIndex;
630         unsigned fHasDomain : 1;
631         unsigned fAAFlags : 4;
632 
domain__anon6e73ecb50111::TextureOp::ColorDomainAndAA633         Domain domain() const { return Domain(fHasDomain); }
aaFlags__anon6e73ecb50111::TextureOp::ColorDomainAndAA634         GrQuadAAFlags aaFlags() const { return static_cast<GrQuadAAFlags>(fAAFlags); }
635     };
636     struct Proxy {
637         GrTextureProxy* fProxy;
638         int fQuadCnt;
639     };
640     GrTQuadList<ColorDomainAndAA> fQuads;
641     // The majority of texture ops will not track a complete src quad so this is indexed separately
642     // and may be of different size to fQuads.
643     GrQuadList fSrcQuads;
644     sk_sp<GrColorSpaceXform> fTextureColorSpaceXform;
645     unsigned fFilter : 2;
646     unsigned fAAType : 2;
647     unsigned fDomain : 1;
648     unsigned fColorType : 2;
649     GR_STATIC_ASSERT(GrQuadPerEdgeAA::kColorTypeCount <= 4);
650     // Used to track whether fProxy is ref'ed or has a pending IO after finalize() is called.
651     unsigned fFinalized : 1;
652     unsigned fCanSkipAllocatorGather : 1;
653     unsigned fProxyCnt : 32 - 9;
654     Proxy fProxies[1];
655 
656     static_assert(kGrQuadTypeCount <= 4, "GrQuadType does not fit in 2 bits");
657 
658     typedef GrMeshDrawOp INHERITED;
659 };
660 
661 }  // anonymous namespace
662 
663 namespace GrTextureOp {
664 
Make(GrRecordingContext * context,sk_sp<GrTextureProxy> proxy,GrSamplerState::Filter filter,const SkPMColor4f & color,const SkRect & srcRect,const SkRect & dstRect,GrAAType aaType,GrQuadAAFlags aaFlags,SkCanvas::SrcRectConstraint constraint,const SkMatrix & viewMatrix,sk_sp<GrColorSpaceXform> textureColorSpaceXform)665 std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
666                                sk_sp<GrTextureProxy> proxy,
667                                GrSamplerState::Filter filter,
668                                const SkPMColor4f& color,
669                                const SkRect& srcRect,
670                                const SkRect& dstRect,
671                                GrAAType aaType,
672                                GrQuadAAFlags aaFlags,
673                                SkCanvas::SrcRectConstraint constraint,
674                                const SkMatrix& viewMatrix,
675                                sk_sp<GrColorSpaceXform> textureColorSpaceXform) {
676     return TextureOp::Make(context, std::move(proxy), filter, color, srcRect, dstRect, aaType,
677                            aaFlags, constraint, viewMatrix, std::move(textureColorSpaceXform));
678 }
679 
MakeQuad(GrRecordingContext * context,sk_sp<GrTextureProxy> proxy,GrSamplerState::Filter filter,const SkPMColor4f & color,const SkPoint srcQuad[4],const SkPoint dstQuad[4],GrAAType aaType,GrQuadAAFlags aaFlags,const SkRect * domain,const SkMatrix & viewMatrix,sk_sp<GrColorSpaceXform> textureXform)680 std::unique_ptr<GrDrawOp> MakeQuad(GrRecordingContext* context,
681                                   sk_sp<GrTextureProxy> proxy,
682                                   GrSamplerState::Filter filter,
683                                   const SkPMColor4f& color,
684                                   const SkPoint srcQuad[4],
685                                   const SkPoint dstQuad[4],
686                                   GrAAType aaType,
687                                   GrQuadAAFlags aaFlags,
688                                   const SkRect* domain,
689                                   const SkMatrix& viewMatrix,
690                                   sk_sp<GrColorSpaceXform> textureXform) {
691     return TextureOp::Make(context, std::move(proxy), filter, color, srcQuad, dstQuad, aaType,
692                            aaFlags, domain, viewMatrix, std::move(textureXform));
693 }
694 
MakeSet(GrRecordingContext * context,const GrRenderTargetContext::TextureSetEntry set[],int cnt,GrSamplerState::Filter filter,GrAAType aaType,const SkMatrix & viewMatrix,sk_sp<GrColorSpaceXform> textureColorSpaceXform)695 std::unique_ptr<GrDrawOp> MakeSet(GrRecordingContext* context,
696                                   const GrRenderTargetContext::TextureSetEntry set[],
697                                   int cnt,
698                                   GrSamplerState::Filter filter,
699                                   GrAAType aaType,
700                                   const SkMatrix& viewMatrix,
701                                   sk_sp<GrColorSpaceXform> textureColorSpaceXform) {
702     return TextureOp::Make(context, set, cnt, filter, aaType, viewMatrix,
703                            std::move(textureColorSpaceXform));
704 }
705 
GetFilterHasEffect(const SkMatrix & viewMatrix,const SkRect & srcRect,const SkRect & dstRect)706 bool GetFilterHasEffect(const SkMatrix& viewMatrix, const SkRect& srcRect, const SkRect& dstRect) {
707     // Hypothetically we could disable bilerp filtering when flipping or rotating 90 degrees, but
708     // that makes the math harder and we don't want to increase the overhead of the checks
709     if (!viewMatrix.isScaleTranslate() ||
710         viewMatrix.getScaleX() < 0.0f || viewMatrix.getScaleY() < 0.0f) {
711         return true;
712     }
713 
714     // Given the matrix conditions ensured above, this computes the device space coordinates for
715     // the top left corner of dstRect and its size.
716     SkScalar dw = viewMatrix.getScaleX() * dstRect.width();
717     SkScalar dh = viewMatrix.getScaleY() * dstRect.height();
718     SkScalar dl = viewMatrix.getScaleX() * dstRect.fLeft + viewMatrix.getTranslateX();
719     SkScalar dt = viewMatrix.getScaleY() * dstRect.fTop + viewMatrix.getTranslateY();
720 
721     // Disable filtering when there is no scaling of the src rect and the src rect and dst rect
722     // align fractionally. If we allow inverted src rects this logic needs to consider that.
723     SkASSERT(srcRect.isSorted());
724     return dw != srcRect.width() || dh != srcRect.height() ||
725            SkScalarFraction(dl) != SkScalarFraction(srcRect.fLeft) ||
726            SkScalarFraction(dt) != SkScalarFraction(srcRect.fTop);
727 }
728 
729 }  // namespace GrTextureOp
730 
731 #if GR_TEST_UTILS
732 #include "GrProxyProvider.h"
733 #include "GrRecordingContext.h"
734 #include "GrRecordingContextPriv.h"
735 
GR_DRAW_OP_TEST_DEFINE(TextureOp)736 GR_DRAW_OP_TEST_DEFINE(TextureOp) {
737     GrSurfaceDesc desc;
738     desc.fConfig = kRGBA_8888_GrPixelConfig;
739     desc.fHeight = random->nextULessThan(90) + 10;
740     desc.fWidth = random->nextULessThan(90) + 10;
741     auto origin = random->nextBool() ? kTopLeft_GrSurfaceOrigin : kBottomLeft_GrSurfaceOrigin;
742     GrMipMapped mipMapped = random->nextBool() ? GrMipMapped::kYes : GrMipMapped::kNo;
743     SkBackingFit fit = SkBackingFit::kExact;
744     if (mipMapped == GrMipMapped::kNo) {
745         fit = random->nextBool() ? SkBackingFit::kApprox : SkBackingFit::kExact;
746     }
747 
748     const GrBackendFormat format =
749             context->priv().caps()->getBackendFormatFromColorType(kRGBA_8888_SkColorType);
750 
751     GrProxyProvider* proxyProvider = context->priv().proxyProvider();
752     sk_sp<GrTextureProxy> proxy = proxyProvider->createProxy(format, desc, origin, mipMapped, fit,
753                                                              SkBudgeted::kNo,
754                                                              GrInternalSurfaceFlags::kNone);
755 
756     SkRect rect = GrTest::TestRect(random);
757     SkRect srcRect;
758     srcRect.fLeft = random->nextRangeScalar(0.f, proxy->width() / 2.f);
759     srcRect.fRight = random->nextRangeScalar(0.f, proxy->width()) + proxy->width() / 2.f;
760     srcRect.fTop = random->nextRangeScalar(0.f, proxy->height() / 2.f);
761     srcRect.fBottom = random->nextRangeScalar(0.f, proxy->height()) + proxy->height() / 2.f;
762     SkMatrix viewMatrix = GrTest::TestMatrixPreservesRightAngles(random);
763     SkPMColor4f color = SkPMColor4f::FromBytes_RGBA(SkColorToPremulGrColor(random->nextU()));
764     GrSamplerState::Filter filter = (GrSamplerState::Filter)random->nextULessThan(
765             static_cast<uint32_t>(GrSamplerState::Filter::kMipMap) + 1);
766     while (mipMapped == GrMipMapped::kNo && filter == GrSamplerState::Filter::kMipMap) {
767         filter = (GrSamplerState::Filter)random->nextULessThan(
768                 static_cast<uint32_t>(GrSamplerState::Filter::kMipMap) + 1);
769     }
770     auto texXform = GrTest::TestColorXform(random);
771     GrAAType aaType = GrAAType::kNone;
772     if (random->nextBool()) {
773         aaType = (fsaaType == GrFSAAType::kUnifiedMSAA) ? GrAAType::kMSAA : GrAAType::kCoverage;
774     }
775     GrQuadAAFlags aaFlags = GrQuadAAFlags::kNone;
776     aaFlags |= random->nextBool() ? GrQuadAAFlags::kLeft : GrQuadAAFlags::kNone;
777     aaFlags |= random->nextBool() ? GrQuadAAFlags::kTop : GrQuadAAFlags::kNone;
778     aaFlags |= random->nextBool() ? GrQuadAAFlags::kRight : GrQuadAAFlags::kNone;
779     aaFlags |= random->nextBool() ? GrQuadAAFlags::kBottom : GrQuadAAFlags::kNone;
780     auto constraint = random->nextBool() ? SkCanvas::kStrict_SrcRectConstraint
781                                          : SkCanvas::kFast_SrcRectConstraint;
782     return GrTextureOp::Make(context, std::move(proxy), filter, color, srcRect, rect, aaType,
783                              aaFlags, constraint, viewMatrix, std::move(texXform));
784 }
785 
786 #endif
787