blob: 5053b1184f09f6a65bf9821d9149ed3f7d0a79ef [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
Brian Salomond7065e72018-10-12 11:42:02 -04008#include <new>
Brian Salomonf19f9ca2019-09-18 15:54:26 -04009
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"
Michael Ludwig22429f92019-06-27 10:44:48 -040014#include "include/private/SkFloatingPoint.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050015#include "include/private/SkTo.h"
16#include "src/core/SkMathPriv.h"
17#include "src/core/SkMatrixPriv.h"
18#include "src/core/SkRectPriv.h"
19#include "src/gpu/GrAppliedClip.h"
20#include "src/gpu/GrCaps.h"
21#include "src/gpu/GrDrawOpTest.h"
22#include "src/gpu/GrGeometryProcessor.h"
23#include "src/gpu/GrGpu.h"
24#include "src/gpu/GrMemoryPool.h"
25#include "src/gpu/GrOpFlushState.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050026#include "src/gpu/GrRecordingContextPriv.h"
27#include "src/gpu/GrResourceProvider.h"
28#include "src/gpu/GrResourceProviderPriv.h"
29#include "src/gpu/GrShaderCaps.h"
30#include "src/gpu/GrTexturePriv.h"
Greg Danielf91aeb22019-06-18 09:58:02 -040031#include "src/gpu/GrTextureProxy.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050032#include "src/gpu/SkGr.h"
Michael Ludwig22429f92019-06-27 10:44:48 -040033#include "src/gpu/effects/GrTextureDomain.h"
Brian Salomonf19f9ca2019-09-18 15:54:26 -040034#include "src/gpu/effects/generated/GrSaturateProcessor.h"
Michael Ludwigfd4f4df2019-05-29 09:51:09 -040035#include "src/gpu/geometry/GrQuad.h"
Michael Ludwig425eb452019-06-27 10:13:27 -040036#include "src/gpu/geometry/GrQuadBuffer.h"
Michael Ludwig0f809022019-06-04 09:14:37 -040037#include "src/gpu/geometry/GrQuadUtils.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050038#include "src/gpu/glsl/GrGLSLVarying.h"
Michael Ludwig22429f92019-06-27 10:44:48 -040039#include "src/gpu/ops/GrFillRectOp.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050040#include "src/gpu/ops/GrMeshDrawOp.h"
41#include "src/gpu/ops/GrQuadPerEdgeAA.h"
Brian Salomonf19f9ca2019-09-18 15:54:26 -040042#include "src/gpu/ops/GrTextureOp.h"
Brian Salomon34169692017-08-28 15:32:01 -040043
44namespace {
45
Michael Ludwig460eb5e2018-10-29 11:09:29 -040046using Domain = GrQuadPerEdgeAA::Domain;
Michael Ludwigc182b942018-11-16 10:27:51 -050047using VertexSpec = GrQuadPerEdgeAA::VertexSpec;
Brian Osman3d139a42018-11-19 10:42:10 -050048using ColorType = GrQuadPerEdgeAA::ColorType;
Brian Salomonb80ffee2018-05-23 16:39:39 -040049
Michael Ludwig22429f92019-06-27 10:44:48 -040050// Extracts lengths of vertical and horizontal edges of axis-aligned quad. "width" is the edge
51// between v0 and v2 (or v1 and v3), "height" is the edge between v0 and v1 (or v2 and v3).
52static SkSize axis_aligned_quad_size(const GrQuad& quad) {
53 SkASSERT(quad.quadType() == GrQuad::Type::kAxisAligned);
54 // Simplification of regular edge length equation, since it's axis aligned and can avoid sqrt
55 float dw = sk_float_abs(quad.x(2) - quad.x(0)) + sk_float_abs(quad.y(2) - quad.y(0));
56 float dh = sk_float_abs(quad.x(1) - quad.x(0)) + sk_float_abs(quad.y(1) - quad.y(0));
57 return {dw, dh};
58}
59
60static bool filter_has_effect(const GrQuad& srcQuad, const GrQuad& dstQuad) {
61 // If not axis-aligned in src or dst, then always say it has an effect
62 if (srcQuad.quadType() != GrQuad::Type::kAxisAligned ||
63 dstQuad.quadType() != GrQuad::Type::kAxisAligned) {
64 return true;
65 }
66
67 SkRect srcRect;
68 SkRect dstRect;
69 if (srcQuad.asRect(&srcRect) && dstQuad.asRect(&dstRect)) {
70 // Disable filtering when there is no scaling (width and height are the same), and the
71 // top-left corners have the same fraction (so src and dst snap to the pixel grid
72 // identically).
73 SkASSERT(srcRect.isSorted());
74 return srcRect.width() != dstRect.width() || srcRect.height() != dstRect.height() ||
75 SkScalarFraction(srcRect.fLeft) != SkScalarFraction(dstRect.fLeft) ||
76 SkScalarFraction(srcRect.fTop) != SkScalarFraction(dstRect.fTop);
77 } else {
78 // Although the quads are axis-aligned, the local coordinate system is transformed such
79 // that fractionally-aligned sample centers will not align with the device coordinate system
80 // So disable filtering when edges are the same length and both srcQuad and dstQuad
81 // 0th vertex is integer aligned.
82 if (SkScalarIsInt(srcQuad.x(0)) && SkScalarIsInt(srcQuad.y(0)) &&
83 SkScalarIsInt(dstQuad.x(0)) && SkScalarIsInt(dstQuad.y(0))) {
84 // Extract edge lengths
85 SkSize srcSize = axis_aligned_quad_size(srcQuad);
86 SkSize dstSize = axis_aligned_quad_size(dstQuad);
87 return srcSize.fWidth != dstSize.fWidth || srcSize.fHeight != dstSize.fHeight;
88 } else {
89 return true;
90 }
91 }
92}
93
Michael Ludwig119ac6d2019-11-21 09:26:46 -050094// Describes function for normalizing src coords: [x * iw, y * ih + yOffset] can represent
95// regular and rectangular textures, w/ or w/o origin correction.
96struct NormalizationParams {
97 float fIW; // 1 / width of texture, or 1.0 for texture rectangles
98 float fIH; // 1 / height of texture, or 1.0 for tex rects, X -1 if bottom-left origin
99 float fYOffset; // 0 for top-left origin, height of [normalized] tex if bottom-left
100};
101static NormalizationParams proxy_normalization_params(const GrSurfaceProxyView& proxyView) {
102 // Whether or not the proxy is instantiated, this is the size its texture will be, so we can
103 // normalize the src coordinates up front.
104 SkISize dimensions = proxyView.proxy()->backingStoreDimensions();
105 float iw, ih, h;
106 if (proxyView.proxy()->backendFormat().textureType() == GrTextureType::kRectangle) {
107 iw = ih = 1.f;
108 h = dimensions.height();
109 } else {
110 iw = 1.f / dimensions.width();
111 ih = 1.f / dimensions.height();
112 h = 1.f;
113 }
114
115 if (proxyView.origin() == kBottomLeft_GrSurfaceOrigin) {
116 return {iw, -ih, h};
117 } else {
118 return {iw, ih, 0.0f};
119 }
120}
121
122static void correct_domain_for_bilerp(const NormalizationParams& params,
123 SkRect* domainRect) {
124 // Normalized pixel size is also equal to iw and ih, so the insets for bilerp are just
125 // in those units and can be applied safely after normalization. However, if the domain is
126 // smaller than a texel, it should clamp to the center of that axis.
127 float dw = domainRect->width() < params.fIW ? domainRect->width() : params.fIW;
128 float dh = domainRect->height() < params.fIH ? domainRect->height() : params.fIH;
129 domainRect->inset(0.5f * dw, 0.5f * dh);
130}
131
132// Normalize the domain and inset for bilerp as necessary. If 'domainRect' is null, it is assumed
133// no domain constraint is desired, so a sufficiently large rect is returned even if the quad
134// ends up batched with an op that uses domains overall.
135static SkRect normalize_domain(GrSamplerState::Filter filter,
136 const NormalizationParams& params,
137 const SkRect* domainRect) {
Brian Salomon246bc3d2018-12-06 15:33:02 -0500138 static constexpr SkRect kLargeRect = {-100000, -100000, 1000000, 1000000};
Michael Ludwig119ac6d2019-11-21 09:26:46 -0500139 if (!domainRect) {
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400140 // Either the quad has no domain constraint and is batched with a domain constrained op
141 // (in which case we want a domain that doesn't restrict normalized tex coords), or the
142 // entire op doesn't use the domain, in which case the returned value is ignored.
Michael Ludwig119ac6d2019-11-21 09:26:46 -0500143 return kLargeRect;
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400144 }
145
Michael Ludwig119ac6d2019-11-21 09:26:46 -0500146 auto ltrb = skvx::Vec<4, float>::Load(domainRect);
147 // Normalize and offset
148 ltrb = mad(ltrb, {params.fIW, params.fIH, params.fIW, params.fIH},
149 {0.f, params.fYOffset, 0.f, params.fYOffset});
150 if (params.fIH < 0.f) {
151 // Flip top and bottom to keep the rect sorted when loaded back to SkRect.
152 ltrb = skvx::shuffle<0, 3, 2, 1>(ltrb);
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400153 }
154
Michael Ludwig119ac6d2019-11-21 09:26:46 -0500155 SkRect out;
156 ltrb.store(&out);
157
158 if (filter != GrSamplerState::Filter::kNearest) {
159 correct_domain_for_bilerp(params, &out);
160 }
161 return out;
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400162}
163
Michael Ludwig009b92e2019-02-15 16:03:53 -0500164// Normalizes logical src coords and corrects for origin
Michael Ludwig119ac6d2019-11-21 09:26:46 -0500165static void normalize_src_quad(const NormalizationParams& params,
166 GrQuad* srcQuad) {
Michael Ludwig009b92e2019-02-15 16:03:53 -0500167 // The src quad should not have any perspective
Michael Ludwig119ac6d2019-11-21 09:26:46 -0500168 SkASSERT(!srcQuad->hasPerspective());
169 skvx::Vec<4, float> xs = srcQuad->x4f() * params.fIW;
170 skvx::Vec<4, float> ys = mad(srcQuad->y4f(), params.fIH, params.fYOffset);
171 xs.store(srcQuad->xs());
172 ys.store(srcQuad->ys());
Michael Ludwig009b92e2019-02-15 16:03:53 -0500173}
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400174
Brian Salomon34169692017-08-28 15:32:01 -0400175/**
176 * Op that implements GrTextureOp::Make. It draws textured quads. Each quad can modulate against a
177 * the texture by color. The blend with the destination is always src-over. The edges are non-AA.
178 */
179class TextureOp final : public GrMeshDrawOp {
180public:
Robert Phillipsb97da532019-02-12 15:24:12 -0500181 static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
Greg Daniel549325c2019-10-30 16:19:20 -0400182 GrSurfaceProxyView proxyView,
Michael Ludwig22429f92019-06-27 10:44:48 -0400183 sk_sp<GrColorSpaceXform> textureXform,
Robert Phillips7c525e62018-06-12 10:11:12 -0400184 GrSamplerState::Filter filter,
Brian Osman3d139a42018-11-19 10:42:10 -0500185 const SkPMColor4f& color,
Brian Salomonf19f9ca2019-09-18 15:54:26 -0400186 GrTextureOp::Saturate saturate,
Robert Phillips7c525e62018-06-12 10:11:12 -0400187 GrAAType aaType,
Brian Salomon2213ee92018-10-02 10:44:21 -0400188 GrQuadAAFlags aaFlags,
Michael Ludwig22429f92019-06-27 10:44:48 -0400189 const GrQuad& deviceQuad,
190 const GrQuad& localQuad,
191 const SkRect* domain) {
Michael Ludwig009b92e2019-02-15 16:03:53 -0500192 GrOpMemoryPool* pool = context->priv().opMemoryPool();
Greg Daniel549325c2019-10-30 16:19:20 -0400193 return pool->allocate<TextureOp>(std::move(proxyView), std::move(textureXform), filter,
194 color, saturate, aaType, aaFlags, deviceQuad, localQuad,
195 domain);
Brian Salomon34169692017-08-28 15:32:01 -0400196 }
Robert Phillipse837e612019-11-15 11:02:50 -0500197
Robert Phillipsb97da532019-02-12 15:24:12 -0500198 static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
Brian Salomond7065e72018-10-12 11:42:02 -0400199 const GrRenderTargetContext::TextureSetEntry set[],
Brian Salomonf19f9ca2019-09-18 15:54:26 -0400200 int cnt,
201 GrSamplerState::Filter filter,
202 GrTextureOp::Saturate saturate,
203 GrAAType aaType,
Michael Ludwig31ba7182019-04-03 10:38:06 -0400204 SkCanvas::SrcRectConstraint constraint,
Brian Salomond003d222018-11-26 13:25:05 -0500205 const SkMatrix& viewMatrix,
Brian Osman3d139a42018-11-19 10:42:10 -0500206 sk_sp<GrColorSpaceXform> textureColorSpaceXform) {
Greg Daniel549325c2019-10-30 16:19:20 -0400207 size_t size = sizeof(TextureOp) + sizeof(ViewCountPair) * (cnt - 1);
Robert Phillips9da87e02019-02-04 13:26:26 -0500208 GrOpMemoryPool* pool = context->priv().opMemoryPool();
Brian Salomond7065e72018-10-12 11:42:02 -0400209 void* mem = pool->allocate(size);
Brian Salomonf19f9ca2019-09-18 15:54:26 -0400210 return std::unique_ptr<GrDrawOp>(new (mem) TextureOp(set, cnt, filter, saturate, aaType,
211 constraint, viewMatrix,
212 std::move(textureColorSpaceXform)));
Brian Salomond7065e72018-10-12 11:42:02 -0400213 }
Brian Salomon34169692017-08-28 15:32:01 -0400214
Brian Salomon336ce7b2017-09-08 08:23:58 -0400215 ~TextureOp() override {
Greg Daniel549325c2019-10-30 16:19:20 -0400216 for (unsigned p = 1; p < fProxyCnt; ++p) {
217 fViewCountPairs[p].~ViewCountPair();
Brian Salomon336ce7b2017-09-08 08:23:58 -0400218 }
219 }
Brian Salomon34169692017-08-28 15:32:01 -0400220
221 const char* name() const override { return "TextureOp"; }
222
Chris Dalton1706cbf2019-05-21 19:35:29 -0600223 void visitProxies(const VisitProxyFunc& func) const override {
Brian Salomond7065e72018-10-12 11:42:02 -0400224 for (unsigned p = 0; p < fProxyCnt; ++p) {
Chris Dalton7eb5c0f2019-05-23 15:15:47 -0600225 bool mipped = (GrSamplerState::Filter::kMipMap == this->filter());
Greg Daniel549325c2019-10-30 16:19:20 -0400226 func(fViewCountPairs[p].fProxyView.asTextureProxy(), GrMipMapped(mipped));
Brian Salomond7065e72018-10-12 11:42:02 -0400227 }
228 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -0400229
Brian Osman9a390ac2018-11-12 09:47:48 -0500230#ifdef SK_DEBUG
Brian Salomon34169692017-08-28 15:32:01 -0400231 SkString dumpInfo() const override {
232 SkString str;
Brian Salomond7065e72018-10-12 11:42:02 -0400233 str.appendf("# draws: %d\n", fQuads.count());
Michael Ludwig425eb452019-06-27 10:13:27 -0400234 auto iter = fQuads.iterator();
Brian Salomond7065e72018-10-12 11:42:02 -0400235 for (unsigned p = 0; p < fProxyCnt; ++p) {
Robert Phillips32803ff2019-10-23 08:26:08 -0400236 str.appendf("Proxy ID: %d, Filter: %d\n",
Greg Daniel549325c2019-10-30 16:19:20 -0400237 fViewCountPairs[p].fProxyView.proxy()->uniqueID().asUInt(),
Brian Salomond7065e72018-10-12 11:42:02 -0400238 static_cast<int>(fFilter));
Michael Ludwig425eb452019-06-27 10:13:27 -0400239 int i = 0;
Greg Daniel549325c2019-10-30 16:19:20 -0400240 while(i < fViewCountPairs[p].fQuadCnt && iter.next()) {
Michael Ludwig425eb452019-06-27 10:13:27 -0400241 const GrQuad& quad = iter.deviceQuad();
Michael Ludwigf339dfe2019-06-27 10:41:28 -0400242 const GrQuad& uv = iter.localQuad();
Michael Ludwig425eb452019-06-27 10:13:27 -0400243 const ColorDomainAndAA& info = iter.metadata();
Brian Salomond7065e72018-10-12 11:42:02 -0400244 str.appendf(
Michael Ludwigf339dfe2019-06-27 10:41:28 -0400245 "%d: Color: 0x%08x, Domain(%d): [L: %.2f, T: %.2f, R: %.2f, B: %.2f]\n"
246 " UVs [(%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f)]\n"
247 " Quad [(%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f)]\n",
Michael Ludwig119ac6d2019-11-21 09:26:46 -0500248 i, info.fColor.toBytes_RGBA(), fDomain, info.fDomainRect.fLeft,
Michael Ludwigf339dfe2019-06-27 10:41:28 -0400249 info.fDomainRect.fTop, info.fDomainRect.fRight, info.fDomainRect.fBottom,
250 quad.point(0).fX, quad.point(0).fY, quad.point(1).fX, quad.point(1).fY,
251 quad.point(2).fX, quad.point(2).fY, quad.point(3).fX, quad.point(3).fY,
252 uv.point(0).fX, uv.point(0).fY, uv.point(1).fX, uv.point(1).fY,
253 uv.point(2).fX, uv.point(2).fY, uv.point(3).fX, uv.point(3).fY);
254
Michael Ludwig425eb452019-06-27 10:13:27 -0400255 i++;
Brian Salomond7065e72018-10-12 11:42:02 -0400256 }
Brian Salomon34169692017-08-28 15:32:01 -0400257 }
258 str += INHERITED::dumpInfo();
259 return str;
260 }
Brian Osman9a390ac2018-11-12 09:47:48 -0500261#endif
Brian Salomon34169692017-08-28 15:32:01 -0400262
Brian Osman5ced0bf2019-03-15 10:15:29 -0400263 GrProcessorSet::Analysis finalize(
Chris Dalton6ce447a2019-06-23 18:07:38 -0600264 const GrCaps& caps, const GrAppliedClip*, bool hasMixedSampledCoverage,
265 GrClampType clampType) override {
Brian Osman8fa7ab42019-03-18 10:22:42 -0400266 fColorType = static_cast<unsigned>(ColorType::kNone);
Michael Ludwig425eb452019-06-27 10:13:27 -0400267 auto iter = fQuads.metadata();
268 while(iter.next()) {
269 auto colorType = GrQuadPerEdgeAA::MinColorType(iter->fColor, clampType, caps);
Brian Osman8fa7ab42019-03-18 10:22:42 -0400270 fColorType = SkTMax(fColorType, static_cast<unsigned>(colorType));
271 }
Chris Dalton4b62aed2019-01-15 11:53:00 -0700272 return GrProcessorSet::EmptySetAnalysis();
Brian Salomon34169692017-08-28 15:32:01 -0400273 }
274
Brian Salomon485b8c62018-01-12 15:11:06 -0500275 FixedFunctionFlags fixedFunctionFlags() const override {
276 return this->aaType() == GrAAType::kMSAA ? FixedFunctionFlags::kUsesHWAA
277 : FixedFunctionFlags::kNone;
278 }
Brian Salomon34169692017-08-28 15:32:01 -0400279
280 DEFINE_OP_CLASS_ID
281
282private:
Robert Phillips7c525e62018-06-12 10:11:12 -0400283 friend class ::GrOpMemoryPool;
Brian Salomon762d5e72017-12-01 10:25:08 -0500284
Michael Ludwig425eb452019-06-27 10:13:27 -0400285 struct ColorDomainAndAA {
Michael Ludwig119ac6d2019-11-21 09:26:46 -0500286 ColorDomainAndAA(const SkPMColor4f& color, const SkRect& domainRect, GrQuadAAFlags aaFlags)
Michael Ludwig425eb452019-06-27 10:13:27 -0400287 : fColor(color)
Michael Ludwig119ac6d2019-11-21 09:26:46 -0500288 , fDomainRect(domainRect)
Michael Ludwig425eb452019-06-27 10:13:27 -0400289 , fAAFlags(static_cast<unsigned>(aaFlags)) {
Michael Ludwig425eb452019-06-27 10:13:27 -0400290 SkASSERT(fAAFlags == static_cast<unsigned>(aaFlags));
291 }
Michael Ludwig425eb452019-06-27 10:13:27 -0400292
293 SkPMColor4f fColor;
Michael Ludwig119ac6d2019-11-21 09:26:46 -0500294 // If the op doesn't use domains, this is ignored. If the op uses domains and the specific
295 // entry does not, this rect will equal kLargeRect, so it automatically has no effect.
Michael Ludwigf339dfe2019-06-27 10:41:28 -0400296 SkRect fDomainRect;
Michael Ludwig425eb452019-06-27 10:13:27 -0400297 unsigned fAAFlags : 4;
298
Michael Ludwig425eb452019-06-27 10:13:27 -0400299 GrQuadAAFlags aaFlags() const { return static_cast<GrQuadAAFlags>(fAAFlags); }
300 };
Greg Daniel549325c2019-10-30 16:19:20 -0400301 struct ViewCountPair {
302 GrSurfaceProxyView fProxyView;
Michael Ludwig425eb452019-06-27 10:13:27 -0400303 int fQuadCnt;
304 };
305
Robert Phillipsc5a2c752019-10-24 13:11:45 -0400306 // This descriptor is used in both onPrePrepareDraws and onPrepareDraws.
307 //
308 // In the onPrePrepareDraws case it is allocated in the creation-time opData
309 // arena. Both allocateCommon and allocatePrePrepareOnly are called and they also allocate
310 // their memory in the creation-time opData arena.
311 //
312 // In the onPrepareDraws case this descriptor is created on the stack and only
313 // allocateCommon is called. In this case the common memory fields are allocated
314 // in the flush-time arena (i.e., as part of the flushState).
Robert Phillips32803ff2019-10-23 08:26:08 -0400315 struct PrePreparedDesc {
Robert Phillips32803ff2019-10-23 08:26:08 -0400316 VertexSpec fVertexSpec;
317 int fNumProxies = 0;
318 int fNumTotalQuads = 0;
Robert Phillipsc5a2c752019-10-24 13:11:45 -0400319 GrPipeline::DynamicStateArrays* fDynamicStateArrays = nullptr;
320 GrPipeline::FixedDynamicState* fFixedDynamicState = nullptr;
Robert Phillips32803ff2019-10-23 08:26:08 -0400321
Robert Phillipsbbd459d2019-10-29 14:40:03 -0400322 // This member variable is only used by 'onPrePrepareDraws'. The prior five are also
Robert Phillipsc5a2c752019-10-24 13:11:45 -0400323 // used by 'onPrepareDraws'
Robert Phillipsc5a2c752019-10-24 13:11:45 -0400324 char* fVertices = nullptr;
325
326 // How big should 'fVertices' be to hold all the vertex data?
327 size_t totalSizeInBytes() const {
328 return fNumTotalQuads * fVertexSpec.verticesPerQuad() * fVertexSpec.vertexSize();
329 }
330
Robert Phillipsc5a2c752019-10-24 13:11:45 -0400331 int totalNumVertices() const {
332 return fNumTotalQuads * fVertexSpec.verticesPerQuad();
333 }
Robert Phillipsc5a2c752019-10-24 13:11:45 -0400334
335 // Helper to fill in the fFixedDynamicState and fDynamicStateArrays. If there is more
336 // than one mesh/proxy they are stored in fDynamicStateArrays but if there is only one
337 // it is stored in fFixedDynamicState.
338 void setMeshProxy(int index, GrTextureProxy* proxy) {
339 SkASSERT(index < fNumProxies);
340
341 if (fDynamicStateArrays) {
342 SkASSERT(fDynamicStateArrays->fPrimitiveProcessorTextures);
343 SkASSERT(fNumProxies > 1);
344
345 fDynamicStateArrays->fPrimitiveProcessorTextures[index] = proxy;
346 } else {
347 SkASSERT(fFixedDynamicState);
348 SkASSERT(fNumProxies == 1);
349
350 fFixedDynamicState->fPrimitiveProcessorTextures[index] = proxy;
351 }
352 }
353
Robert Phillipsc5a2c752019-10-24 13:11:45 -0400354 // Allocate the fields required in both onPrePrepareDraws and onPrepareDraws
355 void allocateCommon(SkArenaAlloc* arena, const GrAppliedClip* clip) {
Robert Phillips32803ff2019-10-23 08:26:08 -0400356 // We'll use a dynamic state array for the GP textures when there are multiple ops.
357 // Otherwise, we use fixed dynamic state to specify the single op's proxy.
358 if (fNumProxies > 1) {
359 fDynamicStateArrays = Target::AllocDynamicStateArrays(arena, fNumProxies, 1, false);
360 fFixedDynamicState = Target::MakeFixedDynamicState(arena, clip, 0);
361 } else {
362 fFixedDynamicState = Target::MakeFixedDynamicState(arena, clip, 1);
Robert Phillips32803ff2019-10-23 08:26:08 -0400363 }
364 }
Robert Phillipsc5a2c752019-10-24 13:11:45 -0400365
366 // Allocate the fields only needed by onPrePrepareDraws
367 void allocatePrePrepareOnly(SkArenaAlloc* arena) {
Robert Phillipsc5a2c752019-10-24 13:11:45 -0400368 fVertices = arena->makeArrayDefault<char>(this->totalSizeInBytes());
369 }
370
Robert Phillips32803ff2019-10-23 08:26:08 -0400371 };
372
Michael Ludwigf339dfe2019-06-27 10:41:28 -0400373 // dstQuad should be the geometry transformed by the view matrix. If domainRect
374 // is not null it will be used to apply the strict src rect constraint.
Greg Daniel549325c2019-10-30 16:19:20 -0400375 TextureOp(GrSurfaceProxyView proxyView,
Brian Salomonf19f9ca2019-09-18 15:54:26 -0400376 sk_sp<GrColorSpaceXform> textureColorSpaceXform,
377 GrSamplerState::Filter filter,
378 const SkPMColor4f& color,
379 GrTextureOp::Saturate saturate,
380 GrAAType aaType,
381 GrQuadAAFlags aaFlags,
382 const GrQuad& dstQuad,
383 const GrQuad& srcQuad,
384 const SkRect* domainRect)
Brian Salomon34169692017-08-28 15:32:01 -0400385 : INHERITED(ClassID())
Michael Ludwigf339dfe2019-06-27 10:41:28 -0400386 , fQuads(1, true /* includes locals */)
Brian Osman3ebd3542018-07-30 14:36:53 -0400387 , fTextureColorSpaceXform(std::move(textureColorSpaceXform))
Robert Phillips32803ff2019-10-23 08:26:08 -0400388 , fPrePreparedDesc(nullptr)
Brian Salomonf19f9ca2019-09-18 15:54:26 -0400389 , fSaturate(static_cast<unsigned>(saturate))
Robert Phillips32803ff2019-10-23 08:26:08 -0400390 , fFilter(static_cast<unsigned>(filter)) {
Michael Ludwig6bee7762018-10-19 09:50:36 -0400391 // Clean up disparities between the overall aa type and edge configuration and apply
392 // optimizations based on the rect and matrix when appropriate
Michael Ludwig0f809022019-06-04 09:14:37 -0400393 GrQuadUtils::ResolveAAType(aaType, aaFlags, dstQuad, &aaType, &aaFlags);
Michael Ludwig6bee7762018-10-19 09:50:36 -0400394 fAAType = static_cast<unsigned>(aaType);
395
Brian Salomonf1709042018-10-03 11:57:00 -0400396 // We expect our caller to have already caught this optimization.
Greg Daniel549325c2019-10-30 16:19:20 -0400397 SkASSERT(!domainRect ||
398 !domainRect->contains(proxyView.proxy()->backingStoreBoundsRect()));
Michael Ludwig009b92e2019-02-15 16:03:53 -0500399
Brian Salomonf09abc52018-10-03 15:59:04 -0400400 // We may have had a strict constraint with nearest filter solely due to possible AA bloat.
401 // If we don't have (or determined we don't need) coverage AA then we can skip using a
402 // domain.
Michael Ludwigf339dfe2019-06-27 10:41:28 -0400403 if (domainRect && this->filter() == GrSamplerState::Filter::kNearest &&
Michael Ludwig6bee7762018-10-19 09:50:36 -0400404 aaType != GrAAType::kCoverage) {
Michael Ludwigf339dfe2019-06-27 10:41:28 -0400405 domainRect = nullptr;
Brian Salomonf09abc52018-10-03 15:59:04 -0400406 }
Michael Ludwigc96fc372019-01-08 15:46:15 -0500407
Michael Ludwig119ac6d2019-11-21 09:26:46 -0500408 // Normalize src coordinates and the domain (if set)
409 NormalizationParams params = proxy_normalization_params(proxyView);
410 GrQuad normalizedSrcQuad = srcQuad;
411 normalize_src_quad(params, &normalizedSrcQuad);
412 SkRect domain = normalize_domain(filter, params, domainRect);
413
414 fQuads.append(dstQuad, {color, domain, aaFlags}, &normalizedSrcQuad);
Michael Ludwig425eb452019-06-27 10:13:27 -0400415
Brian Salomond7065e72018-10-12 11:42:02 -0400416 fProxyCnt = 1;
Greg Daniel549325c2019-10-30 16:19:20 -0400417 fViewCountPairs[0] = {std::move(proxyView), 1};
Robert Phillipsbbd459d2019-10-29 14:40:03 -0400418 fTotNumQuads = 1;
Michael Ludwig41f395d2019-05-23 13:59:45 -0400419 this->setBounds(dstQuad.bounds(), HasAABloat(aaType == GrAAType::kCoverage),
Greg Daniel5faf4742019-10-01 15:14:44 -0400420 IsHairline::kNo);
Michael Ludwigf339dfe2019-06-27 10:41:28 -0400421 fDomain = static_cast<unsigned>(domainRect != nullptr);
Brian Salomond7065e72018-10-12 11:42:02 -0400422 }
Robert Phillipsbbd459d2019-10-29 14:40:03 -0400423
Brian Salomonf19f9ca2019-09-18 15:54:26 -0400424 TextureOp(const GrRenderTargetContext::TextureSetEntry set[],
425 int cnt,
426 GrSamplerState::Filter filter,
427 GrTextureOp::Saturate saturate,
428 GrAAType aaType,
429 SkCanvas::SrcRectConstraint constraint,
430 const SkMatrix& viewMatrix,
Brian Salomond003d222018-11-26 13:25:05 -0500431 sk_sp<GrColorSpaceXform> textureColorSpaceXform)
Brian Salomond7065e72018-10-12 11:42:02 -0400432 : INHERITED(ClassID())
Michael Ludwigf339dfe2019-06-27 10:41:28 -0400433 , fQuads(cnt, true /* includes locals */)
Brian Salomond7065e72018-10-12 11:42:02 -0400434 , fTextureColorSpaceXform(std::move(textureColorSpaceXform))
Robert Phillips32803ff2019-10-23 08:26:08 -0400435 , fPrePreparedDesc(nullptr)
Michael Ludwig119ac6d2019-11-21 09:26:46 -0500436 , fSaturate(static_cast<unsigned>(saturate)) {
Brian Salomond7065e72018-10-12 11:42:02 -0400437 fProxyCnt = SkToUInt(cnt);
438 SkRect bounds = SkRectPriv::MakeLargestInverted();
Michael Ludwig119ac6d2019-11-21 09:26:46 -0500439
440 GrAAType netAAType = GrAAType::kNone; // aa type maximally compatible with all dst rects
Michael Ludwig31ba7182019-04-03 10:38:06 -0400441 Domain netDomain = Domain::kNo;
Michael Ludwig119ac6d2019-11-21 09:26:46 -0500442 GrSamplerState::Filter netFilter = GrSamplerState::Filter::kNearest;
443
444 // Net domain and filter quality are being determined simultaneously while iterating through
445 // the entry set. When filter changes to bilerp, all prior normalized domains in the
446 // GrQuadBuffer must be updated to reflect the 1/2px inset required. All quads appended
447 // afterwards will properly take that into account.
448 int correctDomainUpToIndex = 0;
449 const GrSurfaceProxy* curProxy;
Greg Daniel549325c2019-10-30 16:19:20 -0400450
Brian Salomond7065e72018-10-12 11:42:02 -0400451 for (unsigned p = 0; p < fProxyCnt; ++p) {
Greg Daniel549325c2019-10-30 16:19:20 -0400452 if (p == 0) {
453 // We do not placement new the first ViewCountPair since that one is allocated and
454 // initialized as part of the GrTextureOp creation.
455 fViewCountPairs[p].fProxyView = std::move(set[p].fProxyView);
456 fViewCountPairs[p].fQuadCnt = 1;
457 } else {
458 // We must placement new the ViewCountPairs here so that the sk_sps in the
459 // GrSurfaceProxyView get initialized properly.
460 new(&fViewCountPairs[p])ViewCountPair({std::move(set[p].fProxyView), 1});
461 }
Robert Phillipsbbd459d2019-10-29 14:40:03 -0400462 fTotNumQuads += 1;
Michael Ludwig119ac6d2019-11-21 09:26:46 -0500463 curProxy = fViewCountPairs[p].fProxyView.proxy();
464 SkASSERT(curProxy->backendFormat().textureType() ==
Greg Daniel549325c2019-10-30 16:19:20 -0400465 fViewCountPairs[0].fProxyView.asTextureProxy()->textureType());
466 SkASSERT(curProxy->config() == fViewCountPairs[0].fProxyView.proxy()->config());
Michael Ludwigce62dec2019-02-19 11:48:46 -0500467
Michael Ludwig7ae2ab52019-03-05 16:00:20 -0500468 SkMatrix ctm = viewMatrix;
469 if (set[p].fPreViewMatrix) {
470 ctm.preConcat(*set[p].fPreViewMatrix);
471 }
472
Michael Ludwigf339dfe2019-06-27 10:41:28 -0400473 // Use dstRect/srcRect unless dstClip is provided, in which case derive new source
474 // coordinates by mapping dstClipQuad by the dstRect to srcRect transform.
475 GrQuad quad, srcQuad;
476 if (set[p].fDstClipQuad) {
477 quad = GrQuad::MakeFromSkQuad(set[p].fDstClipQuad, ctm);
478
479 SkPoint srcPts[4];
480 GrMapRectPoints(set[p].fDstRect, set[p].fSrcRect, set[p].fDstClipQuad, srcPts, 4);
481 srcQuad = GrQuad::MakeFromSkQuad(srcPts, SkMatrix::I());
482 } else {
483 quad = GrQuad::MakeFromRect(set[p].fDstRect, ctm);
484 srcQuad = GrQuad(set[p].fSrcRect);
485 }
Michael Ludwigce62dec2019-02-19 11:48:46 -0500486
Michael Ludwig119ac6d2019-11-21 09:26:46 -0500487 // Before normalizing the source coordinates, determine if bilerp is actually needed
488 if (netFilter != filter && filter_has_effect(srcQuad, quad)) {
489 // The only way netFilter != filter is if bilerp is requested and we haven't yet
490 // found a quad that requires bilerp (so net is still nearest).
491 SkASSERT(netFilter == GrSamplerState::Filter::kNearest &&
492 filter == GrSamplerState::Filter::kBilerp);
493 netFilter = GrSamplerState::Filter::kBilerp;
494 // All quads index < p with domains were calculated as if there was no filtering,
495 // which is no longer true.
496 correctDomainUpToIndex = p;
Michael Ludwig22429f92019-06-27 10:44:48 -0400497 }
498
Michael Ludwig119ac6d2019-11-21 09:26:46 -0500499 // Normalize the src quads and apply origin
500 NormalizationParams proxyParams =
501 proxy_normalization_params(fViewCountPairs[p].fProxyView);
502 normalize_src_quad(proxyParams, &srcQuad);
503
504 // Update overall bounds of the op as the union of all quads
Michael Ludwig41f395d2019-05-23 13:59:45 -0400505 bounds.joinPossiblyEmptyRect(quad.bounds());
Michael Ludwig119ac6d2019-11-21 09:26:46 -0500506
507 // Determine the AA type for the quad, then merge with net AA type
Michael Ludwig6bee7762018-10-19 09:50:36 -0400508 GrQuadAAFlags aaFlags;
Michael Ludwig6bee7762018-10-19 09:50:36 -0400509 GrAAType aaForQuad;
Michael Ludwig0f809022019-06-04 09:14:37 -0400510 GrQuadUtils::ResolveAAType(aaType, set[p].fAAFlags, quad, &aaForQuad, &aaFlags);
Michael Ludwig6bee7762018-10-19 09:50:36 -0400511 // Resolve sets aaForQuad to aaType or None, there is never a change between aa methods
512 SkASSERT(aaForQuad == GrAAType::kNone || aaForQuad == aaType);
Michael Ludwig119ac6d2019-11-21 09:26:46 -0500513 if (netAAType == GrAAType::kNone && aaForQuad != GrAAType::kNone) {
514 netAAType = aaType;
Brian Salomond7065e72018-10-12 11:42:02 -0400515 }
Michael Ludwigf339dfe2019-06-27 10:41:28 -0400516
517 // Calculate metadata for the entry
518 const SkRect* domainForQuad = nullptr;
Michael Ludwig31ba7182019-04-03 10:38:06 -0400519 if (constraint == SkCanvas::kStrict_SrcRectConstraint) {
520 // Check (briefly) if the strict constraint is needed for this set entry
Robert Phillipse9462dd2019-10-23 12:41:29 -0400521 if (!set[p].fSrcRect.contains(curProxy->backingStoreBoundsRect()) &&
Michael Ludwig119ac6d2019-11-21 09:26:46 -0500522 (netFilter == GrSamplerState::Filter::kBilerp ||
523 aaForQuad == GrAAType::kCoverage)) {
Michael Ludwig31ba7182019-04-03 10:38:06 -0400524 // Can't rely on hardware clamping and the draw will access outer texels
Michael Ludwig119ac6d2019-11-21 09:26:46 -0500525 // for AA and/or bilerp. Unlike filter quality, this op still has per-quad
526 // control over AA so that can check aaForQuad, not netAAType.
Michael Ludwig31ba7182019-04-03 10:38:06 -0400527 netDomain = Domain::kYes;
Michael Ludwigf339dfe2019-06-27 10:41:28 -0400528 domainForQuad = &set[p].fSrcRect;
Michael Ludwig31ba7182019-04-03 10:38:06 -0400529 }
530 }
Michael Ludwig119ac6d2019-11-21 09:26:46 -0500531
532 SkRect domain = normalize_domain(filter, proxyParams, domainForQuad);
Brian Salomond003d222018-11-26 13:25:05 -0500533 float alpha = SkTPin(set[p].fAlpha, 0.f, 1.f);
Michael Ludwig119ac6d2019-11-21 09:26:46 -0500534 fQuads.append(quad, {{alpha, alpha, alpha, alpha}, domain, aaFlags}, &srcQuad);
Brian Salomond7065e72018-10-12 11:42:02 -0400535 }
Michael Ludwig119ac6d2019-11-21 09:26:46 -0500536
537 // All the quads have been recorded, but some domains need to be fixed
538 if (netDomain == Domain::kYes && correctDomainUpToIndex > 0) {
539 int p = 0;
540 auto iter = fQuads.metadata();
541 while(p < correctDomainUpToIndex && iter.next()) {
542 NormalizationParams proxyParams =
543 proxy_normalization_params(fViewCountPairs[p].fProxyView);
544 correct_domain_for_bilerp(proxyParams, &(iter->fDomainRect));
545 p++;
546 }
Brian Salomon0087c832018-10-15 14:48:20 -0400547 }
Michael Ludwig119ac6d2019-11-21 09:26:46 -0500548
549 fAAType = static_cast<unsigned>(netAAType);
550 fFilter = static_cast<unsigned>(netFilter);
Michael Ludwig31ba7182019-04-03 10:38:06 -0400551 fDomain = static_cast<unsigned>(netDomain);
Brian Salomon34169692017-08-28 15:32:01 -0400552
Michael Ludwig119ac6d2019-11-21 09:26:46 -0500553 this->setBounds(bounds, HasAABloat(netAAType == GrAAType::kCoverage), IsHairline::kNo);
Brian Salomon17031a72018-05-22 14:14:07 -0400554 }
555
Robert Phillipsdf70f152019-11-15 14:57:05 -0500556 void onPrePrepareDraws(GrRecordingContext* context,
557 const GrSurfaceProxyView* dstView,
Robert Phillips8053c972019-11-21 10:44:53 -0500558 GrAppliedClip* clip,
559 const GrXferProcessor::DstProxyView& dstProxyView) override {
Robert Phillips61fc7992019-10-22 11:58:17 -0400560 TRACE_EVENT0("skia.gpu", TRACE_FUNC);
Robert Phillips29f38542019-10-16 09:20:25 -0400561
Robert Phillips61fc7992019-10-22 11:58:17 -0400562 SkDEBUGCODE(this->validate();)
Robert Phillips32803ff2019-10-23 08:26:08 -0400563 SkASSERT(!fPrePreparedDesc);
Robert Phillips61fc7992019-10-22 11:58:17 -0400564
Robert Phillipsd4fb7c72019-11-15 17:28:37 -0500565 SkArenaAlloc* arena = context->priv().recordTimeAllocator();
Robert Phillips61fc7992019-10-22 11:58:17 -0400566
Robert Phillips32803ff2019-10-23 08:26:08 -0400567 fPrePreparedDesc = arena->make<PrePreparedDesc>();
Robert Phillips61fc7992019-10-22 11:58:17 -0400568
Robert Phillipsc554dcf2019-10-28 11:43:55 -0400569 this->characterize(fPrePreparedDesc);
570
Robert Phillipsc5a2c752019-10-24 13:11:45 -0400571 fPrePreparedDesc->allocateCommon(arena, clip);
Robert Phillips61fc7992019-10-22 11:58:17 -0400572
Robert Phillipsc5a2c752019-10-24 13:11:45 -0400573 fPrePreparedDesc->allocatePrePrepareOnly(arena);
574
Robert Phillipsbbd459d2019-10-29 14:40:03 -0400575 // At this juncture we only fill in the vertex data and state arrays. Filling in of
576 // the meshes is left until onPrepareDraws.
577 SkAssertResult(FillInData(this, fPrePreparedDesc, fPrePreparedDesc->fVertices,
578 nullptr, 0, nullptr, nullptr));
579 }
Robert Phillipsc5a2c752019-10-24 13:11:45 -0400580
Robert Phillipsbbd459d2019-10-29 14:40:03 -0400581 static bool FillInData(TextureOp* texOp, PrePreparedDesc* desc,
582 char* pVertexData, GrMesh* meshes, int absBufferOffset,
Robert Phillipsfd0c3b52019-11-01 08:44:42 -0400583 sk_sp<const GrBuffer> vertexBuffer,
584 sk_sp<const GrBuffer> indexBuffer) {
585 int totQuadsSeen = 0;
Robert Phillipsbbd459d2019-10-29 14:40:03 -0400586 SkDEBUGCODE(int totVerticesSeen = 0;)
Robert Phillipsbbd459d2019-10-29 14:40:03 -0400587 char* dst = pVertexData;
588 const size_t vertexSize = desc->fVertexSpec.vertexSize();
Robert Phillipsc5a2c752019-10-24 13:11:45 -0400589
Michael Ludwig73dbea62019-11-19 14:55:36 -0500590 GrQuadPerEdgeAA::Tessellator tessellator(desc->fVertexSpec);
Robert Phillipsbbd459d2019-10-29 14:40:03 -0400591 int meshIndex = 0;
592 for (const auto& op : ChainRange<TextureOp>(texOp)) {
593 auto iter = op.fQuads.iterator();
594 for (unsigned p = 0; p < op.fProxyCnt; ++p) {
Greg Daniel549325c2019-10-30 16:19:20 -0400595 GrTextureProxy* proxy = op.fViewCountPairs[p].fProxyView.asTextureProxy();
Robert Phillipsc5a2c752019-10-24 13:11:45 -0400596
Greg Daniel549325c2019-10-30 16:19:20 -0400597 int quadCnt = op.fViewCountPairs[p].fQuadCnt;
Robert Phillipsc5a2c752019-10-24 13:11:45 -0400598
Robert Phillipsfd0c3b52019-11-01 08:44:42 -0400599 const int meshVertexCnt = quadCnt * desc->fVertexSpec.verticesPerQuad();
Robert Phillipsc5a2c752019-10-24 13:11:45 -0400600
Robert Phillipsbbd459d2019-10-29 14:40:03 -0400601 SkASSERT(meshIndex < desc->fNumProxies);
Robert Phillipsc5a2c752019-10-24 13:11:45 -0400602
Robert Phillipsbbd459d2019-10-29 14:40:03 -0400603 if (dst) {
Michael Ludwig119ac6d2019-11-21 09:26:46 -0500604 int i = 0;
605 void* v = dst;
606 while(i < quadCnt && iter.next()) {
607 SkASSERT(iter.isLocalValid());
608 const ColorDomainAndAA& info = iter.metadata();
609 v = tessellator.append(v, iter.deviceQuad(), iter.localQuad(),
610 info.fColor, info.fDomainRect, info.aaFlags());
611 i++;
612 }
Robert Phillipsbbd459d2019-10-29 14:40:03 -0400613 desc->setMeshProxy(meshIndex, proxy);
614
Robert Phillipsfd0c3b52019-11-01 08:44:42 -0400615 SkASSERT(totVerticesSeen * vertexSize == (size_t)(dst - pVertexData));
Robert Phillipsc5a2c752019-10-24 13:11:45 -0400616 dst += vertexSize * meshVertexCnt;
617 }
Robert Phillipsbbd459d2019-10-29 14:40:03 -0400618
619 if (meshes) {
Robert Phillipsfd0c3b52019-11-01 08:44:42 -0400620 GrQuadPerEdgeAA::ConfigureMesh(&(meshes[meshIndex]), desc->fVertexSpec,
621 totQuadsSeen, quadCnt, desc->totalNumVertices(),
622 vertexBuffer, indexBuffer, absBufferOffset);
Robert Phillipsbbd459d2019-10-29 14:40:03 -0400623 }
624
625 ++meshIndex;
626
Robert Phillipsfd0c3b52019-11-01 08:44:42 -0400627 totQuadsSeen += quadCnt;
628 SkDEBUGCODE(totVerticesSeen += meshVertexCnt);
629 SkASSERT(totQuadsSeen * desc->fVertexSpec.verticesPerQuad() == totVerticesSeen);
Robert Phillipsc5a2c752019-10-24 13:11:45 -0400630 }
631
Robert Phillipsbbd459d2019-10-29 14:40:03 -0400632 // If quad counts per proxy were calculated correctly, the entire iterator
633 // should have been consumed.
634 SkASSERT(!dst || !iter.next());
Robert Phillipsc5a2c752019-10-24 13:11:45 -0400635 }
Robert Phillipsbbd459d2019-10-29 14:40:03 -0400636
637 SkASSERT(!dst || (desc->totalSizeInBytes() == (size_t)(dst - pVertexData)));
638 SkASSERT(meshIndex == desc->fNumProxies);
639 SkASSERT(totQuadsSeen == desc->fNumTotalQuads);
640 SkASSERT(totVerticesSeen == desc->totalNumVertices());
641 return true;
Robert Phillips7327c9d2019-10-08 16:32:56 -0400642 }
643
Robert Phillips29f38542019-10-16 09:20:25 -0400644#ifdef SK_DEBUG
645 void validate() const override {
Greg Daniel549325c2019-10-30 16:19:20 -0400646 auto textureType = fViewCountPairs[0].fProxyView.asTextureProxy()->textureType();
Brian Salomonae7d7702018-10-14 15:05:45 -0400647 GrAAType aaType = this->aaType();
Robert Phillips29f38542019-10-16 09:20:25 -0400648
Robert Phillipse837e612019-11-15 11:02:50 -0500649 int quadCount = 0;
Robert Phillips29f38542019-10-16 09:20:25 -0400650 for (const auto& op : ChainRange<TextureOp>(this)) {
651 for (unsigned p = 0; p < op.fProxyCnt; ++p) {
Greg Daniel549325c2019-10-30 16:19:20 -0400652 auto* proxy = op.fViewCountPairs[p].fProxyView.asTextureProxy();
Robert Phillipse837e612019-11-15 11:02:50 -0500653 quadCount += op.fViewCountPairs[p].fQuadCnt;
Robert Phillips29f38542019-10-16 09:20:25 -0400654 SkASSERT(proxy);
655 SkASSERT(proxy->textureType() == textureType);
Greg Daniel549325c2019-10-30 16:19:20 -0400656 SkASSERT(op.fViewCountPairs[p].fProxyView.swizzle() ==
657 fViewCountPairs[0].fProxyView.swizzle());
Robert Phillips29f38542019-10-16 09:20:25 -0400658 }
659
660 // Each individual op must be a single aaType. kCoverage and kNone ops can chain
661 // together but kMSAA ones do not.
662 if (aaType == GrAAType::kCoverage || aaType == GrAAType::kNone) {
663 SkASSERT(op.aaType() == GrAAType::kCoverage || op.aaType() == GrAAType::kNone);
664 } else {
665 SkASSERT(aaType == GrAAType::kMSAA && op.aaType() == GrAAType::kMSAA);
666 }
667 }
Robert Phillipse837e612019-11-15 11:02:50 -0500668
669 SkASSERT(quadCount == this->numChainedQuads());
Robert Phillips29f38542019-10-16 09:20:25 -0400670 }
671#endif
672
Robert Phillipse837e612019-11-15 11:02:50 -0500673#if GR_TEST_UTILS
674 int numQuads() const final { return this->totNumQuads(); }
675#endif
676
Robert Phillipsc554dcf2019-10-28 11:43:55 -0400677 void characterize(PrePreparedDesc* desc) const {
Robert Phillips29f38542019-10-16 09:20:25 -0400678 GrQuad::Type quadType = GrQuad::Type::kAxisAligned;
679 ColorType colorType = ColorType::kNone;
680 GrQuad::Type srcQuadType = GrQuad::Type::kAxisAligned;
681 Domain domain = Domain::kNo;
682 GrAAType overallAAType = this->aaType();
683
Robert Phillipsc554dcf2019-10-28 11:43:55 -0400684 desc->fNumProxies = 0;
685 desc->fNumTotalQuads = 0;
686 int maxQuadsPerMesh = 0;
Robert Phillips29f38542019-10-16 09:20:25 -0400687
Brian Salomonf7232642018-09-19 08:58:08 -0400688 for (const auto& op : ChainRange<TextureOp>(this)) {
Michael Ludwig425eb452019-06-27 10:13:27 -0400689 if (op.fQuads.deviceQuadType() > quadType) {
690 quadType = op.fQuads.deviceQuadType();
Michael Ludwigf995c052018-11-26 15:24:29 -0500691 }
Michael Ludwig425eb452019-06-27 10:13:27 -0400692 if (op.fQuads.localQuadType() > srcQuadType) {
693 srcQuadType = op.fQuads.localQuadType();
Michael Ludwig009b92e2019-02-15 16:03:53 -0500694 }
Brian Salomonf7232642018-09-19 08:58:08 -0400695 if (op.fDomain) {
696 domain = Domain::kYes;
697 }
Brian Salomon1d835422019-03-13 16:11:44 -0400698 colorType = SkTMax(colorType, static_cast<ColorType>(op.fColorType));
Robert Phillipsc554dcf2019-10-28 11:43:55 -0400699 desc->fNumProxies += op.fProxyCnt;
Robert Phillipsbbd459d2019-10-29 14:40:03 -0400700
Brian Salomond7065e72018-10-12 11:42:02 -0400701 for (unsigned p = 0; p < op.fProxyCnt; ++p) {
Greg Daniel549325c2019-10-30 16:19:20 -0400702 maxQuadsPerMesh = SkTMax(op.fViewCountPairs[p].fQuadCnt, maxQuadsPerMesh);
Brian Salomonf7232642018-09-19 08:58:08 -0400703 }
Robert Phillipsbbd459d2019-10-29 14:40:03 -0400704 desc->fNumTotalQuads += op.totNumQuads();
705
Brian Salomonae7d7702018-10-14 15:05:45 -0400706 if (op.aaType() == GrAAType::kCoverage) {
Robert Phillips29f38542019-10-16 09:20:25 -0400707 overallAAType = GrAAType::kCoverage;
Brian Salomonae7d7702018-10-14 15:05:45 -0400708 }
Brian Salomon34169692017-08-28 15:32:01 -0400709 }
Brian Salomon336ce7b2017-09-08 08:23:58 -0400710
Robert Phillipsbbd459d2019-10-29 14:40:03 -0400711 SkASSERT(desc->fNumTotalQuads == this->numChainedQuads());
712
713 SkASSERT(!CombinedQuadCountWillOverflow(overallAAType, false, desc->fNumTotalQuads));
714
Robert Phillipsc554dcf2019-10-28 11:43:55 -0400715 auto indexBufferOption = GrQuadPerEdgeAA::CalcIndexBufferOption(overallAAType,
716 maxQuadsPerMesh);
717
718 desc->fVertexSpec = VertexSpec(quadType, colorType, srcQuadType, /* hasLocal */ true,
719 domain, overallAAType, /* alpha as coverage */ true,
720 indexBufferOption);
Robert Phillipse837e612019-11-15 11:02:50 -0500721
722 SkASSERT(desc->fNumTotalQuads <= GrQuadPerEdgeAA::QuadLimit(indexBufferOption));
Robert Phillips29f38542019-10-16 09:20:25 -0400723 }
Michael Ludwigc182b942018-11-16 10:27:51 -0500724
Robert Phillipsbbd459d2019-10-29 14:40:03 -0400725 int totNumQuads() const {
726#ifdef SK_DEBUG
727 int tmp = 0;
728 for (unsigned p = 0; p < fProxyCnt; ++p) {
Greg Daniel549325c2019-10-30 16:19:20 -0400729 tmp += fViewCountPairs[p].fQuadCnt;
Robert Phillipsbbd459d2019-10-29 14:40:03 -0400730 }
731 SkASSERT(tmp == fTotNumQuads);
732#endif
733
734 return fTotNumQuads;
735 }
736
737 int numChainedQuads() const {
738 int numChainedQuads = this->totNumQuads();
739
740 for (const GrOp* tmp = this->prevInChain(); tmp; tmp = tmp->prevInChain()) {
741 numChainedQuads += ((const TextureOp*)tmp)->totNumQuads();
742 }
743
744 for (const GrOp* tmp = this->nextInChain(); tmp; tmp = tmp->nextInChain()) {
745 numChainedQuads += ((const TextureOp*)tmp)->totNumQuads();
746 }
747
748 return numChainedQuads;
749 }
750
Robert Phillips29f38542019-10-16 09:20:25 -0400751 // onPrePrepareDraws may or may not have been called at this point
752 void onPrepareDraws(Target* target) override {
753 TRACE_EVENT0("skia.gpu", TRACE_FUNC);
Greg Daniel7a82edf2018-12-04 10:54:34 -0500754
Robert Phillips29f38542019-10-16 09:20:25 -0400755 SkDEBUGCODE(this->validate();)
756
Robert Phillips32803ff2019-10-23 08:26:08 -0400757 PrePreparedDesc desc;
Robert Phillips29f38542019-10-16 09:20:25 -0400758
Robert Phillips32803ff2019-10-23 08:26:08 -0400759 if (fPrePreparedDesc) {
760 desc = *fPrePreparedDesc;
Brian Salomonf7232642018-09-19 08:58:08 -0400761 } else {
Robert Phillips61fc7992019-10-22 11:58:17 -0400762 SkArenaAlloc* arena = target->allocator();
763
Robert Phillipsc554dcf2019-10-28 11:43:55 -0400764 this->characterize(&desc);
Robert Phillipsc5a2c752019-10-24 13:11:45 -0400765 desc.allocateCommon(arena, target->appliedClip());
766
Robert Phillipsbbd459d2019-10-29 14:40:03 -0400767 SkASSERT(!desc.fVertices);
Brian Salomonf7232642018-09-19 08:58:08 -0400768 }
Brian Salomon92be2f72018-06-19 14:33:47 -0400769
Robert Phillips32803ff2019-10-23 08:26:08 -0400770 size_t vertexSize = desc.fVertexSpec.vertexSize();
Brian Salomon92be2f72018-06-19 14:33:47 -0400771
Brian Salomon12d22642019-01-29 14:38:50 -0500772 sk_sp<const GrBuffer> vbuffer;
Brian Salomon4b8178f2018-10-12 13:18:27 -0400773 int vertexOffsetInBuffer = 0;
Brian Salomon4b8178f2018-10-12 13:18:27 -0400774
Robert Phillipsbbd459d2019-10-29 14:40:03 -0400775 void* vdata = target->makeVertexSpace(vertexSize, desc.totalNumVertices(),
776 &vbuffer, &vertexOffsetInBuffer);
777 if (!vdata) {
778 SkDebugf("Could not allocate vertices\n");
779 return;
Brian Salomon34169692017-08-28 15:32:01 -0400780 }
Robert Phillipsbbd459d2019-10-29 14:40:03 -0400781
Robert Phillipsfd0c3b52019-11-01 08:44:42 -0400782 sk_sp<const GrBuffer> indexBuffer;
783 if (desc.fVertexSpec.needsIndexBuffer()) {
784 indexBuffer = GrQuadPerEdgeAA::GetIndexBuffer(target,
785 desc.fVertexSpec.indexBufferOption());
786 if (!indexBuffer) {
787 SkDebugf("Could not allocate indices\n");
788 return;
789 }
790 }
791
Robert Phillipsbbd459d2019-10-29 14:40:03 -0400792 // Note: this allocation is always in the flush-time arena (i.e., the flushState)
793 GrMesh* meshes = target->allocMeshes(desc.fNumProxies);
794
795 bool result;
796 if (fPrePreparedDesc) {
797 memcpy(vdata, desc.fVertices, desc.totalSizeInBytes());
798 // The above memcpy filled in the vertex data - just call FillInData to fill in the
799 // mesh data
800 result = FillInData(this, &desc, nullptr, meshes, vertexOffsetInBuffer,
Robert Phillipsfd0c3b52019-11-01 08:44:42 -0400801 std::move(vbuffer), std::move(indexBuffer));
Robert Phillipsbbd459d2019-10-29 14:40:03 -0400802 } else {
803 // Fills in both vertex data and mesh data
804 result = FillInData(this, &desc, (char*) vdata, meshes, vertexOffsetInBuffer,
Robert Phillipsfd0c3b52019-11-01 08:44:42 -0400805 std::move(vbuffer), std::move(indexBuffer));
Robert Phillipsbbd459d2019-10-29 14:40:03 -0400806 }
807
808 if (!result) {
809 return;
810 }
Robert Phillips29f38542019-10-16 09:20:25 -0400811
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500812 GrGeometryProcessor* gp;
Robert Phillips29f38542019-10-16 09:20:25 -0400813
814 {
Greg Daniel549325c2019-10-30 16:19:20 -0400815 const GrBackendFormat& backendFormat =
816 fViewCountPairs[0].fProxyView.proxy()->backendFormat();
817 const GrSwizzle& swizzle = fViewCountPairs[0].fProxyView.swizzle();
Robert Phillips29f38542019-10-16 09:20:25 -0400818
819 GrSamplerState samplerState = GrSamplerState(GrSamplerState::WrapMode::kClamp,
820 this->filter());
821
822 auto saturate = static_cast<GrTextureOp::Saturate>(fSaturate);
823
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500824 gp = GrQuadPerEdgeAA::MakeTexturedProcessor(target->allocator(),
Robert Phillips32803ff2019-10-23 08:26:08 -0400825 desc.fVertexSpec, *target->caps().shaderCaps(), backendFormat,
Robert Phillips323471e2019-11-11 11:33:37 -0500826 samplerState, swizzle, std::move(fTextureColorSpaceXform), saturate);
Robert Phillips29f38542019-10-16 09:20:25 -0400827
828 SkASSERT(vertexSize == gp->vertexStride());
829 }
830
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500831 target->recordDraw(gp, meshes, desc.fNumProxies,
Robert Phillipscea290f2019-11-06 11:21:03 -0500832 desc.fFixedDynamicState, desc.fDynamicStateArrays,
833 desc.fVertexSpec.primitiveType());
Chris Dalton07cdcfc92019-02-26 11:13:22 -0700834 }
835
836 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
837 auto pipelineFlags = (GrAAType::kMSAA == this->aaType())
Chris Daltonbaa1b352019-04-03 12:03:00 -0600838 ? GrPipeline::InputFlags::kHWAntialias
839 : GrPipeline::InputFlags::kNone;
Chris Dalton07cdcfc92019-02-26 11:13:22 -0700840 flushState->executeDrawsAndUploadsForMeshDrawOp(
841 this, chainBounds, GrProcessorSet::MakeEmptySet(), pipelineFlags);
Brian Salomon34169692017-08-28 15:32:01 -0400842 }
843
Brian Salomonf7232642018-09-19 08:58:08 -0400844 CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon5f394272019-07-02 14:07:49 -0400845 TRACE_EVENT0("skia.gpu", TRACE_FUNC);
Brian Salomon34169692017-08-28 15:32:01 -0400846 const auto* that = t->cast<TextureOp>();
Robert Phillips7327c9d2019-10-08 16:32:56 -0400847
Robert Phillips32803ff2019-10-23 08:26:08 -0400848 if (fPrePreparedDesc || that->fPrePreparedDesc) {
Robert Phillips7327c9d2019-10-08 16:32:56 -0400849 // This should never happen (since only DDL recorded ops should be prePrepared)
850 // but, in any case, we should never combine ops that that been prePrepared
851 return CombineResult::kCannotCombine;
852 }
853
Michael Ludwig2929f512019-04-19 13:05:56 -0400854 if (fDomain != that->fDomain) {
855 // It is technically possible to combine operations across domain modes, but performance
856 // testing suggests it's better to make more draw calls where some take advantage of
857 // the more optimal shader path without coordinate clamping.
858 return CombineResult::kCannotCombine;
859 }
Brian Osman3ebd3542018-07-30 14:36:53 -0400860 if (!GrColorSpaceXform::Equals(fTextureColorSpaceXform.get(),
861 that->fTextureColorSpaceXform.get())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +0000862 return CombineResult::kCannotCombine;
Brian Osman3ebd3542018-07-30 14:36:53 -0400863 }
Robert Phillipsb69001f2019-10-29 12:16:35 -0400864
Brian Salomonae7d7702018-10-14 15:05:45 -0400865 bool upgradeToCoverageAAOnMerge = false;
Brian Salomon485b8c62018-01-12 15:11:06 -0500866 if (this->aaType() != that->aaType()) {
Robert Phillipsbbd459d2019-10-29 14:40:03 -0400867 if (!CanUpgradeAAOnMerge(this->aaType(), that->aaType())) {
Brian Salomonae7d7702018-10-14 15:05:45 -0400868 return CombineResult::kCannotCombine;
869 }
870 upgradeToCoverageAAOnMerge = true;
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500871 }
Robert Phillipsb69001f2019-10-29 12:16:35 -0400872
Robert Phillipsbbd459d2019-10-29 14:40:03 -0400873 if (CombinedQuadCountWillOverflow(this->aaType(), upgradeToCoverageAAOnMerge,
874 this->numChainedQuads() + that->numChainedQuads())) {
875 return CombineResult::kCannotCombine;
Robert Phillipsb69001f2019-10-29 12:16:35 -0400876 }
877
Brian Salomonf19f9ca2019-09-18 15:54:26 -0400878 if (fSaturate != that->fSaturate) {
879 return CombineResult::kCannotCombine;
880 }
Brian Salomonf7232642018-09-19 08:58:08 -0400881 if (fFilter != that->fFilter) {
882 return CombineResult::kCannotCombine;
883 }
Greg Daniel549325c2019-10-30 16:19:20 -0400884 const auto& thisView = fViewCountPairs[0].fProxyView;
885 const auto& thatView = that->fViewCountPairs[0].fProxyView;
886 auto thisProxy = thisView.asTextureProxy();
887 auto thatProxy = thatView.asTextureProxy();
888 if (fProxyCnt > 1 || that->fProxyCnt > 1 || thisView != thatView) {
Brian Salomon588cec72018-11-14 13:56:37 -0500889 // We can't merge across different proxies. Check if 'this' can be chained with 'that'.
Greg Daniel45723ac2018-11-30 10:12:43 -0500890 if (GrTextureProxy::ProxiesAreCompatibleAsDynamicState(thisProxy, thatProxy) &&
Greg Daniel549325c2019-10-30 16:19:20 -0400891 caps.dynamicStateArrayGeometryProcessorTextureSupport() &&
892 thisView.swizzle() == thatView.swizzle() &&
893 thisView.origin() == thatView.origin()) {
Brian Salomonf7232642018-09-19 08:58:08 -0400894 return CombineResult::kMayChain;
895 }
Brian Salomon7eae3e02018-08-07 14:02:38 +0000896 return CombineResult::kCannotCombine;
Brian Salomon336ce7b2017-09-08 08:23:58 -0400897 }
Michael Ludwig009b92e2019-02-15 16:03:53 -0500898
Brian Salomonb80ffee2018-05-23 16:39:39 -0400899 fDomain |= that->fDomain;
Brian Salomon1d835422019-03-13 16:11:44 -0400900 fColorType = SkTMax(fColorType, that->fColorType);
Brian Salomonae7d7702018-10-14 15:05:45 -0400901 if (upgradeToCoverageAAOnMerge) {
902 fAAType = static_cast<unsigned>(GrAAType::kCoverage);
903 }
Michael Ludwig009b92e2019-02-15 16:03:53 -0500904
Michael Ludwig425eb452019-06-27 10:13:27 -0400905 // Concatenate quad lists together
Michael Ludwig009b92e2019-02-15 16:03:53 -0500906 fQuads.concat(that->fQuads);
Greg Daniel549325c2019-10-30 16:19:20 -0400907 fViewCountPairs[0].fQuadCnt += that->fQuads.count();
Robert Phillipsbbd459d2019-10-29 14:40:03 -0400908 fTotNumQuads += that->fQuads.count();
Michael Ludwig009b92e2019-02-15 16:03:53 -0500909
Brian Salomon7eae3e02018-08-07 14:02:38 +0000910 return CombineResult::kMerged;
Brian Salomon34169692017-08-28 15:32:01 -0400911 }
912
Brian Salomon485b8c62018-01-12 15:11:06 -0500913 GrAAType aaType() const { return static_cast<GrAAType>(fAAType); }
Brian Salomon0087c832018-10-15 14:48:20 -0400914 GrSamplerState::Filter filter() const { return static_cast<GrSamplerState::Filter>(fFilter); }
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500915
Michael Ludwig425eb452019-06-27 10:13:27 -0400916 GrQuadBuffer<ColorDomainAndAA> fQuads;
Brian Osman3ebd3542018-07-30 14:36:53 -0400917 sk_sp<GrColorSpaceXform> fTextureColorSpaceXform;
Robert Phillips32803ff2019-10-23 08:26:08 -0400918 // 'fPrePreparedDesc' is only filled in when this op has been prePrepared. In that case,
919 // it - and the matching dynamic and fixed state - have been allocated in the opPOD arena
920 // not in the FlushState arena.
921 PrePreparedDesc* fPrePreparedDesc;
Robert Phillipsbbd459d2019-10-29 14:40:03 -0400922 int fTotNumQuads = 0; // the total number of quads in this op (but not in the whole chain)
Brian Salomonf19f9ca2019-09-18 15:54:26 -0400923 unsigned fSaturate : 1;
Brian Salomon0087c832018-10-15 14:48:20 -0400924 unsigned fFilter : 2;
Brian Salomon485b8c62018-01-12 15:11:06 -0500925 unsigned fAAType : 2;
Brian Salomonb80ffee2018-05-23 16:39:39 -0400926 unsigned fDomain : 1;
Brian Salomon1d835422019-03-13 16:11:44 -0400927 unsigned fColorType : 2;
928 GR_STATIC_ASSERT(GrQuadPerEdgeAA::kColorTypeCount <= 4);
Robert Phillips32803ff2019-10-23 08:26:08 -0400929 unsigned fProxyCnt : 32 - 8;
930
931 // This field must go last. When allocating this op, we will allocate extra space to hold
Greg Daniel549325c2019-10-30 16:19:20 -0400932 // additional ViewCountPairs immediately after the op's allocation so we can treat this
Robert Phillips32803ff2019-10-23 08:26:08 -0400933 // as an fProxyCnt-length array.
Greg Daniel549325c2019-10-30 16:19:20 -0400934 ViewCountPair fViewCountPairs[1];
Brian Salomon336ce7b2017-09-08 08:23:58 -0400935
Michael Ludwigde4c58c2019-06-04 09:12:59 -0400936 static_assert(GrQuad::kTypeCount <= 4, "GrQuad::Type does not fit in 2 bits");
Michael Ludwigf995c052018-11-26 15:24:29 -0500937
Brian Salomon34169692017-08-28 15:32:01 -0400938 typedef GrMeshDrawOp INHERITED;
939};
940
941} // anonymous namespace
942
Robert Phillipse837e612019-11-15 11:02:50 -0500943#if GR_TEST_UTILS
944uint32_t GrTextureOp::ClassID() {
945 return TextureOp::ClassID();
946}
947#endif
Brian Salomon34169692017-08-28 15:32:01 -0400948
Robert Phillipse837e612019-11-15 11:02:50 -0500949std::unique_ptr<GrDrawOp> GrTextureOp::Make(GrRecordingContext* context,
950 GrSurfaceProxyView proxyView,
Brian Salomon078e8fa2019-11-22 04:10:18 +0000951 GrColorType srcColorType,
Robert Phillipse837e612019-11-15 11:02:50 -0500952 sk_sp<GrColorSpaceXform> textureXform,
953 GrSamplerState::Filter filter,
954 const SkPMColor4f& color,
955 Saturate saturate,
956 SkBlendMode blendMode,
957 GrAAType aaType,
958 GrQuadAAFlags aaFlags,
959 const GrQuad& deviceQuad,
960 const GrQuad& localQuad,
961 const SkRect* domain) {
Greg Daniel549325c2019-10-30 16:19:20 -0400962 GrTextureProxy* proxy = proxyView.asTextureProxy();
Michael Ludwig22429f92019-06-27 10:44:48 -0400963 // Apply optimizations that are valid whether or not using GrTextureOp or GrFillRectOp
Robert Phillipse9462dd2019-10-23 12:41:29 -0400964 if (domain && domain->contains(proxy->backingStoreBoundsRect())) {
Michael Ludwig22429f92019-06-27 10:44:48 -0400965 // No need for a shader-based domain if hardware clamping achieves the same effect
966 domain = nullptr;
967 }
968
969 if (filter != GrSamplerState::Filter::kNearest && !filter_has_effect(localQuad, deviceQuad)) {
970 filter = GrSamplerState::Filter::kNearest;
971 }
972
973 if (blendMode == SkBlendMode::kSrcOver) {
Greg Daniel549325c2019-10-30 16:19:20 -0400974 return TextureOp::Make(context, std::move(proxyView), std::move(textureXform), filter,
975 color, saturate, aaType, aaFlags, deviceQuad, localQuad, domain);
Michael Ludwig22429f92019-06-27 10:44:48 -0400976 } else {
977 // Emulate complex blending using GrFillRectOp
978 GrPaint paint;
979 paint.setColor4f(color);
980 paint.setXPFactory(SkBlendMode_AsXPFactory(blendMode));
981
982 std::unique_ptr<GrFragmentProcessor> fp;
983 if (domain) {
Michael Ludwig119ac6d2019-11-21 09:26:46 -0500984 // Update domain to match what GrTextureOp would do for bilerp, but don't do any
985 // normalization since GrTextureDomainEffect handles that and the origin.
986 SkRect correctedDomain = normalize_domain(filter, {1.f, 1.f, 0.f}, domain);
Brian Salomon078e8fa2019-11-22 04:10:18 +0000987 fp = GrTextureDomainEffect::Make(sk_ref_sp(proxy), srcColorType, SkMatrix::I(),
Michael Ludwig170de012019-11-15 21:55:18 +0000988 correctedDomain, GrTextureDomain::kClamp_Mode, filter);
Michael Ludwig22429f92019-06-27 10:44:48 -0400989 } else {
Brian Salomon078e8fa2019-11-22 04:10:18 +0000990 fp = GrSimpleTextureEffect::Make(sk_ref_sp(proxy), srcColorType, SkMatrix::I(), filter);
Michael Ludwig22429f92019-06-27 10:44:48 -0400991 }
992 fp = GrColorSpaceXformEffect::Make(std::move(fp), std::move(textureXform));
993 paint.addColorFragmentProcessor(std::move(fp));
Brian Salomonf19f9ca2019-09-18 15:54:26 -0400994 if (saturate == GrTextureOp::Saturate::kYes) {
995 paint.addColorFragmentProcessor(GrSaturateProcessor::Make());
996 }
Michael Ludwig22429f92019-06-27 10:44:48 -0400997
998 return GrFillRectOp::Make(context, std::move(paint), aaType, aaFlags,
999 deviceQuad, localQuad);
1000 }
1001}
1002
Robert Phillipse837e612019-11-15 11:02:50 -05001003// A helper class that assists in breaking up bulk API quad draws into manageable chunks.
1004class GrTextureOp::BatchSizeLimiter {
1005public:
1006 BatchSizeLimiter(GrRenderTargetContext* rtc,
1007 const GrClip& clip,
1008 GrRecordingContext* context,
1009 int numEntries,
1010 GrSamplerState::Filter filter,
1011 GrTextureOp::Saturate saturate,
1012 SkCanvas::SrcRectConstraint constraint,
1013 const SkMatrix& viewMatrix,
1014 sk_sp<GrColorSpaceXform> textureColorSpaceXform)
1015 : fRTC(rtc)
1016 , fClip(clip)
1017 , fContext(context)
1018 , fFilter(filter)
1019 , fSaturate(saturate)
1020 , fConstraint(constraint)
1021 , fViewMatrix(viewMatrix)
1022 , fTextureColorSpaceXform(textureColorSpaceXform)
1023 , fNumLeft(numEntries) {
1024 }
Brian Salomon34169692017-08-28 15:32:01 -04001025
Robert Phillipse837e612019-11-15 11:02:50 -05001026 void createOp(const GrRenderTargetContext::TextureSetEntry set[],
1027 int clumpSize,
1028 GrAAType aaType) {
1029 std::unique_ptr<GrDrawOp> op = TextureOp::Make(fContext, &set[fNumClumped], clumpSize,
1030 fFilter, fSaturate, aaType,
1031 fConstraint, fViewMatrix,
1032 fTextureColorSpaceXform);
1033 fRTC->addDrawOp(fClip, std::move(op));
1034
1035 fNumLeft -= clumpSize;
1036 fNumClumped += clumpSize;
1037 }
1038
1039 int numLeft() const { return fNumLeft; }
1040 int baseIndex() const { return fNumClumped; }
1041
1042private:
1043 GrRenderTargetContext* fRTC;
1044 const GrClip& fClip;
1045 GrRecordingContext* fContext;
1046 GrSamplerState::Filter fFilter;
1047 GrTextureOp::Saturate fSaturate;
1048 SkCanvas::SrcRectConstraint fConstraint;
1049 const SkMatrix& fViewMatrix;
1050 sk_sp<GrColorSpaceXform> fTextureColorSpaceXform;
1051
1052 int fNumLeft;
1053 int fNumClumped = 0; // also the offset for the start of the next clump
1054};
1055
1056// Greedily clump quad draws together until the index buffer limit is exceeded.
1057void GrTextureOp::CreateTextureSetOps(GrRenderTargetContext* rtc,
1058 const GrClip& clip,
1059 GrRecordingContext* context,
1060 const GrRenderTargetContext::TextureSetEntry set[],
1061 int cnt,
1062 GrSamplerState::Filter filter,
1063 Saturate saturate,
1064 GrAAType aaType,
1065 SkCanvas::SrcRectConstraint constraint,
1066 const SkMatrix& viewMatrix,
1067 sk_sp<GrColorSpaceXform> textureColorSpaceXform) {
1068
1069 // First check if we can always just make a single op and avoid the extra iteration
1070 // needed to clump things together.
1071 if (cnt <= SkTMin(GrResourceProvider::MaxNumNonAAQuads(),
1072 GrResourceProvider::MaxNumAAQuads())) {
1073 auto op = TextureOp::Make(context, set, cnt, filter, saturate, aaType,
1074 constraint, viewMatrix, std::move(textureColorSpaceXform));
1075 rtc->addDrawOp(clip, std::move(op));
1076 return;
1077 }
1078
1079 BatchSizeLimiter state(rtc, clip, context, cnt, filter, saturate, constraint, viewMatrix,
1080 std::move(textureColorSpaceXform));
1081
1082 // kNone and kMSAA never get altered
1083 if (aaType == GrAAType::kNone || aaType == GrAAType::kMSAA) {
1084 // Clump these into series of MaxNumNonAAQuads-sized GrTextureOps
1085 while (state.numLeft() > 0) {
1086 int clumpSize = SkTMin(state.numLeft(), GrResourceProvider::MaxNumNonAAQuads());
1087
1088 state.createOp(set, clumpSize, aaType);
1089 }
1090 } else {
1091 // kCoverage can be downgraded to kNone. Note that the following is conservative. kCoverage
1092 // can also get downgraded to kNone if all the quads are on integer coordinates and
1093 // axis-aligned.
1094 SkASSERT(aaType == GrAAType::kCoverage);
1095
1096 while (state.numLeft() > 0) {
1097 GrAAType runningAA = GrAAType::kNone;
1098 bool clumped = false;
1099
1100 for (int i = 0; i < state.numLeft(); ++i) {
1101 int absIndex = state.baseIndex() + i;
1102
1103 if (set[absIndex].fAAFlags != GrQuadAAFlags::kNone) {
1104
1105 if (i >= GrResourceProvider::MaxNumAAQuads()) {
1106 // Here we either need to boost the AA type to kCoverage, but doing so with
1107 // all the accumulated quads would overflow, or we have a set of AA quads
1108 // that has just gotten too large. In either case, calve off the existing
1109 // quads as their own TextureOp.
1110 state.createOp(
1111 set,
1112 runningAA == GrAAType::kNone ? i : GrResourceProvider::MaxNumAAQuads(),
1113 runningAA); // maybe downgrading AA here
1114 clumped = true;
1115 break;
1116 }
1117
1118 runningAA = GrAAType::kCoverage;
1119 } else if (runningAA == GrAAType::kNone) {
1120
1121 if (i >= GrResourceProvider::MaxNumNonAAQuads()) {
1122 // Here we've found a consistent batch of non-AA quads that has gotten too
1123 // large. Calve it off as its own GrTextureOp.
1124 state.createOp(set, GrResourceProvider::MaxNumNonAAQuads(),
1125 GrAAType::kNone); // definitely downgrading AA here
1126 clumped = true;
1127 break;
1128 }
1129 }
1130 }
1131
1132 if (!clumped) {
1133 // We ran through the above loop w/o hitting a limit. Spit out this last clump of
1134 // quads and call it a day.
1135 state.createOp(set, state.numLeft(), runningAA); // maybe downgrading AA here
1136 }
1137 }
1138 }
1139}
Robert Phillipsae01f622019-11-13 15:56:31 +00001140
Brian Salomon34169692017-08-28 15:32:01 -04001141#if GR_TEST_UTILS
Mike Kleinc0bd9f92019-04-23 12:05:21 -05001142#include "include/private/GrRecordingContext.h"
1143#include "src/gpu/GrProxyProvider.h"
1144#include "src/gpu/GrRecordingContextPriv.h"
Brian Salomon34169692017-08-28 15:32:01 -04001145
1146GR_DRAW_OP_TEST_DEFINE(TextureOp) {
1147 GrSurfaceDesc desc;
1148 desc.fConfig = kRGBA_8888_GrPixelConfig;
1149 desc.fHeight = random->nextULessThan(90) + 10;
1150 desc.fWidth = random->nextULessThan(90) + 10;
Brian Salomon2a4f9832018-03-03 22:43:43 -05001151 auto origin = random->nextBool() ? kTopLeft_GrSurfaceOrigin : kBottomLeft_GrSurfaceOrigin;
Greg Daniel09c94002018-06-08 22:11:51 +00001152 GrMipMapped mipMapped = random->nextBool() ? GrMipMapped::kYes : GrMipMapped::kNo;
1153 SkBackingFit fit = SkBackingFit::kExact;
1154 if (mipMapped == GrMipMapped::kNo) {
1155 fit = random->nextBool() ? SkBackingFit::kApprox : SkBackingFit::kExact;
1156 }
Greg Daniel4065d452018-11-16 15:43:41 -05001157 const GrBackendFormat format =
Robert Phillips0a15cc62019-07-30 12:49:10 -04001158 context->priv().caps()->getDefaultBackendFormat(GrColorType::kRGBA_8888,
1159 GrRenderable::kNo);
Greg Daniel4065d452018-11-16 15:43:41 -05001160
Robert Phillips9da87e02019-02-04 13:26:26 -05001161 GrProxyProvider* proxyProvider = context->priv().proxyProvider();
Brian Salomone8a766b2019-07-19 14:24:36 -04001162 sk_sp<GrTextureProxy> proxy = proxyProvider->createProxy(
Brian Salomon27b4d8d2019-07-22 14:23:45 -04001163 format, desc, GrRenderable::kNo, 1, origin, mipMapped, fit, SkBudgeted::kNo,
Brian Salomone8a766b2019-07-19 14:24:36 -04001164 GrProtected::kNo, GrInternalSurfaceFlags::kNone);
Robert Phillips0bd24dc2018-01-16 08:06:32 -05001165
Brian Salomon34169692017-08-28 15:32:01 -04001166 SkRect rect = GrTest::TestRect(random);
1167 SkRect srcRect;
1168 srcRect.fLeft = random->nextRangeScalar(0.f, proxy->width() / 2.f);
1169 srcRect.fRight = random->nextRangeScalar(0.f, proxy->width()) + proxy->width() / 2.f;
1170 srcRect.fTop = random->nextRangeScalar(0.f, proxy->height() / 2.f);
1171 srcRect.fBottom = random->nextRangeScalar(0.f, proxy->height()) + proxy->height() / 2.f;
1172 SkMatrix viewMatrix = GrTest::TestMatrixPreservesRightAngles(random);
Brian Osman3d139a42018-11-19 10:42:10 -05001173 SkPMColor4f color = SkPMColor4f::FromBytes_RGBA(SkColorToPremulGrColor(random->nextU()));
Brian Salomon2bbdcc42017-09-07 12:36:34 -04001174 GrSamplerState::Filter filter = (GrSamplerState::Filter)random->nextULessThan(
1175 static_cast<uint32_t>(GrSamplerState::Filter::kMipMap) + 1);
Greg Daniel09c94002018-06-08 22:11:51 +00001176 while (mipMapped == GrMipMapped::kNo && filter == GrSamplerState::Filter::kMipMap) {
1177 filter = (GrSamplerState::Filter)random->nextULessThan(
1178 static_cast<uint32_t>(GrSamplerState::Filter::kMipMap) + 1);
1179 }
Brian Osman3ebd3542018-07-30 14:36:53 -04001180 auto texXform = GrTest::TestColorXform(random);
Brian Salomon485b8c62018-01-12 15:11:06 -05001181 GrAAType aaType = GrAAType::kNone;
1182 if (random->nextBool()) {
Chris Dalton6ce447a2019-06-23 18:07:38 -06001183 aaType = (numSamples > 1) ? GrAAType::kMSAA : GrAAType::kCoverage;
Brian Salomon485b8c62018-01-12 15:11:06 -05001184 }
Brian Salomon2213ee92018-10-02 10:44:21 -04001185 GrQuadAAFlags aaFlags = GrQuadAAFlags::kNone;
1186 aaFlags |= random->nextBool() ? GrQuadAAFlags::kLeft : GrQuadAAFlags::kNone;
1187 aaFlags |= random->nextBool() ? GrQuadAAFlags::kTop : GrQuadAAFlags::kNone;
1188 aaFlags |= random->nextBool() ? GrQuadAAFlags::kRight : GrQuadAAFlags::kNone;
1189 aaFlags |= random->nextBool() ? GrQuadAAFlags::kBottom : GrQuadAAFlags::kNone;
Michael Ludwig205224f2019-06-27 10:47:42 -04001190 bool useDomain = random->nextBool();
Brian Salomonf19f9ca2019-09-18 15:54:26 -04001191 auto saturate = random->nextBool() ? GrTextureOp::Saturate::kYes : GrTextureOp::Saturate::kNo;
Greg Daniel549325c2019-10-30 16:19:20 -04001192 GrSurfaceProxyView proxyView(
1193 std::move(proxy), origin,
1194 context->priv().caps()->getTextureSwizzle(format, GrColorType::kRGBA_8888));
1195
Brian Salomon078e8fa2019-11-22 04:10:18 +00001196 return GrTextureOp::Make(context, std::move(proxyView), GrColorType::kRGBA_8888,
1197 std::move(texXform), filter, color, saturate, SkBlendMode::kSrcOver,
1198 aaType, aaFlags, GrQuad::MakeFromRect(rect, viewMatrix),
1199 GrQuad(srcRect), useDomain ? &srcRect : nullptr);
Brian Salomon34169692017-08-28 15:32:01 -04001200}
1201
1202#endif