blob: 68a5851afec34e0bf0b208e321ddfc06ab1cfcf7 [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"
Mike Klein79aea6a2018-06-11 10:45:26 -040031#include <new>
Brian Salomon34169692017-08-28 15:32:01 -040032
33namespace {
34
Brian Salomon17031a72018-05-22 14:14:07 -040035enum class MultiTexture : bool { kNo = false, kYes = true };
36
Brian Salomonb80ffee2018-05-23 16:39:39 -040037enum class Domain : bool { kNo = false, kYes = true };
38
Brian Salomon34169692017-08-28 15:32:01 -040039/**
40 * Geometry Processor that draws a texture modulated by a vertex color (though, this is meant to be
41 * the same value across all vertices of a quad and uses flat interpolation when available). This is
42 * used by TextureOp below.
43 */
44class TextureGeometryProcessor : public GrGeometryProcessor {
45public:
Brian Salomon17031a72018-05-22 14:14:07 -040046 template <typename Pos> struct VertexCommon {
47 using Position = Pos;
48 Position fPosition;
Brian Salomon34169692017-08-28 15:32:01 -040049 GrColor fColor;
Brian Salomon17031a72018-05-22 14:14:07 -040050 SkPoint fTextureCoords;
Brian Salomon34169692017-08-28 15:32:01 -040051 };
Brian Salomon17031a72018-05-22 14:14:07 -040052
53 template <typename Pos, MultiTexture MT> struct OptionalMultiTextureVertex;
54 template <typename Pos>
55 struct OptionalMultiTextureVertex<Pos, MultiTexture::kNo> : VertexCommon<Pos> {
56 static constexpr MultiTexture kMultiTexture = MultiTexture::kNo;
Brian Salomonb5ef1f92018-01-11 11:46:21 -050057 };
Brian Salomon17031a72018-05-22 14:14:07 -040058 template <typename Pos>
59 struct OptionalMultiTextureVertex<Pos, MultiTexture::kYes> : VertexCommon<Pos> {
60 static constexpr MultiTexture kMultiTexture = MultiTexture::kYes;
Brian Salomon336ce7b2017-09-08 08:23:58 -040061 int fTextureIdx;
Brian Salomon336ce7b2017-09-08 08:23:58 -040062 };
Brian Salomon17031a72018-05-22 14:14:07 -040063
Brian Salomonb80ffee2018-05-23 16:39:39 -040064 template <typename Pos, MultiTexture MT, Domain D> struct OptionalDomainVertex;
Brian Salomon17031a72018-05-22 14:14:07 -040065 template <typename Pos, MultiTexture MT>
Brian Salomonb80ffee2018-05-23 16:39:39 -040066 struct OptionalDomainVertex<Pos, MT, Domain::kNo> : OptionalMultiTextureVertex<Pos, MT> {
67 static constexpr Domain kDomain = Domain::kNo;
Brian Salomona0047bc2018-05-23 16:39:39 -040068 };
Stephen White633f20e2018-05-26 18:07:27 +000069 template <typename Pos, MultiTexture MT>
Brian Salomonb80ffee2018-05-23 16:39:39 -040070 struct OptionalDomainVertex<Pos, MT, Domain::kYes> : OptionalMultiTextureVertex<Pos, MT> {
71 static constexpr Domain kDomain = Domain::kYes;
72 SkRect fTextureDomain;
73 };
74
75 template <typename Pos, MultiTexture MT, Domain D, GrAA> struct OptionalAAVertex;
76 template <typename Pos, MultiTexture MT, Domain D>
77 struct OptionalAAVertex<Pos, MT, D, GrAA::kNo> : OptionalDomainVertex<Pos, MT, D> {
78 static constexpr GrAA kAA = GrAA::kNo;
79 };
80 template <typename Pos, MultiTexture MT, Domain D>
81 struct OptionalAAVertex<Pos, MT, D, GrAA::kYes> : OptionalDomainVertex<Pos, MT, D> {
Brian Salomonbe3c1d22018-05-21 12:54:39 -040082 static constexpr GrAA kAA = GrAA::kYes;
Brian Salomonb5ef1f92018-01-11 11:46:21 -050083 SkPoint3 fEdges[4];
Brian Salomonb5ef1f92018-01-11 11:46:21 -050084 };
Brian Salomon336ce7b2017-09-08 08:23:58 -040085
Brian Salomonb80ffee2018-05-23 16:39:39 -040086 template <typename Pos, MultiTexture MT, Domain D, GrAA AA>
87 using Vertex = OptionalAAVertex<Pos, MT, D, AA>;
Brian Salomon17031a72018-05-22 14:14:07 -040088
Brian Salomon336ce7b2017-09-08 08:23:58 -040089 // Maximum number of textures supported by this op. Must also be checked against the caps
90 // limit. These numbers were based on some limited experiments on a HP Z840 and Pixel XL 2016
91 // and could probably use more tuning.
92#ifdef SK_BUILD_FOR_ANDROID
93 static constexpr int kMaxTextures = 4;
94#else
95 static constexpr int kMaxTextures = 8;
96#endif
97
Brian Salomon0b4d8aa2017-10-11 15:34:27 -040098 static int SupportsMultitexture(const GrShaderCaps& caps) {
Brian Salomon762d5e72017-12-01 10:25:08 -050099 return caps.integerSupport() && caps.maxFragmentSamplers() > 1;
Brian Salomon0b4d8aa2017-10-11 15:34:27 -0400100 }
Brian Salomon336ce7b2017-09-08 08:23:58 -0400101
102 static sk_sp<GrGeometryProcessor> Make(sk_sp<GrTextureProxy> proxies[], int proxyCnt,
Brian Salomon485b8c62018-01-12 15:11:06 -0500103 sk_sp<GrColorSpaceXform> csxf, bool coverageAA,
Brian Salomonb80ffee2018-05-23 16:39:39 -0400104 bool perspective, Domain domain,
105 const GrSamplerState::Filter filters[],
Brian Salomon336ce7b2017-09-08 08:23:58 -0400106 const GrShaderCaps& caps) {
107 // We use placement new to avoid always allocating space for kMaxTextures TextureSampler
108 // instances.
109 int samplerCnt = NumSamplersToUse(proxyCnt, caps);
110 size_t size = sizeof(TextureGeometryProcessor) + sizeof(TextureSampler) * (samplerCnt - 1);
111 void* mem = GrGeometryProcessor::operator new(size);
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400112 return sk_sp<TextureGeometryProcessor>(
113 new (mem) TextureGeometryProcessor(proxies, proxyCnt, samplerCnt, std::move(csxf),
Brian Salomonb80ffee2018-05-23 16:39:39 -0400114 coverageAA, perspective, domain, filters, caps));
Brian Salomon336ce7b2017-09-08 08:23:58 -0400115 }
116
117 ~TextureGeometryProcessor() override {
118 int cnt = this->numTextureSamplers();
119 for (int i = 1; i < cnt; ++i) {
120 fSamplers[i].~TextureSampler();
121 }
Brian Salomon34169692017-08-28 15:32:01 -0400122 }
123
124 const char* name() const override { return "TextureGeometryProcessor"; }
125
126 void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const override {
127 b->add32(GrColorSpaceXform::XformKey(fColorSpaceXform.get()));
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400128 uint32_t x = this->usesCoverageEdgeAA() ? 0 : 1;
Brian Salomon70132d02018-05-29 15:33:06 -0400129 x |= kFloat3_GrVertexAttribType == fPositions.type() ? 0 : 2;
Brian Salomonb80ffee2018-05-23 16:39:39 -0400130 x |= fDomain.isInitialized() ? 4 : 0;
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400131 b->add32(x);
Brian Salomon34169692017-08-28 15:32:01 -0400132 }
133
134 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps& caps) const override {
135 class GLSLProcessor : public GrGLSLGeometryProcessor {
136 public:
137 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& proc,
138 FPCoordTransformIter&& transformIter) override {
139 const auto& textureGP = proc.cast<TextureGeometryProcessor>();
140 this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
141 if (fColorSpaceXformHelper.isValid()) {
142 fColorSpaceXformHelper.setData(pdman, textureGP.fColorSpaceXform.get());
143 }
144 }
145
146 private:
147 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
Chris Dalton7b046312018-02-02 11:06:30 -0700148 using Interpolation = GrGLSLVaryingHandler::Interpolation;
Brian Salomon34169692017-08-28 15:32:01 -0400149 const auto& textureGP = args.fGP.cast<TextureGeometryProcessor>();
150 fColorSpaceXformHelper.emitCode(
151 args.fUniformHandler, textureGP.fColorSpaceXform.get());
Brian Salomon70132d02018-05-29 15:33:06 -0400152 if (kFloat2_GrVertexAttribType == textureGP.fPositions.type()) {
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400153 args.fVaryingHandler->setNoPerspective();
154 }
Brian Salomon34169692017-08-28 15:32:01 -0400155 args.fVaryingHandler->emitAttributes(textureGP);
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400156 gpArgs->fPositionVar = textureGP.fPositions.asShaderVar();
157
Brian Salomon34169692017-08-28 15:32:01 -0400158 this->emitTransforms(args.fVertBuilder,
159 args.fVaryingHandler,
160 args.fUniformHandler,
Brian Salomon04460cc2017-12-06 14:47:42 -0500161 textureGP.fTextureCoords.asShaderVar(),
Brian Salomon34169692017-08-28 15:32:01 -0400162 args.fFPCoordTransformHandler);
Chris Dalton7b046312018-02-02 11:06:30 -0700163 args.fVaryingHandler->addPassThroughAttribute(&textureGP.fColors,
164 args.fOutputColor,
165 Interpolation::kCanBeFlat);
Ethan Nicholas8aa45692017-09-20 11:24:15 -0400166 args.fFragBuilder->codeAppend("float2 texCoord;");
Chris Daltonfdde34e2017-10-16 14:15:26 -0600167 args.fVaryingHandler->addPassThroughAttribute(&textureGP.fTextureCoords,
168 "texCoord");
Brian Salomonb80ffee2018-05-23 16:39:39 -0400169 if (textureGP.fDomain.isInitialized()) {
170 args.fFragBuilder->codeAppend("float4 domain;");
171 args.fVaryingHandler->addPassThroughAttribute(
172 &textureGP.fDomain, "domain",
173 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
174 args.fFragBuilder->codeAppend(
175 "texCoord = clamp(texCoord, domain.xy, domain.zw);");
176 }
Brian Salomon336ce7b2017-09-08 08:23:58 -0400177 if (textureGP.numTextureSamplers() > 1) {
Chris Dalton7b046312018-02-02 11:06:30 -0700178 // If this changes to float, reconsider Interpolation::kMustBeFlat.
Brian Salomon70132d02018-05-29 15:33:06 -0400179 SkASSERT(kInt_GrVertexAttribType == textureGP.fTextureIdx.type());
Brian Salomon336ce7b2017-09-08 08:23:58 -0400180 SkASSERT(args.fShaderCaps->integerSupport());
181 args.fFragBuilder->codeAppend("int texIdx;");
Chris Dalton7b046312018-02-02 11:06:30 -0700182 args.fVaryingHandler->addPassThroughAttribute(&textureGP.fTextureIdx, "texIdx",
183 Interpolation::kMustBeFlat);
Brian Salomon336ce7b2017-09-08 08:23:58 -0400184 args.fFragBuilder->codeAppend("switch (texIdx) {");
185 for (int i = 0; i < textureGP.numTextureSamplers(); ++i) {
186 args.fFragBuilder->codeAppendf("case %d: %s = ", i, args.fOutputColor);
187 args.fFragBuilder->appendTextureLookupAndModulate(args.fOutputColor,
188 args.fTexSamplers[i],
189 "texCoord",
Ethan Nicholas8aa45692017-09-20 11:24:15 -0400190 kFloat2_GrSLType,
Brian Salomon336ce7b2017-09-08 08:23:58 -0400191 &fColorSpaceXformHelper);
192 args.fFragBuilder->codeAppend("; break;");
193 }
194 args.fFragBuilder->codeAppend("}");
195 } else {
196 args.fFragBuilder->codeAppendf("%s = ", args.fOutputColor);
197 args.fFragBuilder->appendTextureLookupAndModulate(args.fOutputColor,
198 args.fTexSamplers[0],
199 "texCoord",
Ethan Nicholas8aa45692017-09-20 11:24:15 -0400200 kFloat2_GrSLType,
Brian Salomon336ce7b2017-09-08 08:23:58 -0400201 &fColorSpaceXformHelper);
202 }
Brian Salomon34169692017-08-28 15:32:01 -0400203 args.fFragBuilder->codeAppend(";");
Brian Salomon485b8c62018-01-12 15:11:06 -0500204 if (textureGP.usesCoverageEdgeAA()) {
Brian Salomondba65f92018-01-22 08:43:38 -0500205 const char* aaDistName = nullptr;
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400206 bool mulByFragCoordW = false;
207 // When interpolation is inaccurate we perform the evaluation of the edge
Brian Salomondba65f92018-01-22 08:43:38 -0500208 // equations in the fragment shader rather than interpolating values computed
209 // in the vertex shader.
210 if (!args.fShaderCaps->interpolantsAreInaccurate()) {
211 GrGLSLVarying aaDistVarying(kFloat4_GrSLType,
212 GrGLSLVarying::Scope::kVertToFrag);
Brian Salomon70132d02018-05-29 15:33:06 -0400213 if (kFloat3_GrVertexAttribType == textureGP.fPositions.type()) {
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400214 args.fVaryingHandler->addVarying("aaDists", &aaDistVarying);
215 // The distance from edge equation e to homogenous point p=sk_Position
216 // is e.x*p.x/p.wx + e.y*p.y/p.w + e.z. However, we want screen space
217 // interpolation of this distance. We can do this by multiplying the
218 // varying in the VS by p.w and then multiplying by sk_FragCoord.w in
219 // the FS. So we output e.x*p.x + e.y*p.y + e.z * p.w
220 args.fVertBuilder->codeAppendf(
221 R"(%s = float4(dot(aaEdge0, %s), dot(aaEdge1, %s),
222 dot(aaEdge2, %s), dot(aaEdge3, %s));)",
Brian Salomon70132d02018-05-29 15:33:06 -0400223 aaDistVarying.vsOut(), textureGP.fPositions.name(),
224 textureGP.fPositions.name(), textureGP.fPositions.name(),
225 textureGP.fPositions.name());
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400226 mulByFragCoordW = true;
227 } else {
228 args.fVaryingHandler->addVarying("aaDists", &aaDistVarying);
229 args.fVertBuilder->codeAppendf(
230 R"(%s = float4(dot(aaEdge0.xy, %s.xy) + aaEdge0.z,
231 dot(aaEdge1.xy, %s.xy) + aaEdge1.z,
232 dot(aaEdge2.xy, %s.xy) + aaEdge2.z,
233 dot(aaEdge3.xy, %s.xy) + aaEdge3.z);)",
Brian Salomon70132d02018-05-29 15:33:06 -0400234 aaDistVarying.vsOut(), textureGP.fPositions.name(),
235 textureGP.fPositions.name(), textureGP.fPositions.name(),
236 textureGP.fPositions.name());
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400237 }
Brian Salomondba65f92018-01-22 08:43:38 -0500238 aaDistName = aaDistVarying.fsIn();
239 } else {
240 GrGLSLVarying aaEdgeVarying[4]{
241 {kFloat3_GrSLType, GrGLSLVarying::Scope::kVertToFrag},
242 {kFloat3_GrSLType, GrGLSLVarying::Scope::kVertToFrag},
243 {kFloat3_GrSLType, GrGLSLVarying::Scope::kVertToFrag},
244 {kFloat3_GrSLType, GrGLSLVarying::Scope::kVertToFrag}
245 };
246 for (int i = 0; i < 4; ++i) {
247 SkString name;
248 name.printf("aaEdge%d", i);
Brian Salomon7d982c62018-02-05 16:20:47 -0500249 args.fVaryingHandler->addVarying(name.c_str(), &aaEdgeVarying[i],
250 Interpolation::kCanBeFlat);
Brian Salomondba65f92018-01-22 08:43:38 -0500251 args.fVertBuilder->codeAppendf(
252 "%s = aaEdge%d;", aaEdgeVarying[i].vsOut(), i);
253 }
254 args.fFragBuilder->codeAppendf(
255 R"(float4 aaDists = float4(dot(%s.xy, sk_FragCoord.xy) + %s.z,
256 dot(%s.xy, sk_FragCoord.xy) + %s.z,
257 dot(%s.xy, sk_FragCoord.xy) + %s.z,
258 dot(%s.xy, sk_FragCoord.xy) + %s.z);)",
259 aaEdgeVarying[0].fsIn(), aaEdgeVarying[0].fsIn(),
260 aaEdgeVarying[1].fsIn(), aaEdgeVarying[1].fsIn(),
261 aaEdgeVarying[2].fsIn(), aaEdgeVarying[2].fsIn(),
262 aaEdgeVarying[3].fsIn(), aaEdgeVarying[3].fsIn());
263 aaDistName = "aaDists";
264 }
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500265 args.fFragBuilder->codeAppendf(
266 "float mindist = min(min(%s.x, %s.y), min(%s.z, %s.w));",
Brian Salomondba65f92018-01-22 08:43:38 -0500267 aaDistName, aaDistName, aaDistName, aaDistName);
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400268 if (mulByFragCoordW) {
269 args.fFragBuilder->codeAppend("mindist *= sk_FragCoord.w;");
270 }
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500271 args.fFragBuilder->codeAppendf("%s = float4(clamp(mindist, 0, 1));",
272 args.fOutputCoverage);
273 } else {
274 args.fFragBuilder->codeAppendf("%s = float4(1);", args.fOutputCoverage);
275 }
Brian Salomon34169692017-08-28 15:32:01 -0400276 }
277 GrGLSLColorSpaceXformHelper fColorSpaceXformHelper;
278 };
279 return new GLSLProcessor;
280 }
281
Brian Salomon485b8c62018-01-12 15:11:06 -0500282 bool usesCoverageEdgeAA() const { return SkToBool(fAAEdges[0].isInitialized()); }
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500283
Brian Salomon34169692017-08-28 15:32:01 -0400284private:
Brian Salomon336ce7b2017-09-08 08:23:58 -0400285 // This exists to reduce the number of shaders generated. It does some rounding of sampler
286 // counts.
287 static int NumSamplersToUse(int numRealProxies, const GrShaderCaps& caps) {
288 SkASSERT(numRealProxies > 0 && numRealProxies <= kMaxTextures &&
289 numRealProxies <= caps.maxFragmentSamplers());
290 if (1 == numRealProxies) {
291 return 1;
292 }
293 if (numRealProxies <= 4) {
294 return 4;
295 }
296 // Round to the next power of 2 and then clamp to kMaxTextures and the max allowed by caps.
297 return SkTMin(SkNextPow2(numRealProxies), SkTMin(kMaxTextures, caps.maxFragmentSamplers()));
298 }
299
300 TextureGeometryProcessor(sk_sp<GrTextureProxy> proxies[], int proxyCnt, int samplerCnt,
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400301 sk_sp<GrColorSpaceXform> csxf, bool coverageAA, bool perspective,
Brian Salomonb80ffee2018-05-23 16:39:39 -0400302 Domain domain, const GrSamplerState::Filter filters[],
303 const GrShaderCaps& caps)
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500304 : INHERITED(kTextureGeometryProcessor_ClassID), fColorSpaceXform(std::move(csxf)) {
Brian Salomon336ce7b2017-09-08 08:23:58 -0400305 SkASSERT(proxyCnt > 0 && samplerCnt >= proxyCnt);
Brian Salomon336ce7b2017-09-08 08:23:58 -0400306 fSamplers[0].reset(std::move(proxies[0]), filters[0]);
307 this->addTextureSampler(&fSamplers[0]);
308 for (int i = 1; i < proxyCnt; ++i) {
309 // This class has one sampler built in, the rest come from memory this processor was
310 // placement-newed into and so haven't been constructed.
311 new (&fSamplers[i]) TextureSampler(std::move(proxies[i]), filters[i]);
312 this->addTextureSampler(&fSamplers[i]);
313 }
Brian Salomon30e1a5e2018-05-18 12:32:32 -0400314
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400315 if (perspective) {
316 fPositions = this->addVertexAttrib("position", kFloat3_GrVertexAttribType);
317 } else {
318 fPositions = this->addVertexAttrib("position", kFloat2_GrVertexAttribType);
319 }
Brian Salomon30e1a5e2018-05-18 12:32:32 -0400320 fColors = this->addVertexAttrib("color", kUByte4_norm_GrVertexAttribType);
Brian Salomon17031a72018-05-22 14:14:07 -0400321 fTextureCoords = this->addVertexAttrib("textureCoords", kFloat2_GrVertexAttribType);
Brian Salomon30e1a5e2018-05-18 12:32:32 -0400322
Brian Salomon336ce7b2017-09-08 08:23:58 -0400323 if (samplerCnt > 1) {
324 // Here we initialize any extra samplers by repeating the last one samplerCnt - proxyCnt
325 // times.
326 GrTextureProxy* dupeProxy = fSamplers[proxyCnt - 1].proxy();
327 for (int i = proxyCnt; i < samplerCnt; ++i) {
328 new (&fSamplers[i]) TextureSampler(sk_ref_sp(dupeProxy), filters[proxyCnt - 1]);
329 this->addTextureSampler(&fSamplers[i]);
330 }
331 SkASSERT(caps.integerSupport());
332 fTextureIdx = this->addVertexAttrib("textureIdx", kInt_GrVertexAttribType);
333 }
Brian Salomonb80ffee2018-05-23 16:39:39 -0400334 if (domain == Domain::kYes) {
335 fDomain = this->addVertexAttrib("domain", kFloat4_GrVertexAttribType);
336 }
Brian Salomon485b8c62018-01-12 15:11:06 -0500337 if (coverageAA) {
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500338 fAAEdges[0] = this->addVertexAttrib("aaEdge0", kFloat3_GrVertexAttribType);
339 fAAEdges[1] = this->addVertexAttrib("aaEdge1", kFloat3_GrVertexAttribType);
340 fAAEdges[2] = this->addVertexAttrib("aaEdge2", kFloat3_GrVertexAttribType);
341 fAAEdges[3] = this->addVertexAttrib("aaEdge3", kFloat3_GrVertexAttribType);
342 }
Brian Salomon34169692017-08-28 15:32:01 -0400343 }
344
345 Attribute fPositions;
Brian Salomon34169692017-08-28 15:32:01 -0400346 Attribute fColors;
Brian Salomon30e1a5e2018-05-18 12:32:32 -0400347 Attribute fTextureCoords;
348 Attribute fTextureIdx;
Brian Salomonb80ffee2018-05-23 16:39:39 -0400349 Attribute fDomain;
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500350 Attribute fAAEdges[4];
Brian Salomon34169692017-08-28 15:32:01 -0400351 sk_sp<GrColorSpaceXform> fColorSpaceXform;
Brian Salomon336ce7b2017-09-08 08:23:58 -0400352 TextureSampler fSamplers[1];
Ethan Nicholasabff9562017-10-09 10:54:08 -0400353
354 typedef GrGeometryProcessor INHERITED;
Brian Salomon34169692017-08-28 15:32:01 -0400355};
356
Brian Salomon6872e942018-05-18 10:29:54 -0400357// This computes the four edge equations for a quad, then outsets them and computes a new quad
358// as the intersection points of the outset edges. 'x' and 'y' contain the original points as input
359// and the outset points as output. 'a', 'b', and 'c' are the edge equation coefficients on output.
360static void compute_quad_edges_and_outset_vertices(Sk4f* x, Sk4f* y, Sk4f* a, Sk4f* b, Sk4f* c) {
361 static constexpr auto fma = SkNx_fma<4, float>;
362 // These rotate the points/edge values either clockwise or counterclockwise assuming tri strip
363 // order.
364 auto nextCW = [](const Sk4f& v) { return SkNx_shuffle<2, 0, 3, 1>(v); };
365 auto nextCCW = [](const Sk4f& v) { return SkNx_shuffle<1, 3, 0, 2>(v); };
366
367 auto xnext = nextCCW(*x);
368 auto ynext = nextCCW(*y);
369 *a = ynext - *y;
370 *b = *x - xnext;
371 *c = fma(xnext, *y, -ynext * *x);
372 Sk4f invNormLengths = (*a * *a + *b * *b).rsqrt();
373 // Make sure the edge equations have their normals facing into the quad in device space.
374 auto test = fma(*a, nextCW(*x), fma(*b, nextCW(*y), *c));
375 if ((test < Sk4f(0)).anyTrue()) {
376 invNormLengths = -invNormLengths;
377 }
378 *a *= invNormLengths;
379 *b *= invNormLengths;
380 *c *= invNormLengths;
381
382 // Here is the outset. This makes our edge equations compute coverage without requiring a
383 // half pixel offset and is also used to compute the bloated quad that will cover all
384 // pixels.
385 *c += Sk4f(0.5f);
386
387 // Reverse the process to compute the points of the bloated quad from the edge equations.
388 // This time the inputs don't have 1s as their third coord and we want to homogenize rather
389 // than normalize.
390 auto anext = nextCW(*a);
391 auto bnext = nextCW(*b);
392 auto cnext = nextCW(*c);
393 *x = fma(bnext, *c, -*b * cnext);
394 *y = fma(*a, cnext, -anext * *c);
395 auto ic = (fma(anext, *b, -bnext * *a)).invert();
396 *x *= ic;
397 *y *= ic;
398}
399
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500400namespace {
401// This is a class soley so it can be partially specialized (functions cannot be).
Brian Salomon86c40012018-05-22 10:48:49 -0400402template <typename V, GrAA AA = V::kAA, typename Position = typename V::Position>
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400403class VertexAAHandler;
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500404
Brian Salomon86c40012018-05-22 10:48:49 -0400405template<typename V> class VertexAAHandler<V, GrAA::kNo, SkPoint> {
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500406public:
Brian Salomon86c40012018-05-22 10:48:49 -0400407 static void AssignPositionsAndTexCoords(V* vertices, const GrPerspQuad& quad,
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500408 const SkRect& texRect) {
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400409 SkASSERT((quad.w4f() == Sk4f(1.f)).allTrue());
Brian Salomon86c40012018-05-22 10:48:49 -0400410 SkPointPriv::SetRectTriStrip(&vertices[0].fTextureCoords, texRect, sizeof(V));
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400411 for (int i = 0; i < 4; ++i) {
412 vertices[i].fPosition = {quad.x(i), quad.y(i)};
413 }
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500414 }
415};
416
Brian Salomon86c40012018-05-22 10:48:49 -0400417template<typename V> class VertexAAHandler<V, GrAA::kNo, SkPoint3> {
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500418public:
Brian Salomon86c40012018-05-22 10:48:49 -0400419 static void AssignPositionsAndTexCoords(V* vertices, const GrPerspQuad& quad,
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500420 const SkRect& texRect) {
Brian Salomon86c40012018-05-22 10:48:49 -0400421 SkPointPriv::SetRectTriStrip(&vertices[0].fTextureCoords, texRect, sizeof(V));
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400422 for (int i = 0; i < 4; ++i) {
423 vertices[i].fPosition = quad.point(i);
424 }
425 }
426};
427
Brian Salomon86c40012018-05-22 10:48:49 -0400428template<typename V> class VertexAAHandler<V, GrAA::kYes, SkPoint> {
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400429public:
Brian Salomon86c40012018-05-22 10:48:49 -0400430 static void AssignPositionsAndTexCoords(V* vertices, const GrPerspQuad& quad,
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400431 const SkRect& texRect) {
432 SkASSERT((quad.w4f() == Sk4f(1.f)).allTrue());
Brian Salomon6872e942018-05-18 10:29:54 -0400433 auto x = quad.x4f();
434 auto y = quad.y4f();
435 Sk4f a, b, c;
436 compute_quad_edges_and_outset_vertices(&x, &y, &a, &b, &c);
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500437
438 for (int i = 0; i < 4; ++i) {
Brian Salomon6872e942018-05-18 10:29:54 -0400439 vertices[i].fPosition = {x[i], y[i]};
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500440 for (int j = 0; j < 4; ++j) {
Brian Salomon6872e942018-05-18 10:29:54 -0400441 vertices[i].fEdges[j] = {a[j], b[j], c[j]};
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500442 }
443 }
444
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500445 AssignTexCoords(vertices, quad, texRect);
446 }
447
448private:
Brian Salomon86c40012018-05-22 10:48:49 -0400449 static void AssignTexCoords(V* vertices, const GrPerspQuad& quad, const SkRect& tex) {
Brian Salomona33b67c2018-05-17 10:42:14 -0400450 SkMatrix q = SkMatrix::MakeAll(quad.x(0), quad.x(1), quad.x(2),
451 quad.y(0), quad.y(1), quad.y(2),
452 1.f, 1.f, 1.f);
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500453 SkMatrix qinv;
454 if (!q.invert(&qinv)) {
455 return;
456 }
457 SkMatrix t = SkMatrix::MakeAll(tex.fLeft, tex.fLeft, tex.fRight,
458 tex.fTop, tex.fBottom, tex.fTop,
459 1.f, 1.f, 1.f);
460 SkMatrix map;
461 map.setConcat(t, qinv);
Brian Salomon86c40012018-05-22 10:48:49 -0400462 SkMatrixPriv::MapPointsWithStride(map, &vertices[0].fTextureCoords, sizeof(V),
463 &vertices[0].fPosition, sizeof(V), 4);
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500464 }
465};
466
Brian Salomon86c40012018-05-22 10:48:49 -0400467template<typename V> class VertexAAHandler<V, GrAA::kYes, SkPoint3> {
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400468public:
Brian Salomon86c40012018-05-22 10:48:49 -0400469 static void AssignPositionsAndTexCoords(V* vertices, const GrPerspQuad& quad,
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400470 const SkRect& texRect) {
471 auto x = quad.x4f();
472 auto y = quad.y4f();
473 auto iw = quad.iw4f();
474 x *= iw;
475 y *= iw;
476
477 // Get an equation for w from device space coords.
478 SkMatrix P;
479 P.setAll(x[0], y[0], 1, x[1], y[1], 1, x[2], y[2], 1);
480 SkAssertResult(P.invert(&P));
481 SkPoint3 weq{quad.w(0), quad.w(1), quad.w(2)};
482 P.mapHomogeneousPoints(&weq, &weq, 1);
483
484 Sk4f a, b, c;
485 compute_quad_edges_and_outset_vertices(&x, &y, &a, &b, &c);
486
487 // Compute new w values for the output vertices;
488 auto w = Sk4f(weq.fX) * x + Sk4f(weq.fY) * y + Sk4f(weq.fZ);
489 x *= w;
490 y *= w;
491
492 for (int i = 0; i < 4; ++i) {
493 vertices[i].fPosition = {x[i], y[i], w[i]};
494 for (int j = 0; j < 4; ++j) {
495 vertices[i].fEdges[j] = {a[j], b[j], c[j]};
496 }
497 }
498
499 AssignTexCoords(vertices, quad, texRect);
500 }
501
502private:
Brian Salomon86c40012018-05-22 10:48:49 -0400503 static void AssignTexCoords(V* vertices, const GrPerspQuad& quad, const SkRect& tex) {
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400504 SkMatrix q = SkMatrix::MakeAll(quad.x(0), quad.x(1), quad.x(2),
505 quad.y(0), quad.y(1), quad.y(2),
506 quad.w(0), quad.w(1), quad.w(2));
507 SkMatrix qinv;
508 if (!q.invert(&qinv)) {
509 return;
510 }
511 SkMatrix t = SkMatrix::MakeAll(tex.fLeft, tex.fLeft, tex.fRight,
512 tex.fTop, tex.fBottom, tex.fTop,
513 1.f, 1.f, 1.f);
514 SkMatrix map;
515 map.setConcat(t, qinv);
516 SkPoint3 tempTexCoords[4];
517 SkMatrixPriv::MapHomogeneousPointsWithStride(map, tempTexCoords, sizeof(SkPoint3),
Brian Salomon86c40012018-05-22 10:48:49 -0400518 &vertices[0].fPosition, sizeof(V), 4);
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400519 for (int i = 0; i < 4; ++i) {
520 auto invW = 1.f / tempTexCoords[i].fZ;
521 vertices[i].fTextureCoords.fX = tempTexCoords[i].fX * invW;
522 vertices[i].fTextureCoords.fY = tempTexCoords[i].fY * invW;
523 }
524 }
525};
526
Brian Salomon17031a72018-05-22 14:14:07 -0400527template <typename V, MultiTexture MT = V::kMultiTexture> struct TexIdAssigner;
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500528
Brian Salomon17031a72018-05-22 14:14:07 -0400529template <typename V> struct TexIdAssigner<V, MultiTexture::kYes> {
Brian Salomon86c40012018-05-22 10:48:49 -0400530 static void Assign(V* vertices, int textureIdx) {
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400531 for (int i = 0; i < 4; ++i) {
532 vertices[i].fTextureIdx = textureIdx;
533 }
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500534 }
535};
536
Brian Salomon17031a72018-05-22 14:14:07 -0400537template <typename V> struct TexIdAssigner<V, MultiTexture::kNo> {
Brian Salomon86c40012018-05-22 10:48:49 -0400538 static void Assign(V* vertices, int textureIdx) {}
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500539};
Brian Salomonb80ffee2018-05-23 16:39:39 -0400540
541template <typename V, Domain D = V::kDomain> struct DomainAssigner;
542
543template <typename V> struct DomainAssigner<V, Domain::kYes> {
544 static void Assign(V* vertices, Domain domain, GrSamplerState::Filter filter,
545 const SkRect& srcRect, GrSurfaceOrigin origin, float iw, float ih) {
546 static constexpr SkRect kLargeRect = {-2, -2, 2, 2};
547 SkRect domainRect;
548 if (domain == Domain::kYes) {
549 auto ltrb = Sk4f::Load(&srcRect);
550 if (filter == GrSamplerState::Filter::kBilerp) {
551 auto rblt = SkNx_shuffle<2, 3, 0, 1>(ltrb);
552 auto whwh = (rblt - ltrb).abs();
553 auto c = (rblt + ltrb) * 0.5f;
554 static const Sk4f kOffsets = {0.5f, 0.5f, -0.5f, -0.5f};
555 ltrb = (whwh < 1.f).thenElse(c, ltrb + kOffsets);
556 }
557 ltrb *= Sk4f(iw, ih, iw, ih);
558 if (origin == kBottomLeft_GrSurfaceOrigin) {
559 static const Sk4f kMul = {1.f, -1.f, 1.f, -1.f};
560 static const Sk4f kAdd = {0.f, 1.f, 0.f, 1.f};
561 ltrb = SkNx_shuffle<0, 3, 2, 1>(kMul * ltrb + kAdd);
562 }
563 ltrb.store(&domainRect);
564 } else {
565 domainRect = kLargeRect;
566 }
567 for (int i = 0; i < 4; ++i) {
568 vertices[i].fTextureDomain = domainRect;
569 }
570 }
571};
572
573template <typename V> struct DomainAssigner<V, Domain::kNo> {
574 static void Assign(V*, Domain domain, GrSamplerState::Filter, const SkRect&, GrSurfaceOrigin,
575 float iw, float ih) {
576 SkASSERT(domain == Domain::kNo);
577 }
578};
579
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500580} // anonymous namespace
581
Brian Salomon86c40012018-05-22 10:48:49 -0400582template <typename V>
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400583static void tessellate_quad(const GrPerspQuad& devQuad, const SkRect& srcRect, GrColor color,
Brian Salomonb80ffee2018-05-23 16:39:39 -0400584 GrSurfaceOrigin origin, GrSamplerState::Filter filter, V* vertices,
585 SkScalar iw, SkScalar ih, int textureIdx, Domain domain) {
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500586 SkRect texRect = {
587 iw * srcRect.fLeft,
588 ih * srcRect.fTop,
589 iw * srcRect.fRight,
590 ih * srcRect.fBottom
591 };
592 if (origin == kBottomLeft_GrSurfaceOrigin) {
593 texRect.fTop = 1.f - texRect.fTop;
594 texRect.fBottom = 1.f - texRect.fBottom;
595 }
Brian Salomon86c40012018-05-22 10:48:49 -0400596 VertexAAHandler<V>::AssignPositionsAndTexCoords(vertices, devQuad, texRect);
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500597 vertices[0].fColor = color;
598 vertices[1].fColor = color;
599 vertices[2].fColor = color;
600 vertices[3].fColor = color;
Brian Salomon86c40012018-05-22 10:48:49 -0400601 TexIdAssigner<V>::Assign(vertices, textureIdx);
Brian Salomonb80ffee2018-05-23 16:39:39 -0400602 DomainAssigner<V>::Assign(vertices, domain, filter, srcRect, origin, iw, ih);
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500603}
Brian Salomon17031a72018-05-22 14:14:07 -0400604
Brian Salomon34169692017-08-28 15:32:01 -0400605/**
606 * Op that implements GrTextureOp::Make. It draws textured quads. Each quad can modulate against a
607 * the texture by color. The blend with the destination is always src-over. The edges are non-AA.
608 */
609class TextureOp final : public GrMeshDrawOp {
610public:
611 static std::unique_ptr<GrDrawOp> Make(sk_sp<GrTextureProxy> proxy,
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400612 GrSamplerState::Filter filter, GrColor color,
Brian Salomon485b8c62018-01-12 15:11:06 -0500613 const SkRect& srcRect, const SkRect& dstRect,
Brian Salomonb80ffee2018-05-23 16:39:39 -0400614 GrAAType aaType, SkCanvas::SrcRectConstraint constraint,
Brian Osman2b23c4b2018-06-01 12:25:08 -0400615 const SkMatrix& viewMatrix,
616 sk_sp<GrColorSpaceXform> csxf) {
Brian Salomon34169692017-08-28 15:32:01 -0400617 return std::unique_ptr<GrDrawOp>(new TextureOp(std::move(proxy), filter, color, srcRect,
Brian Salomonb80ffee2018-05-23 16:39:39 -0400618 dstRect, aaType, constraint, viewMatrix,
Brian Osman2b23c4b2018-06-01 12:25:08 -0400619 std::move(csxf)));
Brian Salomon34169692017-08-28 15:32:01 -0400620 }
621
Brian Salomon336ce7b2017-09-08 08:23:58 -0400622 ~TextureOp() override {
623 if (fFinalized) {
624 auto proxies = this->proxies();
625 for (int i = 0; i < fProxyCnt; ++i) {
626 proxies[i]->completedRead();
627 }
628 if (fProxyCnt > 1) {
629 delete[] reinterpret_cast<const char*>(proxies);
630 }
631 } else {
632 SkASSERT(1 == fProxyCnt);
633 fProxy0->unref();
634 }
635 }
Brian Salomon34169692017-08-28 15:32:01 -0400636
637 const char* name() const override { return "TextureOp"; }
638
Robert Phillipsf1748f52017-09-14 14:11:24 -0400639 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillipsb493eeb2017-09-13 13:10:52 -0400640 auto proxies = this->proxies();
641 for (int i = 0; i < fProxyCnt; ++i) {
642 func(proxies[i]);
643 }
644 }
645
Brian Salomon34169692017-08-28 15:32:01 -0400646 SkString dumpInfo() const override {
647 SkString str;
Brian Salomon34169692017-08-28 15:32:01 -0400648 str.appendf("# draws: %d\n", fDraws.count());
Brian Salomon336ce7b2017-09-08 08:23:58 -0400649 auto proxies = this->proxies();
650 for (int i = 0; i < fProxyCnt; ++i) {
651 str.appendf("Proxy ID %d: %d, Filter: %d\n", i, proxies[i]->uniqueID().asUInt(),
652 static_cast<int>(this->filters()[i]));
653 }
Brian Salomon34169692017-08-28 15:32:01 -0400654 for (int i = 0; i < fDraws.count(); ++i) {
655 const Draw& draw = fDraws[i];
656 str.appendf(
Brian Salomon336ce7b2017-09-08 08:23:58 -0400657 "%d: Color: 0x%08x, ProxyIdx: %d, TexRect [L: %.2f, T: %.2f, R: %.2f, B: %.2f] "
658 "Quad [(%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f)]\n",
Brian Salomonb80ffee2018-05-23 16:39:39 -0400659 i, draw.color(), draw.textureIdx(), draw.srcRect().fLeft, draw.srcRect().fTop,
660 draw.srcRect().fRight, draw.srcRect().fBottom, draw.quad().point(0).fX,
661 draw.quad().point(0).fY, draw.quad().point(1).fX, draw.quad().point(1).fY,
662 draw.quad().point(2).fX, draw.quad().point(2).fY, draw.quad().point(3).fX,
663 draw.quad().point(3).fY);
Brian Salomon34169692017-08-28 15:32:01 -0400664 }
665 str += INHERITED::dumpInfo();
666 return str;
667 }
668
Brian Osman9a725dd2017-09-20 09:53:22 -0400669 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip,
670 GrPixelConfigIsClamped dstIsClamped) override {
Brian Salomon34169692017-08-28 15:32:01 -0400671 SkASSERT(!fFinalized);
Brian Salomon336ce7b2017-09-08 08:23:58 -0400672 SkASSERT(1 == fProxyCnt);
Brian Salomon34169692017-08-28 15:32:01 -0400673 fFinalized = true;
Brian Salomon336ce7b2017-09-08 08:23:58 -0400674 fProxy0->addPendingRead();
675 fProxy0->unref();
Brian Salomon34169692017-08-28 15:32:01 -0400676 return RequiresDstTexture::kNo;
677 }
678
Brian Salomon485b8c62018-01-12 15:11:06 -0500679 FixedFunctionFlags fixedFunctionFlags() const override {
680 return this->aaType() == GrAAType::kMSAA ? FixedFunctionFlags::kUsesHWAA
681 : FixedFunctionFlags::kNone;
682 }
Brian Salomon34169692017-08-28 15:32:01 -0400683
684 DEFINE_OP_CLASS_ID
685
686private:
Brian Salomon762d5e72017-12-01 10:25:08 -0500687
688 // This is used in a heursitic for choosing a code path. We don't care what happens with
689 // really large rects, infs, nans, etc.
690#if defined(__clang__) && (__clang_major__ * 1000 + __clang_minor__) >= 3007
691__attribute__((no_sanitize("float-cast-overflow")))
692#endif
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500693 size_t RectSizeAsSizeT(const SkRect& rect) {;
Brian Salomon762d5e72017-12-01 10:25:08 -0500694 return static_cast<size_t>(SkTMax(rect.width(), 1.f) * SkTMax(rect.height(), 1.f));
695 }
696
Brian Salomon336ce7b2017-09-08 08:23:58 -0400697 static constexpr int kMaxTextures = TextureGeometryProcessor::kMaxTextures;
698
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400699 TextureOp(sk_sp<GrTextureProxy> proxy, GrSamplerState::Filter filter, GrColor color,
Brian Salomon485b8c62018-01-12 15:11:06 -0500700 const SkRect& srcRect, const SkRect& dstRect, GrAAType aaType,
Brian Salomonb80ffee2018-05-23 16:39:39 -0400701 SkCanvas::SrcRectConstraint constraint, const SkMatrix& viewMatrix,
Brian Osman2b23c4b2018-06-01 12:25:08 -0400702 sk_sp<GrColorSpaceXform> csxf)
Brian Salomon34169692017-08-28 15:32:01 -0400703 : INHERITED(ClassID())
Brian Salomon34169692017-08-28 15:32:01 -0400704 , fColorSpaceXform(std::move(csxf))
Brian Salomon336ce7b2017-09-08 08:23:58 -0400705 , fProxy0(proxy.release())
706 , fFilter0(filter)
707 , fProxyCnt(1)
Brian Salomon485b8c62018-01-12 15:11:06 -0500708 , fAAType(static_cast<unsigned>(aaType))
Brian Osman2b23c4b2018-06-01 12:25:08 -0400709 , fFinalized(0) {
Brian Salomon485b8c62018-01-12 15:11:06 -0500710 SkASSERT(aaType != GrAAType::kMixedSamples);
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400711 fPerspective = viewMatrix.hasPerspective();
Brian Salomon594b64c2018-05-29 12:47:57 -0400712 auto quad = GrPerspQuad(dstRect, viewMatrix);
713 auto bounds = quad.bounds();
714#ifndef SK_DONT_DROP_UNNECESSARY_AA_IN_TEXTURE_OP
715 if (GrAAType::kCoverage == this->aaType() && viewMatrix.rectStaysRect()) {
716 // Disable coverage AA when rect falls on integers in device space.
717 auto is_int = [](float f) { return f == sk_float_floor(f); };
718 if (is_int(bounds.fLeft) && is_int(bounds.fTop) && is_int(bounds.fRight) &&
719 is_int(bounds.fBottom)) {
720 fAAType = static_cast<unsigned>(GrAAType::kNone);
721 // We may have had a strict constraint with nearest filter soley due to possible AA
722 // bloat. In that case it's no longer necessary.
723 if (constraint == SkCanvas::kStrict_SrcRectConstraint &&
724 filter == GrSamplerState::Filter::kNearest) {
725 constraint = SkCanvas::kFast_SrcRectConstraint;
726 }
727 }
728 }
729#endif
730 const auto& draw = fDraws.emplace_back(srcRect, 0, quad, constraint, color);
Brian Salomon34169692017-08-28 15:32:01 -0400731 this->setBounds(bounds, HasAABloat::kNo, IsZeroArea::kNo);
Brian Salomon594b64c2018-05-29 12:47:57 -0400732 fDomain = static_cast<bool>(draw.domain());
Brian Salomon762d5e72017-12-01 10:25:08 -0500733 fMaxApproxDstPixelArea = RectSizeAsSizeT(bounds);
Brian Salomon34169692017-08-28 15:32:01 -0400734 }
735
Brian Salomonb80ffee2018-05-23 16:39:39 -0400736 template <typename Pos, MultiTexture MT, Domain D, GrAA AA>
Brian Salomon17031a72018-05-22 14:14:07 -0400737 void tess(void* v, const float iw[], const float ih[], const GrGeometryProcessor* gp) {
Brian Salomonb80ffee2018-05-23 16:39:39 -0400738 using Vertex = TextureGeometryProcessor::Vertex<Pos, MT, D, AA>;
Brian Salomon17031a72018-05-22 14:14:07 -0400739 SkASSERT(gp->getVertexStride() == sizeof(Vertex));
740 auto vertices = static_cast<Vertex*>(v);
741 auto proxies = this->proxies();
Brian Salomonb80ffee2018-05-23 16:39:39 -0400742 auto filters = this->filters();
Brian Salomon17031a72018-05-22 14:14:07 -0400743 for (const auto& draw : fDraws) {
Brian Salomonb80ffee2018-05-23 16:39:39 -0400744 auto textureIdx = draw.textureIdx();
745 auto origin = proxies[textureIdx]->origin();
746 tessellate_quad<Vertex>(draw.quad(), draw.srcRect(), draw.color(), origin,
747 filters[textureIdx], vertices, iw[textureIdx], ih[textureIdx],
748 textureIdx, draw.domain());
Brian Salomon17031a72018-05-22 14:14:07 -0400749 vertices += 4;
750 }
751 }
752
Brian Salomon34169692017-08-28 15:32:01 -0400753 void onPrepareDraws(Target* target) override {
Brian Salomon336ce7b2017-09-08 08:23:58 -0400754 sk_sp<GrTextureProxy> proxiesSPs[kMaxTextures];
755 auto proxies = this->proxies();
756 auto filters = this->filters();
757 for (int i = 0; i < fProxyCnt; ++i) {
758 if (!proxies[i]->instantiate(target->resourceProvider())) {
759 return;
760 }
761 proxiesSPs[i] = sk_ref_sp(proxies[i]);
Brian Salomon34169692017-08-28 15:32:01 -0400762 }
Brian Salomon336ce7b2017-09-08 08:23:58 -0400763
Brian Salomonb80ffee2018-05-23 16:39:39 -0400764 Domain domain = fDomain ? Domain::kYes : Domain::kNo;
Brian Salomon485b8c62018-01-12 15:11:06 -0500765 bool coverageAA = GrAAType::kCoverage == this->aaType();
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400766 sk_sp<GrGeometryProcessor> gp = TextureGeometryProcessor::Make(
767 proxiesSPs, fProxyCnt, std::move(fColorSpaceXform), coverageAA, fPerspective,
Brian Salomonb80ffee2018-05-23 16:39:39 -0400768 domain, filters, *target->caps().shaderCaps());
Brian Salomon34169692017-08-28 15:32:01 -0400769 GrPipeline::InitArgs args;
770 args.fProxy = target->proxy();
771 args.fCaps = &target->caps();
772 args.fResourceProvider = target->resourceProvider();
Brian Salomon485b8c62018-01-12 15:11:06 -0500773 args.fFlags = 0;
Brian Salomon485b8c62018-01-12 15:11:06 -0500774 if (GrAAType::kMSAA == this->aaType()) {
775 args.fFlags |= GrPipeline::kHWAntialias_Flag;
776 }
777
Brian Salomon34169692017-08-28 15:32:01 -0400778 const GrPipeline* pipeline = target->allocPipeline(args, GrProcessorSet::MakeEmptySet(),
779 target->detachAppliedClip());
Brian Salomon34169692017-08-28 15:32:01 -0400780 int vstart;
781 const GrBuffer* vbuffer;
Brian Salomon336ce7b2017-09-08 08:23:58 -0400782 void* vdata = target->makeVertexSpace(gp->getVertexStride(), 4 * fDraws.count(), &vbuffer,
783 &vstart);
784 if (!vdata) {
Brian Salomon34169692017-08-28 15:32:01 -0400785 SkDebugf("Could not allocate vertices\n");
786 return;
787 }
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400788
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400789 float iw[kMaxTextures];
790 float ih[kMaxTextures];
791 for (int t = 0; t < fProxyCnt; ++t) {
792 const auto* texture = proxies[t]->priv().peekTexture();
793 iw[t] = 1.f / texture->width();
794 ih[t] = 1.f / texture->height();
795 }
796
Brian Salomonb80ffee2018-05-23 16:39:39 -0400797 using TessFn =
798 decltype(&TextureOp::tess<SkPoint, MultiTexture::kNo, Domain::kNo, GrAA::kNo>);
Brian Salomonfff19662018-06-08 12:22:10 -0400799 static constexpr TessFn kTessFns[] = {
Brian Salomonb80ffee2018-05-23 16:39:39 -0400800 &TextureOp::tess<SkPoint, MultiTexture::kNo, Domain::kNo, GrAA::kNo>,
801 &TextureOp::tess<SkPoint, MultiTexture::kNo, Domain::kNo, GrAA::kYes>,
802 &TextureOp::tess<SkPoint, MultiTexture::kNo, Domain::kYes, GrAA::kNo>,
803 &TextureOp::tess<SkPoint, MultiTexture::kNo, Domain::kYes, GrAA::kYes>,
804 &TextureOp::tess<SkPoint, MultiTexture::kYes, Domain::kNo, GrAA::kNo>,
805 &TextureOp::tess<SkPoint, MultiTexture::kYes, Domain::kNo, GrAA::kYes>,
806 &TextureOp::tess<SkPoint, MultiTexture::kYes, Domain::kYes, GrAA::kNo>,
807 &TextureOp::tess<SkPoint, MultiTexture::kYes, Domain::kYes, GrAA::kYes>,
808 &TextureOp::tess<SkPoint3, MultiTexture::kNo, Domain::kNo, GrAA::kNo>,
809 &TextureOp::tess<SkPoint3, MultiTexture::kNo, Domain::kNo, GrAA::kYes>,
810 &TextureOp::tess<SkPoint3, MultiTexture::kNo, Domain::kYes, GrAA::kNo>,
811 &TextureOp::tess<SkPoint3, MultiTexture::kNo, Domain::kYes, GrAA::kYes>,
812 &TextureOp::tess<SkPoint3, MultiTexture::kYes, Domain::kNo, GrAA::kNo>,
813 &TextureOp::tess<SkPoint3, MultiTexture::kYes, Domain::kNo, GrAA::kYes>,
814 &TextureOp::tess<SkPoint3, MultiTexture::kYes, Domain::kYes, GrAA::kNo>,
815 &TextureOp::tess<SkPoint3, MultiTexture::kYes, Domain::kYes, GrAA::kYes>,
816 };
817 int tessFnIdx = 0;
818 tessFnIdx |= coverageAA ? 0x1 : 0x0;
819 tessFnIdx |= fDomain ? 0x2 : 0x0;
820 tessFnIdx |= (fProxyCnt > 1) ? 0x4 : 0x0;
821 tessFnIdx |= fPerspective ? 0x8 : 0x0;
822 (this->*(kTessFns[tessFnIdx]))(vdata, iw, ih, gp.get());
823
Brian Salomon57caa662017-10-18 12:21:05 +0000824 GrPrimitiveType primitiveType =
825 fDraws.count() > 1 ? GrPrimitiveType::kTriangles : GrPrimitiveType::kTriangleStrip;
826 GrMesh mesh(primitiveType);
Brian Salomon34169692017-08-28 15:32:01 -0400827 if (fDraws.count() > 1) {
Brian Salomon57caa662017-10-18 12:21:05 +0000828 sk_sp<const GrBuffer> ibuffer = target->resourceProvider()->refQuadIndexBuffer();
Brian Salomon34169692017-08-28 15:32:01 -0400829 if (!ibuffer) {
830 SkDebugf("Could not allocate quad indices\n");
831 return;
832 }
Brian Salomon34169692017-08-28 15:32:01 -0400833 mesh.setIndexedPatterned(ibuffer.get(), 6, 4, fDraws.count(),
834 GrResourceProvider::QuadCountOfQuadBuffer());
Brian Salomon34169692017-08-28 15:32:01 -0400835 } else {
Brian Salomon34169692017-08-28 15:32:01 -0400836 mesh.setNonIndexedNonInstanced(4);
Brian Salomon34169692017-08-28 15:32:01 -0400837 }
Brian Salomon57caa662017-10-18 12:21:05 +0000838 mesh.setVertexData(vbuffer, vstart);
839 target->draw(gp.get(), pipeline, mesh);
Brian Salomon34169692017-08-28 15:32:01 -0400840 }
841
842 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
843 const auto* that = t->cast<TextureOp>();
Brian Salomon762d5e72017-12-01 10:25:08 -0500844 const auto& shaderCaps = *caps.shaderCaps();
Brian Salomon336ce7b2017-09-08 08:23:58 -0400845 if (!GrColorSpaceXform::Equals(fColorSpaceXform.get(), that->fColorSpaceXform.get())) {
Brian Salomon34169692017-08-28 15:32:01 -0400846 return false;
847 }
Brian Salomon485b8c62018-01-12 15:11:06 -0500848 if (this->aaType() != that->aaType()) {
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500849 return false;
850 }
Brian Salomon336ce7b2017-09-08 08:23:58 -0400851 // Because of an issue where GrColorSpaceXform adds the same function every time it is used
852 // in a texture lookup, we only allow multiple textures when there is no transform.
Brian Salomon762d5e72017-12-01 10:25:08 -0500853 if (TextureGeometryProcessor::SupportsMultitexture(shaderCaps) && !fColorSpaceXform &&
854 fMaxApproxDstPixelArea <= shaderCaps.disableImageMultitexturingDstRectAreaThreshold() &&
855 that->fMaxApproxDstPixelArea <=
856 shaderCaps.disableImageMultitexturingDstRectAreaThreshold()) {
Brian Salomon336ce7b2017-09-08 08:23:58 -0400857 int map[kMaxTextures];
Brian Salomon762d5e72017-12-01 10:25:08 -0500858 int numNewProxies = this->mergeProxies(that, map, shaderCaps);
Brian Salomon336ce7b2017-09-08 08:23:58 -0400859 if (numNewProxies < 0) {
860 return false;
861 }
862 if (1 == fProxyCnt && numNewProxies) {
863 void* mem = new char[(sizeof(GrSamplerState::Filter) + sizeof(GrTextureProxy*)) *
864 kMaxTextures];
865 auto proxies = reinterpret_cast<GrTextureProxy**>(mem);
866 auto filters = reinterpret_cast<GrSamplerState::Filter*>(proxies + kMaxTextures);
867 proxies[0] = fProxy0;
868 filters[0] = fFilter0;
869 fProxyArray = proxies;
870 }
871 fProxyCnt += numNewProxies;
872 auto thisProxies = fProxyArray;
873 auto thatProxies = that->proxies();
874 auto thatFilters = that->filters();
875 auto thisFilters = reinterpret_cast<GrSamplerState::Filter*>(thisProxies +
876 kMaxTextures);
877 for (int i = 0; i < that->fProxyCnt; ++i) {
878 if (map[i] < 0) {
879 thatProxies[i]->addPendingRead();
Robert Phillipsb493eeb2017-09-13 13:10:52 -0400880
Brian Salomon336ce7b2017-09-08 08:23:58 -0400881 thisProxies[-map[i]] = thatProxies[i];
882 thisFilters[-map[i]] = thatFilters[i];
883 map[i] = -map[i];
884 }
885 }
886 int firstNewDraw = fDraws.count();
887 fDraws.push_back_n(that->fDraws.count(), that->fDraws.begin());
888 for (int i = firstNewDraw; i < fDraws.count(); ++i) {
Brian Salomonb80ffee2018-05-23 16:39:39 -0400889 fDraws[i].setTextureIdx(map[fDraws[i].textureIdx()]);
Brian Salomon336ce7b2017-09-08 08:23:58 -0400890 }
891 } else {
Brian Salomonbbf05752017-11-30 11:30:48 -0500892 // We can get here when one of the ops is already multitextured but the other cannot
893 // be because of the dst rect size.
894 if (fProxyCnt > 1 || that->fProxyCnt > 1) {
895 return false;
896 }
Brian Salomon336ce7b2017-09-08 08:23:58 -0400897 if (fProxy0->uniqueID() != that->fProxy0->uniqueID() || fFilter0 != that->fFilter0) {
898 return false;
899 }
900 fDraws.push_back_n(that->fDraws.count(), that->fDraws.begin());
901 }
Brian Salomon34169692017-08-28 15:32:01 -0400902 this->joinBounds(*that);
Brian Salomon762d5e72017-12-01 10:25:08 -0500903 fMaxApproxDstPixelArea = SkTMax(that->fMaxApproxDstPixelArea, fMaxApproxDstPixelArea);
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400904 fPerspective |= that->fPerspective;
Brian Salomonb80ffee2018-05-23 16:39:39 -0400905 fDomain |= that->fDomain;
Brian Salomon34169692017-08-28 15:32:01 -0400906 return true;
907 }
908
Brian Salomon336ce7b2017-09-08 08:23:58 -0400909 /**
910 * Determines a mapping of indices from that's proxy array to this's proxy array. A negative map
911 * value means that's proxy should be added to this's proxy array at the absolute value of
912 * the map entry. If it is determined that the ops shouldn't combine their proxies then a
913 * negative value is returned. Otherwise, return value indicates the number of proxies that have
914 * to be added to this op or, equivalently, the number of negative entries in map.
915 */
916 int mergeProxies(const TextureOp* that, int map[kMaxTextures], const GrShaderCaps& caps) const {
917 std::fill_n(map, kMaxTextures, -kMaxTextures);
918 int sharedProxyCnt = 0;
919 auto thisProxies = this->proxies();
920 auto thisFilters = this->filters();
921 auto thatProxies = that->proxies();
922 auto thatFilters = that->filters();
923 for (int i = 0; i < fProxyCnt; ++i) {
924 for (int j = 0; j < that->fProxyCnt; ++j) {
925 if (thisProxies[i]->uniqueID() == thatProxies[j]->uniqueID()) {
926 if (thisFilters[i] != thatFilters[j]) {
927 // In GL we don't currently support using the same texture with different
928 // samplers. If we added support for sampler objects and a cap bit to know
929 // it's ok to use different filter modes then we could support this.
930 // Otherwise, we could also only allow a single filter mode for each op
931 // instance.
932 return -1;
933 }
934 map[j] = i;
935 ++sharedProxyCnt;
936 break;
937 }
938 }
939 }
Brian Salomon2b6f6142017-11-13 11:49:13 -0500940 int actualMaxTextures = SkTMin(caps.maxFragmentSamplers(), kMaxTextures);
Brian Salomon336ce7b2017-09-08 08:23:58 -0400941 int newProxyCnt = that->fProxyCnt - sharedProxyCnt;
942 if (newProxyCnt + fProxyCnt > actualMaxTextures) {
943 return -1;
944 }
945 GrPixelConfig config = thisProxies[0]->config();
946 int nextSlot = fProxyCnt;
947 for (int j = 0; j < that->fProxyCnt; ++j) {
948 // We want to avoid making many shaders because of different permutations of shader
949 // based swizzle and sampler types. The approach taken here is to require the configs to
950 // be the same and to only allow already instantiated proxies that have the most
951 // common sampler type. Otherwise we don't merge.
952 if (thatProxies[j]->config() != config) {
953 return -1;
954 }
955 if (GrTexture* tex = thatProxies[j]->priv().peekTexture()) {
956 if (tex->texturePriv().samplerType() != kTexture2DSampler_GrSLType) {
957 return -1;
958 }
959 }
960 if (map[j] < 0) {
961 map[j] = -(nextSlot++);
962 }
963 }
964 return newProxyCnt;
965 }
966
Brian Salomon485b8c62018-01-12 15:11:06 -0500967 GrAAType aaType() const { return static_cast<GrAAType>(fAAType); }
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500968
Brian Salomon336ce7b2017-09-08 08:23:58 -0400969 GrTextureProxy* const* proxies() const { return fProxyCnt > 1 ? fProxyArray : &fProxy0; }
970
971 const GrSamplerState::Filter* filters() const {
972 if (fProxyCnt > 1) {
973 return reinterpret_cast<const GrSamplerState::Filter*>(fProxyArray + kMaxTextures);
974 }
975 return &fFilter0;
976 }
977
Brian Salomonb80ffee2018-05-23 16:39:39 -0400978 class Draw {
979 public:
980 Draw(const SkRect& srcRect, int textureIdx, const GrPerspQuad& quad,
981 SkCanvas::SrcRectConstraint constraint, GrColor color)
982 : fSrcRect(srcRect)
983 , fHasDomain(constraint == SkCanvas::kStrict_SrcRectConstraint)
984 , fTextureIdx(SkToUInt(textureIdx))
985 , fQuad(quad)
986 , fColor(color) {}
987 const GrPerspQuad& quad() const { return fQuad; }
988 int textureIdx() const { return SkToInt(fTextureIdx); }
989 const SkRect& srcRect() const { return fSrcRect; }
990 GrColor color() const { return fColor; }
991 Domain domain() const { return Domain(fHasDomain); }
992 void setTextureIdx(int i) { fTextureIdx = SkToUInt(i); }
993
994 private:
Brian Salomon34169692017-08-28 15:32:01 -0400995 SkRect fSrcRect;
Brian Salomonb80ffee2018-05-23 16:39:39 -0400996 unsigned fHasDomain : 1;
997 unsigned fTextureIdx : 31;
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400998 GrPerspQuad fQuad;
Brian Salomon34169692017-08-28 15:32:01 -0400999 GrColor fColor;
1000 };
1001 SkSTArray<1, Draw, true> fDraws;
Brian Salomon34169692017-08-28 15:32:01 -04001002 sk_sp<GrColorSpaceXform> fColorSpaceXform;
Brian Salomon336ce7b2017-09-08 08:23:58 -04001003 // Initially we store a single proxy ptr and a single filter. If we grow to have more than
1004 // one proxy we instead store pointers to dynamically allocated arrays of size kMaxTextures
1005 // followed by kMaxTextures filters.
1006 union {
1007 GrTextureProxy* fProxy0;
1008 GrTextureProxy** fProxyArray;
1009 };
Brian Salomonbbf05752017-11-30 11:30:48 -05001010 size_t fMaxApproxDstPixelArea;
Brian Salomon336ce7b2017-09-08 08:23:58 -04001011 GrSamplerState::Filter fFilter0;
1012 uint8_t fProxyCnt;
Brian Salomon485b8c62018-01-12 15:11:06 -05001013 unsigned fAAType : 2;
Brian Salomonbe3c1d22018-05-21 12:54:39 -04001014 unsigned fPerspective : 1;
Brian Salomonb80ffee2018-05-23 16:39:39 -04001015 unsigned fDomain : 1;
Brian Salomon34169692017-08-28 15:32:01 -04001016 // Used to track whether fProxy is ref'ed or has a pending IO after finalize() is called.
Brian Salomonb5ef1f92018-01-11 11:46:21 -05001017 unsigned fFinalized : 1;
Brian Salomon336ce7b2017-09-08 08:23:58 -04001018
Brian Salomon34169692017-08-28 15:32:01 -04001019 typedef GrMeshDrawOp INHERITED;
1020};
1021
Brian Salomon336ce7b2017-09-08 08:23:58 -04001022constexpr int TextureGeometryProcessor::kMaxTextures;
1023constexpr int TextureOp::kMaxTextures;
1024
Brian Salomon34169692017-08-28 15:32:01 -04001025} // anonymous namespace
1026
1027namespace GrTextureOp {
1028
Brian Salomon2bbdcc42017-09-07 12:36:34 -04001029std::unique_ptr<GrDrawOp> Make(sk_sp<GrTextureProxy> proxy, GrSamplerState::Filter filter,
Brian Salomon485b8c62018-01-12 15:11:06 -05001030 GrColor color, const SkRect& srcRect, const SkRect& dstRect,
Brian Salomonb80ffee2018-05-23 16:39:39 -04001031 GrAAType aaType, SkCanvas::SrcRectConstraint constraint,
Brian Osman2b23c4b2018-06-01 12:25:08 -04001032 const SkMatrix& viewMatrix, sk_sp<GrColorSpaceXform> csxf) {
Brian Salomonb80ffee2018-05-23 16:39:39 -04001033 return TextureOp::Make(std::move(proxy), filter, color, srcRect, dstRect, aaType, constraint,
Brian Osman2b23c4b2018-06-01 12:25:08 -04001034 viewMatrix, std::move(csxf));
Brian Salomon34169692017-08-28 15:32:01 -04001035}
1036
1037} // namespace GrTextureOp
1038
1039#if GR_TEST_UTILS
1040#include "GrContext.h"
Robert Phillips1afd4cd2018-01-08 13:40:32 -05001041#include "GrContextPriv.h"
Robert Phillips0bd24dc2018-01-16 08:06:32 -05001042#include "GrProxyProvider.h"
Brian Salomon34169692017-08-28 15:32:01 -04001043
1044GR_DRAW_OP_TEST_DEFINE(TextureOp) {
1045 GrSurfaceDesc desc;
1046 desc.fConfig = kRGBA_8888_GrPixelConfig;
1047 desc.fHeight = random->nextULessThan(90) + 10;
1048 desc.fWidth = random->nextULessThan(90) + 10;
Brian Salomon2a4f9832018-03-03 22:43:43 -05001049 auto origin = random->nextBool() ? kTopLeft_GrSurfaceOrigin : kBottomLeft_GrSurfaceOrigin;
Greg Daniel09c94002018-06-08 22:11:51 +00001050 GrMipMapped mipMapped = random->nextBool() ? GrMipMapped::kYes : GrMipMapped::kNo;
1051 SkBackingFit fit = SkBackingFit::kExact;
1052 if (mipMapped == GrMipMapped::kNo) {
1053 fit = random->nextBool() ? SkBackingFit::kApprox : SkBackingFit::kExact;
1054 }
Robert Phillips0bd24dc2018-01-16 08:06:32 -05001055
1056 GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider();
Greg Daniel09c94002018-06-08 22:11:51 +00001057 sk_sp<GrTextureProxy> proxy = proxyProvider->createProxy(desc, origin, mipMapped, fit,
1058 SkBudgeted::kNo,
1059 GrInternalSurfaceFlags::kNone);
Robert Phillips0bd24dc2018-01-16 08:06:32 -05001060
Brian Salomon34169692017-08-28 15:32:01 -04001061 SkRect rect = GrTest::TestRect(random);
1062 SkRect srcRect;
1063 srcRect.fLeft = random->nextRangeScalar(0.f, proxy->width() / 2.f);
1064 srcRect.fRight = random->nextRangeScalar(0.f, proxy->width()) + proxy->width() / 2.f;
1065 srcRect.fTop = random->nextRangeScalar(0.f, proxy->height() / 2.f);
1066 srcRect.fBottom = random->nextRangeScalar(0.f, proxy->height()) + proxy->height() / 2.f;
1067 SkMatrix viewMatrix = GrTest::TestMatrixPreservesRightAngles(random);
1068 GrColor color = SkColorToPremulGrColor(random->nextU());
Brian Salomon2bbdcc42017-09-07 12:36:34 -04001069 GrSamplerState::Filter filter = (GrSamplerState::Filter)random->nextULessThan(
1070 static_cast<uint32_t>(GrSamplerState::Filter::kMipMap) + 1);
Greg Daniel09c94002018-06-08 22:11:51 +00001071 while (mipMapped == GrMipMapped::kNo && filter == GrSamplerState::Filter::kMipMap) {
1072 filter = (GrSamplerState::Filter)random->nextULessThan(
1073 static_cast<uint32_t>(GrSamplerState::Filter::kMipMap) + 1);
1074 }
Brian Salomon34169692017-08-28 15:32:01 -04001075 auto csxf = GrTest::TestColorXform(random);
Brian Salomon485b8c62018-01-12 15:11:06 -05001076 GrAAType aaType = GrAAType::kNone;
1077 if (random->nextBool()) {
1078 aaType = (fsaaType == GrFSAAType::kUnifiedMSAA) ? GrAAType::kMSAA : GrAAType::kCoverage;
1079 }
Brian Salomonb80ffee2018-05-23 16:39:39 -04001080 auto constraint = random->nextBool() ? SkCanvas::kStrict_SrcRectConstraint
1081 : SkCanvas::kFast_SrcRectConstraint;
1082 return GrTextureOp::Make(std::move(proxy), filter, color, srcRect, rect, aaType, constraint,
Brian Osman2b23c4b2018-06-01 12:25:08 -04001083 viewMatrix, std::move(csxf));
Brian Salomon34169692017-08-28 15:32:01 -04001084}
1085
1086#endif