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