blob: a8dd784016d219ebc7f78aac12909cfa976a115e [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
8#include "GrTextureOp.h"
Hal Canaryc640d0d2018-06-13 09:59:02 -04009
Brian Salomon34169692017-08-28 15:32:01 -040010#include "GrAppliedClip.h"
Brian Salomon336ce7b2017-09-08 08:23:58 -040011#include "GrCaps.h"
Robert Phillips7c525e62018-06-12 10:11:12 -040012#include "GrContext.h"
13#include "GrContextPriv.h"
Brian Salomon34169692017-08-28 15:32:01 -040014#include "GrDrawOpTest.h"
15#include "GrGeometryProcessor.h"
Robert Phillips7c525e62018-06-12 10:11:12 -040016#include "GrMemoryPool.h"
Brian Salomon34169692017-08-28 15:32:01 -040017#include "GrMeshDrawOp.h"
18#include "GrOpFlushState.h"
19#include "GrQuad.h"
20#include "GrResourceProvider.h"
21#include "GrShaderCaps.h"
22#include "GrTexture.h"
Brian Salomon336ce7b2017-09-08 08:23:58 -040023#include "GrTexturePriv.h"
Brian Salomon34169692017-08-28 15:32:01 -040024#include "GrTextureProxy.h"
25#include "SkGr.h"
Brian Salomon336ce7b2017-09-08 08:23:58 -040026#include "SkMathPriv.h"
Brian Salomona33b67c2018-05-17 10:42:14 -040027#include "SkMatrixPriv.h"
Brian Salomonb5ef1f92018-01-11 11:46:21 -050028#include "SkPoint.h"
29#include "SkPoint3.h"
Hal Canaryc640d0d2018-06-13 09:59:02 -040030#include "SkTo.h"
Brian Salomon34169692017-08-28 15:32:01 -040031#include "glsl/GrGLSLColorSpaceXformHelper.h"
Brian Salomonb5ef1f92018-01-11 11:46:21 -050032#include "glsl/GrGLSLFragmentShaderBuilder.h"
Brian Salomon34169692017-08-28 15:32:01 -040033#include "glsl/GrGLSLGeometryProcessor.h"
34#include "glsl/GrGLSLVarying.h"
Brian Salomonb5ef1f92018-01-11 11:46:21 -050035#include "glsl/GrGLSLVertexGeoBuilder.h"
Mike Klein79aea6a2018-06-11 10:45:26 -040036#include <new>
Brian Salomon34169692017-08-28 15:32:01 -040037
38namespace {
39
Brian Salomonb80ffee2018-05-23 16:39:39 -040040enum class Domain : bool { kNo = false, kYes = true };
41
Brian Salomon34169692017-08-28 15:32:01 -040042/**
43 * Geometry Processor that draws a texture modulated by a vertex color (though, this is meant to be
44 * the same value across all vertices of a quad and uses flat interpolation when available). This is
45 * used by TextureOp below.
46 */
47class TextureGeometryProcessor : public GrGeometryProcessor {
48public:
Brian Salomon17031a72018-05-22 14:14:07 -040049 template <typename Pos> struct VertexCommon {
50 using Position = Pos;
51 Position fPosition;
Brian Salomon34169692017-08-28 15:32:01 -040052 GrColor fColor;
Brian Salomon17031a72018-05-22 14:14:07 -040053 SkPoint fTextureCoords;
Brian Salomon34169692017-08-28 15:32:01 -040054 };
Brian Salomon17031a72018-05-22 14:14:07 -040055
Brian Salomon7eae3e02018-08-07 14:02:38 +000056 template <typename Pos, Domain D> struct OptionalDomainVertex;
Brian Salomon17031a72018-05-22 14:14:07 -040057 template <typename Pos>
Brian Salomon7eae3e02018-08-07 14:02:38 +000058 struct OptionalDomainVertex<Pos, Domain::kNo> : VertexCommon<Pos> {
Brian Salomonfdf05f42018-08-06 17:51:35 -040059 static constexpr Domain kDomain = Domain::kNo;
60 };
Brian Salomon7eae3e02018-08-07 14:02:38 +000061 template <typename Pos>
62 struct OptionalDomainVertex<Pos, Domain::kYes> : VertexCommon<Pos> {
Brian Salomonb80ffee2018-05-23 16:39:39 -040063 static constexpr Domain kDomain = Domain::kYes;
64 SkRect fTextureDomain;
65 };
66
Brian Salomon7eae3e02018-08-07 14:02:38 +000067 template <typename Pos, Domain D, GrAA> struct OptionalAAVertex;
68 template <typename Pos, Domain D>
69 struct OptionalAAVertex<Pos, D, GrAA::kNo> : OptionalDomainVertex<Pos, D> {
Brian Salomonb80ffee2018-05-23 16:39:39 -040070 static constexpr GrAA kAA = GrAA::kNo;
71 };
Brian Salomon7eae3e02018-08-07 14:02:38 +000072 template <typename Pos, Domain D>
73 struct OptionalAAVertex<Pos, D, GrAA::kYes> : OptionalDomainVertex<Pos, D> {
Brian Salomonbe3c1d22018-05-21 12:54:39 -040074 static constexpr GrAA kAA = GrAA::kYes;
Brian Salomonb5ef1f92018-01-11 11:46:21 -050075 SkPoint3 fEdges[4];
Brian Salomonb5ef1f92018-01-11 11:46:21 -050076 };
Brian Salomon336ce7b2017-09-08 08:23:58 -040077
Brian Salomon7eae3e02018-08-07 14:02:38 +000078 template <typename Pos, Domain D, GrAA AA>
79 using Vertex = OptionalAAVertex<Pos, D, AA>;
Brian Salomon336ce7b2017-09-08 08:23:58 -040080
Brian Salomon7eae3e02018-08-07 14:02:38 +000081 static sk_sp<GrGeometryProcessor> Make(GrTextureType textureType, GrPixelConfig textureConfig,
82 const GrSamplerState::Filter filter,
Brian Osman3ebd3542018-07-30 14:36:53 -040083 sk_sp<GrColorSpaceXform> textureColorSpaceXform,
84 sk_sp<GrColorSpaceXform> paintColorSpaceXform,
Brian Salomon7eae3e02018-08-07 14:02:38 +000085 bool coverageAA, bool perspective, Domain domain,
Brian Salomon336ce7b2017-09-08 08:23:58 -040086 const GrShaderCaps& caps) {
Brian Salomon7eae3e02018-08-07 14:02:38 +000087 return sk_sp<TextureGeometryProcessor>(new TextureGeometryProcessor(
88 textureType, textureConfig, filter, std::move(textureColorSpaceXform),
89 std::move(paintColorSpaceXform), coverageAA, perspective, domain, caps));
Brian Salomon34169692017-08-28 15:32:01 -040090 }
91
92 const char* name() const override { return "TextureGeometryProcessor"; }
93
94 void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const override {
Brian Osman3ebd3542018-07-30 14:36:53 -040095 b->add32(GrColorSpaceXform::XformKey(fTextureColorSpaceXform.get()));
96 b->add32(GrColorSpaceXform::XformKey(fPaintColorSpaceXform.get()));
Brian Salomonbe3c1d22018-05-21 12:54:39 -040097 uint32_t x = this->usesCoverageEdgeAA() ? 0 : 1;
Brian Salomon70132d02018-05-29 15:33:06 -040098 x |= kFloat3_GrVertexAttribType == fPositions.type() ? 0 : 2;
Brian Salomonb80ffee2018-05-23 16:39:39 -040099 x |= fDomain.isInitialized() ? 4 : 0;
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400100 b->add32(x);
Brian Salomon34169692017-08-28 15:32:01 -0400101 }
102
103 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps& caps) const override {
104 class GLSLProcessor : public GrGLSLGeometryProcessor {
105 public:
106 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& proc,
107 FPCoordTransformIter&& transformIter) override {
108 const auto& textureGP = proc.cast<TextureGeometryProcessor>();
109 this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
Brian Osman3ebd3542018-07-30 14:36:53 -0400110 fTextureColorSpaceXformHelper.setData(
111 pdman, textureGP.fTextureColorSpaceXform.get());
112 fPaintColorSpaceXformHelper.setData(pdman, textureGP.fPaintColorSpaceXform.get());
Brian Salomon34169692017-08-28 15:32:01 -0400113 }
114
115 private:
116 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
Chris Dalton7b046312018-02-02 11:06:30 -0700117 using Interpolation = GrGLSLVaryingHandler::Interpolation;
Brian Salomon34169692017-08-28 15:32:01 -0400118 const auto& textureGP = args.fGP.cast<TextureGeometryProcessor>();
Brian Osman3ebd3542018-07-30 14:36:53 -0400119 fTextureColorSpaceXformHelper.emitCode(
120 args.fUniformHandler, textureGP.fTextureColorSpaceXform.get());
121 fPaintColorSpaceXformHelper.emitCode(
122 args.fUniformHandler, textureGP.fPaintColorSpaceXform.get(),
123 kVertex_GrShaderFlag);
Brian Salomon70132d02018-05-29 15:33:06 -0400124 if (kFloat2_GrVertexAttribType == textureGP.fPositions.type()) {
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400125 args.fVaryingHandler->setNoPerspective();
126 }
Brian Salomon34169692017-08-28 15:32:01 -0400127 args.fVaryingHandler->emitAttributes(textureGP);
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400128 gpArgs->fPositionVar = textureGP.fPositions.asShaderVar();
129
Brian Salomon34169692017-08-28 15:32:01 -0400130 this->emitTransforms(args.fVertBuilder,
131 args.fVaryingHandler,
132 args.fUniformHandler,
Brian Salomon04460cc2017-12-06 14:47:42 -0500133 textureGP.fTextureCoords.asShaderVar(),
Brian Salomon34169692017-08-28 15:32:01 -0400134 args.fFPCoordTransformHandler);
Brian Osman3ebd3542018-07-30 14:36:53 -0400135 if (fPaintColorSpaceXformHelper.isNoop()) {
136 args.fVaryingHandler->addPassThroughAttribute(
137 textureGP.fColors, args.fOutputColor, Interpolation::kCanBeFlat);
138 } else {
139 GrGLSLVarying varying(kHalf4_GrSLType);
140 args.fVaryingHandler->addVarying("color", &varying);
141 args.fVertBuilder->codeAppend("half4 color = ");
142 args.fVertBuilder->appendColorGamutXform(textureGP.fColors.name(),
143 &fPaintColorSpaceXformHelper);
144 args.fVertBuilder->codeAppend(";");
145 args.fVertBuilder->codeAppendf("%s = half4(color.rgb * color.a, color.a);",
146 varying.vsOut());
147 args.fFragBuilder->codeAppendf("%s = %s;", args.fOutputColor, varying.fsIn());
148 }
Ethan Nicholas8aa45692017-09-20 11:24:15 -0400149 args.fFragBuilder->codeAppend("float2 texCoord;");
Brian Salomon92be2f72018-06-19 14:33:47 -0400150 args.fVaryingHandler->addPassThroughAttribute(textureGP.fTextureCoords, "texCoord");
Brian Salomonb80ffee2018-05-23 16:39:39 -0400151 if (textureGP.fDomain.isInitialized()) {
152 args.fFragBuilder->codeAppend("float4 domain;");
153 args.fVaryingHandler->addPassThroughAttribute(
Brian Salomon92be2f72018-06-19 14:33:47 -0400154 textureGP.fDomain, "domain",
Brian Salomonb80ffee2018-05-23 16:39:39 -0400155 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
156 args.fFragBuilder->codeAppend(
157 "texCoord = clamp(texCoord, domain.xy, domain.zw);");
158 }
Brian Salomon7eae3e02018-08-07 14:02:38 +0000159 args.fFragBuilder->codeAppendf("%s = ", args.fOutputColor);
160 args.fFragBuilder->appendTextureLookupAndModulate(
161 args.fOutputColor, args.fTexSamplers[0], "texCoord", kFloat2_GrSLType,
162 &fTextureColorSpaceXformHelper);
Brian Salomon34169692017-08-28 15:32:01 -0400163 args.fFragBuilder->codeAppend(";");
Brian Salomon485b8c62018-01-12 15:11:06 -0500164 if (textureGP.usesCoverageEdgeAA()) {
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400165 bool mulByFragCoordW = false;
Brian Salomon9b4bd592018-07-10 09:23:40 -0400166 GrGLSLVarying aaDistVarying(kFloat4_GrSLType,
167 GrGLSLVarying::Scope::kVertToFrag);
168 if (kFloat3_GrVertexAttribType == textureGP.fPositions.type()) {
169 args.fVaryingHandler->addVarying("aaDists", &aaDistVarying);
170 // The distance from edge equation e to homogenous point p=sk_Position
171 // is e.x*p.x/p.wx + e.y*p.y/p.w + e.z. However, we want screen space
172 // interpolation of this distance. We can do this by multiplying the
173 // varying in the VS by p.w and then multiplying by sk_FragCoord.w in
174 // the FS. So we output e.x*p.x + e.y*p.y + e.z * p.w
175 args.fVertBuilder->codeAppendf(
176 R"(%s = float4(dot(aaEdge0, %s), dot(aaEdge1, %s),
177 dot(aaEdge2, %s), dot(aaEdge3, %s));)",
178 aaDistVarying.vsOut(), textureGP.fPositions.name(),
179 textureGP.fPositions.name(), textureGP.fPositions.name(),
180 textureGP.fPositions.name());
181 mulByFragCoordW = true;
Brian Salomondba65f92018-01-22 08:43:38 -0500182 } else {
Brian Salomon9b4bd592018-07-10 09:23:40 -0400183 args.fVaryingHandler->addVarying("aaDists", &aaDistVarying);
184 args.fVertBuilder->codeAppendf(
185 R"(%s = float4(dot(aaEdge0.xy, %s.xy) + aaEdge0.z,
186 dot(aaEdge1.xy, %s.xy) + aaEdge1.z,
187 dot(aaEdge2.xy, %s.xy) + aaEdge2.z,
188 dot(aaEdge3.xy, %s.xy) + aaEdge3.z);)",
189 aaDistVarying.vsOut(), textureGP.fPositions.name(),
190 textureGP.fPositions.name(), textureGP.fPositions.name(),
191 textureGP.fPositions.name());
Brian Salomondba65f92018-01-22 08:43:38 -0500192 }
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500193 args.fFragBuilder->codeAppendf(
194 "float mindist = min(min(%s.x, %s.y), min(%s.z, %s.w));",
Brian Salomon9b4bd592018-07-10 09:23:40 -0400195 aaDistVarying.fsIn(), aaDistVarying.fsIn(), aaDistVarying.fsIn(),
196 aaDistVarying.fsIn());
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400197 if (mulByFragCoordW) {
198 args.fFragBuilder->codeAppend("mindist *= sk_FragCoord.w;");
199 }
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400200 args.fFragBuilder->codeAppendf("%s = float4(saturate(mindist));",
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500201 args.fOutputCoverage);
202 } else {
203 args.fFragBuilder->codeAppendf("%s = float4(1);", args.fOutputCoverage);
204 }
Brian Salomon34169692017-08-28 15:32:01 -0400205 }
Brian Osman3ebd3542018-07-30 14:36:53 -0400206 GrGLSLColorSpaceXformHelper fTextureColorSpaceXformHelper;
207 GrGLSLColorSpaceXformHelper fPaintColorSpaceXformHelper;
Brian Salomon34169692017-08-28 15:32:01 -0400208 };
209 return new GLSLProcessor;
210 }
211
Brian Salomon485b8c62018-01-12 15:11:06 -0500212 bool usesCoverageEdgeAA() const { return SkToBool(fAAEdges[0].isInitialized()); }
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500213
Brian Salomon34169692017-08-28 15:32:01 -0400214private:
Brian Salomon7eae3e02018-08-07 14:02:38 +0000215 TextureGeometryProcessor(GrTextureType textureType, GrPixelConfig textureConfig,
216 GrSamplerState::Filter filter,
Brian Salomon986f64c2018-08-06 15:25:15 -0400217 sk_sp<GrColorSpaceXform> textureColorSpaceXform,
Brian Salomon7eae3e02018-08-07 14:02:38 +0000218 sk_sp<GrColorSpaceXform> paintColorSpaceXform, bool coverageAA,
219 bool perspective, Domain domain, const GrShaderCaps& caps)
Brian Osman3ebd3542018-07-30 14:36:53 -0400220 : INHERITED(kTextureGeometryProcessor_ClassID)
221 , fTextureColorSpaceXform(std::move(textureColorSpaceXform))
Brian Salomon7eae3e02018-08-07 14:02:38 +0000222 , fPaintColorSpaceXform(std::move(paintColorSpaceXform))
223 , fSampler(textureType, textureConfig, filter) {
224 this->setTextureSamplerCnt(1);
Brian Salomon30e1a5e2018-05-18 12:32:32 -0400225
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400226 if (perspective) {
Brian Salomon92be2f72018-06-19 14:33:47 -0400227 fPositions = {"position", kFloat3_GrVertexAttribType};
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400228 } else {
Brian Salomon92be2f72018-06-19 14:33:47 -0400229 fPositions = {"position", kFloat2_GrVertexAttribType};
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400230 }
Brian Salomon92be2f72018-06-19 14:33:47 -0400231 fColors = {"color", kUByte4_norm_GrVertexAttribType};
232 fTextureCoords = {"textureCoords", kFloat2_GrVertexAttribType};
233 int vertexAttributeCnt = 3;
Brian Salomon30e1a5e2018-05-18 12:32:32 -0400234
Brian Salomonb80ffee2018-05-23 16:39:39 -0400235 if (domain == Domain::kYes) {
Brian Salomon92be2f72018-06-19 14:33:47 -0400236 fDomain = {"domain", kFloat4_GrVertexAttribType};
237 ++vertexAttributeCnt;
Brian Salomonb80ffee2018-05-23 16:39:39 -0400238 }
Brian Salomon485b8c62018-01-12 15:11:06 -0500239 if (coverageAA) {
Brian Salomon92be2f72018-06-19 14:33:47 -0400240 fAAEdges[0] = {"aaEdge0", kFloat3_GrVertexAttribType};
241 fAAEdges[1] = {"aaEdge1", kFloat3_GrVertexAttribType};
242 fAAEdges[2] = {"aaEdge2", kFloat3_GrVertexAttribType};
243 fAAEdges[3] = {"aaEdge3", kFloat3_GrVertexAttribType};
244 vertexAttributeCnt += 4;
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500245 }
Brian Salomon92be2f72018-06-19 14:33:47 -0400246 this->setVertexAttributeCnt(vertexAttributeCnt);
247 }
248
249 const Attribute& onVertexAttribute(int i) const override {
Brian Salomon7eae3e02018-08-07 14:02:38 +0000250 return IthInitializedAttribute(i, fPositions, fColors, fTextureCoords, fDomain, fAAEdges[0],
251 fAAEdges[1], fAAEdges[2], fAAEdges[3]);
Brian Salomon34169692017-08-28 15:32:01 -0400252 }
253
Brian Salomon7eae3e02018-08-07 14:02:38 +0000254 const TextureSampler& onTextureSampler(int) const override { return fSampler; }
Brian Salomonf7dcd762018-07-30 14:48:15 -0400255
Brian Salomon34169692017-08-28 15:32:01 -0400256 Attribute fPositions;
Brian Salomon34169692017-08-28 15:32:01 -0400257 Attribute fColors;
Brian Salomon30e1a5e2018-05-18 12:32:32 -0400258 Attribute fTextureCoords;
Brian Salomonb80ffee2018-05-23 16:39:39 -0400259 Attribute fDomain;
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500260 Attribute fAAEdges[4];
Brian Osman3ebd3542018-07-30 14:36:53 -0400261 sk_sp<GrColorSpaceXform> fTextureColorSpaceXform;
262 sk_sp<GrColorSpaceXform> fPaintColorSpaceXform;
Brian Salomon7eae3e02018-08-07 14:02:38 +0000263 TextureSampler fSampler;
Ethan Nicholasabff9562017-10-09 10:54:08 -0400264
265 typedef GrGeometryProcessor INHERITED;
Brian Salomon34169692017-08-28 15:32:01 -0400266};
267
Brian Salomon6872e942018-05-18 10:29:54 -0400268// This computes the four edge equations for a quad, then outsets them and computes a new quad
269// as the intersection points of the outset edges. 'x' and 'y' contain the original points as input
270// and the outset points as output. 'a', 'b', and 'c' are the edge equation coefficients on output.
271static void compute_quad_edges_and_outset_vertices(Sk4f* x, Sk4f* y, Sk4f* a, Sk4f* b, Sk4f* c) {
272 static constexpr auto fma = SkNx_fma<4, float>;
273 // These rotate the points/edge values either clockwise or counterclockwise assuming tri strip
274 // order.
275 auto nextCW = [](const Sk4f& v) { return SkNx_shuffle<2, 0, 3, 1>(v); };
276 auto nextCCW = [](const Sk4f& v) { return SkNx_shuffle<1, 3, 0, 2>(v); };
277
278 auto xnext = nextCCW(*x);
279 auto ynext = nextCCW(*y);
280 *a = ynext - *y;
281 *b = *x - xnext;
282 *c = fma(xnext, *y, -ynext * *x);
283 Sk4f invNormLengths = (*a * *a + *b * *b).rsqrt();
284 // Make sure the edge equations have their normals facing into the quad in device space.
285 auto test = fma(*a, nextCW(*x), fma(*b, nextCW(*y), *c));
286 if ((test < Sk4f(0)).anyTrue()) {
287 invNormLengths = -invNormLengths;
288 }
289 *a *= invNormLengths;
290 *b *= invNormLengths;
291 *c *= invNormLengths;
292
293 // Here is the outset. This makes our edge equations compute coverage without requiring a
294 // half pixel offset and is also used to compute the bloated quad that will cover all
295 // pixels.
296 *c += Sk4f(0.5f);
297
298 // Reverse the process to compute the points of the bloated quad from the edge equations.
299 // This time the inputs don't have 1s as their third coord and we want to homogenize rather
300 // than normalize.
301 auto anext = nextCW(*a);
302 auto bnext = nextCW(*b);
303 auto cnext = nextCW(*c);
304 *x = fma(bnext, *c, -*b * cnext);
305 *y = fma(*a, cnext, -anext * *c);
306 auto ic = (fma(anext, *b, -bnext * *a)).invert();
307 *x *= ic;
308 *y *= ic;
309}
310
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500311namespace {
312// This is a class soley so it can be partially specialized (functions cannot be).
Brian Salomon86c40012018-05-22 10:48:49 -0400313template <typename V, GrAA AA = V::kAA, typename Position = typename V::Position>
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400314class VertexAAHandler;
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500315
Brian Salomon86c40012018-05-22 10:48:49 -0400316template<typename V> class VertexAAHandler<V, GrAA::kNo, SkPoint> {
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500317public:
Brian Salomon86c40012018-05-22 10:48:49 -0400318 static void AssignPositionsAndTexCoords(V* vertices, const GrPerspQuad& quad,
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500319 const SkRect& texRect) {
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400320 SkASSERT((quad.w4f() == Sk4f(1.f)).allTrue());
Brian Salomon86c40012018-05-22 10:48:49 -0400321 SkPointPriv::SetRectTriStrip(&vertices[0].fTextureCoords, texRect, sizeof(V));
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400322 for (int i = 0; i < 4; ++i) {
323 vertices[i].fPosition = {quad.x(i), quad.y(i)};
324 }
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500325 }
326};
327
Brian Salomon86c40012018-05-22 10:48:49 -0400328template<typename V> class VertexAAHandler<V, GrAA::kNo, SkPoint3> {
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500329public:
Brian Salomon86c40012018-05-22 10:48:49 -0400330 static void AssignPositionsAndTexCoords(V* vertices, const GrPerspQuad& quad,
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500331 const SkRect& texRect) {
Brian Salomon86c40012018-05-22 10:48:49 -0400332 SkPointPriv::SetRectTriStrip(&vertices[0].fTextureCoords, texRect, sizeof(V));
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400333 for (int i = 0; i < 4; ++i) {
334 vertices[i].fPosition = quad.point(i);
335 }
336 }
337};
338
Brian Salomon86c40012018-05-22 10:48:49 -0400339template<typename V> class VertexAAHandler<V, GrAA::kYes, SkPoint> {
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400340public:
Brian Salomon86c40012018-05-22 10:48:49 -0400341 static void AssignPositionsAndTexCoords(V* vertices, const GrPerspQuad& quad,
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400342 const SkRect& texRect) {
343 SkASSERT((quad.w4f() == Sk4f(1.f)).allTrue());
Brian Salomon6872e942018-05-18 10:29:54 -0400344 auto x = quad.x4f();
345 auto y = quad.y4f();
346 Sk4f a, b, c;
347 compute_quad_edges_and_outset_vertices(&x, &y, &a, &b, &c);
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500348
349 for (int i = 0; i < 4; ++i) {
Brian Salomon6872e942018-05-18 10:29:54 -0400350 vertices[i].fPosition = {x[i], y[i]};
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500351 for (int j = 0; j < 4; ++j) {
Brian Salomon6872e942018-05-18 10:29:54 -0400352 vertices[i].fEdges[j] = {a[j], b[j], c[j]};
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500353 }
354 }
355
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500356 AssignTexCoords(vertices, quad, texRect);
357 }
358
359private:
Brian Salomon86c40012018-05-22 10:48:49 -0400360 static void AssignTexCoords(V* vertices, const GrPerspQuad& quad, const SkRect& tex) {
Brian Salomona33b67c2018-05-17 10:42:14 -0400361 SkMatrix q = SkMatrix::MakeAll(quad.x(0), quad.x(1), quad.x(2),
362 quad.y(0), quad.y(1), quad.y(2),
363 1.f, 1.f, 1.f);
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500364 SkMatrix qinv;
365 if (!q.invert(&qinv)) {
366 return;
367 }
368 SkMatrix t = SkMatrix::MakeAll(tex.fLeft, tex.fLeft, tex.fRight,
369 tex.fTop, tex.fBottom, tex.fTop,
370 1.f, 1.f, 1.f);
371 SkMatrix map;
372 map.setConcat(t, qinv);
Brian Salomon86c40012018-05-22 10:48:49 -0400373 SkMatrixPriv::MapPointsWithStride(map, &vertices[0].fTextureCoords, sizeof(V),
374 &vertices[0].fPosition, sizeof(V), 4);
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500375 }
376};
377
Brian Salomon86c40012018-05-22 10:48:49 -0400378template<typename V> class VertexAAHandler<V, GrAA::kYes, SkPoint3> {
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400379public:
Brian Salomon86c40012018-05-22 10:48:49 -0400380 static void AssignPositionsAndTexCoords(V* vertices, const GrPerspQuad& quad,
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400381 const SkRect& texRect) {
382 auto x = quad.x4f();
383 auto y = quad.y4f();
384 auto iw = quad.iw4f();
385 x *= iw;
386 y *= iw;
387
388 // Get an equation for w from device space coords.
389 SkMatrix P;
390 P.setAll(x[0], y[0], 1, x[1], y[1], 1, x[2], y[2], 1);
391 SkAssertResult(P.invert(&P));
392 SkPoint3 weq{quad.w(0), quad.w(1), quad.w(2)};
393 P.mapHomogeneousPoints(&weq, &weq, 1);
394
395 Sk4f a, b, c;
396 compute_quad_edges_and_outset_vertices(&x, &y, &a, &b, &c);
397
398 // Compute new w values for the output vertices;
399 auto w = Sk4f(weq.fX) * x + Sk4f(weq.fY) * y + Sk4f(weq.fZ);
400 x *= w;
401 y *= w;
402
403 for (int i = 0; i < 4; ++i) {
404 vertices[i].fPosition = {x[i], y[i], w[i]};
405 for (int j = 0; j < 4; ++j) {
406 vertices[i].fEdges[j] = {a[j], b[j], c[j]};
407 }
408 }
409
410 AssignTexCoords(vertices, quad, texRect);
411 }
412
413private:
Brian Salomon86c40012018-05-22 10:48:49 -0400414 static void AssignTexCoords(V* vertices, const GrPerspQuad& quad, const SkRect& tex) {
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400415 SkMatrix q = SkMatrix::MakeAll(quad.x(0), quad.x(1), quad.x(2),
416 quad.y(0), quad.y(1), quad.y(2),
417 quad.w(0), quad.w(1), quad.w(2));
418 SkMatrix qinv;
419 if (!q.invert(&qinv)) {
420 return;
421 }
422 SkMatrix t = SkMatrix::MakeAll(tex.fLeft, tex.fLeft, tex.fRight,
423 tex.fTop, tex.fBottom, tex.fTop,
424 1.f, 1.f, 1.f);
425 SkMatrix map;
426 map.setConcat(t, qinv);
427 SkPoint3 tempTexCoords[4];
428 SkMatrixPriv::MapHomogeneousPointsWithStride(map, tempTexCoords, sizeof(SkPoint3),
Brian Salomon86c40012018-05-22 10:48:49 -0400429 &vertices[0].fPosition, sizeof(V), 4);
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400430 for (int i = 0; i < 4; ++i) {
431 auto invW = 1.f / tempTexCoords[i].fZ;
432 vertices[i].fTextureCoords.fX = tempTexCoords[i].fX * invW;
433 vertices[i].fTextureCoords.fY = tempTexCoords[i].fY * invW;
434 }
435 }
436};
437
Brian Salomonb80ffee2018-05-23 16:39:39 -0400438template <typename V, Domain D = V::kDomain> struct DomainAssigner;
439
440template <typename V> struct DomainAssigner<V, Domain::kYes> {
441 static void Assign(V* vertices, Domain domain, GrSamplerState::Filter filter,
442 const SkRect& srcRect, GrSurfaceOrigin origin, float iw, float ih) {
443 static constexpr SkRect kLargeRect = {-2, -2, 2, 2};
444 SkRect domainRect;
445 if (domain == Domain::kYes) {
446 auto ltrb = Sk4f::Load(&srcRect);
447 if (filter == GrSamplerState::Filter::kBilerp) {
448 auto rblt = SkNx_shuffle<2, 3, 0, 1>(ltrb);
449 auto whwh = (rblt - ltrb).abs();
450 auto c = (rblt + ltrb) * 0.5f;
451 static const Sk4f kOffsets = {0.5f, 0.5f, -0.5f, -0.5f};
452 ltrb = (whwh < 1.f).thenElse(c, ltrb + kOffsets);
453 }
454 ltrb *= Sk4f(iw, ih, iw, ih);
455 if (origin == kBottomLeft_GrSurfaceOrigin) {
456 static const Sk4f kMul = {1.f, -1.f, 1.f, -1.f};
457 static const Sk4f kAdd = {0.f, 1.f, 0.f, 1.f};
458 ltrb = SkNx_shuffle<0, 3, 2, 1>(kMul * ltrb + kAdd);
459 }
460 ltrb.store(&domainRect);
461 } else {
462 domainRect = kLargeRect;
463 }
464 for (int i = 0; i < 4; ++i) {
465 vertices[i].fTextureDomain = domainRect;
466 }
467 }
468};
469
470template <typename V> struct DomainAssigner<V, Domain::kNo> {
471 static void Assign(V*, Domain domain, GrSamplerState::Filter, const SkRect&, GrSurfaceOrigin,
472 float iw, float ih) {
473 SkASSERT(domain == Domain::kNo);
474 }
475};
476
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500477} // anonymous namespace
478
Brian Salomon86c40012018-05-22 10:48:49 -0400479template <typename V>
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400480static void tessellate_quad(const GrPerspQuad& devQuad, const SkRect& srcRect, GrColor color,
Brian Salomonb80ffee2018-05-23 16:39:39 -0400481 GrSurfaceOrigin origin, GrSamplerState::Filter filter, V* vertices,
Brian Salomon7eae3e02018-08-07 14:02:38 +0000482 SkScalar iw, SkScalar ih, Domain domain) {
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500483 SkRect texRect = {
484 iw * srcRect.fLeft,
485 ih * srcRect.fTop,
486 iw * srcRect.fRight,
487 ih * srcRect.fBottom
488 };
489 if (origin == kBottomLeft_GrSurfaceOrigin) {
490 texRect.fTop = 1.f - texRect.fTop;
491 texRect.fBottom = 1.f - texRect.fBottom;
492 }
Brian Salomon86c40012018-05-22 10:48:49 -0400493 VertexAAHandler<V>::AssignPositionsAndTexCoords(vertices, devQuad, texRect);
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500494 vertices[0].fColor = color;
495 vertices[1].fColor = color;
496 vertices[2].fColor = color;
497 vertices[3].fColor = color;
Brian Salomonb80ffee2018-05-23 16:39:39 -0400498 DomainAssigner<V>::Assign(vertices, domain, filter, srcRect, origin, iw, ih);
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500499}
Brian Salomon17031a72018-05-22 14:14:07 -0400500
Brian Salomon34169692017-08-28 15:32:01 -0400501/**
502 * Op that implements GrTextureOp::Make. It draws textured quads. Each quad can modulate against a
503 * the texture by color. The blend with the destination is always src-over. The edges are non-AA.
504 */
505class TextureOp final : public GrMeshDrawOp {
506public:
Robert Phillips7c525e62018-06-12 10:11:12 -0400507 static std::unique_ptr<GrDrawOp> Make(GrContext* context,
508 sk_sp<GrTextureProxy> proxy,
509 GrSamplerState::Filter filter,
510 GrColor color,
511 const SkRect& srcRect,
512 const SkRect& dstRect,
513 GrAAType aaType,
514 SkCanvas::SrcRectConstraint constraint,
Brian Osman2b23c4b2018-06-01 12:25:08 -0400515 const SkMatrix& viewMatrix,
Brian Osman3ebd3542018-07-30 14:36:53 -0400516 sk_sp<GrColorSpaceXform> textureColorSpaceXform,
517 sk_sp<GrColorSpaceXform> paintColorSpaceXform) {
Robert Phillipsc994a932018-06-19 13:09:54 -0400518 GrOpMemoryPool* pool = context->contextPriv().opMemoryPool();
519
520 return pool->allocate<TextureOp>(std::move(proxy), filter, color,
521 srcRect, dstRect, aaType, constraint,
Brian Osman3ebd3542018-07-30 14:36:53 -0400522 viewMatrix, std::move(textureColorSpaceXform),
523 std::move(paintColorSpaceXform));
Brian Salomon34169692017-08-28 15:32:01 -0400524 }
525
Brian Salomon336ce7b2017-09-08 08:23:58 -0400526 ~TextureOp() override {
527 if (fFinalized) {
Brian Salomon7eae3e02018-08-07 14:02:38 +0000528 fProxy->completedRead();
Brian Salomon336ce7b2017-09-08 08:23:58 -0400529 } else {
Brian Salomon7eae3e02018-08-07 14:02:38 +0000530 fProxy->unref();
Brian Salomon336ce7b2017-09-08 08:23:58 -0400531 }
532 }
Brian Salomon34169692017-08-28 15:32:01 -0400533
534 const char* name() const override { return "TextureOp"; }
535
Brian Salomon7eae3e02018-08-07 14:02:38 +0000536 void visitProxies(const VisitProxyFunc& func) const override { func(fProxy); }
Robert Phillipsb493eeb2017-09-13 13:10:52 -0400537
Brian Salomon34169692017-08-28 15:32:01 -0400538 SkString dumpInfo() const override {
539 SkString str;
Brian Salomon34169692017-08-28 15:32:01 -0400540 str.appendf("# draws: %d\n", fDraws.count());
Brian Salomon7eae3e02018-08-07 14:02:38 +0000541 str.appendf("Proxy ID: %d, Filter: %d\n", fProxy->uniqueID().asUInt(),
542 static_cast<int>(fFilter));
Brian Salomon34169692017-08-28 15:32:01 -0400543 for (int i = 0; i < fDraws.count(); ++i) {
544 const Draw& draw = fDraws[i];
545 str.appendf(
Brian Salomon7eae3e02018-08-07 14:02:38 +0000546 "%d: Color: 0x%08x, TexRect [L: %.2f, T: %.2f, R: %.2f, B: %.2f] "
Brian Salomon336ce7b2017-09-08 08:23:58 -0400547 "Quad [(%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f)]\n",
Brian Salomon7eae3e02018-08-07 14:02:38 +0000548 i, draw.color(), draw.srcRect().fLeft, draw.srcRect().fTop,
Brian Salomonb80ffee2018-05-23 16:39:39 -0400549 draw.srcRect().fRight, draw.srcRect().fBottom, draw.quad().point(0).fX,
550 draw.quad().point(0).fY, draw.quad().point(1).fX, draw.quad().point(1).fY,
551 draw.quad().point(2).fX, draw.quad().point(2).fY, draw.quad().point(3).fX,
552 draw.quad().point(3).fY);
Brian Salomon34169692017-08-28 15:32:01 -0400553 }
554 str += INHERITED::dumpInfo();
555 return str;
556 }
557
Brian Osman532b3f92018-07-11 10:02:07 -0400558 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip) override {
Brian Salomon34169692017-08-28 15:32:01 -0400559 SkASSERT(!fFinalized);
560 fFinalized = true;
Brian Salomon7eae3e02018-08-07 14:02:38 +0000561 fProxy->addPendingRead();
562 fProxy->unref();
Brian Salomon34169692017-08-28 15:32:01 -0400563 return RequiresDstTexture::kNo;
564 }
565
Brian Salomon485b8c62018-01-12 15:11:06 -0500566 FixedFunctionFlags fixedFunctionFlags() const override {
567 return this->aaType() == GrAAType::kMSAA ? FixedFunctionFlags::kUsesHWAA
568 : FixedFunctionFlags::kNone;
569 }
Brian Salomon34169692017-08-28 15:32:01 -0400570
571 DEFINE_OP_CLASS_ID
572
573private:
Robert Phillips7c525e62018-06-12 10:11:12 -0400574 friend class ::GrOpMemoryPool;
Brian Salomon762d5e72017-12-01 10:25:08 -0500575
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400576 TextureOp(sk_sp<GrTextureProxy> proxy, GrSamplerState::Filter filter, GrColor color,
Brian Salomon485b8c62018-01-12 15:11:06 -0500577 const SkRect& srcRect, const SkRect& dstRect, GrAAType aaType,
Brian Salomonb80ffee2018-05-23 16:39:39 -0400578 SkCanvas::SrcRectConstraint constraint, const SkMatrix& viewMatrix,
Brian Osman3ebd3542018-07-30 14:36:53 -0400579 sk_sp<GrColorSpaceXform> textureColorSpaceXform,
580 sk_sp<GrColorSpaceXform> paintColorSpaceXform)
Brian Salomon34169692017-08-28 15:32:01 -0400581 : INHERITED(ClassID())
Brian Osman3ebd3542018-07-30 14:36:53 -0400582 , fTextureColorSpaceXform(std::move(textureColorSpaceXform))
583 , fPaintColorSpaceXform(std::move(paintColorSpaceXform))
Brian Salomon7eae3e02018-08-07 14:02:38 +0000584 , fProxy(proxy.release())
585 , fFilter(filter)
Brian Salomon485b8c62018-01-12 15:11:06 -0500586 , fAAType(static_cast<unsigned>(aaType))
Brian Osman2b23c4b2018-06-01 12:25:08 -0400587 , fFinalized(0) {
Brian Salomon485b8c62018-01-12 15:11:06 -0500588 SkASSERT(aaType != GrAAType::kMixedSamples);
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400589 fPerspective = viewMatrix.hasPerspective();
Brian Salomon594b64c2018-05-29 12:47:57 -0400590 auto quad = GrPerspQuad(dstRect, viewMatrix);
591 auto bounds = quad.bounds();
592#ifndef SK_DONT_DROP_UNNECESSARY_AA_IN_TEXTURE_OP
593 if (GrAAType::kCoverage == this->aaType() && viewMatrix.rectStaysRect()) {
594 // Disable coverage AA when rect falls on integers in device space.
595 auto is_int = [](float f) { return f == sk_float_floor(f); };
596 if (is_int(bounds.fLeft) && is_int(bounds.fTop) && is_int(bounds.fRight) &&
597 is_int(bounds.fBottom)) {
598 fAAType = static_cast<unsigned>(GrAAType::kNone);
599 // We may have had a strict constraint with nearest filter soley due to possible AA
600 // bloat. In that case it's no longer necessary.
601 if (constraint == SkCanvas::kStrict_SrcRectConstraint &&
602 filter == GrSamplerState::Filter::kNearest) {
603 constraint = SkCanvas::kFast_SrcRectConstraint;
604 }
605 }
606 }
607#endif
Brian Salomon7eae3e02018-08-07 14:02:38 +0000608 const auto& draw = fDraws.emplace_back(srcRect, quad, constraint, color);
Brian Salomon34169692017-08-28 15:32:01 -0400609 this->setBounds(bounds, HasAABloat::kNo, IsZeroArea::kNo);
Brian Salomon594b64c2018-05-29 12:47:57 -0400610 fDomain = static_cast<bool>(draw.domain());
Brian Salomon34169692017-08-28 15:32:01 -0400611 }
612
Brian Salomon7eae3e02018-08-07 14:02:38 +0000613 template <typename Pos, Domain D, GrAA AA>
Brian Salomon2d0a6a12018-08-22 15:22:24 +0000614 void tess(void* v, const GrGeometryProcessor* gp) const {
Brian Salomon7eae3e02018-08-07 14:02:38 +0000615 using Vertex = TextureGeometryProcessor::Vertex<Pos, D, AA>;
Brian Salomon92be2f72018-06-19 14:33:47 -0400616 SkASSERT(gp->debugOnly_vertexStride() == sizeof(Vertex));
Brian Salomon17031a72018-05-22 14:14:07 -0400617 auto vertices = static_cast<Vertex*>(v);
Brian Salomon7eae3e02018-08-07 14:02:38 +0000618 auto origin = fProxy->origin();
619 const auto* texture = fProxy->peekTexture();
620 float iw = 1.f / texture->width();
621 float ih = 1.f / texture->height();
622
Brian Salomon17031a72018-05-22 14:14:07 -0400623 for (const auto& draw : fDraws) {
Brian Salomon7eae3e02018-08-07 14:02:38 +0000624 tessellate_quad<Vertex>(draw.quad(), draw.srcRect(), draw.color(), origin, fFilter,
625 vertices, iw, ih, draw.domain());
Brian Salomon17031a72018-05-22 14:14:07 -0400626 vertices += 4;
627 }
628 }
629
Brian Salomon34169692017-08-28 15:32:01 -0400630 void onPrepareDraws(Target* target) override {
Brian Salomon2d0a6a12018-08-22 15:22:24 +0000631 bool hasPerspective = false;
632 Domain domain = Domain::kNo;
633 int numOps = 0;
634 for (const auto& op : ChainRange<TextureOp>(this)) {
635 ++numOps;
636 hasPerspective |= op.fPerspective;
637 if (op.fDomain) {
638 domain = Domain::kYes;
639 }
640 if (!op.fProxy->instantiate(target->resourceProvider())) {
641 return;
642 }
Brian Salomon34169692017-08-28 15:32:01 -0400643 }
Brian Salomon336ce7b2017-09-08 08:23:58 -0400644
Brian Salomon485b8c62018-01-12 15:11:06 -0500645 bool coverageAA = GrAAType::kCoverage == this->aaType();
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400646 sk_sp<GrGeometryProcessor> gp = TextureGeometryProcessor::Make(
Brian Salomon7eae3e02018-08-07 14:02:38 +0000647 fProxy->textureType(), fProxy->config(), fFilter,
648 std::move(fTextureColorSpaceXform), std::move(fPaintColorSpaceXform), coverageAA,
Brian Salomon2d0a6a12018-08-22 15:22:24 +0000649 hasPerspective, domain, *target->caps().shaderCaps());
Brian Salomon34169692017-08-28 15:32:01 -0400650 GrPipeline::InitArgs args;
651 args.fProxy = target->proxy();
652 args.fCaps = &target->caps();
653 args.fResourceProvider = target->resourceProvider();
Brian Salomon485b8c62018-01-12 15:11:06 -0500654 args.fFlags = 0;
Brian Salomon485b8c62018-01-12 15:11:06 -0500655 if (GrAAType::kMSAA == this->aaType()) {
656 args.fFlags |= GrPipeline::kHWAntialias_Flag;
657 }
658
Brian Salomon49348902018-06-26 09:12:38 -0400659 auto clip = target->detachAppliedClip();
Brian Salomon2d0a6a12018-08-22 15:22:24 +0000660 // We'll use a dynamic state array for the GP textures when there are multiple ops.
661 // Otherwise, we use fixed dynamic state to specify the single op's proxy.
662 GrPipeline::DynamicStateArrays* dynamicStateArrays = nullptr;
663 GrPipeline::FixedDynamicState* fixedDynamicState;
664 if (numOps > 1) {
665 dynamicStateArrays = target->allocDynamicStateArrays(numOps, 1, false);
666 fixedDynamicState = target->allocFixedDynamicState(clip.scissorState().rect(), 0);
667 } else {
668 fixedDynamicState = target->allocFixedDynamicState(clip.scissorState().rect(), 1);
669 fixedDynamicState->fPrimitiveProcessorTextures[0] = fProxy;
670 }
Brian Salomon49348902018-06-26 09:12:38 -0400671 const auto* pipeline =
672 target->allocPipeline(args, GrProcessorSet::MakeEmptySet(), std::move(clip));
Brian Salomon7eae3e02018-08-07 14:02:38 +0000673 using TessFn = decltype(&TextureOp::tess<SkPoint, Domain::kNo, GrAA::kNo>);
674#define TESS_FN_AND_VERTEX_SIZE(Point, Domain, AA) \
675 { \
676 &TextureOp::tess<Point, Domain, AA>, \
677 sizeof(TextureGeometryProcessor::Vertex<Point, Domain, AA>) \
Brian Salomon92be2f72018-06-19 14:33:47 -0400678 }
679 static constexpr struct {
680 TessFn fTessFn;
681 size_t fVertexSize;
682 } kTessFnsAndVertexSizes[] = {
Brian Salomon7eae3e02018-08-07 14:02:38 +0000683 TESS_FN_AND_VERTEX_SIZE(SkPoint, Domain::kNo, GrAA::kNo),
684 TESS_FN_AND_VERTEX_SIZE(SkPoint, Domain::kNo, GrAA::kYes),
685 TESS_FN_AND_VERTEX_SIZE(SkPoint, Domain::kYes, GrAA::kNo),
686 TESS_FN_AND_VERTEX_SIZE(SkPoint, Domain::kYes, GrAA::kYes),
687 TESS_FN_AND_VERTEX_SIZE(SkPoint3, Domain::kNo, GrAA::kNo),
688 TESS_FN_AND_VERTEX_SIZE(SkPoint3, Domain::kNo, GrAA::kYes),
689 TESS_FN_AND_VERTEX_SIZE(SkPoint3, Domain::kYes, GrAA::kNo),
690 TESS_FN_AND_VERTEX_SIZE(SkPoint3, Domain::kYes, GrAA::kYes),
Brian Salomon92be2f72018-06-19 14:33:47 -0400691 };
692#undef TESS_FN_AND_VERTEX_SIZE
693 int tessFnIdx = 0;
Brian Salomon2d0a6a12018-08-22 15:22:24 +0000694 tessFnIdx |= coverageAA ? 0x1 : 0x0;
695 tessFnIdx |= (domain == Domain::kYes) ? 0x2 : 0x0;
696 tessFnIdx |= hasPerspective ? 0x4 : 0x0;
Brian Salomon92be2f72018-06-19 14:33:47 -0400697
698 SkASSERT(kTessFnsAndVertexSizes[tessFnIdx].fVertexSize == gp->debugOnly_vertexStride());
699
Brian Salomon2d0a6a12018-08-22 15:22:24 +0000700 GrMesh* meshes = target->allocMeshes(numOps);
701 int i = 0;
702 for (const auto& op : ChainRange<TextureOp>(this)) {
703 int vstart;
704 const GrBuffer* vbuffer;
705 void* vdata = target->makeVertexSpace(kTessFnsAndVertexSizes[tessFnIdx].fVertexSize,
706 4 * op.fDraws.count(), &vbuffer, &vstart);
707 if (!vdata) {
708 SkDebugf("Could not allocate vertices\n");
Brian Salomon34169692017-08-28 15:32:01 -0400709 return;
710 }
Brian Salomon2d0a6a12018-08-22 15:22:24 +0000711
712 (op.*(kTessFnsAndVertexSizes[tessFnIdx].fTessFn))(vdata, gp.get());
713
Brian Salomon5e81a122018-08-23 16:46:07 -0400714 meshes[i].setPrimitiveType(GrPrimitiveType::kTriangles);
715 sk_sp<const GrBuffer> ibuffer = target->resourceProvider()->refQuadIndexBuffer();
716 if (!ibuffer) {
717 SkDebugf("Could not allocate quad indices\n");
718 return;
Brian Salomon2d0a6a12018-08-22 15:22:24 +0000719 }
Brian Salomon5e81a122018-08-23 16:46:07 -0400720 meshes[i].setIndexedPatterned(ibuffer.get(), 6, 4, op.fDraws.count(),
721 GrResourceProvider::QuadCountOfQuadBuffer());
Brian Salomon2d0a6a12018-08-22 15:22:24 +0000722 meshes[i].setVertexData(vbuffer, vstart);
723 if (dynamicStateArrays) {
724 dynamicStateArrays->fPrimitiveProcessorTextures[i] = op.fProxy;
725 }
726 ++i;
Brian Salomon34169692017-08-28 15:32:01 -0400727 }
Brian Salomon2d0a6a12018-08-22 15:22:24 +0000728 target->draw(std::move(gp), pipeline, fixedDynamicState, dynamicStateArrays, meshes,
729 numOps);
Brian Salomon34169692017-08-28 15:32:01 -0400730 }
731
Brian Salomon2d0a6a12018-08-22 15:22:24 +0000732 CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon34169692017-08-28 15:32:01 -0400733 const auto* that = t->cast<TextureOp>();
Brian Osman3ebd3542018-07-30 14:36:53 -0400734 if (!GrColorSpaceXform::Equals(fTextureColorSpaceXform.get(),
735 that->fTextureColorSpaceXform.get())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +0000736 return CombineResult::kCannotCombine;
Brian Osman3ebd3542018-07-30 14:36:53 -0400737 }
738 if (!GrColorSpaceXform::Equals(fPaintColorSpaceXform.get(),
739 that->fPaintColorSpaceXform.get())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +0000740 return CombineResult::kCannotCombine;
Brian Salomon34169692017-08-28 15:32:01 -0400741 }
Brian Salomon485b8c62018-01-12 15:11:06 -0500742 if (this->aaType() != that->aaType()) {
Brian Salomon7eae3e02018-08-07 14:02:38 +0000743 return CombineResult::kCannotCombine;
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500744 }
Brian Salomon2d0a6a12018-08-22 15:22:24 +0000745 if (fFilter != that->fFilter) {
746 return CombineResult::kCannotCombine;
747 }
748 if (fProxy->uniqueID() != that->fProxy->uniqueID() || that->isChained()) {
749 // We can't merge across different proxies (and we're disallowed from merging when
750 // 'that' is chained. Check if we can be chained with 'that'.
751 if (fProxy->config() == that->fProxy->config() &&
752 fProxy->textureType() == that->fProxy->textureType() &&
753 caps.dynamicStateArrayGeometryProcessorTextureSupport()) {
754 return CombineResult::kMayChain;
755 }
Brian Salomon7eae3e02018-08-07 14:02:38 +0000756 return CombineResult::kCannotCombine;
Brian Salomon336ce7b2017-09-08 08:23:58 -0400757 }
Brian Salomon7eae3e02018-08-07 14:02:38 +0000758 fDraws.push_back_n(that->fDraws.count(), that->fDraws.begin());
Brian Salomon34169692017-08-28 15:32:01 -0400759 this->joinBounds(*that);
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400760 fPerspective |= that->fPerspective;
Brian Salomonb80ffee2018-05-23 16:39:39 -0400761 fDomain |= that->fDomain;
Brian Salomon7eae3e02018-08-07 14:02:38 +0000762 return CombineResult::kMerged;
Brian Salomon34169692017-08-28 15:32:01 -0400763 }
764
Brian Salomon485b8c62018-01-12 15:11:06 -0500765 GrAAType aaType() const { return static_cast<GrAAType>(fAAType); }
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500766
Brian Salomonb80ffee2018-05-23 16:39:39 -0400767 class Draw {
768 public:
Brian Salomon7eae3e02018-08-07 14:02:38 +0000769 Draw(const SkRect& srcRect, const GrPerspQuad& quad, SkCanvas::SrcRectConstraint constraint,
770 GrColor color)
Brian Salomonb80ffee2018-05-23 16:39:39 -0400771 : fSrcRect(srcRect)
Brian Salomonb80ffee2018-05-23 16:39:39 -0400772 , fQuad(quad)
Brian Salomon7eae3e02018-08-07 14:02:38 +0000773 , fColor(color)
774 , fHasDomain(constraint == SkCanvas::kStrict_SrcRectConstraint) {}
Brian Salomonb80ffee2018-05-23 16:39:39 -0400775 const GrPerspQuad& quad() const { return fQuad; }
Brian Salomonb80ffee2018-05-23 16:39:39 -0400776 const SkRect& srcRect() const { return fSrcRect; }
777 GrColor color() const { return fColor; }
778 Domain domain() const { return Domain(fHasDomain); }
Brian Salomonb80ffee2018-05-23 16:39:39 -0400779
780 private:
Brian Salomon34169692017-08-28 15:32:01 -0400781 SkRect fSrcRect;
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400782 GrPerspQuad fQuad;
Brian Salomon34169692017-08-28 15:32:01 -0400783 GrColor fColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +0000784 bool fHasDomain;
Brian Salomon34169692017-08-28 15:32:01 -0400785 };
786 SkSTArray<1, Draw, true> fDraws;
Brian Osman3ebd3542018-07-30 14:36:53 -0400787 sk_sp<GrColorSpaceXform> fTextureColorSpaceXform;
788 sk_sp<GrColorSpaceXform> fPaintColorSpaceXform;
Brian Salomon7eae3e02018-08-07 14:02:38 +0000789 GrTextureProxy* fProxy;
790 GrSamplerState::Filter fFilter;
Brian Salomon485b8c62018-01-12 15:11:06 -0500791 unsigned fAAType : 2;
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400792 unsigned fPerspective : 1;
Brian Salomonb80ffee2018-05-23 16:39:39 -0400793 unsigned fDomain : 1;
Brian Salomon34169692017-08-28 15:32:01 -0400794 // Used to track whether fProxy is ref'ed or has a pending IO after finalize() is called.
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500795 unsigned fFinalized : 1;
Brian Salomon336ce7b2017-09-08 08:23:58 -0400796
Brian Salomon34169692017-08-28 15:32:01 -0400797 typedef GrMeshDrawOp INHERITED;
798};
799
800} // anonymous namespace
801
802namespace GrTextureOp {
803
Robert Phillips7c525e62018-06-12 10:11:12 -0400804std::unique_ptr<GrDrawOp> Make(GrContext* context,
805 sk_sp<GrTextureProxy> proxy,
806 GrSamplerState::Filter filter,
807 GrColor color,
808 const SkRect& srcRect,
809 const SkRect& dstRect,
810 GrAAType aaType,
811 SkCanvas::SrcRectConstraint constraint,
812 const SkMatrix& viewMatrix,
Brian Osman3ebd3542018-07-30 14:36:53 -0400813 sk_sp<GrColorSpaceXform> textureColorSpaceXform,
814 sk_sp<GrColorSpaceXform> paintColorSpaceXform) {
Robert Phillips7c525e62018-06-12 10:11:12 -0400815 return TextureOp::Make(context, std::move(proxy), filter, color, srcRect, dstRect, aaType,
Brian Osman3ebd3542018-07-30 14:36:53 -0400816 constraint, viewMatrix, std::move(textureColorSpaceXform),
817 std::move(paintColorSpaceXform));
Brian Salomon34169692017-08-28 15:32:01 -0400818}
819
820} // namespace GrTextureOp
821
822#if GR_TEST_UTILS
823#include "GrContext.h"
Robert Phillips1afd4cd2018-01-08 13:40:32 -0500824#include "GrContextPriv.h"
Robert Phillips0bd24dc2018-01-16 08:06:32 -0500825#include "GrProxyProvider.h"
Brian Salomon34169692017-08-28 15:32:01 -0400826
827GR_DRAW_OP_TEST_DEFINE(TextureOp) {
828 GrSurfaceDesc desc;
829 desc.fConfig = kRGBA_8888_GrPixelConfig;
830 desc.fHeight = random->nextULessThan(90) + 10;
831 desc.fWidth = random->nextULessThan(90) + 10;
Brian Salomon2a4f9832018-03-03 22:43:43 -0500832 auto origin = random->nextBool() ? kTopLeft_GrSurfaceOrigin : kBottomLeft_GrSurfaceOrigin;
Greg Daniel09c94002018-06-08 22:11:51 +0000833 GrMipMapped mipMapped = random->nextBool() ? GrMipMapped::kYes : GrMipMapped::kNo;
834 SkBackingFit fit = SkBackingFit::kExact;
835 if (mipMapped == GrMipMapped::kNo) {
836 fit = random->nextBool() ? SkBackingFit::kApprox : SkBackingFit::kExact;
837 }
Robert Phillips0bd24dc2018-01-16 08:06:32 -0500838
839 GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider();
Greg Daniel09c94002018-06-08 22:11:51 +0000840 sk_sp<GrTextureProxy> proxy = proxyProvider->createProxy(desc, origin, mipMapped, fit,
841 SkBudgeted::kNo,
842 GrInternalSurfaceFlags::kNone);
Robert Phillips0bd24dc2018-01-16 08:06:32 -0500843
Brian Salomon34169692017-08-28 15:32:01 -0400844 SkRect rect = GrTest::TestRect(random);
845 SkRect srcRect;
846 srcRect.fLeft = random->nextRangeScalar(0.f, proxy->width() / 2.f);
847 srcRect.fRight = random->nextRangeScalar(0.f, proxy->width()) + proxy->width() / 2.f;
848 srcRect.fTop = random->nextRangeScalar(0.f, proxy->height() / 2.f);
849 srcRect.fBottom = random->nextRangeScalar(0.f, proxy->height()) + proxy->height() / 2.f;
850 SkMatrix viewMatrix = GrTest::TestMatrixPreservesRightAngles(random);
851 GrColor color = SkColorToPremulGrColor(random->nextU());
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400852 GrSamplerState::Filter filter = (GrSamplerState::Filter)random->nextULessThan(
853 static_cast<uint32_t>(GrSamplerState::Filter::kMipMap) + 1);
Greg Daniel09c94002018-06-08 22:11:51 +0000854 while (mipMapped == GrMipMapped::kNo && filter == GrSamplerState::Filter::kMipMap) {
855 filter = (GrSamplerState::Filter)random->nextULessThan(
856 static_cast<uint32_t>(GrSamplerState::Filter::kMipMap) + 1);
857 }
Brian Osman3ebd3542018-07-30 14:36:53 -0400858 auto texXform = GrTest::TestColorXform(random);
859 auto paintXform = GrTest::TestColorXform(random);
Brian Salomon485b8c62018-01-12 15:11:06 -0500860 GrAAType aaType = GrAAType::kNone;
861 if (random->nextBool()) {
862 aaType = (fsaaType == GrFSAAType::kUnifiedMSAA) ? GrAAType::kMSAA : GrAAType::kCoverage;
863 }
Brian Salomonb80ffee2018-05-23 16:39:39 -0400864 auto constraint = random->nextBool() ? SkCanvas::kStrict_SrcRectConstraint
865 : SkCanvas::kFast_SrcRectConstraint;
Robert Phillips7c525e62018-06-12 10:11:12 -0400866 return GrTextureOp::Make(context, std::move(proxy), filter, color, srcRect, rect, aaType,
Brian Osman3ebd3542018-07-30 14:36:53 -0400867 constraint, viewMatrix, std::move(texXform), std::move(paintXform));
Brian Salomon34169692017-08-28 15:32:01 -0400868}
869
870#endif