blob: f583777f640af3b2fb82b7e3e417b2e23354a511 [file] [log] [blame]
Brian Salomon34169692017-08-28 15:32:01 -04001/*
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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "src/gpu/ops/GrTextureOp.h"
Brian Salomond7065e72018-10-12 11:42:02 -04009#include <new>
Mike Kleinc0bd9f92019-04-23 12:05:21 -050010#include "include/core/SkPoint.h"
11#include "include/core/SkPoint3.h"
12#include "include/gpu/GrTexture.h"
13#include "include/private/GrRecordingContext.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050014#include "include/private/SkTo.h"
15#include "src/core/SkMathPriv.h"
16#include "src/core/SkMatrixPriv.h"
17#include "src/core/SkRectPriv.h"
18#include "src/gpu/GrAppliedClip.h"
19#include "src/gpu/GrCaps.h"
20#include "src/gpu/GrDrawOpTest.h"
21#include "src/gpu/GrGeometryProcessor.h"
22#include "src/gpu/GrGpu.h"
23#include "src/gpu/GrMemoryPool.h"
24#include "src/gpu/GrOpFlushState.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050025#include "src/gpu/GrRecordingContextPriv.h"
26#include "src/gpu/GrResourceProvider.h"
27#include "src/gpu/GrResourceProviderPriv.h"
28#include "src/gpu/GrShaderCaps.h"
29#include "src/gpu/GrTexturePriv.h"
Greg Danielf91aeb22019-06-18 09:58:02 -040030#include "src/gpu/GrTextureProxy.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050031#include "src/gpu/SkGr.h"
Michael Ludwigfd4f4df2019-05-29 09:51:09 -040032#include "src/gpu/geometry/GrQuad.h"
Michael Ludwigd17e05a2019-06-04 09:10:34 -040033#include "src/gpu/geometry/GrQuadList.h"
Michael Ludwig0f809022019-06-04 09:14:37 -040034#include "src/gpu/geometry/GrQuadUtils.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050035#include "src/gpu/glsl/GrGLSLVarying.h"
36#include "src/gpu/ops/GrMeshDrawOp.h"
37#include "src/gpu/ops/GrQuadPerEdgeAA.h"
Brian Salomon34169692017-08-28 15:32:01 -040038
39namespace {
40
Michael Ludwig460eb5e2018-10-29 11:09:29 -040041using Domain = GrQuadPerEdgeAA::Domain;
Michael Ludwigc182b942018-11-16 10:27:51 -050042using VertexSpec = GrQuadPerEdgeAA::VertexSpec;
Brian Osman3d139a42018-11-19 10:42:10 -050043using ColorType = GrQuadPerEdgeAA::ColorType;
Brian Salomonb80ffee2018-05-23 16:39:39 -040044
Brian Salomon246bc3d2018-12-06 15:33:02 -050045// if normalizing the domain then pass 1/width, 1/height, 1 for iw, ih, h. Otherwise pass
46// 1, 1, and height.
47static SkRect compute_domain(Domain domain, GrSamplerState::Filter filter, GrSurfaceOrigin origin,
48 const SkRect& srcRect, float iw, float ih, float h) {
49 static constexpr SkRect kLargeRect = {-100000, -100000, 1000000, 1000000};
Michael Ludwig460eb5e2018-10-29 11:09:29 -040050 if (domain == Domain::kNo) {
51 // Either the quad has no domain constraint and is batched with a domain constrained op
52 // (in which case we want a domain that doesn't restrict normalized tex coords), or the
53 // entire op doesn't use the domain, in which case the returned value is ignored.
54 return kLargeRect;
55 }
56
57 auto ltrb = Sk4f::Load(&srcRect);
58 if (filter == GrSamplerState::Filter::kBilerp) {
59 auto rblt = SkNx_shuffle<2, 3, 0, 1>(ltrb);
60 auto whwh = (rblt - ltrb).abs();
61 auto c = (rblt + ltrb) * 0.5f;
62 static const Sk4f kOffsets = {0.5f, 0.5f, -0.5f, -0.5f};
63 ltrb = (whwh < 1.f).thenElse(c, ltrb + kOffsets);
64 }
65 ltrb *= Sk4f(iw, ih, iw, ih);
66 if (origin == kBottomLeft_GrSurfaceOrigin) {
67 static const Sk4f kMul = {1.f, -1.f, 1.f, -1.f};
Brian Salomon246bc3d2018-12-06 15:33:02 -050068 const Sk4f kAdd = {0.f, h, 0.f, h};
Michael Ludwig460eb5e2018-10-29 11:09:29 -040069 ltrb = SkNx_shuffle<0, 3, 2, 1>(kMul * ltrb + kAdd);
70 }
71
72 SkRect domainRect;
73 ltrb.store(&domainRect);
74 return domainRect;
75}
76
Brian Salomon246bc3d2018-12-06 15:33:02 -050077// If normalizing the src quad then pass 1/width, 1/height, 1 for iw, ih, h. Otherwise pass
78// 1, 1, and height.
Michael Ludwigde4c58c2019-06-04 09:12:59 -040079static GrQuad compute_src_quad_from_rect(GrSurfaceOrigin origin, const SkRect& srcRect,
80 float iw, float ih, float h) {
Michael Ludwig460eb5e2018-10-29 11:09:29 -040081 // Convert the pixel-space src rectangle into normalized texture coordinates
82 SkRect texRect = {
83 iw * srcRect.fLeft,
84 ih * srcRect.fTop,
85 iw * srcRect.fRight,
86 ih * srcRect.fBottom
87 };
88 if (origin == kBottomLeft_GrSurfaceOrigin) {
Brian Salomon246bc3d2018-12-06 15:33:02 -050089 texRect.fTop = h - texRect.fTop;
90 texRect.fBottom = h - texRect.fBottom;
Michael Ludwig460eb5e2018-10-29 11:09:29 -040091 }
Michael Ludwigde4c58c2019-06-04 09:12:59 -040092 return GrQuad(texRect);
Michael Ludwig460eb5e2018-10-29 11:09:29 -040093}
Michael Ludwig009b92e2019-02-15 16:03:53 -050094// Normalizes logical src coords and corrects for origin
Michael Ludwigde4c58c2019-06-04 09:12:59 -040095static GrQuad compute_src_quad(GrSurfaceOrigin origin, const GrQuad& srcQuad,
96 float iw, float ih, float h) {
Michael Ludwig009b92e2019-02-15 16:03:53 -050097 // The src quad should not have any perspective
98 SkASSERT(!srcQuad.hasPerspective());
Michael Ludwigb3461fa2019-04-30 11:50:55 -040099 skvx::Vec<4, float> xs = srcQuad.x4f() * iw;
100 skvx::Vec<4, float> ys = srcQuad.y4f() * ih;
Michael Ludwig009b92e2019-02-15 16:03:53 -0500101 if (origin == kBottomLeft_GrSurfaceOrigin) {
102 ys = h - ys;
103 }
Michael Ludwigde4c58c2019-06-04 09:12:59 -0400104 return GrQuad(xs, ys, srcQuad.quadType());
Michael Ludwig009b92e2019-02-15 16:03:53 -0500105}
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400106
Brian Salomon34169692017-08-28 15:32:01 -0400107/**
108 * Op that implements GrTextureOp::Make. It draws textured quads. Each quad can modulate against a
109 * the texture by color. The blend with the destination is always src-over. The edges are non-AA.
110 */
111class TextureOp final : public GrMeshDrawOp {
112public:
Robert Phillipsb97da532019-02-12 15:24:12 -0500113 static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -0400114 sk_sp<GrTextureProxy> proxy,
115 GrSamplerState::Filter filter,
Brian Osman3d139a42018-11-19 10:42:10 -0500116 const SkPMColor4f& color,
Robert Phillips7c525e62018-06-12 10:11:12 -0400117 const SkRect& srcRect,
118 const SkRect& dstRect,
119 GrAAType aaType,
Brian Salomon2213ee92018-10-02 10:44:21 -0400120 GrQuadAAFlags aaFlags,
Robert Phillips7c525e62018-06-12 10:11:12 -0400121 SkCanvas::SrcRectConstraint constraint,
Brian Osman2b23c4b2018-06-01 12:25:08 -0400122 const SkMatrix& viewMatrix,
Brian Osman3d139a42018-11-19 10:42:10 -0500123 sk_sp<GrColorSpaceXform> textureColorSpaceXform) {
Michael Ludwigde4c58c2019-06-04 09:12:59 -0400124 GrQuad dstQuad = GrQuad::MakeFromRect(dstRect, viewMatrix);
Robert Phillipsc994a932018-06-19 13:09:54 -0400125
Michael Ludwigde4c58c2019-06-04 09:12:59 -0400126 if (dstQuad.quadType() == GrQuad::Type::kAxisAligned) {
Michael Ludwig009b92e2019-02-15 16:03:53 -0500127 // Disable filtering if possible (note AA optimizations for rects are automatically
128 // handled above in GrResolveAATypeForQuad).
129 if (filter != GrSamplerState::Filter::kNearest &&
130 !GrTextureOp::GetFilterHasEffect(viewMatrix, srcRect, dstRect)) {
131 filter = GrSamplerState::Filter::kNearest;
132 }
133 }
134
135 GrOpMemoryPool* pool = context->priv().opMemoryPool();
136 // srcRect provides both local coords and domain (if needed), so use nullptr for srcQuad
Brian Salomon2213ee92018-10-02 10:44:21 -0400137 return pool->allocate<TextureOp>(
Michael Ludwig41f395d2019-05-23 13:59:45 -0400138 std::move(proxy), filter, color, dstQuad, srcRect, constraint,
139 nullptr, aaType, aaFlags, std::move(textureColorSpaceXform));
Michael Ludwig009b92e2019-02-15 16:03:53 -0500140 }
141 static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
142 sk_sp<GrTextureProxy> proxy,
143 GrSamplerState::Filter filter,
144 const SkPMColor4f& color,
145 const SkPoint srcQuad[4],
146 const SkPoint dstQuad[4],
147 GrAAType aaType,
148 GrQuadAAFlags aaFlags,
149 const SkRect* domain,
150 const SkMatrix& viewMatrix,
151 sk_sp<GrColorSpaceXform> textureColorSpaceXform) {
Michael Ludwigde4c58c2019-06-04 09:12:59 -0400152 GrQuad grDstQuad = GrQuad::MakeFromSkQuad(dstQuad, viewMatrix);
153 GrQuad grSrcQuad = GrQuad::MakeFromSkQuad(srcQuad, SkMatrix::I());
Michael Ludwig009b92e2019-02-15 16:03:53 -0500154
155 // If constraint remains fast, the value in srcRect will be ignored since srcQuads provides
156 // the local coordinates and a domain won't be used.
157 SkRect srcRect = SkRect::MakeEmpty();
158 SkCanvas::SrcRectConstraint constraint = SkCanvas::kFast_SrcRectConstraint;
159 if (domain) {
160 srcRect = *domain;
161 constraint = SkCanvas::kStrict_SrcRectConstraint;
162 }
163
164 GrOpMemoryPool* pool = context->priv().opMemoryPool();
Michael Ludwigde4c58c2019-06-04 09:12:59 -0400165 // Pass domain as srcRect if provided, but send srcQuad as a GrQuad for local coords
Michael Ludwig009b92e2019-02-15 16:03:53 -0500166 return pool->allocate<TextureOp>(
Michael Ludwig41f395d2019-05-23 13:59:45 -0400167 std::move(proxy), filter, color, grDstQuad, srcRect, constraint, &grSrcQuad,
168 aaType, aaFlags, std::move(textureColorSpaceXform));
Brian Salomon34169692017-08-28 15:32:01 -0400169 }
Robert Phillipsb97da532019-02-12 15:24:12 -0500170 static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
Brian Salomond7065e72018-10-12 11:42:02 -0400171 const GrRenderTargetContext::TextureSetEntry set[],
Brian Salomond003d222018-11-26 13:25:05 -0500172 int cnt, GrSamplerState::Filter filter, GrAAType aaType,
Michael Ludwig31ba7182019-04-03 10:38:06 -0400173 SkCanvas::SrcRectConstraint constraint,
Brian Salomond003d222018-11-26 13:25:05 -0500174 const SkMatrix& viewMatrix,
Brian Osman3d139a42018-11-19 10:42:10 -0500175 sk_sp<GrColorSpaceXform> textureColorSpaceXform) {
Brian Salomond7065e72018-10-12 11:42:02 -0400176 size_t size = sizeof(TextureOp) + sizeof(Proxy) * (cnt - 1);
Robert Phillips9da87e02019-02-04 13:26:26 -0500177 GrOpMemoryPool* pool = context->priv().opMemoryPool();
Brian Salomond7065e72018-10-12 11:42:02 -0400178 void* mem = pool->allocate(size);
Michael Ludwig31ba7182019-04-03 10:38:06 -0400179 return std::unique_ptr<GrDrawOp>(new (mem) TextureOp(
180 set, cnt, filter, aaType, constraint, viewMatrix,
181 std::move(textureColorSpaceXform)));
Brian Salomond7065e72018-10-12 11:42:02 -0400182 }
Brian Salomon34169692017-08-28 15:32:01 -0400183
Brian Salomon336ce7b2017-09-08 08:23:58 -0400184 ~TextureOp() override {
Brian Salomond7065e72018-10-12 11:42:02 -0400185 for (unsigned p = 0; p < fProxyCnt; ++p) {
Robert Phillips3d4cac52019-06-11 08:08:08 -0400186 fProxies[p].fProxy->unref();
Brian Salomon336ce7b2017-09-08 08:23:58 -0400187 }
188 }
Brian Salomon34169692017-08-28 15:32:01 -0400189
190 const char* name() const override { return "TextureOp"; }
191
Chris Dalton1706cbf2019-05-21 19:35:29 -0600192 void visitProxies(const VisitProxyFunc& func) const override {
Brian Salomond7065e72018-10-12 11:42:02 -0400193 for (unsigned p = 0; p < fProxyCnt; ++p) {
Chris Dalton7eb5c0f2019-05-23 15:15:47 -0600194 bool mipped = (GrSamplerState::Filter::kMipMap == this->filter());
195 func(fProxies[p].fProxy, GrMipMapped(mipped));
Brian Salomond7065e72018-10-12 11:42:02 -0400196 }
197 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -0400198
Brian Osman9a390ac2018-11-12 09:47:48 -0500199#ifdef SK_DEBUG
Brian Salomon34169692017-08-28 15:32:01 -0400200 SkString dumpInfo() const override {
201 SkString str;
Brian Salomond7065e72018-10-12 11:42:02 -0400202 str.appendf("# draws: %d\n", fQuads.count());
203 int q = 0;
204 for (unsigned p = 0; p < fProxyCnt; ++p) {
205 str.appendf("Proxy ID: %d, Filter: %d\n", fProxies[p].fProxy->uniqueID().asUInt(),
206 static_cast<int>(fFilter));
207 for (int i = 0; i < fProxies[p].fQuadCnt; ++i, ++q) {
Michael Ludwigde4c58c2019-06-04 09:12:59 -0400208 GrQuad quad = fQuads[q];
Michael Ludwigc96fc372019-01-08 15:46:15 -0500209 const ColorDomainAndAA& info = fQuads.metadata(i);
Brian Salomond7065e72018-10-12 11:42:02 -0400210 str.appendf(
211 "%d: Color: 0x%08x, TexRect [L: %.2f, T: %.2f, R: %.2f, B: %.2f] "
212 "Quad [(%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f)]\n",
Michael Ludwigc96fc372019-01-08 15:46:15 -0500213 i, info.fColor.toBytes_RGBA(), info.fSrcRect.fLeft, info.fSrcRect.fTop,
214 info.fSrcRect.fRight, info.fSrcRect.fBottom, quad.point(0).fX,
215 quad.point(0).fY, quad.point(1).fX, quad.point(1).fY,
216 quad.point(2).fX, quad.point(2).fY, quad.point(3).fX,
217 quad.point(3).fY);
Brian Salomond7065e72018-10-12 11:42:02 -0400218 }
Brian Salomon34169692017-08-28 15:32:01 -0400219 }
220 str += INHERITED::dumpInfo();
221 return str;
222 }
Brian Osman9a390ac2018-11-12 09:47:48 -0500223#endif
Brian Salomon34169692017-08-28 15:32:01 -0400224
Brian Osman5ced0bf2019-03-15 10:15:29 -0400225 GrProcessorSet::Analysis finalize(
Brian Osman8fa7ab42019-03-18 10:22:42 -0400226 const GrCaps& caps, const GrAppliedClip*, GrFSAAType, GrClampType clampType) override {
Brian Osman8fa7ab42019-03-18 10:22:42 -0400227 fColorType = static_cast<unsigned>(ColorType::kNone);
228 for (int q = 0; q < fQuads.count(); ++q) {
229 const ColorDomainAndAA& info = fQuads.metadata(q);
230 auto colorType = GrQuadPerEdgeAA::MinColorType(info.fColor, clampType, caps);
231 fColorType = SkTMax(fColorType, static_cast<unsigned>(colorType));
232 }
Chris Dalton4b62aed2019-01-15 11:53:00 -0700233 return GrProcessorSet::EmptySetAnalysis();
Brian Salomon34169692017-08-28 15:32:01 -0400234 }
235
Brian Salomon485b8c62018-01-12 15:11:06 -0500236 FixedFunctionFlags fixedFunctionFlags() const override {
237 return this->aaType() == GrAAType::kMSAA ? FixedFunctionFlags::kUsesHWAA
238 : FixedFunctionFlags::kNone;
239 }
Brian Salomon34169692017-08-28 15:32:01 -0400240
241 DEFINE_OP_CLASS_ID
242
243private:
Robert Phillips7c525e62018-06-12 10:11:12 -0400244 friend class ::GrOpMemoryPool;
Brian Salomon762d5e72017-12-01 10:25:08 -0500245
Michael Ludwig009b92e2019-02-15 16:03:53 -0500246 // dstQuad and dstQuadType should be the geometry transformed by the view matrix.
247 // srcRect represents original src rect and will be used as the domain when constraint is strict
248 // If srcQuad is provided, it will be used for the local coords instead of srcRect, although
249 // srcRect will still specify the domain constraint if needed.
Brian Osman3d139a42018-11-19 10:42:10 -0500250 TextureOp(sk_sp<GrTextureProxy> proxy, GrSamplerState::Filter filter, const SkPMColor4f& color,
Michael Ludwigde4c58c2019-06-04 09:12:59 -0400251 const GrQuad& dstQuad, const SkRect& srcRect,
252 SkCanvas::SrcRectConstraint constraint, const GrQuad* srcQuad, GrAAType aaType,
Michael Ludwig009b92e2019-02-15 16:03:53 -0500253 GrQuadAAFlags aaFlags, sk_sp<GrColorSpaceXform> textureColorSpaceXform)
Brian Salomon34169692017-08-28 15:32:01 -0400254 : INHERITED(ClassID())
Brian Osman3ebd3542018-07-30 14:36:53 -0400255 , fTextureColorSpaceXform(std::move(textureColorSpaceXform))
Robert Phillips3d4cac52019-06-11 08:08:08 -0400256 , fFilter(static_cast<unsigned>(filter)) {
Michael Ludwig6bee7762018-10-19 09:50:36 -0400257 // Clean up disparities between the overall aa type and edge configuration and apply
258 // optimizations based on the rect and matrix when appropriate
Michael Ludwig0f809022019-06-04 09:14:37 -0400259 GrQuadUtils::ResolveAAType(aaType, aaFlags, dstQuad, &aaType, &aaFlags);
Michael Ludwig6bee7762018-10-19 09:50:36 -0400260 fAAType = static_cast<unsigned>(aaType);
261
Brian Salomonf1709042018-10-03 11:57:00 -0400262 // We expect our caller to have already caught this optimization.
Brian Salomond7065e72018-10-12 11:42:02 -0400263 SkASSERT(!srcRect.contains(proxy->getWorstCaseBoundsRect()) ||
Brian Salomonf1709042018-10-03 11:57:00 -0400264 constraint == SkCanvas::kFast_SrcRectConstraint);
Michael Ludwig009b92e2019-02-15 16:03:53 -0500265
Brian Salomonf09abc52018-10-03 15:59:04 -0400266 // We may have had a strict constraint with nearest filter solely due to possible AA bloat.
267 // If we don't have (or determined we don't need) coverage AA then we can skip using a
268 // domain.
269 if (constraint == SkCanvas::kStrict_SrcRectConstraint &&
Brian Salomon0087c832018-10-15 14:48:20 -0400270 this->filter() == GrSamplerState::Filter::kNearest &&
Michael Ludwig6bee7762018-10-19 09:50:36 -0400271 aaType != GrAAType::kCoverage) {
Brian Salomonf09abc52018-10-03 15:59:04 -0400272 constraint = SkCanvas::kFast_SrcRectConstraint;
273 }
Michael Ludwigc96fc372019-01-08 15:46:15 -0500274
275 Domain domain = constraint == SkCanvas::kStrict_SrcRectConstraint ? Domain::kYes
276 : Domain::kNo;
Michael Ludwig009b92e2019-02-15 16:03:53 -0500277 // Initially, if srcQuad is provided it will always be at index 0 of fSrcQuads
Michael Ludwig41f395d2019-05-23 13:59:45 -0400278 fQuads.push_back(dstQuad, {color, srcRect, srcQuad ? 0 : -1, domain, aaFlags});
Michael Ludwig009b92e2019-02-15 16:03:53 -0500279 if (srcQuad) {
Michael Ludwig41f395d2019-05-23 13:59:45 -0400280 fSrcQuads.push_back(*srcQuad);
Michael Ludwig009b92e2019-02-15 16:03:53 -0500281 }
Brian Salomond7065e72018-10-12 11:42:02 -0400282 fProxyCnt = 1;
283 fProxies[0] = {proxy.release(), 1};
Michael Ludwig41f395d2019-05-23 13:59:45 -0400284 this->setBounds(dstQuad.bounds(), HasAABloat(aaType == GrAAType::kCoverage),
285 IsZeroArea::kNo);
Michael Ludwigc96fc372019-01-08 15:46:15 -0500286 fDomain = static_cast<unsigned>(domain);
Brian Salomond7065e72018-10-12 11:42:02 -0400287 }
288 TextureOp(const GrRenderTargetContext::TextureSetEntry set[], int cnt,
Michael Ludwig31ba7182019-04-03 10:38:06 -0400289 GrSamplerState::Filter filter, GrAAType aaType,
290 SkCanvas::SrcRectConstraint constraint, const SkMatrix& viewMatrix,
Brian Salomond003d222018-11-26 13:25:05 -0500291 sk_sp<GrColorSpaceXform> textureColorSpaceXform)
Brian Salomond7065e72018-10-12 11:42:02 -0400292 : INHERITED(ClassID())
293 , fTextureColorSpaceXform(std::move(textureColorSpaceXform))
Robert Phillips3d4cac52019-06-11 08:08:08 -0400294 , fFilter(static_cast<unsigned>(filter)) {
Brian Salomond7065e72018-10-12 11:42:02 -0400295 fProxyCnt = SkToUInt(cnt);
296 SkRect bounds = SkRectPriv::MakeLargestInverted();
Michael Ludwig6bee7762018-10-19 09:50:36 -0400297 GrAAType overallAAType = GrAAType::kNone; // aa type maximally compatible with all dst rects
Brian Salomon0087c832018-10-15 14:48:20 -0400298 bool mustFilter = false;
Michael Ludwig7ae2ab52019-03-05 16:00:20 -0500299 // Most dst rects are transformed by the same view matrix, so their quad types start
300 // identical, unless an entry provides a dstClip or additional transform that changes it.
301 // The quad list will automatically adapt to that.
Michael Ludwig41f395d2019-05-23 13:59:45 -0400302 fQuads.reserve(cnt, viewMatrix.hasPerspective());
Brian Salomon1d835422019-03-13 16:11:44 -0400303 bool allOpaque = true;
Michael Ludwig31ba7182019-04-03 10:38:06 -0400304 Domain netDomain = Domain::kNo;
Brian Salomond7065e72018-10-12 11:42:02 -0400305 for (unsigned p = 0; p < fProxyCnt; ++p) {
306 fProxies[p].fProxy = SkRef(set[p].fProxy.get());
307 fProxies[p].fQuadCnt = 1;
308 SkASSERT(fProxies[p].fProxy->textureType() == fProxies[0].fProxy->textureType());
309 SkASSERT(fProxies[p].fProxy->config() == fProxies[0].fProxy->config());
Michael Ludwigce62dec2019-02-19 11:48:46 -0500310
Michael Ludwig7ae2ab52019-03-05 16:00:20 -0500311 SkMatrix ctm = viewMatrix;
312 if (set[p].fPreViewMatrix) {
313 ctm.preConcat(*set[p].fPreViewMatrix);
314 }
315
Michael Ludwigce62dec2019-02-19 11:48:46 -0500316 // Use dstRect unless dstClip is provided, which is assumed to be a quad
Michael Ludwig1433cfd2019-02-27 17:12:30 -0500317 auto quad = set[p].fDstClipQuad == nullptr ?
Michael Ludwigde4c58c2019-06-04 09:12:59 -0400318 GrQuad::MakeFromRect(set[p].fDstRect, ctm) :
319 GrQuad::MakeFromSkQuad(set[p].fDstClipQuad, ctm);
Michael Ludwigce62dec2019-02-19 11:48:46 -0500320
Michael Ludwig41f395d2019-05-23 13:59:45 -0400321 bounds.joinPossiblyEmptyRect(quad.bounds());
Michael Ludwig6bee7762018-10-19 09:50:36 -0400322 GrQuadAAFlags aaFlags;
323 // Don't update the overall aaType, might be inappropriate for some of the quads
324 GrAAType aaForQuad;
Michael Ludwig0f809022019-06-04 09:14:37 -0400325 GrQuadUtils::ResolveAAType(aaType, set[p].fAAFlags, quad, &aaForQuad, &aaFlags);
Michael Ludwig6bee7762018-10-19 09:50:36 -0400326 // Resolve sets aaForQuad to aaType or None, there is never a change between aa methods
327 SkASSERT(aaForQuad == GrAAType::kNone || aaForQuad == aaType);
328 if (overallAAType == GrAAType::kNone && aaForQuad != GrAAType::kNone) {
329 overallAAType = aaType;
Brian Salomond7065e72018-10-12 11:42:02 -0400330 }
Brian Salomon0087c832018-10-15 14:48:20 -0400331 if (!mustFilter && this->filter() != GrSamplerState::Filter::kNearest) {
Michael Ludwigde4c58c2019-06-04 09:12:59 -0400332 mustFilter = quad.quadType() != GrQuad::Type::kAxisAligned ||
Michael Ludwig7ae2ab52019-03-05 16:00:20 -0500333 GrTextureOp::GetFilterHasEffect(ctm, set[p].fSrcRect,
Michael Ludwiga3c45c72019-01-17 17:26:48 -0500334 set[p].fDstRect);
Brian Salomon0087c832018-10-15 14:48:20 -0400335 }
Michael Ludwig31ba7182019-04-03 10:38:06 -0400336 Domain domainForQuad = Domain::kNo;
337 if (constraint == SkCanvas::kStrict_SrcRectConstraint) {
338 // Check (briefly) if the strict constraint is needed for this set entry
339 if (!set[p].fSrcRect.contains(fProxies[p].fProxy->getWorstCaseBoundsRect()) &&
340 (mustFilter || aaForQuad == GrAAType::kCoverage)) {
341 // Can't rely on hardware clamping and the draw will access outer texels
342 // for AA and/or bilerp
343 netDomain = Domain::kYes;
344 domainForQuad = Domain::kYes;
345 }
346 }
Brian Salomond003d222018-11-26 13:25:05 -0500347 float alpha = SkTPin(set[p].fAlpha, 0.f, 1.f);
Brian Salomon1d835422019-03-13 16:11:44 -0400348 allOpaque &= (1.f == alpha);
Brian Salomond003d222018-11-26 13:25:05 -0500349 SkPMColor4f color{alpha, alpha, alpha, alpha};
Michael Ludwigce62dec2019-02-19 11:48:46 -0500350 int srcQuadIndex = -1;
Michael Ludwig1433cfd2019-02-27 17:12:30 -0500351 if (set[p].fDstClipQuad) {
Michael Ludwigce62dec2019-02-19 11:48:46 -0500352 // Derive new source coordinates that match dstClip's relative locations in dstRect,
353 // but with respect to srcRect
354 SkPoint srcQuad[4];
Michael Ludwig1433cfd2019-02-27 17:12:30 -0500355 GrMapRectPoints(set[p].fDstRect, set[p].fSrcRect, set[p].fDstClipQuad, srcQuad, 4);
Michael Ludwigde4c58c2019-06-04 09:12:59 -0400356 fSrcQuads.push_back(GrQuad::MakeFromSkQuad(srcQuad, SkMatrix::I()));
Michael Ludwigce62dec2019-02-19 11:48:46 -0500357 srcQuadIndex = fSrcQuads.count() - 1;
358 }
Michael Ludwig41f395d2019-05-23 13:59:45 -0400359 fQuads.push_back(quad, {color, set[p].fSrcRect, srcQuadIndex, domainForQuad, aaFlags});
Brian Salomond7065e72018-10-12 11:42:02 -0400360 }
Michael Ludwig6bee7762018-10-19 09:50:36 -0400361 fAAType = static_cast<unsigned>(overallAAType);
Brian Salomon0087c832018-10-15 14:48:20 -0400362 if (!mustFilter) {
363 fFilter = static_cast<unsigned>(GrSamplerState::Filter::kNearest);
364 }
Brian Salomond7065e72018-10-12 11:42:02 -0400365 this->setBounds(bounds, HasAABloat(this->aaType() == GrAAType::kCoverage), IsZeroArea::kNo);
Michael Ludwig31ba7182019-04-03 10:38:06 -0400366 fDomain = static_cast<unsigned>(netDomain);
Brian Salomon34169692017-08-28 15:32:01 -0400367 }
368
Brian Salomon574d6162018-11-19 16:57:25 -0500369 void tess(void* v, const VertexSpec& spec, const GrTextureProxy* proxy, int start,
370 int cnt) const {
Brian Salomond7065e72018-10-12 11:42:02 -0400371 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Salomond7065e72018-10-12 11:42:02 -0400372 auto origin = proxy->origin();
373 const auto* texture = proxy->peekTexture();
Brian Salomon246bc3d2018-12-06 15:33:02 -0500374 float iw, ih, h;
375 if (proxy->textureType() == GrTextureType::kRectangle) {
376 iw = ih = 1.f;
377 h = texture->height();
378 } else {
379 iw = 1.f / texture->width();
380 ih = 1.f / texture->height();
381 h = 1.f;
382 }
Brian Salomon7eae3e02018-08-07 14:02:38 +0000383
Brian Salomond7065e72018-10-12 11:42:02 -0400384 for (int i = start; i < start + cnt; ++i) {
Michael Ludwigde4c58c2019-06-04 09:12:59 -0400385 const GrQuad& device = fQuads[i];
Michael Ludwigc96fc372019-01-08 15:46:15 -0500386 const ColorDomainAndAA& info = fQuads.metadata(i);
387
Michael Ludwigde4c58c2019-06-04 09:12:59 -0400388 GrQuad srcQuad = info.fSrcQuadIndex >= 0 ?
Michael Ludwig009b92e2019-02-15 16:03:53 -0500389 compute_src_quad(origin, fSrcQuads[info.fSrcQuadIndex], iw, ih, h) :
390 compute_src_quad_from_rect(origin, info.fSrcRect, iw, ih, h);
Brian Salomon246bc3d2018-12-06 15:33:02 -0500391 SkRect domain =
Michael Ludwigc96fc372019-01-08 15:46:15 -0500392 compute_domain(info.domain(), this->filter(), origin, info.fSrcRect, iw, ih, h);
393 v = GrQuadPerEdgeAA::Tessellate(v, spec, device, info.fColor, srcQuad, domain,
394 info.aaFlags());
Brian Salomon17031a72018-05-22 14:14:07 -0400395 }
396 }
397
Brian Salomon34169692017-08-28 15:32:01 -0400398 void onPrepareDraws(Target* target) override {
Brian Salomond7065e72018-10-12 11:42:02 -0400399 TRACE_EVENT0("skia", TRACE_FUNC);
Michael Ludwigde4c58c2019-06-04 09:12:59 -0400400 GrQuad::Type quadType = GrQuad::Type::kAxisAligned;
401 GrQuad::Type srcQuadType = GrQuad::Type::kAxisAligned;
Brian Salomonf7232642018-09-19 08:58:08 -0400402 Domain domain = Domain::kNo;
Brian Salomon1d835422019-03-13 16:11:44 -0400403 ColorType colorType = ColorType::kNone;
Brian Salomond7065e72018-10-12 11:42:02 -0400404 int numProxies = 0;
Brian Salomon4b8178f2018-10-12 13:18:27 -0400405 int numTotalQuads = 0;
Brian Salomond7065e72018-10-12 11:42:02 -0400406 auto textureType = fProxies[0].fProxy->textureType();
407 auto config = fProxies[0].fProxy->config();
Greg Daniel2c3398d2019-06-19 11:58:01 -0400408 const GrSwizzle& swizzle = fProxies[0].fProxy->textureSwizzle();
Brian Salomonae7d7702018-10-14 15:05:45 -0400409 GrAAType aaType = this->aaType();
Brian Salomonf7232642018-09-19 08:58:08 -0400410 for (const auto& op : ChainRange<TextureOp>(this)) {
Michael Ludwigc96fc372019-01-08 15:46:15 -0500411 if (op.fQuads.quadType() > quadType) {
412 quadType = op.fQuads.quadType();
Michael Ludwigf995c052018-11-26 15:24:29 -0500413 }
Michael Ludwig009b92e2019-02-15 16:03:53 -0500414 if (op.fSrcQuads.quadType() > srcQuadType) {
415 // Should only become more general if there are quads to use instead of fSrcRect
416 SkASSERT(op.fSrcQuads.count() > 0);
417 srcQuadType = op.fSrcQuads.quadType();
418 }
Brian Salomonf7232642018-09-19 08:58:08 -0400419 if (op.fDomain) {
420 domain = Domain::kYes;
421 }
Brian Salomon1d835422019-03-13 16:11:44 -0400422 colorType = SkTMax(colorType, static_cast<ColorType>(op.fColorType));
Brian Salomond7065e72018-10-12 11:42:02 -0400423 numProxies += op.fProxyCnt;
424 for (unsigned p = 0; p < op.fProxyCnt; ++p) {
Brian Salomon4b8178f2018-10-12 13:18:27 -0400425 numTotalQuads += op.fProxies[p].fQuadCnt;
Brian Salomond7065e72018-10-12 11:42:02 -0400426 auto* proxy = op.fProxies[p].fProxy;
Robert Phillips12c46292019-04-23 07:36:17 -0400427 if (!proxy->isInstantiated()) {
Brian Salomond7065e72018-10-12 11:42:02 -0400428 return;
429 }
430 SkASSERT(proxy->config() == config);
431 SkASSERT(proxy->textureType() == textureType);
Greg Daniel2c3398d2019-06-19 11:58:01 -0400432 SkASSERT(proxy->textureSwizzle() == swizzle);
Brian Salomonf7232642018-09-19 08:58:08 -0400433 }
Brian Salomonae7d7702018-10-14 15:05:45 -0400434 if (op.aaType() == GrAAType::kCoverage) {
435 SkASSERT(aaType == GrAAType::kCoverage || aaType == GrAAType::kNone);
436 aaType = GrAAType::kCoverage;
437 }
Brian Salomon34169692017-08-28 15:32:01 -0400438 }
Brian Salomon336ce7b2017-09-08 08:23:58 -0400439
Brian Salomon1d835422019-03-13 16:11:44 -0400440 VertexSpec vertexSpec(quadType, colorType, srcQuadType, /* hasLocal */ true, domain, aaType,
Michael Ludwig93aeba02018-12-21 09:50:31 -0500441 /* alpha as coverage */ true);
Michael Ludwigc182b942018-11-16 10:27:51 -0500442
Greg Daniel7a82edf2018-12-04 10:54:34 -0500443 GrSamplerState samplerState = GrSamplerState(GrSamplerState::WrapMode::kClamp,
444 this->filter());
445 GrGpu* gpu = target->resourceProvider()->priv().gpu();
446 uint32_t extraSamplerKey = gpu->getExtraSamplerKeyForProgram(
447 samplerState, fProxies[0].fProxy->backendFormat());
448
Michael Ludwig467994d2018-12-03 14:58:31 +0000449 sk_sp<GrGeometryProcessor> gp = GrQuadPerEdgeAA::MakeTexturedProcessor(
450 vertexSpec, *target->caps().shaderCaps(),
Greg Daniel2c3398d2019-06-19 11:58:01 -0400451 textureType, config, samplerState, swizzle, extraSamplerKey,
Greg Daniel7a82edf2018-12-04 10:54:34 -0500452 std::move(fTextureColorSpaceXform));
453
Brian Salomonf7232642018-09-19 08:58:08 -0400454 // We'll use a dynamic state array for the GP textures when there are multiple ops.
455 // Otherwise, we use fixed dynamic state to specify the single op's proxy.
456 GrPipeline::DynamicStateArrays* dynamicStateArrays = nullptr;
457 GrPipeline::FixedDynamicState* fixedDynamicState;
Brian Salomond7065e72018-10-12 11:42:02 -0400458 if (numProxies > 1) {
459 dynamicStateArrays = target->allocDynamicStateArrays(numProxies, 1, false);
Chris Dalton07cdcfc92019-02-26 11:13:22 -0700460 fixedDynamicState = target->makeFixedDynamicState(0);
Brian Salomonf7232642018-09-19 08:58:08 -0400461 } else {
Chris Dalton07cdcfc92019-02-26 11:13:22 -0700462 fixedDynamicState = target->makeFixedDynamicState(1);
Brian Salomond7065e72018-10-12 11:42:02 -0400463 fixedDynamicState->fPrimitiveProcessorTextures[0] = fProxies[0].fProxy;
Brian Salomonf7232642018-09-19 08:58:08 -0400464 }
Brian Salomon92be2f72018-06-19 14:33:47 -0400465
Michael Ludwigc182b942018-11-16 10:27:51 -0500466 size_t vertexSize = gp->vertexStride();
Brian Salomon92be2f72018-06-19 14:33:47 -0400467
Brian Salomond7065e72018-10-12 11:42:02 -0400468 GrMesh* meshes = target->allocMeshes(numProxies);
Brian Salomon12d22642019-01-29 14:38:50 -0500469 sk_sp<const GrBuffer> vbuffer;
Brian Salomon4b8178f2018-10-12 13:18:27 -0400470 int vertexOffsetInBuffer = 0;
Michael Ludwig93aeba02018-12-21 09:50:31 -0500471 int numQuadVerticesLeft = numTotalQuads * vertexSpec.verticesPerQuad();
Brian Salomon4b8178f2018-10-12 13:18:27 -0400472 int numAllocatedVertices = 0;
473 void* vdata = nullptr;
474
Brian Salomond7065e72018-10-12 11:42:02 -0400475 int m = 0;
Brian Salomonf7232642018-09-19 08:58:08 -0400476 for (const auto& op : ChainRange<TextureOp>(this)) {
Brian Salomond7065e72018-10-12 11:42:02 -0400477 int q = 0;
478 for (unsigned p = 0; p < op.fProxyCnt; ++p) {
479 int quadCnt = op.fProxies[p].fQuadCnt;
480 auto* proxy = op.fProxies[p].fProxy;
Michael Ludwig93aeba02018-12-21 09:50:31 -0500481 int meshVertexCnt = quadCnt * vertexSpec.verticesPerQuad();
Brian Salomon4b8178f2018-10-12 13:18:27 -0400482 if (numAllocatedVertices < meshVertexCnt) {
483 vdata = target->makeVertexSpaceAtLeast(
484 vertexSize, meshVertexCnt, numQuadVerticesLeft, &vbuffer,
485 &vertexOffsetInBuffer, &numAllocatedVertices);
486 SkASSERT(numAllocatedVertices <= numQuadVerticesLeft);
487 if (!vdata) {
488 SkDebugf("Could not allocate vertices\n");
489 return;
490 }
Brian Salomonf7232642018-09-19 08:58:08 -0400491 }
Brian Salomon4b8178f2018-10-12 13:18:27 -0400492 SkASSERT(numAllocatedVertices >= meshVertexCnt);
Brian Salomond7065e72018-10-12 11:42:02 -0400493
Michael Ludwigc182b942018-11-16 10:27:51 -0500494 op.tess(vdata, vertexSpec, proxy, q, quadCnt);
Brian Salomond7065e72018-10-12 11:42:02 -0400495
Michael Ludwig93aeba02018-12-21 09:50:31 -0500496 if (!GrQuadPerEdgeAA::ConfigureMeshIndices(target, &(meshes[m]), vertexSpec,
497 quadCnt)) {
498 SkDebugf("Could not allocate indices");
499 return;
Brian Salomond7065e72018-10-12 11:42:02 -0400500 }
Brian Salomon4b8178f2018-10-12 13:18:27 -0400501 meshes[m].setVertexData(vbuffer, vertexOffsetInBuffer);
Brian Salomond7065e72018-10-12 11:42:02 -0400502 if (dynamicStateArrays) {
503 dynamicStateArrays->fPrimitiveProcessorTextures[m] = proxy;
504 }
505 ++m;
Brian Salomon4b8178f2018-10-12 13:18:27 -0400506 numAllocatedVertices -= meshVertexCnt;
507 numQuadVerticesLeft -= meshVertexCnt;
508 vertexOffsetInBuffer += meshVertexCnt;
509 vdata = reinterpret_cast<char*>(vdata) + vertexSize * meshVertexCnt;
Brian Salomond7065e72018-10-12 11:42:02 -0400510 q += quadCnt;
Brian Salomonf7232642018-09-19 08:58:08 -0400511 }
Brian Salomon34169692017-08-28 15:32:01 -0400512 }
Brian Salomon4b8178f2018-10-12 13:18:27 -0400513 SkASSERT(!numQuadVerticesLeft);
514 SkASSERT(!numAllocatedVertices);
Chris Dalton07cdcfc92019-02-26 11:13:22 -0700515 target->recordDraw(
516 std::move(gp), meshes, numProxies, fixedDynamicState, dynamicStateArrays);
517 }
518
519 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
520 auto pipelineFlags = (GrAAType::kMSAA == this->aaType())
Chris Daltonbaa1b352019-04-03 12:03:00 -0600521 ? GrPipeline::InputFlags::kHWAntialias
522 : GrPipeline::InputFlags::kNone;
Chris Dalton07cdcfc92019-02-26 11:13:22 -0700523 flushState->executeDrawsAndUploadsForMeshDrawOp(
524 this, chainBounds, GrProcessorSet::MakeEmptySet(), pipelineFlags);
Brian Salomon34169692017-08-28 15:32:01 -0400525 }
526
Brian Salomonf7232642018-09-19 08:58:08 -0400527 CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomond7065e72018-10-12 11:42:02 -0400528 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Salomon34169692017-08-28 15:32:01 -0400529 const auto* that = t->cast<TextureOp>();
Michael Ludwig2929f512019-04-19 13:05:56 -0400530 if (fDomain != that->fDomain) {
531 // It is technically possible to combine operations across domain modes, but performance
532 // testing suggests it's better to make more draw calls where some take advantage of
533 // the more optimal shader path without coordinate clamping.
534 return CombineResult::kCannotCombine;
535 }
Brian Osman3ebd3542018-07-30 14:36:53 -0400536 if (!GrColorSpaceXform::Equals(fTextureColorSpaceXform.get(),
537 that->fTextureColorSpaceXform.get())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +0000538 return CombineResult::kCannotCombine;
Brian Osman3ebd3542018-07-30 14:36:53 -0400539 }
Brian Salomonae7d7702018-10-14 15:05:45 -0400540 bool upgradeToCoverageAAOnMerge = false;
Brian Salomon485b8c62018-01-12 15:11:06 -0500541 if (this->aaType() != that->aaType()) {
Brian Salomonae7d7702018-10-14 15:05:45 -0400542 if (!((this->aaType() == GrAAType::kCoverage && that->aaType() == GrAAType::kNone) ||
543 (that->aaType() == GrAAType::kCoverage && this->aaType() == GrAAType::kNone))) {
544 return CombineResult::kCannotCombine;
545 }
546 upgradeToCoverageAAOnMerge = true;
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500547 }
Brian Salomonf7232642018-09-19 08:58:08 -0400548 if (fFilter != that->fFilter) {
549 return CombineResult::kCannotCombine;
550 }
Brian Salomond7065e72018-10-12 11:42:02 -0400551 auto thisProxy = fProxies[0].fProxy;
552 auto thatProxy = that->fProxies[0].fProxy;
553 if (fProxyCnt > 1 || that->fProxyCnt > 1 ||
Brian Salomon588cec72018-11-14 13:56:37 -0500554 thisProxy->uniqueID() != thatProxy->uniqueID()) {
555 // We can't merge across different proxies. Check if 'this' can be chained with 'that'.
Greg Daniel45723ac2018-11-30 10:12:43 -0500556 if (GrTextureProxy::ProxiesAreCompatibleAsDynamicState(thisProxy, thatProxy) &&
Brian Salomonf7232642018-09-19 08:58:08 -0400557 caps.dynamicStateArrayGeometryProcessorTextureSupport()) {
558 return CombineResult::kMayChain;
559 }
Brian Salomon7eae3e02018-08-07 14:02:38 +0000560 return CombineResult::kCannotCombine;
Brian Salomon336ce7b2017-09-08 08:23:58 -0400561 }
Michael Ludwig009b92e2019-02-15 16:03:53 -0500562
Brian Salomonb80ffee2018-05-23 16:39:39 -0400563 fDomain |= that->fDomain;
Brian Salomon1d835422019-03-13 16:11:44 -0400564 fColorType = SkTMax(fColorType, that->fColorType);
Brian Salomonae7d7702018-10-14 15:05:45 -0400565 if (upgradeToCoverageAAOnMerge) {
566 fAAType = static_cast<unsigned>(GrAAType::kCoverage);
567 }
Michael Ludwig009b92e2019-02-15 16:03:53 -0500568
569 // Concatenate quad lists together, updating the fSrcQuadIndex in the appended quads
570 // to account for the new starting index in fSrcQuads
571 int srcQuadOffset = fSrcQuads.count();
572 int oldQuadCount = fQuads.count();
573
574 fSrcQuads.concat(that->fSrcQuads);
575 fQuads.concat(that->fQuads);
576 fProxies[0].fQuadCnt += that->fQuads.count();
577
578 if (that->fSrcQuads.count() > 0) {
579 // Some of the concatenated quads pointed to fSrcQuads, so adjust the indices for the
580 // newly appended quads
581 for (int i = oldQuadCount; i < fQuads.count(); ++i) {
582 if (fQuads.metadata(i).fSrcQuadIndex >= 0) {
583 fQuads.metadata(i).fSrcQuadIndex += srcQuadOffset;
584 }
585 }
586 }
587
588 // Confirm all tracked state makes sense when in debug builds
589#ifdef SK_DEBUG
590 SkASSERT(fSrcQuads.count() <= fQuads.count());
591 for (int i = 0; i < fQuads.count(); ++i) {
592 int srcIndex = fQuads.metadata(i).fSrcQuadIndex;
593 if (srcIndex >= 0) {
594 // Make sure it points to a valid index, in the right region of the list
595 SkASSERT(srcIndex < fSrcQuads.count());
596 SkASSERT((i < oldQuadCount && srcIndex < srcQuadOffset) ||
597 (i >= oldQuadCount && srcIndex >= srcQuadOffset));
598 }
599 }
600#endif
Brian Salomon7eae3e02018-08-07 14:02:38 +0000601 return CombineResult::kMerged;
Brian Salomon34169692017-08-28 15:32:01 -0400602 }
603
Brian Salomon485b8c62018-01-12 15:11:06 -0500604 GrAAType aaType() const { return static_cast<GrAAType>(fAAType); }
Brian Salomon0087c832018-10-15 14:48:20 -0400605 GrSamplerState::Filter filter() const { return static_cast<GrSamplerState::Filter>(fFilter); }
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500606
Michael Ludwigc96fc372019-01-08 15:46:15 -0500607 struct ColorDomainAndAA {
608 // Special constructor to convert enums into the packed bits, which should not delete
609 // the implicit move constructor (but it does require us to declare an empty ctor for
610 // use with the GrTQuadList).
Michael Ludwig009b92e2019-02-15 16:03:53 -0500611 ColorDomainAndAA(const SkPMColor4f& color, const SkRect& srcRect, int srcQuadIndex,
Michael Ludwigc96fc372019-01-08 15:46:15 -0500612 Domain hasDomain, GrQuadAAFlags aaFlags)
613 : fColor(color)
614 , fSrcRect(srcRect)
Michael Ludwig009b92e2019-02-15 16:03:53 -0500615 , fSrcQuadIndex(srcQuadIndex)
Michael Ludwigc96fc372019-01-08 15:46:15 -0500616 , fHasDomain(static_cast<unsigned>(hasDomain))
Brian Salomon2213ee92018-10-02 10:44:21 -0400617 , fAAFlags(static_cast<unsigned>(aaFlags)) {
Michael Ludwigc96fc372019-01-08 15:46:15 -0500618 SkASSERT(fHasDomain == static_cast<unsigned>(hasDomain));
Brian Salomon2213ee92018-10-02 10:44:21 -0400619 SkASSERT(fAAFlags == static_cast<unsigned>(aaFlags));
620 }
Michael Ludwigc96fc372019-01-08 15:46:15 -0500621 ColorDomainAndAA() = default;
Michael Ludwig23d8943e2019-01-04 14:51:40 +0000622
Michael Ludwig23d8943e2019-01-04 14:51:40 +0000623 SkPMColor4f fColor;
Michael Ludwig009b92e2019-02-15 16:03:53 -0500624 // Even if fSrcQuadIndex provides source coords, use fSrcRect for domain constraint
Michael Ludwigc96fc372019-01-08 15:46:15 -0500625 SkRect fSrcRect;
Michael Ludwig009b92e2019-02-15 16:03:53 -0500626 // If >= 0, use to access fSrcQuads instead of fSrcRect for the source coordinates
627 int fSrcQuadIndex;
Michael Ludwig23d8943e2019-01-04 14:51:40 +0000628 unsigned fHasDomain : 1;
629 unsigned fAAFlags : 4;
Michael Ludwigc96fc372019-01-08 15:46:15 -0500630
631 Domain domain() const { return Domain(fHasDomain); }
632 GrQuadAAFlags aaFlags() const { return static_cast<GrQuadAAFlags>(fAAFlags); }
Brian Salomon34169692017-08-28 15:32:01 -0400633 };
Brian Salomond7065e72018-10-12 11:42:02 -0400634 struct Proxy {
635 GrTextureProxy* fProxy;
636 int fQuadCnt;
637 };
Michael Ludwigc96fc372019-01-08 15:46:15 -0500638 GrTQuadList<ColorDomainAndAA> fQuads;
Michael Ludwig009b92e2019-02-15 16:03:53 -0500639 // The majority of texture ops will not track a complete src quad so this is indexed separately
640 // and may be of different size to fQuads.
641 GrQuadList fSrcQuads;
Brian Osman3ebd3542018-07-30 14:36:53 -0400642 sk_sp<GrColorSpaceXform> fTextureColorSpaceXform;
Brian Salomon0087c832018-10-15 14:48:20 -0400643 unsigned fFilter : 2;
Brian Salomon485b8c62018-01-12 15:11:06 -0500644 unsigned fAAType : 2;
Brian Salomonb80ffee2018-05-23 16:39:39 -0400645 unsigned fDomain : 1;
Brian Salomon1d835422019-03-13 16:11:44 -0400646 unsigned fColorType : 2;
647 GR_STATIC_ASSERT(GrQuadPerEdgeAA::kColorTypeCount <= 4);
Robert Phillips3d4cac52019-06-11 08:08:08 -0400648 unsigned fProxyCnt : 32 - 7;
Brian Salomond7065e72018-10-12 11:42:02 -0400649 Proxy fProxies[1];
Brian Salomon336ce7b2017-09-08 08:23:58 -0400650
Michael Ludwigde4c58c2019-06-04 09:12:59 -0400651 static_assert(GrQuad::kTypeCount <= 4, "GrQuad::Type does not fit in 2 bits");
Michael Ludwigf995c052018-11-26 15:24:29 -0500652
Brian Salomon34169692017-08-28 15:32:01 -0400653 typedef GrMeshDrawOp INHERITED;
654};
655
656} // anonymous namespace
657
658namespace GrTextureOp {
659
Robert Phillipsb97da532019-02-12 15:24:12 -0500660std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -0400661 sk_sp<GrTextureProxy> proxy,
662 GrSamplerState::Filter filter,
Brian Osman3d139a42018-11-19 10:42:10 -0500663 const SkPMColor4f& color,
Robert Phillips7c525e62018-06-12 10:11:12 -0400664 const SkRect& srcRect,
665 const SkRect& dstRect,
666 GrAAType aaType,
Brian Salomon2213ee92018-10-02 10:44:21 -0400667 GrQuadAAFlags aaFlags,
Robert Phillips7c525e62018-06-12 10:11:12 -0400668 SkCanvas::SrcRectConstraint constraint,
669 const SkMatrix& viewMatrix,
Brian Osman3d139a42018-11-19 10:42:10 -0500670 sk_sp<GrColorSpaceXform> textureColorSpaceXform) {
Robert Phillips7c525e62018-06-12 10:11:12 -0400671 return TextureOp::Make(context, std::move(proxy), filter, color, srcRect, dstRect, aaType,
Brian Osman3d139a42018-11-19 10:42:10 -0500672 aaFlags, constraint, viewMatrix, std::move(textureColorSpaceXform));
Brian Salomon34169692017-08-28 15:32:01 -0400673}
674
Michael Ludwig009b92e2019-02-15 16:03:53 -0500675std::unique_ptr<GrDrawOp> MakeQuad(GrRecordingContext* context,
676 sk_sp<GrTextureProxy> proxy,
677 GrSamplerState::Filter filter,
678 const SkPMColor4f& color,
679 const SkPoint srcQuad[4],
680 const SkPoint dstQuad[4],
681 GrAAType aaType,
682 GrQuadAAFlags aaFlags,
683 const SkRect* domain,
684 const SkMatrix& viewMatrix,
685 sk_sp<GrColorSpaceXform> textureXform) {
686 return TextureOp::Make(context, std::move(proxy), filter, color, srcQuad, dstQuad, aaType,
687 aaFlags, domain, viewMatrix, std::move(textureXform));
688}
689
690std::unique_ptr<GrDrawOp> MakeSet(GrRecordingContext* context,
691 const GrRenderTargetContext::TextureSetEntry set[],
692 int cnt,
693 GrSamplerState::Filter filter,
694 GrAAType aaType,
Michael Ludwig31ba7182019-04-03 10:38:06 -0400695 SkCanvas::SrcRectConstraint constraint,
Michael Ludwig009b92e2019-02-15 16:03:53 -0500696 const SkMatrix& viewMatrix,
697 sk_sp<GrColorSpaceXform> textureColorSpaceXform) {
Michael Ludwig31ba7182019-04-03 10:38:06 -0400698 return TextureOp::Make(context, set, cnt, filter, aaType, constraint, viewMatrix,
Brian Osman3d139a42018-11-19 10:42:10 -0500699 std::move(textureColorSpaceXform));
Brian Salomond7065e72018-10-12 11:42:02 -0400700}
701
Michael Ludwiga3c45c72019-01-17 17:26:48 -0500702bool GetFilterHasEffect(const SkMatrix& viewMatrix, const SkRect& srcRect, const SkRect& dstRect) {
703 // Hypothetically we could disable bilerp filtering when flipping or rotating 90 degrees, but
704 // that makes the math harder and we don't want to increase the overhead of the checks
705 if (!viewMatrix.isScaleTranslate() ||
706 viewMatrix.getScaleX() < 0.0f || viewMatrix.getScaleY() < 0.0f) {
707 return true;
708 }
709
710 // Given the matrix conditions ensured above, this computes the device space coordinates for
711 // the top left corner of dstRect and its size.
712 SkScalar dw = viewMatrix.getScaleX() * dstRect.width();
713 SkScalar dh = viewMatrix.getScaleY() * dstRect.height();
714 SkScalar dl = viewMatrix.getScaleX() * dstRect.fLeft + viewMatrix.getTranslateX();
715 SkScalar dt = viewMatrix.getScaleY() * dstRect.fTop + viewMatrix.getTranslateY();
716
717 // Disable filtering when there is no scaling of the src rect and the src rect and dst rect
718 // align fractionally. If we allow inverted src rects this logic needs to consider that.
719 SkASSERT(srcRect.isSorted());
720 return dw != srcRect.width() || dh != srcRect.height() ||
721 SkScalarFraction(dl) != SkScalarFraction(srcRect.fLeft) ||
722 SkScalarFraction(dt) != SkScalarFraction(srcRect.fTop);
723}
724
Brian Salomon34169692017-08-28 15:32:01 -0400725} // namespace GrTextureOp
726
727#if GR_TEST_UTILS
Mike Kleinc0bd9f92019-04-23 12:05:21 -0500728#include "include/private/GrRecordingContext.h"
729#include "src/gpu/GrProxyProvider.h"
730#include "src/gpu/GrRecordingContextPriv.h"
Brian Salomon34169692017-08-28 15:32:01 -0400731
732GR_DRAW_OP_TEST_DEFINE(TextureOp) {
733 GrSurfaceDesc desc;
734 desc.fConfig = kRGBA_8888_GrPixelConfig;
735 desc.fHeight = random->nextULessThan(90) + 10;
736 desc.fWidth = random->nextULessThan(90) + 10;
Brian Salomon2a4f9832018-03-03 22:43:43 -0500737 auto origin = random->nextBool() ? kTopLeft_GrSurfaceOrigin : kBottomLeft_GrSurfaceOrigin;
Greg Daniel09c94002018-06-08 22:11:51 +0000738 GrMipMapped mipMapped = random->nextBool() ? GrMipMapped::kYes : GrMipMapped::kNo;
739 SkBackingFit fit = SkBackingFit::kExact;
740 if (mipMapped == GrMipMapped::kNo) {
741 fit = random->nextBool() ? SkBackingFit::kApprox : SkBackingFit::kExact;
742 }
Robert Phillips0bd24dc2018-01-16 08:06:32 -0500743
Greg Daniel4065d452018-11-16 15:43:41 -0500744 const GrBackendFormat format =
Robert Phillips9da87e02019-02-04 13:26:26 -0500745 context->priv().caps()->getBackendFormatFromColorType(kRGBA_8888_SkColorType);
Greg Daniel4065d452018-11-16 15:43:41 -0500746
Robert Phillips9da87e02019-02-04 13:26:26 -0500747 GrProxyProvider* proxyProvider = context->priv().proxyProvider();
Greg Daniel4065d452018-11-16 15:43:41 -0500748 sk_sp<GrTextureProxy> proxy = proxyProvider->createProxy(format, desc, origin, mipMapped, fit,
Greg Daniel09c94002018-06-08 22:11:51 +0000749 SkBudgeted::kNo,
750 GrInternalSurfaceFlags::kNone);
Robert Phillips0bd24dc2018-01-16 08:06:32 -0500751
Brian Salomon34169692017-08-28 15:32:01 -0400752 SkRect rect = GrTest::TestRect(random);
753 SkRect srcRect;
754 srcRect.fLeft = random->nextRangeScalar(0.f, proxy->width() / 2.f);
755 srcRect.fRight = random->nextRangeScalar(0.f, proxy->width()) + proxy->width() / 2.f;
756 srcRect.fTop = random->nextRangeScalar(0.f, proxy->height() / 2.f);
757 srcRect.fBottom = random->nextRangeScalar(0.f, proxy->height()) + proxy->height() / 2.f;
758 SkMatrix viewMatrix = GrTest::TestMatrixPreservesRightAngles(random);
Brian Osman3d139a42018-11-19 10:42:10 -0500759 SkPMColor4f color = SkPMColor4f::FromBytes_RGBA(SkColorToPremulGrColor(random->nextU()));
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400760 GrSamplerState::Filter filter = (GrSamplerState::Filter)random->nextULessThan(
761 static_cast<uint32_t>(GrSamplerState::Filter::kMipMap) + 1);
Greg Daniel09c94002018-06-08 22:11:51 +0000762 while (mipMapped == GrMipMapped::kNo && filter == GrSamplerState::Filter::kMipMap) {
763 filter = (GrSamplerState::Filter)random->nextULessThan(
764 static_cast<uint32_t>(GrSamplerState::Filter::kMipMap) + 1);
765 }
Brian Osman3ebd3542018-07-30 14:36:53 -0400766 auto texXform = GrTest::TestColorXform(random);
Brian Salomon485b8c62018-01-12 15:11:06 -0500767 GrAAType aaType = GrAAType::kNone;
768 if (random->nextBool()) {
769 aaType = (fsaaType == GrFSAAType::kUnifiedMSAA) ? GrAAType::kMSAA : GrAAType::kCoverage;
770 }
Brian Salomon2213ee92018-10-02 10:44:21 -0400771 GrQuadAAFlags aaFlags = GrQuadAAFlags::kNone;
772 aaFlags |= random->nextBool() ? GrQuadAAFlags::kLeft : GrQuadAAFlags::kNone;
773 aaFlags |= random->nextBool() ? GrQuadAAFlags::kTop : GrQuadAAFlags::kNone;
774 aaFlags |= random->nextBool() ? GrQuadAAFlags::kRight : GrQuadAAFlags::kNone;
775 aaFlags |= random->nextBool() ? GrQuadAAFlags::kBottom : GrQuadAAFlags::kNone;
Brian Salomonb80ffee2018-05-23 16:39:39 -0400776 auto constraint = random->nextBool() ? SkCanvas::kStrict_SrcRectConstraint
777 : SkCanvas::kFast_SrcRectConstraint;
Robert Phillips7c525e62018-06-12 10:11:12 -0400778 return GrTextureOp::Make(context, std::move(proxy), filter, color, srcRect, rect, aaType,
Brian Osman3d139a42018-11-19 10:42:10 -0500779 aaFlags, constraint, viewMatrix, std::move(texXform));
Brian Salomon34169692017-08-28 15:32:01 -0400780}
781
782#endif