blob: d2f775b05f59ab9e5c1a32f3809b149ba6b09798 [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"
9#include "GrAppliedClip.h"
Brian Salomon336ce7b2017-09-08 08:23:58 -040010#include "GrCaps.h"
Brian Salomon34169692017-08-28 15:32:01 -040011#include "GrDrawOpTest.h"
12#include "GrGeometryProcessor.h"
13#include "GrMeshDrawOp.h"
14#include "GrOpFlushState.h"
15#include "GrQuad.h"
16#include "GrResourceProvider.h"
17#include "GrShaderCaps.h"
18#include "GrTexture.h"
Brian Salomon336ce7b2017-09-08 08:23:58 -040019#include "GrTexturePriv.h"
Brian Salomon34169692017-08-28 15:32:01 -040020#include "GrTextureProxy.h"
21#include "SkGr.h"
Brian Salomon336ce7b2017-09-08 08:23:58 -040022#include "SkMathPriv.h"
Brian Salomona33b67c2018-05-17 10:42:14 -040023#include "SkMatrixPriv.h"
Brian Salomonb5ef1f92018-01-11 11:46:21 -050024#include "SkPoint.h"
25#include "SkPoint3.h"
Brian Salomon34169692017-08-28 15:32:01 -040026#include "glsl/GrGLSLColorSpaceXformHelper.h"
Brian Salomonb5ef1f92018-01-11 11:46:21 -050027#include "glsl/GrGLSLFragmentShaderBuilder.h"
Brian Salomon34169692017-08-28 15:32:01 -040028#include "glsl/GrGLSLGeometryProcessor.h"
29#include "glsl/GrGLSLVarying.h"
Brian Salomonb5ef1f92018-01-11 11:46:21 -050030#include "glsl/GrGLSLVertexGeoBuilder.h"
Brian Salomon34169692017-08-28 15:32:01 -040031
32namespace {
33
34/**
35 * Geometry Processor that draws a texture modulated by a vertex color (though, this is meant to be
36 * the same value across all vertices of a quad and uses flat interpolation when available). This is
37 * used by TextureOp below.
38 */
39class TextureGeometryProcessor : public GrGeometryProcessor {
40public:
Brian Salomonbe3c1d22018-05-21 12:54:39 -040041 template <typename P> struct Vertex {
42 static constexpr GrAA kAA = GrAA::kNo;
43 static constexpr bool kIsMultiTexture = false;
44 using Position = P;
45 P fPosition;
Brian Salomon34169692017-08-28 15:32:01 -040046 SkPoint fTextureCoords;
47 GrColor fColor;
48 };
Brian Salomonbe3c1d22018-05-21 12:54:39 -040049 template <typename P> struct AAVertex : Vertex<P> {
50 static constexpr GrAA kAA = GrAA::kYes;
Brian Salomonb5ef1f92018-01-11 11:46:21 -050051 SkPoint3 fEdges[4];
Brian Salomonb5ef1f92018-01-11 11:46:21 -050052 };
Brian Salomonbe3c1d22018-05-21 12:54:39 -040053 template <typename P> struct MultiTextureVertex : Vertex<P> {
54 static constexpr bool kIsMultiTexture = true;
Brian Salomon336ce7b2017-09-08 08:23:58 -040055 int fTextureIdx;
Brian Salomon336ce7b2017-09-08 08:23:58 -040056 };
Brian Salomonbe3c1d22018-05-21 12:54:39 -040057 template <typename P> struct AAMultiTextureVertex : MultiTextureVertex<P> {
58 static constexpr GrAA kAA = GrAA::kYes;
Brian Salomonb5ef1f92018-01-11 11:46:21 -050059 SkPoint3 fEdges[4];
Brian Salomonb5ef1f92018-01-11 11:46:21 -050060 };
Brian Salomon336ce7b2017-09-08 08:23:58 -040061
62 // Maximum number of textures supported by this op. Must also be checked against the caps
63 // limit. These numbers were based on some limited experiments on a HP Z840 and Pixel XL 2016
64 // and could probably use more tuning.
65#ifdef SK_BUILD_FOR_ANDROID
66 static constexpr int kMaxTextures = 4;
67#else
68 static constexpr int kMaxTextures = 8;
69#endif
70
Brian Salomon0b4d8aa2017-10-11 15:34:27 -040071 static int SupportsMultitexture(const GrShaderCaps& caps) {
Brian Salomon762d5e72017-12-01 10:25:08 -050072 return caps.integerSupport() && caps.maxFragmentSamplers() > 1;
Brian Salomon0b4d8aa2017-10-11 15:34:27 -040073 }
Brian Salomon336ce7b2017-09-08 08:23:58 -040074
75 static sk_sp<GrGeometryProcessor> Make(sk_sp<GrTextureProxy> proxies[], int proxyCnt,
Brian Salomon485b8c62018-01-12 15:11:06 -050076 sk_sp<GrColorSpaceXform> csxf, bool coverageAA,
Brian Salomonbe3c1d22018-05-21 12:54:39 -040077 bool perspective, const GrSamplerState::Filter filters[],
Brian Salomon336ce7b2017-09-08 08:23:58 -040078 const GrShaderCaps& caps) {
79 // We use placement new to avoid always allocating space for kMaxTextures TextureSampler
80 // instances.
81 int samplerCnt = NumSamplersToUse(proxyCnt, caps);
82 size_t size = sizeof(TextureGeometryProcessor) + sizeof(TextureSampler) * (samplerCnt - 1);
83 void* mem = GrGeometryProcessor::operator new(size);
Brian Salomonbe3c1d22018-05-21 12:54:39 -040084 return sk_sp<TextureGeometryProcessor>(
85 new (mem) TextureGeometryProcessor(proxies, proxyCnt, samplerCnt, std::move(csxf),
86 coverageAA, perspective, filters, caps));
Brian Salomon336ce7b2017-09-08 08:23:58 -040087 }
88
89 ~TextureGeometryProcessor() override {
90 int cnt = this->numTextureSamplers();
91 for (int i = 1; i < cnt; ++i) {
92 fSamplers[i].~TextureSampler();
93 }
Brian Salomon34169692017-08-28 15:32:01 -040094 }
95
96 const char* name() const override { return "TextureGeometryProcessor"; }
97
98 void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const override {
99 b->add32(GrColorSpaceXform::XformKey(fColorSpaceXform.get()));
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400100 uint32_t x = this->usesCoverageEdgeAA() ? 0 : 1;
101 x |= kFloat3_GrVertexAttribType == fPositions.fType ? 0 : 2;
102 b->add32(x);
Brian Salomon34169692017-08-28 15:32:01 -0400103 }
104
105 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps& caps) const override {
106 class GLSLProcessor : public GrGLSLGeometryProcessor {
107 public:
108 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& proc,
109 FPCoordTransformIter&& transformIter) override {
110 const auto& textureGP = proc.cast<TextureGeometryProcessor>();
111 this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
112 if (fColorSpaceXformHelper.isValid()) {
113 fColorSpaceXformHelper.setData(pdman, textureGP.fColorSpaceXform.get());
114 }
115 }
116
117 private:
118 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
Chris Dalton7b046312018-02-02 11:06:30 -0700119 using Interpolation = GrGLSLVaryingHandler::Interpolation;
Brian Salomon34169692017-08-28 15:32:01 -0400120 const auto& textureGP = args.fGP.cast<TextureGeometryProcessor>();
121 fColorSpaceXformHelper.emitCode(
122 args.fUniformHandler, textureGP.fColorSpaceXform.get());
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400123 if (kFloat2_GrVertexAttribType == textureGP.fPositions.fType) {
124 args.fVaryingHandler->setNoPerspective();
125 }
Brian Salomon34169692017-08-28 15:32:01 -0400126 args.fVaryingHandler->emitAttributes(textureGP);
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400127 gpArgs->fPositionVar = textureGP.fPositions.asShaderVar();
128
Brian Salomon34169692017-08-28 15:32:01 -0400129 this->emitTransforms(args.fVertBuilder,
130 args.fVaryingHandler,
131 args.fUniformHandler,
Brian Salomon04460cc2017-12-06 14:47:42 -0500132 textureGP.fTextureCoords.asShaderVar(),
Brian Salomon34169692017-08-28 15:32:01 -0400133 args.fFPCoordTransformHandler);
Chris Dalton7b046312018-02-02 11:06:30 -0700134 args.fVaryingHandler->addPassThroughAttribute(&textureGP.fColors,
135 args.fOutputColor,
136 Interpolation::kCanBeFlat);
Ethan Nicholas8aa45692017-09-20 11:24:15 -0400137 args.fFragBuilder->codeAppend("float2 texCoord;");
Chris Daltonfdde34e2017-10-16 14:15:26 -0600138 args.fVaryingHandler->addPassThroughAttribute(&textureGP.fTextureCoords,
139 "texCoord");
Brian Salomon336ce7b2017-09-08 08:23:58 -0400140 if (textureGP.numTextureSamplers() > 1) {
Chris Dalton7b046312018-02-02 11:06:30 -0700141 // If this changes to float, reconsider Interpolation::kMustBeFlat.
142 SkASSERT(kInt_GrVertexAttribType == textureGP.fTextureIdx.fType);
Brian Salomon336ce7b2017-09-08 08:23:58 -0400143 SkASSERT(args.fShaderCaps->integerSupport());
144 args.fFragBuilder->codeAppend("int texIdx;");
Chris Dalton7b046312018-02-02 11:06:30 -0700145 args.fVaryingHandler->addPassThroughAttribute(&textureGP.fTextureIdx, "texIdx",
146 Interpolation::kMustBeFlat);
Brian Salomon336ce7b2017-09-08 08:23:58 -0400147 args.fFragBuilder->codeAppend("switch (texIdx) {");
148 for (int i = 0; i < textureGP.numTextureSamplers(); ++i) {
149 args.fFragBuilder->codeAppendf("case %d: %s = ", i, args.fOutputColor);
150 args.fFragBuilder->appendTextureLookupAndModulate(args.fOutputColor,
151 args.fTexSamplers[i],
152 "texCoord",
Ethan Nicholas8aa45692017-09-20 11:24:15 -0400153 kFloat2_GrSLType,
Brian Salomon336ce7b2017-09-08 08:23:58 -0400154 &fColorSpaceXformHelper);
155 args.fFragBuilder->codeAppend("; break;");
156 }
157 args.fFragBuilder->codeAppend("}");
158 } else {
159 args.fFragBuilder->codeAppendf("%s = ", args.fOutputColor);
160 args.fFragBuilder->appendTextureLookupAndModulate(args.fOutputColor,
161 args.fTexSamplers[0],
162 "texCoord",
Ethan Nicholas8aa45692017-09-20 11:24:15 -0400163 kFloat2_GrSLType,
Brian Salomon336ce7b2017-09-08 08:23:58 -0400164 &fColorSpaceXformHelper);
165 }
Brian Salomon34169692017-08-28 15:32:01 -0400166 args.fFragBuilder->codeAppend(";");
Brian Salomon485b8c62018-01-12 15:11:06 -0500167 if (textureGP.usesCoverageEdgeAA()) {
Brian Salomondba65f92018-01-22 08:43:38 -0500168 const char* aaDistName = nullptr;
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400169 bool mulByFragCoordW = false;
170 // When interpolation is inaccurate we perform the evaluation of the edge
Brian Salomondba65f92018-01-22 08:43:38 -0500171 // equations in the fragment shader rather than interpolating values computed
172 // in the vertex shader.
173 if (!args.fShaderCaps->interpolantsAreInaccurate()) {
174 GrGLSLVarying aaDistVarying(kFloat4_GrSLType,
175 GrGLSLVarying::Scope::kVertToFrag);
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400176 if (kFloat3_GrVertexAttribType == textureGP.fPositions.fType) {
177 args.fVaryingHandler->addVarying("aaDists", &aaDistVarying);
178 // The distance from edge equation e to homogenous point p=sk_Position
179 // is e.x*p.x/p.wx + e.y*p.y/p.w + e.z. However, we want screen space
180 // interpolation of this distance. We can do this by multiplying the
181 // varying in the VS by p.w and then multiplying by sk_FragCoord.w in
182 // the FS. So we output e.x*p.x + e.y*p.y + e.z * p.w
183 args.fVertBuilder->codeAppendf(
184 R"(%s = float4(dot(aaEdge0, %s), dot(aaEdge1, %s),
185 dot(aaEdge2, %s), dot(aaEdge3, %s));)",
186 aaDistVarying.vsOut(), textureGP.fPositions.fName,
187 textureGP.fPositions.fName, textureGP.fPositions.fName,
188 textureGP.fPositions.fName);
189 mulByFragCoordW = true;
190 } else {
191 args.fVaryingHandler->addVarying("aaDists", &aaDistVarying);
192 args.fVertBuilder->codeAppendf(
193 R"(%s = float4(dot(aaEdge0.xy, %s.xy) + aaEdge0.z,
194 dot(aaEdge1.xy, %s.xy) + aaEdge1.z,
195 dot(aaEdge2.xy, %s.xy) + aaEdge2.z,
196 dot(aaEdge3.xy, %s.xy) + aaEdge3.z);)",
197 aaDistVarying.vsOut(), textureGP.fPositions.fName,
198 textureGP.fPositions.fName, textureGP.fPositions.fName,
199 textureGP.fPositions.fName);
200 }
Brian Salomondba65f92018-01-22 08:43:38 -0500201 aaDistName = aaDistVarying.fsIn();
202 } else {
203 GrGLSLVarying aaEdgeVarying[4]{
204 {kFloat3_GrSLType, GrGLSLVarying::Scope::kVertToFrag},
205 {kFloat3_GrSLType, GrGLSLVarying::Scope::kVertToFrag},
206 {kFloat3_GrSLType, GrGLSLVarying::Scope::kVertToFrag},
207 {kFloat3_GrSLType, GrGLSLVarying::Scope::kVertToFrag}
208 };
209 for (int i = 0; i < 4; ++i) {
210 SkString name;
211 name.printf("aaEdge%d", i);
Brian Salomon7d982c62018-02-05 16:20:47 -0500212 args.fVaryingHandler->addVarying(name.c_str(), &aaEdgeVarying[i],
213 Interpolation::kCanBeFlat);
Brian Salomondba65f92018-01-22 08:43:38 -0500214 args.fVertBuilder->codeAppendf(
215 "%s = aaEdge%d;", aaEdgeVarying[i].vsOut(), i);
216 }
217 args.fFragBuilder->codeAppendf(
218 R"(float4 aaDists = float4(dot(%s.xy, sk_FragCoord.xy) + %s.z,
219 dot(%s.xy, sk_FragCoord.xy) + %s.z,
220 dot(%s.xy, sk_FragCoord.xy) + %s.z,
221 dot(%s.xy, sk_FragCoord.xy) + %s.z);)",
222 aaEdgeVarying[0].fsIn(), aaEdgeVarying[0].fsIn(),
223 aaEdgeVarying[1].fsIn(), aaEdgeVarying[1].fsIn(),
224 aaEdgeVarying[2].fsIn(), aaEdgeVarying[2].fsIn(),
225 aaEdgeVarying[3].fsIn(), aaEdgeVarying[3].fsIn());
226 aaDistName = "aaDists";
227 }
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500228 args.fFragBuilder->codeAppendf(
229 "float mindist = min(min(%s.x, %s.y), min(%s.z, %s.w));",
Brian Salomondba65f92018-01-22 08:43:38 -0500230 aaDistName, aaDistName, aaDistName, aaDistName);
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400231 if (mulByFragCoordW) {
232 args.fFragBuilder->codeAppend("mindist *= sk_FragCoord.w;");
233 }
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500234 args.fFragBuilder->codeAppendf("%s = float4(clamp(mindist, 0, 1));",
235 args.fOutputCoverage);
236 } else {
237 args.fFragBuilder->codeAppendf("%s = float4(1);", args.fOutputCoverage);
238 }
Brian Salomon34169692017-08-28 15:32:01 -0400239 }
240 GrGLSLColorSpaceXformHelper fColorSpaceXformHelper;
241 };
242 return new GLSLProcessor;
243 }
244
Brian Salomon485b8c62018-01-12 15:11:06 -0500245 bool usesCoverageEdgeAA() const { return SkToBool(fAAEdges[0].isInitialized()); }
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500246
Brian Salomon34169692017-08-28 15:32:01 -0400247private:
Brian Salomon336ce7b2017-09-08 08:23:58 -0400248 // This exists to reduce the number of shaders generated. It does some rounding of sampler
249 // counts.
250 static int NumSamplersToUse(int numRealProxies, const GrShaderCaps& caps) {
251 SkASSERT(numRealProxies > 0 && numRealProxies <= kMaxTextures &&
252 numRealProxies <= caps.maxFragmentSamplers());
253 if (1 == numRealProxies) {
254 return 1;
255 }
256 if (numRealProxies <= 4) {
257 return 4;
258 }
259 // Round to the next power of 2 and then clamp to kMaxTextures and the max allowed by caps.
260 return SkTMin(SkNextPow2(numRealProxies), SkTMin(kMaxTextures, caps.maxFragmentSamplers()));
261 }
262
263 TextureGeometryProcessor(sk_sp<GrTextureProxy> proxies[], int proxyCnt, int samplerCnt,
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400264 sk_sp<GrColorSpaceXform> csxf, bool coverageAA, bool perspective,
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500265 const GrSamplerState::Filter filters[], const GrShaderCaps& caps)
266 : INHERITED(kTextureGeometryProcessor_ClassID), fColorSpaceXform(std::move(csxf)) {
Brian Salomon336ce7b2017-09-08 08:23:58 -0400267 SkASSERT(proxyCnt > 0 && samplerCnt >= proxyCnt);
Brian Salomon336ce7b2017-09-08 08:23:58 -0400268 fSamplers[0].reset(std::move(proxies[0]), filters[0]);
269 this->addTextureSampler(&fSamplers[0]);
270 for (int i = 1; i < proxyCnt; ++i) {
271 // This class has one sampler built in, the rest come from memory this processor was
272 // placement-newed into and so haven't been constructed.
273 new (&fSamplers[i]) TextureSampler(std::move(proxies[i]), filters[i]);
274 this->addTextureSampler(&fSamplers[i]);
275 }
Brian Salomon30e1a5e2018-05-18 12:32:32 -0400276
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400277 if (perspective) {
278 fPositions = this->addVertexAttrib("position", kFloat3_GrVertexAttribType);
279 } else {
280 fPositions = this->addVertexAttrib("position", kFloat2_GrVertexAttribType);
281 }
Brian Salomon30e1a5e2018-05-18 12:32:32 -0400282 fTextureCoords = this->addVertexAttrib("textureCoords", kFloat2_GrVertexAttribType);
283 fColors = this->addVertexAttrib("color", kUByte4_norm_GrVertexAttribType);
284
Brian Salomon336ce7b2017-09-08 08:23:58 -0400285 if (samplerCnt > 1) {
286 // Here we initialize any extra samplers by repeating the last one samplerCnt - proxyCnt
287 // times.
288 GrTextureProxy* dupeProxy = fSamplers[proxyCnt - 1].proxy();
289 for (int i = proxyCnt; i < samplerCnt; ++i) {
290 new (&fSamplers[i]) TextureSampler(sk_ref_sp(dupeProxy), filters[proxyCnt - 1]);
291 this->addTextureSampler(&fSamplers[i]);
292 }
293 SkASSERT(caps.integerSupport());
294 fTextureIdx = this->addVertexAttrib("textureIdx", kInt_GrVertexAttribType);
295 }
296
Brian Salomon485b8c62018-01-12 15:11:06 -0500297 if (coverageAA) {
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500298 fAAEdges[0] = this->addVertexAttrib("aaEdge0", kFloat3_GrVertexAttribType);
299 fAAEdges[1] = this->addVertexAttrib("aaEdge1", kFloat3_GrVertexAttribType);
300 fAAEdges[2] = this->addVertexAttrib("aaEdge2", kFloat3_GrVertexAttribType);
301 fAAEdges[3] = this->addVertexAttrib("aaEdge3", kFloat3_GrVertexAttribType);
302 }
Brian Salomon34169692017-08-28 15:32:01 -0400303 }
304
305 Attribute fPositions;
Brian Salomon34169692017-08-28 15:32:01 -0400306 Attribute fColors;
Brian Salomon30e1a5e2018-05-18 12:32:32 -0400307 Attribute fTextureCoords;
308 Attribute fTextureIdx;
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500309 Attribute fAAEdges[4];
Brian Salomon34169692017-08-28 15:32:01 -0400310 sk_sp<GrColorSpaceXform> fColorSpaceXform;
Brian Salomon336ce7b2017-09-08 08:23:58 -0400311 TextureSampler fSamplers[1];
Ethan Nicholasabff9562017-10-09 10:54:08 -0400312
313 typedef GrGeometryProcessor INHERITED;
Brian Salomon34169692017-08-28 15:32:01 -0400314};
315
Brian Salomon6872e942018-05-18 10:29:54 -0400316// This computes the four edge equations for a quad, then outsets them and computes a new quad
317// as the intersection points of the outset edges. 'x' and 'y' contain the original points as input
318// and the outset points as output. 'a', 'b', and 'c' are the edge equation coefficients on output.
319static void compute_quad_edges_and_outset_vertices(Sk4f* x, Sk4f* y, Sk4f* a, Sk4f* b, Sk4f* c) {
320 static constexpr auto fma = SkNx_fma<4, float>;
321 // These rotate the points/edge values either clockwise or counterclockwise assuming tri strip
322 // order.
323 auto nextCW = [](const Sk4f& v) { return SkNx_shuffle<2, 0, 3, 1>(v); };
324 auto nextCCW = [](const Sk4f& v) { return SkNx_shuffle<1, 3, 0, 2>(v); };
325
326 auto xnext = nextCCW(*x);
327 auto ynext = nextCCW(*y);
328 *a = ynext - *y;
329 *b = *x - xnext;
330 *c = fma(xnext, *y, -ynext * *x);
331 Sk4f invNormLengths = (*a * *a + *b * *b).rsqrt();
332 // Make sure the edge equations have their normals facing into the quad in device space.
333 auto test = fma(*a, nextCW(*x), fma(*b, nextCW(*y), *c));
334 if ((test < Sk4f(0)).anyTrue()) {
335 invNormLengths = -invNormLengths;
336 }
337 *a *= invNormLengths;
338 *b *= invNormLengths;
339 *c *= invNormLengths;
340
341 // Here is the outset. This makes our edge equations compute coverage without requiring a
342 // half pixel offset and is also used to compute the bloated quad that will cover all
343 // pixels.
344 *c += Sk4f(0.5f);
345
346 // Reverse the process to compute the points of the bloated quad from the edge equations.
347 // This time the inputs don't have 1s as their third coord and we want to homogenize rather
348 // than normalize.
349 auto anext = nextCW(*a);
350 auto bnext = nextCW(*b);
351 auto cnext = nextCW(*c);
352 *x = fma(bnext, *c, -*b * cnext);
353 *y = fma(*a, cnext, -anext * *c);
354 auto ic = (fma(anext, *b, -bnext * *a)).invert();
355 *x *= ic;
356 *y *= ic;
357}
358
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500359namespace {
360// This is a class soley so it can be partially specialized (functions cannot be).
Brian Salomon86c40012018-05-22 10:48:49 -0400361template <typename V, GrAA AA = V::kAA, typename Position = typename V::Position>
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400362class VertexAAHandler;
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500363
Brian Salomon86c40012018-05-22 10:48:49 -0400364template<typename V> class VertexAAHandler<V, GrAA::kNo, SkPoint> {
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500365public:
Brian Salomon86c40012018-05-22 10:48:49 -0400366 static void AssignPositionsAndTexCoords(V* vertices, const GrPerspQuad& quad,
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500367 const SkRect& texRect) {
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400368 SkASSERT((quad.w4f() == Sk4f(1.f)).allTrue());
Brian Salomon86c40012018-05-22 10:48:49 -0400369 SkPointPriv::SetRectTriStrip(&vertices[0].fTextureCoords, texRect, sizeof(V));
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400370 for (int i = 0; i < 4; ++i) {
371 vertices[i].fPosition = {quad.x(i), quad.y(i)};
372 }
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500373 }
374};
375
Brian Salomon86c40012018-05-22 10:48:49 -0400376template<typename V> class VertexAAHandler<V, GrAA::kNo, SkPoint3> {
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500377public:
Brian Salomon86c40012018-05-22 10:48:49 -0400378 static void AssignPositionsAndTexCoords(V* vertices, const GrPerspQuad& quad,
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500379 const SkRect& texRect) {
Brian Salomon86c40012018-05-22 10:48:49 -0400380 SkPointPriv::SetRectTriStrip(&vertices[0].fTextureCoords, texRect, sizeof(V));
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400381 for (int i = 0; i < 4; ++i) {
382 vertices[i].fPosition = quad.point(i);
383 }
384 }
385};
386
Brian Salomon86c40012018-05-22 10:48:49 -0400387template<typename V> class VertexAAHandler<V, GrAA::kYes, SkPoint> {
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400388public:
Brian Salomon86c40012018-05-22 10:48:49 -0400389 static void AssignPositionsAndTexCoords(V* vertices, const GrPerspQuad& quad,
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400390 const SkRect& texRect) {
391 SkASSERT((quad.w4f() == Sk4f(1.f)).allTrue());
Brian Salomon6872e942018-05-18 10:29:54 -0400392 auto x = quad.x4f();
393 auto y = quad.y4f();
394 Sk4f a, b, c;
395 compute_quad_edges_and_outset_vertices(&x, &y, &a, &b, &c);
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500396
397 for (int i = 0; i < 4; ++i) {
Brian Salomon6872e942018-05-18 10:29:54 -0400398 vertices[i].fPosition = {x[i], y[i]};
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500399 for (int j = 0; j < 4; ++j) {
Brian Salomon6872e942018-05-18 10:29:54 -0400400 vertices[i].fEdges[j] = {a[j], b[j], c[j]};
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500401 }
402 }
403
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500404 AssignTexCoords(vertices, quad, texRect);
405 }
406
407private:
Brian Salomon86c40012018-05-22 10:48:49 -0400408 static void AssignTexCoords(V* vertices, const GrPerspQuad& quad, const SkRect& tex) {
Brian Salomona33b67c2018-05-17 10:42:14 -0400409 SkMatrix q = SkMatrix::MakeAll(quad.x(0), quad.x(1), quad.x(2),
410 quad.y(0), quad.y(1), quad.y(2),
411 1.f, 1.f, 1.f);
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500412 SkMatrix qinv;
413 if (!q.invert(&qinv)) {
414 return;
415 }
416 SkMatrix t = SkMatrix::MakeAll(tex.fLeft, tex.fLeft, tex.fRight,
417 tex.fTop, tex.fBottom, tex.fTop,
418 1.f, 1.f, 1.f);
419 SkMatrix map;
420 map.setConcat(t, qinv);
Brian Salomon86c40012018-05-22 10:48:49 -0400421 SkMatrixPriv::MapPointsWithStride(map, &vertices[0].fTextureCoords, sizeof(V),
422 &vertices[0].fPosition, sizeof(V), 4);
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500423 }
424};
425
Brian Salomon86c40012018-05-22 10:48:49 -0400426template<typename V> class VertexAAHandler<V, GrAA::kYes, SkPoint3> {
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400427public:
Brian Salomon86c40012018-05-22 10:48:49 -0400428 static void AssignPositionsAndTexCoords(V* vertices, const GrPerspQuad& quad,
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400429 const SkRect& texRect) {
430 auto x = quad.x4f();
431 auto y = quad.y4f();
432 auto iw = quad.iw4f();
433 x *= iw;
434 y *= iw;
435
436 // Get an equation for w from device space coords.
437 SkMatrix P;
438 P.setAll(x[0], y[0], 1, x[1], y[1], 1, x[2], y[2], 1);
439 SkAssertResult(P.invert(&P));
440 SkPoint3 weq{quad.w(0), quad.w(1), quad.w(2)};
441 P.mapHomogeneousPoints(&weq, &weq, 1);
442
443 Sk4f a, b, c;
444 compute_quad_edges_and_outset_vertices(&x, &y, &a, &b, &c);
445
446 // Compute new w values for the output vertices;
447 auto w = Sk4f(weq.fX) * x + Sk4f(weq.fY) * y + Sk4f(weq.fZ);
448 x *= w;
449 y *= w;
450
451 for (int i = 0; i < 4; ++i) {
452 vertices[i].fPosition = {x[i], y[i], w[i]};
453 for (int j = 0; j < 4; ++j) {
454 vertices[i].fEdges[j] = {a[j], b[j], c[j]};
455 }
456 }
457
458 AssignTexCoords(vertices, quad, texRect);
459 }
460
461private:
Brian Salomon86c40012018-05-22 10:48:49 -0400462 static void AssignTexCoords(V* vertices, const GrPerspQuad& quad, const SkRect& tex) {
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400463 SkMatrix q = SkMatrix::MakeAll(quad.x(0), quad.x(1), quad.x(2),
464 quad.y(0), quad.y(1), quad.y(2),
465 quad.w(0), quad.w(1), quad.w(2));
466 SkMatrix qinv;
467 if (!q.invert(&qinv)) {
468 return;
469 }
470 SkMatrix t = SkMatrix::MakeAll(tex.fLeft, tex.fLeft, tex.fRight,
471 tex.fTop, tex.fBottom, tex.fTop,
472 1.f, 1.f, 1.f);
473 SkMatrix map;
474 map.setConcat(t, qinv);
475 SkPoint3 tempTexCoords[4];
476 SkMatrixPriv::MapHomogeneousPointsWithStride(map, tempTexCoords, sizeof(SkPoint3),
Brian Salomon86c40012018-05-22 10:48:49 -0400477 &vertices[0].fPosition, sizeof(V), 4);
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400478 for (int i = 0; i < 4; ++i) {
479 auto invW = 1.f / tempTexCoords[i].fZ;
480 vertices[i].fTextureCoords.fX = tempTexCoords[i].fX * invW;
481 vertices[i].fTextureCoords.fY = tempTexCoords[i].fY * invW;
482 }
483 }
484};
485
Brian Salomon86c40012018-05-22 10:48:49 -0400486template <typename V, bool MT = V::kIsMultiTexture> struct TexIdAssigner;
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500487
Brian Salomon86c40012018-05-22 10:48:49 -0400488template <typename V> struct TexIdAssigner<V, true> {
489 static void Assign(V* vertices, int textureIdx) {
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400490 for (int i = 0; i < 4; ++i) {
491 vertices[i].fTextureIdx = textureIdx;
492 }
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500493 }
494};
495
Brian Salomon86c40012018-05-22 10:48:49 -0400496template <typename V> struct TexIdAssigner<V, false> {
497 static void Assign(V* vertices, int textureIdx) {}
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500498};
499} // anonymous namespace
500
Brian Salomon86c40012018-05-22 10:48:49 -0400501template <typename V>
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400502static void tessellate_quad(const GrPerspQuad& devQuad, const SkRect& srcRect, GrColor color,
Brian Salomon86c40012018-05-22 10:48:49 -0400503 GrSurfaceOrigin origin, V* vertices, SkScalar iw, SkScalar ih,
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500504 int textureIdx) {
505 SkRect texRect = {
506 iw * srcRect.fLeft,
507 ih * srcRect.fTop,
508 iw * srcRect.fRight,
509 ih * srcRect.fBottom
510 };
511 if (origin == kBottomLeft_GrSurfaceOrigin) {
512 texRect.fTop = 1.f - texRect.fTop;
513 texRect.fBottom = 1.f - texRect.fBottom;
514 }
Brian Salomon86c40012018-05-22 10:48:49 -0400515 VertexAAHandler<V>::AssignPositionsAndTexCoords(vertices, devQuad, texRect);
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500516 vertices[0].fColor = color;
517 vertices[1].fColor = color;
518 vertices[2].fColor = color;
519 vertices[3].fColor = color;
Brian Salomon86c40012018-05-22 10:48:49 -0400520 TexIdAssigner<V>::Assign(vertices, textureIdx);
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500521}
Brian Salomon34169692017-08-28 15:32:01 -0400522/**
523 * Op that implements GrTextureOp::Make. It draws textured quads. Each quad can modulate against a
524 * the texture by color. The blend with the destination is always src-over. The edges are non-AA.
525 */
526class TextureOp final : public GrMeshDrawOp {
527public:
528 static std::unique_ptr<GrDrawOp> Make(sk_sp<GrTextureProxy> proxy,
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400529 GrSamplerState::Filter filter, GrColor color,
Brian Salomon485b8c62018-01-12 15:11:06 -0500530 const SkRect& srcRect, const SkRect& dstRect,
531 GrAAType aaType, const SkMatrix& viewMatrix,
532 sk_sp<GrColorSpaceXform> csxf, bool allowSRBInputs) {
Brian Salomon34169692017-08-28 15:32:01 -0400533 return std::unique_ptr<GrDrawOp>(new TextureOp(std::move(proxy), filter, color, srcRect,
Brian Salomon485b8c62018-01-12 15:11:06 -0500534 dstRect, aaType, viewMatrix, std::move(csxf),
Brian Salomon34169692017-08-28 15:32:01 -0400535 allowSRBInputs));
536 }
537
Brian Salomon336ce7b2017-09-08 08:23:58 -0400538 ~TextureOp() override {
539 if (fFinalized) {
540 auto proxies = this->proxies();
541 for (int i = 0; i < fProxyCnt; ++i) {
542 proxies[i]->completedRead();
543 }
544 if (fProxyCnt > 1) {
545 delete[] reinterpret_cast<const char*>(proxies);
546 }
547 } else {
548 SkASSERT(1 == fProxyCnt);
549 fProxy0->unref();
550 }
551 }
Brian Salomon34169692017-08-28 15:32:01 -0400552
553 const char* name() const override { return "TextureOp"; }
554
Robert Phillipsf1748f52017-09-14 14:11:24 -0400555 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillipsb493eeb2017-09-13 13:10:52 -0400556 auto proxies = this->proxies();
557 for (int i = 0; i < fProxyCnt; ++i) {
558 func(proxies[i]);
559 }
560 }
561
Brian Salomon34169692017-08-28 15:32:01 -0400562 SkString dumpInfo() const override {
563 SkString str;
Brian Salomon336ce7b2017-09-08 08:23:58 -0400564 str.appendf("AllowSRGBInputs: %d\n", fAllowSRGBInputs);
Brian Salomon34169692017-08-28 15:32:01 -0400565 str.appendf("# draws: %d\n", fDraws.count());
Brian Salomon336ce7b2017-09-08 08:23:58 -0400566 auto proxies = this->proxies();
567 for (int i = 0; i < fProxyCnt; ++i) {
568 str.appendf("Proxy ID %d: %d, Filter: %d\n", i, proxies[i]->uniqueID().asUInt(),
569 static_cast<int>(this->filters()[i]));
570 }
Brian Salomon34169692017-08-28 15:32:01 -0400571 for (int i = 0; i < fDraws.count(); ++i) {
572 const Draw& draw = fDraws[i];
573 str.appendf(
Brian Salomon336ce7b2017-09-08 08:23:58 -0400574 "%d: Color: 0x%08x, ProxyIdx: %d, TexRect [L: %.2f, T: %.2f, R: %.2f, B: %.2f] "
575 "Quad [(%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f)]\n",
576 i, draw.fColor, draw.fTextureIdx, draw.fSrcRect.fLeft, draw.fSrcRect.fTop,
Brian Salomona33b67c2018-05-17 10:42:14 -0400577 draw.fSrcRect.fRight, draw.fSrcRect.fBottom, draw.fQuad.point(0).fX,
578 draw.fQuad.point(0).fY, draw.fQuad.point(1).fX, draw.fQuad.point(1).fY,
579 draw.fQuad.point(2).fX, draw.fQuad.point(2).fY, draw.fQuad.point(3).fX,
580 draw.fQuad.point(3).fY);
Brian Salomon34169692017-08-28 15:32:01 -0400581 }
582 str += INHERITED::dumpInfo();
583 return str;
584 }
585
Brian Osman9a725dd2017-09-20 09:53:22 -0400586 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip,
587 GrPixelConfigIsClamped dstIsClamped) override {
Brian Salomon34169692017-08-28 15:32:01 -0400588 SkASSERT(!fFinalized);
Brian Salomon336ce7b2017-09-08 08:23:58 -0400589 SkASSERT(1 == fProxyCnt);
Brian Salomon34169692017-08-28 15:32:01 -0400590 fFinalized = true;
Brian Salomon336ce7b2017-09-08 08:23:58 -0400591 fProxy0->addPendingRead();
592 fProxy0->unref();
Brian Salomon34169692017-08-28 15:32:01 -0400593 return RequiresDstTexture::kNo;
594 }
595
Brian Salomon485b8c62018-01-12 15:11:06 -0500596 FixedFunctionFlags fixedFunctionFlags() const override {
597 return this->aaType() == GrAAType::kMSAA ? FixedFunctionFlags::kUsesHWAA
598 : FixedFunctionFlags::kNone;
599 }
Brian Salomon34169692017-08-28 15:32:01 -0400600
601 DEFINE_OP_CLASS_ID
602
603private:
Brian Salomon762d5e72017-12-01 10:25:08 -0500604
605 // This is used in a heursitic for choosing a code path. We don't care what happens with
606 // really large rects, infs, nans, etc.
607#if defined(__clang__) && (__clang_major__ * 1000 + __clang_minor__) >= 3007
608__attribute__((no_sanitize("float-cast-overflow")))
609#endif
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500610 size_t RectSizeAsSizeT(const SkRect& rect) {;
Brian Salomon762d5e72017-12-01 10:25:08 -0500611 return static_cast<size_t>(SkTMax(rect.width(), 1.f) * SkTMax(rect.height(), 1.f));
612 }
613
Brian Salomon336ce7b2017-09-08 08:23:58 -0400614 static constexpr int kMaxTextures = TextureGeometryProcessor::kMaxTextures;
615
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400616 TextureOp(sk_sp<GrTextureProxy> proxy, GrSamplerState::Filter filter, GrColor color,
Brian Salomon485b8c62018-01-12 15:11:06 -0500617 const SkRect& srcRect, const SkRect& dstRect, GrAAType aaType,
618 const SkMatrix& viewMatrix, sk_sp<GrColorSpaceXform> csxf, bool allowSRGBInputs)
Brian Salomon34169692017-08-28 15:32:01 -0400619 : INHERITED(ClassID())
Brian Salomon34169692017-08-28 15:32:01 -0400620 , fColorSpaceXform(std::move(csxf))
Brian Salomon336ce7b2017-09-08 08:23:58 -0400621 , fProxy0(proxy.release())
622 , fFilter0(filter)
623 , fProxyCnt(1)
Brian Salomon485b8c62018-01-12 15:11:06 -0500624 , fAAType(static_cast<unsigned>(aaType))
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500625 , fFinalized(0)
626 , fAllowSRGBInputs(allowSRGBInputs ? 1 : 0) {
Brian Salomon485b8c62018-01-12 15:11:06 -0500627 SkASSERT(aaType != GrAAType::kMixedSamples);
Brian Salomon34169692017-08-28 15:32:01 -0400628 Draw& draw = fDraws.push_back();
629 draw.fSrcRect = srcRect;
Brian Salomon336ce7b2017-09-08 08:23:58 -0400630 draw.fTextureIdx = 0;
Brian Salomon34169692017-08-28 15:32:01 -0400631 draw.fColor = color;
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400632 fPerspective = viewMatrix.hasPerspective();
633 SkRect bounds;
634 draw.fQuad = GrPerspQuad(dstRect, viewMatrix);
635 bounds = draw.fQuad.bounds();
Brian Salomon34169692017-08-28 15:32:01 -0400636 this->setBounds(bounds, HasAABloat::kNo, IsZeroArea::kNo);
Brian Salomon762d5e72017-12-01 10:25:08 -0500637
638 fMaxApproxDstPixelArea = RectSizeAsSizeT(bounds);
Brian Salomon34169692017-08-28 15:32:01 -0400639 }
640
641 void onPrepareDraws(Target* target) override {
Brian Salomon336ce7b2017-09-08 08:23:58 -0400642 sk_sp<GrTextureProxy> proxiesSPs[kMaxTextures];
643 auto proxies = this->proxies();
644 auto filters = this->filters();
645 for (int i = 0; i < fProxyCnt; ++i) {
646 if (!proxies[i]->instantiate(target->resourceProvider())) {
647 return;
648 }
649 proxiesSPs[i] = sk_ref_sp(proxies[i]);
Brian Salomon34169692017-08-28 15:32:01 -0400650 }
Brian Salomon336ce7b2017-09-08 08:23:58 -0400651
Brian Salomon485b8c62018-01-12 15:11:06 -0500652 bool coverageAA = GrAAType::kCoverage == this->aaType();
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400653 sk_sp<GrGeometryProcessor> gp = TextureGeometryProcessor::Make(
654 proxiesSPs, fProxyCnt, std::move(fColorSpaceXform), coverageAA, fPerspective,
655 filters, *target->caps().shaderCaps());
Brian Salomon34169692017-08-28 15:32:01 -0400656 GrPipeline::InitArgs args;
657 args.fProxy = target->proxy();
658 args.fCaps = &target->caps();
659 args.fResourceProvider = target->resourceProvider();
Brian Salomon485b8c62018-01-12 15:11:06 -0500660 args.fFlags = 0;
661 if (fAllowSRGBInputs) {
662 args.fFlags |= GrPipeline::kAllowSRGBInputs_Flag;
663 }
664 if (GrAAType::kMSAA == this->aaType()) {
665 args.fFlags |= GrPipeline::kHWAntialias_Flag;
666 }
667
Brian Salomon34169692017-08-28 15:32:01 -0400668 const GrPipeline* pipeline = target->allocPipeline(args, GrProcessorSet::MakeEmptySet(),
669 target->detachAppliedClip());
Brian Salomon34169692017-08-28 15:32:01 -0400670 int vstart;
671 const GrBuffer* vbuffer;
Brian Salomon336ce7b2017-09-08 08:23:58 -0400672 void* vdata = target->makeVertexSpace(gp->getVertexStride(), 4 * fDraws.count(), &vbuffer,
673 &vstart);
674 if (!vdata) {
Brian Salomon34169692017-08-28 15:32:01 -0400675 SkDebugf("Could not allocate vertices\n");
676 return;
677 }
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400678
679// Generic lambda in C++14?
680#define TESS_VERTS(Vertex) \
681 SkASSERT(gp->getVertexStride() == sizeof(Vertex)); \
682 auto vertices = static_cast<Vertex*>(vdata); \
683 for (const auto& draw : fDraws) { \
684 auto origin = proxies[draw.fTextureIdx]->origin(); \
685 tessellate_quad<Vertex>(draw.fQuad, draw.fSrcRect, draw.fColor, origin, vertices, \
686 iw[draw.fTextureIdx], ih[draw.fTextureIdx], draw.fTextureIdx); \
687 vertices += 4; \
688 }
689
690 float iw[kMaxTextures];
691 float ih[kMaxTextures];
692 for (int t = 0; t < fProxyCnt; ++t) {
693 const auto* texture = proxies[t]->priv().peekTexture();
694 iw[t] = 1.f / texture->width();
695 ih[t] = 1.f / texture->height();
696 }
697
Brian Salomon57caa662017-10-18 12:21:05 +0000698 if (1 == fProxyCnt) {
Brian Salomon485b8c62018-01-12 15:11:06 -0500699 if (coverageAA) {
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400700 if (fPerspective) {
701 TESS_VERTS(TextureGeometryProcessor::AAVertex<SkPoint3>)
702 } else {
703 TESS_VERTS(TextureGeometryProcessor::AAVertex<SkPoint>)
Brian Salomon57caa662017-10-18 12:21:05 +0000704 }
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500705 } else {
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400706 if (fPerspective) {
707 TESS_VERTS(TextureGeometryProcessor::Vertex<SkPoint3>)
708 } else {
709 TESS_VERTS(TextureGeometryProcessor::Vertex<SkPoint>)
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500710 }
Brian Salomon57caa662017-10-18 12:21:05 +0000711 }
712 } else {
Brian Salomon485b8c62018-01-12 15:11:06 -0500713 if (coverageAA) {
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400714 if (fPerspective) {
715 TESS_VERTS(TextureGeometryProcessor::AAMultiTextureVertex<SkPoint3>)
716 } else {
717 TESS_VERTS(TextureGeometryProcessor::AAMultiTextureVertex<SkPoint>)
Brian Salomon57caa662017-10-18 12:21:05 +0000718 }
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500719 } else {
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400720 if (fPerspective) {
721 TESS_VERTS(TextureGeometryProcessor::MultiTextureVertex<SkPoint3>)
722 } else {
723 TESS_VERTS(TextureGeometryProcessor::MultiTextureVertex<SkPoint>)
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500724 }
Brian Salomon57caa662017-10-18 12:21:05 +0000725 }
726 }
727 GrPrimitiveType primitiveType =
728 fDraws.count() > 1 ? GrPrimitiveType::kTriangles : GrPrimitiveType::kTriangleStrip;
729 GrMesh mesh(primitiveType);
Brian Salomon34169692017-08-28 15:32:01 -0400730 if (fDraws.count() > 1) {
Brian Salomon57caa662017-10-18 12:21:05 +0000731 sk_sp<const GrBuffer> ibuffer = target->resourceProvider()->refQuadIndexBuffer();
Brian Salomon34169692017-08-28 15:32:01 -0400732 if (!ibuffer) {
733 SkDebugf("Could not allocate quad indices\n");
734 return;
735 }
Brian Salomon34169692017-08-28 15:32:01 -0400736 mesh.setIndexedPatterned(ibuffer.get(), 6, 4, fDraws.count(),
737 GrResourceProvider::QuadCountOfQuadBuffer());
Brian Salomon34169692017-08-28 15:32:01 -0400738 } else {
Brian Salomon34169692017-08-28 15:32:01 -0400739 mesh.setNonIndexedNonInstanced(4);
Brian Salomon34169692017-08-28 15:32:01 -0400740 }
Brian Salomon57caa662017-10-18 12:21:05 +0000741 mesh.setVertexData(vbuffer, vstart);
742 target->draw(gp.get(), pipeline, mesh);
Brian Salomon34169692017-08-28 15:32:01 -0400743 }
744
745 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
746 const auto* that = t->cast<TextureOp>();
Brian Salomon762d5e72017-12-01 10:25:08 -0500747 const auto& shaderCaps = *caps.shaderCaps();
Brian Salomon336ce7b2017-09-08 08:23:58 -0400748 if (!GrColorSpaceXform::Equals(fColorSpaceXform.get(), that->fColorSpaceXform.get())) {
Brian Salomon34169692017-08-28 15:32:01 -0400749 return false;
750 }
Brian Salomon485b8c62018-01-12 15:11:06 -0500751 if (this->aaType() != that->aaType()) {
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500752 return false;
753 }
Brian Salomon336ce7b2017-09-08 08:23:58 -0400754 // Because of an issue where GrColorSpaceXform adds the same function every time it is used
755 // in a texture lookup, we only allow multiple textures when there is no transform.
Brian Salomon762d5e72017-12-01 10:25:08 -0500756 if (TextureGeometryProcessor::SupportsMultitexture(shaderCaps) && !fColorSpaceXform &&
757 fMaxApproxDstPixelArea <= shaderCaps.disableImageMultitexturingDstRectAreaThreshold() &&
758 that->fMaxApproxDstPixelArea <=
759 shaderCaps.disableImageMultitexturingDstRectAreaThreshold()) {
Brian Salomon336ce7b2017-09-08 08:23:58 -0400760 int map[kMaxTextures];
Brian Salomon762d5e72017-12-01 10:25:08 -0500761 int numNewProxies = this->mergeProxies(that, map, shaderCaps);
Brian Salomon336ce7b2017-09-08 08:23:58 -0400762 if (numNewProxies < 0) {
763 return false;
764 }
765 if (1 == fProxyCnt && numNewProxies) {
766 void* mem = new char[(sizeof(GrSamplerState::Filter) + sizeof(GrTextureProxy*)) *
767 kMaxTextures];
768 auto proxies = reinterpret_cast<GrTextureProxy**>(mem);
769 auto filters = reinterpret_cast<GrSamplerState::Filter*>(proxies + kMaxTextures);
770 proxies[0] = fProxy0;
771 filters[0] = fFilter0;
772 fProxyArray = proxies;
773 }
774 fProxyCnt += numNewProxies;
775 auto thisProxies = fProxyArray;
776 auto thatProxies = that->proxies();
777 auto thatFilters = that->filters();
778 auto thisFilters = reinterpret_cast<GrSamplerState::Filter*>(thisProxies +
779 kMaxTextures);
780 for (int i = 0; i < that->fProxyCnt; ++i) {
781 if (map[i] < 0) {
782 thatProxies[i]->addPendingRead();
Robert Phillipsb493eeb2017-09-13 13:10:52 -0400783
Brian Salomon336ce7b2017-09-08 08:23:58 -0400784 thisProxies[-map[i]] = thatProxies[i];
785 thisFilters[-map[i]] = thatFilters[i];
786 map[i] = -map[i];
787 }
788 }
789 int firstNewDraw = fDraws.count();
790 fDraws.push_back_n(that->fDraws.count(), that->fDraws.begin());
791 for (int i = firstNewDraw; i < fDraws.count(); ++i) {
792 fDraws[i].fTextureIdx = map[fDraws[i].fTextureIdx];
793 }
794 } else {
Brian Salomonbbf05752017-11-30 11:30:48 -0500795 // We can get here when one of the ops is already multitextured but the other cannot
796 // be because of the dst rect size.
797 if (fProxyCnt > 1 || that->fProxyCnt > 1) {
798 return false;
799 }
Brian Salomon336ce7b2017-09-08 08:23:58 -0400800 if (fProxy0->uniqueID() != that->fProxy0->uniqueID() || fFilter0 != that->fFilter0) {
801 return false;
802 }
803 fDraws.push_back_n(that->fDraws.count(), that->fDraws.begin());
804 }
Brian Salomon34169692017-08-28 15:32:01 -0400805 this->joinBounds(*that);
Brian Salomon762d5e72017-12-01 10:25:08 -0500806 fMaxApproxDstPixelArea = SkTMax(that->fMaxApproxDstPixelArea, fMaxApproxDstPixelArea);
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400807 fPerspective |= that->fPerspective;
Brian Salomon34169692017-08-28 15:32:01 -0400808 return true;
809 }
810
Brian Salomon336ce7b2017-09-08 08:23:58 -0400811 /**
812 * Determines a mapping of indices from that's proxy array to this's proxy array. A negative map
813 * value means that's proxy should be added to this's proxy array at the absolute value of
814 * the map entry. If it is determined that the ops shouldn't combine their proxies then a
815 * negative value is returned. Otherwise, return value indicates the number of proxies that have
816 * to be added to this op or, equivalently, the number of negative entries in map.
817 */
818 int mergeProxies(const TextureOp* that, int map[kMaxTextures], const GrShaderCaps& caps) const {
819 std::fill_n(map, kMaxTextures, -kMaxTextures);
820 int sharedProxyCnt = 0;
821 auto thisProxies = this->proxies();
822 auto thisFilters = this->filters();
823 auto thatProxies = that->proxies();
824 auto thatFilters = that->filters();
825 for (int i = 0; i < fProxyCnt; ++i) {
826 for (int j = 0; j < that->fProxyCnt; ++j) {
827 if (thisProxies[i]->uniqueID() == thatProxies[j]->uniqueID()) {
828 if (thisFilters[i] != thatFilters[j]) {
829 // In GL we don't currently support using the same texture with different
830 // samplers. If we added support for sampler objects and a cap bit to know
831 // it's ok to use different filter modes then we could support this.
832 // Otherwise, we could also only allow a single filter mode for each op
833 // instance.
834 return -1;
835 }
836 map[j] = i;
837 ++sharedProxyCnt;
838 break;
839 }
840 }
841 }
Brian Salomon2b6f6142017-11-13 11:49:13 -0500842 int actualMaxTextures = SkTMin(caps.maxFragmentSamplers(), kMaxTextures);
Brian Salomon336ce7b2017-09-08 08:23:58 -0400843 int newProxyCnt = that->fProxyCnt - sharedProxyCnt;
844 if (newProxyCnt + fProxyCnt > actualMaxTextures) {
845 return -1;
846 }
847 GrPixelConfig config = thisProxies[0]->config();
848 int nextSlot = fProxyCnt;
849 for (int j = 0; j < that->fProxyCnt; ++j) {
850 // We want to avoid making many shaders because of different permutations of shader
851 // based swizzle and sampler types. The approach taken here is to require the configs to
852 // be the same and to only allow already instantiated proxies that have the most
853 // common sampler type. Otherwise we don't merge.
854 if (thatProxies[j]->config() != config) {
855 return -1;
856 }
857 if (GrTexture* tex = thatProxies[j]->priv().peekTexture()) {
858 if (tex->texturePriv().samplerType() != kTexture2DSampler_GrSLType) {
859 return -1;
860 }
861 }
862 if (map[j] < 0) {
863 map[j] = -(nextSlot++);
864 }
865 }
866 return newProxyCnt;
867 }
868
Brian Salomon485b8c62018-01-12 15:11:06 -0500869 GrAAType aaType() const { return static_cast<GrAAType>(fAAType); }
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500870
Brian Salomon336ce7b2017-09-08 08:23:58 -0400871 GrTextureProxy* const* proxies() const { return fProxyCnt > 1 ? fProxyArray : &fProxy0; }
872
873 const GrSamplerState::Filter* filters() const {
874 if (fProxyCnt > 1) {
875 return reinterpret_cast<const GrSamplerState::Filter*>(fProxyArray + kMaxTextures);
876 }
877 return &fFilter0;
878 }
879
Brian Salomon34169692017-08-28 15:32:01 -0400880 struct Draw {
881 SkRect fSrcRect;
Brian Salomon336ce7b2017-09-08 08:23:58 -0400882 int fTextureIdx;
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400883 GrPerspQuad fQuad;
Brian Salomon34169692017-08-28 15:32:01 -0400884 GrColor fColor;
885 };
886 SkSTArray<1, Draw, true> fDraws;
Brian Salomon34169692017-08-28 15:32:01 -0400887 sk_sp<GrColorSpaceXform> fColorSpaceXform;
Brian Salomon336ce7b2017-09-08 08:23:58 -0400888 // Initially we store a single proxy ptr and a single filter. If we grow to have more than
889 // one proxy we instead store pointers to dynamically allocated arrays of size kMaxTextures
890 // followed by kMaxTextures filters.
891 union {
892 GrTextureProxy* fProxy0;
893 GrTextureProxy** fProxyArray;
894 };
Brian Salomonbbf05752017-11-30 11:30:48 -0500895 size_t fMaxApproxDstPixelArea;
Brian Salomon336ce7b2017-09-08 08:23:58 -0400896 GrSamplerState::Filter fFilter0;
897 uint8_t fProxyCnt;
Brian Salomon485b8c62018-01-12 15:11:06 -0500898 unsigned fAAType : 2;
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400899 unsigned fPerspective : 1;
Brian Salomon34169692017-08-28 15:32:01 -0400900 // Used to track whether fProxy is ref'ed or has a pending IO after finalize() is called.
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500901 unsigned fFinalized : 1;
902 unsigned fAllowSRGBInputs : 1;
Brian Salomon336ce7b2017-09-08 08:23:58 -0400903
Brian Salomon34169692017-08-28 15:32:01 -0400904 typedef GrMeshDrawOp INHERITED;
905};
906
Brian Salomon336ce7b2017-09-08 08:23:58 -0400907constexpr int TextureGeometryProcessor::kMaxTextures;
908constexpr int TextureOp::kMaxTextures;
909
Brian Salomon34169692017-08-28 15:32:01 -0400910} // anonymous namespace
911
912namespace GrTextureOp {
913
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400914std::unique_ptr<GrDrawOp> Make(sk_sp<GrTextureProxy> proxy, GrSamplerState::Filter filter,
Brian Salomon485b8c62018-01-12 15:11:06 -0500915 GrColor color, const SkRect& srcRect, const SkRect& dstRect,
916 GrAAType aaType, const SkMatrix& viewMatrix,
917 sk_sp<GrColorSpaceXform> csxf, bool allowSRGBInputs) {
Brian Salomon485b8c62018-01-12 15:11:06 -0500918 return TextureOp::Make(std::move(proxy), filter, color, srcRect, dstRect, aaType, viewMatrix,
Brian Salomon34169692017-08-28 15:32:01 -0400919 std::move(csxf), allowSRGBInputs);
920}
921
922} // namespace GrTextureOp
923
924#if GR_TEST_UTILS
925#include "GrContext.h"
Robert Phillips1afd4cd2018-01-08 13:40:32 -0500926#include "GrContextPriv.h"
Robert Phillips0bd24dc2018-01-16 08:06:32 -0500927#include "GrProxyProvider.h"
Brian Salomon34169692017-08-28 15:32:01 -0400928
929GR_DRAW_OP_TEST_DEFINE(TextureOp) {
930 GrSurfaceDesc desc;
931 desc.fConfig = kRGBA_8888_GrPixelConfig;
932 desc.fHeight = random->nextULessThan(90) + 10;
933 desc.fWidth = random->nextULessThan(90) + 10;
Brian Salomon2a4f9832018-03-03 22:43:43 -0500934 auto origin = random->nextBool() ? kTopLeft_GrSurfaceOrigin : kBottomLeft_GrSurfaceOrigin;
Brian Salomon34169692017-08-28 15:32:01 -0400935 SkBackingFit fit = random->nextBool() ? SkBackingFit::kApprox : SkBackingFit::kExact;
Robert Phillips0bd24dc2018-01-16 08:06:32 -0500936
937 GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider();
Brian Salomon2a4f9832018-03-03 22:43:43 -0500938 sk_sp<GrTextureProxy> proxy = proxyProvider->createProxy(desc, origin, fit, SkBudgeted::kNo);
Robert Phillips0bd24dc2018-01-16 08:06:32 -0500939
Brian Salomon34169692017-08-28 15:32:01 -0400940 SkRect rect = GrTest::TestRect(random);
941 SkRect srcRect;
942 srcRect.fLeft = random->nextRangeScalar(0.f, proxy->width() / 2.f);
943 srcRect.fRight = random->nextRangeScalar(0.f, proxy->width()) + proxy->width() / 2.f;
944 srcRect.fTop = random->nextRangeScalar(0.f, proxy->height() / 2.f);
945 srcRect.fBottom = random->nextRangeScalar(0.f, proxy->height()) + proxy->height() / 2.f;
946 SkMatrix viewMatrix = GrTest::TestMatrixPreservesRightAngles(random);
947 GrColor color = SkColorToPremulGrColor(random->nextU());
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400948 GrSamplerState::Filter filter = (GrSamplerState::Filter)random->nextULessThan(
949 static_cast<uint32_t>(GrSamplerState::Filter::kMipMap) + 1);
Brian Salomon34169692017-08-28 15:32:01 -0400950 auto csxf = GrTest::TestColorXform(random);
951 bool allowSRGBInputs = random->nextBool();
Brian Salomon485b8c62018-01-12 15:11:06 -0500952 GrAAType aaType = GrAAType::kNone;
953 if (random->nextBool()) {
954 aaType = (fsaaType == GrFSAAType::kUnifiedMSAA) ? GrAAType::kMSAA : GrAAType::kCoverage;
955 }
956 return GrTextureOp::Make(std::move(proxy), filter, color, srcRect, rect, aaType, viewMatrix,
Brian Salomon34169692017-08-28 15:32:01 -0400957 std::move(csxf), allowSRGBInputs);
958}
959
960#endif