blob: 950fe6e26b4dd26190c6c14d2fce0dd6e57b1990 [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
Brian Salomon17031a72018-05-22 14:14:07 -040034enum class MultiTexture : bool { kNo = false, kYes = true };
35
Brian Salomonb80ffee2018-05-23 16:39:39 -040036enum class Domain : bool { kNo = false, kYes = true };
37
Brian Salomon34169692017-08-28 15:32:01 -040038/**
39 * Geometry Processor that draws a texture modulated by a vertex color (though, this is meant to be
40 * the same value across all vertices of a quad and uses flat interpolation when available). This is
41 * used by TextureOp below.
42 */
43class TextureGeometryProcessor : public GrGeometryProcessor {
44public:
Brian Salomon17031a72018-05-22 14:14:07 -040045 template <typename Pos> struct VertexCommon {
46 using Position = Pos;
47 Position fPosition;
Brian Salomon34169692017-08-28 15:32:01 -040048 GrColor fColor;
Brian Salomon17031a72018-05-22 14:14:07 -040049 SkPoint fTextureCoords;
Brian Salomon34169692017-08-28 15:32:01 -040050 };
Brian Salomon17031a72018-05-22 14:14:07 -040051
52 template <typename Pos, MultiTexture MT> struct OptionalMultiTextureVertex;
53 template <typename Pos>
54 struct OptionalMultiTextureVertex<Pos, MultiTexture::kNo> : VertexCommon<Pos> {
55 static constexpr MultiTexture kMultiTexture = MultiTexture::kNo;
Brian Salomonb5ef1f92018-01-11 11:46:21 -050056 };
Brian Salomon17031a72018-05-22 14:14:07 -040057 template <typename Pos>
58 struct OptionalMultiTextureVertex<Pos, MultiTexture::kYes> : VertexCommon<Pos> {
59 static constexpr MultiTexture kMultiTexture = MultiTexture::kYes;
Brian Salomon336ce7b2017-09-08 08:23:58 -040060 int fTextureIdx;
Brian Salomon336ce7b2017-09-08 08:23:58 -040061 };
Brian Salomon17031a72018-05-22 14:14:07 -040062
Brian Salomonb80ffee2018-05-23 16:39:39 -040063 template <typename Pos, MultiTexture MT, Domain D> struct OptionalDomainVertex;
Brian Salomon17031a72018-05-22 14:14:07 -040064 template <typename Pos, MultiTexture MT>
Brian Salomonb80ffee2018-05-23 16:39:39 -040065 struct OptionalDomainVertex<Pos, MT, Domain::kNo> : OptionalMultiTextureVertex<Pos, MT> {
66 static constexpr Domain kDomain = Domain::kNo;
Brian Salomona0047bc2018-05-23 16:39:39 -040067 };
Stephen White633f20e2018-05-26 18:07:27 +000068 template <typename Pos, MultiTexture MT>
Brian Salomonb80ffee2018-05-23 16:39:39 -040069 struct OptionalDomainVertex<Pos, MT, Domain::kYes> : OptionalMultiTextureVertex<Pos, MT> {
70 static constexpr Domain kDomain = Domain::kYes;
71 SkRect fTextureDomain;
72 };
73
74 template <typename Pos, MultiTexture MT, Domain D, GrAA> struct OptionalAAVertex;
75 template <typename Pos, MultiTexture MT, Domain D>
76 struct OptionalAAVertex<Pos, MT, D, GrAA::kNo> : OptionalDomainVertex<Pos, MT, D> {
77 static constexpr GrAA kAA = GrAA::kNo;
78 };
79 template <typename Pos, MultiTexture MT, Domain D>
80 struct OptionalAAVertex<Pos, MT, D, GrAA::kYes> : OptionalDomainVertex<Pos, MT, D> {
Brian Salomonbe3c1d22018-05-21 12:54:39 -040081 static constexpr GrAA kAA = GrAA::kYes;
Brian Salomonb5ef1f92018-01-11 11:46:21 -050082 SkPoint3 fEdges[4];
Brian Salomonb5ef1f92018-01-11 11:46:21 -050083 };
Brian Salomon336ce7b2017-09-08 08:23:58 -040084
Brian Salomonb80ffee2018-05-23 16:39:39 -040085 template <typename Pos, MultiTexture MT, Domain D, GrAA AA>
86 using Vertex = OptionalAAVertex<Pos, MT, D, AA>;
Brian Salomon17031a72018-05-22 14:14:07 -040087
Brian Salomon336ce7b2017-09-08 08:23:58 -040088 // Maximum number of textures supported by this op. Must also be checked against the caps
89 // limit. These numbers were based on some limited experiments on a HP Z840 and Pixel XL 2016
90 // and could probably use more tuning.
91#ifdef SK_BUILD_FOR_ANDROID
92 static constexpr int kMaxTextures = 4;
93#else
94 static constexpr int kMaxTextures = 8;
95#endif
96
Brian Salomon0b4d8aa2017-10-11 15:34:27 -040097 static int SupportsMultitexture(const GrShaderCaps& caps) {
Brian Salomon762d5e72017-12-01 10:25:08 -050098 return caps.integerSupport() && caps.maxFragmentSamplers() > 1;
Brian Salomon0b4d8aa2017-10-11 15:34:27 -040099 }
Brian Salomon336ce7b2017-09-08 08:23:58 -0400100
101 static sk_sp<GrGeometryProcessor> Make(sk_sp<GrTextureProxy> proxies[], int proxyCnt,
Brian Salomon485b8c62018-01-12 15:11:06 -0500102 sk_sp<GrColorSpaceXform> csxf, bool coverageAA,
Brian Salomonb80ffee2018-05-23 16:39:39 -0400103 bool perspective, Domain domain,
104 const GrSamplerState::Filter filters[],
Brian Salomon336ce7b2017-09-08 08:23:58 -0400105 const GrShaderCaps& caps) {
106 // We use placement new to avoid always allocating space for kMaxTextures TextureSampler
107 // instances.
108 int samplerCnt = NumSamplersToUse(proxyCnt, caps);
109 size_t size = sizeof(TextureGeometryProcessor) + sizeof(TextureSampler) * (samplerCnt - 1);
110 void* mem = GrGeometryProcessor::operator new(size);
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400111 return sk_sp<TextureGeometryProcessor>(
112 new (mem) TextureGeometryProcessor(proxies, proxyCnt, samplerCnt, std::move(csxf),
Brian Salomonb80ffee2018-05-23 16:39:39 -0400113 coverageAA, perspective, domain, filters, caps));
Brian Salomon336ce7b2017-09-08 08:23:58 -0400114 }
115
116 ~TextureGeometryProcessor() override {
117 int cnt = this->numTextureSamplers();
118 for (int i = 1; i < cnt; ++i) {
119 fSamplers[i].~TextureSampler();
120 }
Brian Salomon34169692017-08-28 15:32:01 -0400121 }
122
123 const char* name() const override { return "TextureGeometryProcessor"; }
124
125 void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const override {
126 b->add32(GrColorSpaceXform::XformKey(fColorSpaceXform.get()));
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400127 uint32_t x = this->usesCoverageEdgeAA() ? 0 : 1;
Brian Salomon70132d02018-05-29 15:33:06 -0400128 x |= kFloat3_GrVertexAttribType == fPositions.type() ? 0 : 2;
Brian Salomonb80ffee2018-05-23 16:39:39 -0400129 x |= fDomain.isInitialized() ? 4 : 0;
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400130 b->add32(x);
Brian Salomon34169692017-08-28 15:32:01 -0400131 }
132
133 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps& caps) const override {
134 class GLSLProcessor : public GrGLSLGeometryProcessor {
135 public:
136 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& proc,
137 FPCoordTransformIter&& transformIter) override {
138 const auto& textureGP = proc.cast<TextureGeometryProcessor>();
139 this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
140 if (fColorSpaceXformHelper.isValid()) {
141 fColorSpaceXformHelper.setData(pdman, textureGP.fColorSpaceXform.get());
142 }
143 }
144
145 private:
146 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
Chris Dalton7b046312018-02-02 11:06:30 -0700147 using Interpolation = GrGLSLVaryingHandler::Interpolation;
Brian Salomon34169692017-08-28 15:32:01 -0400148 const auto& textureGP = args.fGP.cast<TextureGeometryProcessor>();
149 fColorSpaceXformHelper.emitCode(
150 args.fUniformHandler, textureGP.fColorSpaceXform.get());
Brian Salomon70132d02018-05-29 15:33:06 -0400151 if (kFloat2_GrVertexAttribType == textureGP.fPositions.type()) {
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400152 args.fVaryingHandler->setNoPerspective();
153 }
Brian Salomon34169692017-08-28 15:32:01 -0400154 args.fVaryingHandler->emitAttributes(textureGP);
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400155 gpArgs->fPositionVar = textureGP.fPositions.asShaderVar();
156
Brian Salomon34169692017-08-28 15:32:01 -0400157 this->emitTransforms(args.fVertBuilder,
158 args.fVaryingHandler,
159 args.fUniformHandler,
Brian Salomon04460cc2017-12-06 14:47:42 -0500160 textureGP.fTextureCoords.asShaderVar(),
Brian Salomon34169692017-08-28 15:32:01 -0400161 args.fFPCoordTransformHandler);
Chris Dalton7b046312018-02-02 11:06:30 -0700162 args.fVaryingHandler->addPassThroughAttribute(&textureGP.fColors,
163 args.fOutputColor,
164 Interpolation::kCanBeFlat);
Ethan Nicholas8aa45692017-09-20 11:24:15 -0400165 args.fFragBuilder->codeAppend("float2 texCoord;");
Chris Daltonfdde34e2017-10-16 14:15:26 -0600166 args.fVaryingHandler->addPassThroughAttribute(&textureGP.fTextureCoords,
167 "texCoord");
Brian Salomonb80ffee2018-05-23 16:39:39 -0400168 if (textureGP.fDomain.isInitialized()) {
169 args.fFragBuilder->codeAppend("float4 domain;");
170 args.fVaryingHandler->addPassThroughAttribute(
171 &textureGP.fDomain, "domain",
172 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
173 args.fFragBuilder->codeAppend(
174 "texCoord = clamp(texCoord, domain.xy, domain.zw);");
175 }
Brian Salomon336ce7b2017-09-08 08:23:58 -0400176 if (textureGP.numTextureSamplers() > 1) {
Chris Dalton7b046312018-02-02 11:06:30 -0700177 // If this changes to float, reconsider Interpolation::kMustBeFlat.
Brian Salomon70132d02018-05-29 15:33:06 -0400178 SkASSERT(kInt_GrVertexAttribType == textureGP.fTextureIdx.type());
Brian Salomon336ce7b2017-09-08 08:23:58 -0400179 SkASSERT(args.fShaderCaps->integerSupport());
180 args.fFragBuilder->codeAppend("int texIdx;");
Chris Dalton7b046312018-02-02 11:06:30 -0700181 args.fVaryingHandler->addPassThroughAttribute(&textureGP.fTextureIdx, "texIdx",
182 Interpolation::kMustBeFlat);
Brian Salomon336ce7b2017-09-08 08:23:58 -0400183 args.fFragBuilder->codeAppend("switch (texIdx) {");
184 for (int i = 0; i < textureGP.numTextureSamplers(); ++i) {
185 args.fFragBuilder->codeAppendf("case %d: %s = ", i, args.fOutputColor);
186 args.fFragBuilder->appendTextureLookupAndModulate(args.fOutputColor,
187 args.fTexSamplers[i],
188 "texCoord",
Ethan Nicholas8aa45692017-09-20 11:24:15 -0400189 kFloat2_GrSLType,
Brian Salomon336ce7b2017-09-08 08:23:58 -0400190 &fColorSpaceXformHelper);
191 args.fFragBuilder->codeAppend("; break;");
192 }
193 args.fFragBuilder->codeAppend("}");
194 } else {
195 args.fFragBuilder->codeAppendf("%s = ", args.fOutputColor);
196 args.fFragBuilder->appendTextureLookupAndModulate(args.fOutputColor,
197 args.fTexSamplers[0],
198 "texCoord",
Ethan Nicholas8aa45692017-09-20 11:24:15 -0400199 kFloat2_GrSLType,
Brian Salomon336ce7b2017-09-08 08:23:58 -0400200 &fColorSpaceXformHelper);
201 }
Brian Salomon34169692017-08-28 15:32:01 -0400202 args.fFragBuilder->codeAppend(";");
Brian Salomon485b8c62018-01-12 15:11:06 -0500203 if (textureGP.usesCoverageEdgeAA()) {
Brian Salomondba65f92018-01-22 08:43:38 -0500204 const char* aaDistName = nullptr;
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400205 bool mulByFragCoordW = false;
206 // When interpolation is inaccurate we perform the evaluation of the edge
Brian Salomondba65f92018-01-22 08:43:38 -0500207 // equations in the fragment shader rather than interpolating values computed
208 // in the vertex shader.
209 if (!args.fShaderCaps->interpolantsAreInaccurate()) {
210 GrGLSLVarying aaDistVarying(kFloat4_GrSLType,
211 GrGLSLVarying::Scope::kVertToFrag);
Brian Salomon70132d02018-05-29 15:33:06 -0400212 if (kFloat3_GrVertexAttribType == textureGP.fPositions.type()) {
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400213 args.fVaryingHandler->addVarying("aaDists", &aaDistVarying);
214 // The distance from edge equation e to homogenous point p=sk_Position
215 // is e.x*p.x/p.wx + e.y*p.y/p.w + e.z. However, we want screen space
216 // interpolation of this distance. We can do this by multiplying the
217 // varying in the VS by p.w and then multiplying by sk_FragCoord.w in
218 // the FS. So we output e.x*p.x + e.y*p.y + e.z * p.w
219 args.fVertBuilder->codeAppendf(
220 R"(%s = float4(dot(aaEdge0, %s), dot(aaEdge1, %s),
221 dot(aaEdge2, %s), dot(aaEdge3, %s));)",
Brian Salomon70132d02018-05-29 15:33:06 -0400222 aaDistVarying.vsOut(), textureGP.fPositions.name(),
223 textureGP.fPositions.name(), textureGP.fPositions.name(),
224 textureGP.fPositions.name());
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400225 mulByFragCoordW = true;
226 } else {
227 args.fVaryingHandler->addVarying("aaDists", &aaDistVarying);
228 args.fVertBuilder->codeAppendf(
229 R"(%s = float4(dot(aaEdge0.xy, %s.xy) + aaEdge0.z,
230 dot(aaEdge1.xy, %s.xy) + aaEdge1.z,
231 dot(aaEdge2.xy, %s.xy) + aaEdge2.z,
232 dot(aaEdge3.xy, %s.xy) + aaEdge3.z);)",
Brian Salomon70132d02018-05-29 15:33:06 -0400233 aaDistVarying.vsOut(), textureGP.fPositions.name(),
234 textureGP.fPositions.name(), textureGP.fPositions.name(),
235 textureGP.fPositions.name());
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400236 }
Brian Salomondba65f92018-01-22 08:43:38 -0500237 aaDistName = aaDistVarying.fsIn();
238 } else {
239 GrGLSLVarying aaEdgeVarying[4]{
240 {kFloat3_GrSLType, GrGLSLVarying::Scope::kVertToFrag},
241 {kFloat3_GrSLType, GrGLSLVarying::Scope::kVertToFrag},
242 {kFloat3_GrSLType, GrGLSLVarying::Scope::kVertToFrag},
243 {kFloat3_GrSLType, GrGLSLVarying::Scope::kVertToFrag}
244 };
245 for (int i = 0; i < 4; ++i) {
246 SkString name;
247 name.printf("aaEdge%d", i);
Brian Salomon7d982c62018-02-05 16:20:47 -0500248 args.fVaryingHandler->addVarying(name.c_str(), &aaEdgeVarying[i],
249 Interpolation::kCanBeFlat);
Brian Salomondba65f92018-01-22 08:43:38 -0500250 args.fVertBuilder->codeAppendf(
251 "%s = aaEdge%d;", aaEdgeVarying[i].vsOut(), i);
252 }
253 args.fFragBuilder->codeAppendf(
254 R"(float4 aaDists = float4(dot(%s.xy, sk_FragCoord.xy) + %s.z,
255 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 aaEdgeVarying[0].fsIn(), aaEdgeVarying[0].fsIn(),
259 aaEdgeVarying[1].fsIn(), aaEdgeVarying[1].fsIn(),
260 aaEdgeVarying[2].fsIn(), aaEdgeVarying[2].fsIn(),
261 aaEdgeVarying[3].fsIn(), aaEdgeVarying[3].fsIn());
262 aaDistName = "aaDists";
263 }
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500264 args.fFragBuilder->codeAppendf(
265 "float mindist = min(min(%s.x, %s.y), min(%s.z, %s.w));",
Brian Salomondba65f92018-01-22 08:43:38 -0500266 aaDistName, aaDistName, aaDistName, aaDistName);
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400267 if (mulByFragCoordW) {
268 args.fFragBuilder->codeAppend("mindist *= sk_FragCoord.w;");
269 }
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500270 args.fFragBuilder->codeAppendf("%s = float4(clamp(mindist, 0, 1));",
271 args.fOutputCoverage);
272 } else {
273 args.fFragBuilder->codeAppendf("%s = float4(1);", args.fOutputCoverage);
274 }
Brian Salomon34169692017-08-28 15:32:01 -0400275 }
276 GrGLSLColorSpaceXformHelper fColorSpaceXformHelper;
277 };
278 return new GLSLProcessor;
279 }
280
Brian Salomon485b8c62018-01-12 15:11:06 -0500281 bool usesCoverageEdgeAA() const { return SkToBool(fAAEdges[0].isInitialized()); }
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500282
Brian Salomon34169692017-08-28 15:32:01 -0400283private:
Brian Salomon336ce7b2017-09-08 08:23:58 -0400284 // This exists to reduce the number of shaders generated. It does some rounding of sampler
285 // counts.
286 static int NumSamplersToUse(int numRealProxies, const GrShaderCaps& caps) {
287 SkASSERT(numRealProxies > 0 && numRealProxies <= kMaxTextures &&
288 numRealProxies <= caps.maxFragmentSamplers());
289 if (1 == numRealProxies) {
290 return 1;
291 }
292 if (numRealProxies <= 4) {
293 return 4;
294 }
295 // Round to the next power of 2 and then clamp to kMaxTextures and the max allowed by caps.
296 return SkTMin(SkNextPow2(numRealProxies), SkTMin(kMaxTextures, caps.maxFragmentSamplers()));
297 }
298
299 TextureGeometryProcessor(sk_sp<GrTextureProxy> proxies[], int proxyCnt, int samplerCnt,
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400300 sk_sp<GrColorSpaceXform> csxf, bool coverageAA, bool perspective,
Brian Salomonb80ffee2018-05-23 16:39:39 -0400301 Domain domain, const GrSamplerState::Filter filters[],
302 const GrShaderCaps& caps)
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500303 : INHERITED(kTextureGeometryProcessor_ClassID), fColorSpaceXform(std::move(csxf)) {
Brian Salomon336ce7b2017-09-08 08:23:58 -0400304 SkASSERT(proxyCnt > 0 && samplerCnt >= proxyCnt);
Brian Salomon336ce7b2017-09-08 08:23:58 -0400305 fSamplers[0].reset(std::move(proxies[0]), filters[0]);
306 this->addTextureSampler(&fSamplers[0]);
307 for (int i = 1; i < proxyCnt; ++i) {
308 // This class has one sampler built in, the rest come from memory this processor was
309 // placement-newed into and so haven't been constructed.
310 new (&fSamplers[i]) TextureSampler(std::move(proxies[i]), filters[i]);
311 this->addTextureSampler(&fSamplers[i]);
312 }
Brian Salomon30e1a5e2018-05-18 12:32:32 -0400313
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400314 if (perspective) {
315 fPositions = this->addVertexAttrib("position", kFloat3_GrVertexAttribType);
316 } else {
317 fPositions = this->addVertexAttrib("position", kFloat2_GrVertexAttribType);
318 }
Brian Salomon30e1a5e2018-05-18 12:32:32 -0400319 fColors = this->addVertexAttrib("color", kUByte4_norm_GrVertexAttribType);
Brian Salomon17031a72018-05-22 14:14:07 -0400320 fTextureCoords = this->addVertexAttrib("textureCoords", kFloat2_GrVertexAttribType);
Brian Salomon30e1a5e2018-05-18 12:32:32 -0400321
Brian Salomon336ce7b2017-09-08 08:23:58 -0400322 if (samplerCnt > 1) {
323 // Here we initialize any extra samplers by repeating the last one samplerCnt - proxyCnt
324 // times.
325 GrTextureProxy* dupeProxy = fSamplers[proxyCnt - 1].proxy();
326 for (int i = proxyCnt; i < samplerCnt; ++i) {
327 new (&fSamplers[i]) TextureSampler(sk_ref_sp(dupeProxy), filters[proxyCnt - 1]);
328 this->addTextureSampler(&fSamplers[i]);
329 }
330 SkASSERT(caps.integerSupport());
331 fTextureIdx = this->addVertexAttrib("textureIdx", kInt_GrVertexAttribType);
332 }
Brian Salomonb80ffee2018-05-23 16:39:39 -0400333 if (domain == Domain::kYes) {
334 fDomain = this->addVertexAttrib("domain", kFloat4_GrVertexAttribType);
335 }
Brian Salomon485b8c62018-01-12 15:11:06 -0500336 if (coverageAA) {
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500337 fAAEdges[0] = this->addVertexAttrib("aaEdge0", kFloat3_GrVertexAttribType);
338 fAAEdges[1] = this->addVertexAttrib("aaEdge1", kFloat3_GrVertexAttribType);
339 fAAEdges[2] = this->addVertexAttrib("aaEdge2", kFloat3_GrVertexAttribType);
340 fAAEdges[3] = this->addVertexAttrib("aaEdge3", kFloat3_GrVertexAttribType);
341 }
Brian Salomon34169692017-08-28 15:32:01 -0400342 }
343
344 Attribute fPositions;
Brian Salomon34169692017-08-28 15:32:01 -0400345 Attribute fColors;
Brian Salomon30e1a5e2018-05-18 12:32:32 -0400346 Attribute fTextureCoords;
347 Attribute fTextureIdx;
Brian Salomonb80ffee2018-05-23 16:39:39 -0400348 Attribute fDomain;
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500349 Attribute fAAEdges[4];
Brian Salomon34169692017-08-28 15:32:01 -0400350 sk_sp<GrColorSpaceXform> fColorSpaceXform;
Brian Salomon336ce7b2017-09-08 08:23:58 -0400351 TextureSampler fSamplers[1];
Ethan Nicholasabff9562017-10-09 10:54:08 -0400352
353 typedef GrGeometryProcessor INHERITED;
Brian Salomon34169692017-08-28 15:32:01 -0400354};
355
Brian Salomon6872e942018-05-18 10:29:54 -0400356// This computes the four edge equations for a quad, then outsets them and computes a new quad
357// as the intersection points of the outset edges. 'x' and 'y' contain the original points as input
358// and the outset points as output. 'a', 'b', and 'c' are the edge equation coefficients on output.
359static void compute_quad_edges_and_outset_vertices(Sk4f* x, Sk4f* y, Sk4f* a, Sk4f* b, Sk4f* c) {
360 static constexpr auto fma = SkNx_fma<4, float>;
361 // These rotate the points/edge values either clockwise or counterclockwise assuming tri strip
362 // order.
363 auto nextCW = [](const Sk4f& v) { return SkNx_shuffle<2, 0, 3, 1>(v); };
364 auto nextCCW = [](const Sk4f& v) { return SkNx_shuffle<1, 3, 0, 2>(v); };
365
366 auto xnext = nextCCW(*x);
367 auto ynext = nextCCW(*y);
368 *a = ynext - *y;
369 *b = *x - xnext;
370 *c = fma(xnext, *y, -ynext * *x);
371 Sk4f invNormLengths = (*a * *a + *b * *b).rsqrt();
372 // Make sure the edge equations have their normals facing into the quad in device space.
373 auto test = fma(*a, nextCW(*x), fma(*b, nextCW(*y), *c));
374 if ((test < Sk4f(0)).anyTrue()) {
375 invNormLengths = -invNormLengths;
376 }
377 *a *= invNormLengths;
378 *b *= invNormLengths;
379 *c *= invNormLengths;
380
381 // Here is the outset. This makes our edge equations compute coverage without requiring a
382 // half pixel offset and is also used to compute the bloated quad that will cover all
383 // pixels.
384 *c += Sk4f(0.5f);
385
386 // Reverse the process to compute the points of the bloated quad from the edge equations.
387 // This time the inputs don't have 1s as their third coord and we want to homogenize rather
388 // than normalize.
389 auto anext = nextCW(*a);
390 auto bnext = nextCW(*b);
391 auto cnext = nextCW(*c);
392 *x = fma(bnext, *c, -*b * cnext);
393 *y = fma(*a, cnext, -anext * *c);
394 auto ic = (fma(anext, *b, -bnext * *a)).invert();
395 *x *= ic;
396 *y *= ic;
397}
398
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500399namespace {
400// This is a class soley so it can be partially specialized (functions cannot be).
Brian Salomon86c40012018-05-22 10:48:49 -0400401template <typename V, GrAA AA = V::kAA, typename Position = typename V::Position>
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400402class VertexAAHandler;
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500403
Brian Salomon86c40012018-05-22 10:48:49 -0400404template<typename V> class VertexAAHandler<V, GrAA::kNo, SkPoint> {
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500405public:
Brian Salomon86c40012018-05-22 10:48:49 -0400406 static void AssignPositionsAndTexCoords(V* vertices, const GrPerspQuad& quad,
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500407 const SkRect& texRect) {
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400408 SkASSERT((quad.w4f() == Sk4f(1.f)).allTrue());
Brian Salomon86c40012018-05-22 10:48:49 -0400409 SkPointPriv::SetRectTriStrip(&vertices[0].fTextureCoords, texRect, sizeof(V));
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400410 for (int i = 0; i < 4; ++i) {
411 vertices[i].fPosition = {quad.x(i), quad.y(i)};
412 }
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500413 }
414};
415
Brian Salomon86c40012018-05-22 10:48:49 -0400416template<typename V> class VertexAAHandler<V, GrAA::kNo, SkPoint3> {
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500417public:
Brian Salomon86c40012018-05-22 10:48:49 -0400418 static void AssignPositionsAndTexCoords(V* vertices, const GrPerspQuad& quad,
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500419 const SkRect& texRect) {
Brian Salomon86c40012018-05-22 10:48:49 -0400420 SkPointPriv::SetRectTriStrip(&vertices[0].fTextureCoords, texRect, sizeof(V));
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400421 for (int i = 0; i < 4; ++i) {
422 vertices[i].fPosition = quad.point(i);
423 }
424 }
425};
426
Brian Salomon86c40012018-05-22 10:48:49 -0400427template<typename V> class VertexAAHandler<V, GrAA::kYes, SkPoint> {
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400428public:
Brian Salomon86c40012018-05-22 10:48:49 -0400429 static void AssignPositionsAndTexCoords(V* vertices, const GrPerspQuad& quad,
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400430 const SkRect& texRect) {
431 SkASSERT((quad.w4f() == Sk4f(1.f)).allTrue());
Brian Salomon6872e942018-05-18 10:29:54 -0400432 auto x = quad.x4f();
433 auto y = quad.y4f();
434 Sk4f a, b, c;
435 compute_quad_edges_and_outset_vertices(&x, &y, &a, &b, &c);
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500436
437 for (int i = 0; i < 4; ++i) {
Brian Salomon6872e942018-05-18 10:29:54 -0400438 vertices[i].fPosition = {x[i], y[i]};
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500439 for (int j = 0; j < 4; ++j) {
Brian Salomon6872e942018-05-18 10:29:54 -0400440 vertices[i].fEdges[j] = {a[j], b[j], c[j]};
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500441 }
442 }
443
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500444 AssignTexCoords(vertices, quad, texRect);
445 }
446
447private:
Brian Salomon86c40012018-05-22 10:48:49 -0400448 static void AssignTexCoords(V* vertices, const GrPerspQuad& quad, const SkRect& tex) {
Brian Salomona33b67c2018-05-17 10:42:14 -0400449 SkMatrix q = SkMatrix::MakeAll(quad.x(0), quad.x(1), quad.x(2),
450 quad.y(0), quad.y(1), quad.y(2),
451 1.f, 1.f, 1.f);
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500452 SkMatrix qinv;
453 if (!q.invert(&qinv)) {
454 return;
455 }
456 SkMatrix t = SkMatrix::MakeAll(tex.fLeft, tex.fLeft, tex.fRight,
457 tex.fTop, tex.fBottom, tex.fTop,
458 1.f, 1.f, 1.f);
459 SkMatrix map;
460 map.setConcat(t, qinv);
Brian Salomon86c40012018-05-22 10:48:49 -0400461 SkMatrixPriv::MapPointsWithStride(map, &vertices[0].fTextureCoords, sizeof(V),
462 &vertices[0].fPosition, sizeof(V), 4);
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500463 }
464};
465
Brian Salomon86c40012018-05-22 10:48:49 -0400466template<typename V> class VertexAAHandler<V, GrAA::kYes, SkPoint3> {
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400467public:
Brian Salomon86c40012018-05-22 10:48:49 -0400468 static void AssignPositionsAndTexCoords(V* vertices, const GrPerspQuad& quad,
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400469 const SkRect& texRect) {
470 auto x = quad.x4f();
471 auto y = quad.y4f();
472 auto iw = quad.iw4f();
473 x *= iw;
474 y *= iw;
475
476 // Get an equation for w from device space coords.
477 SkMatrix P;
478 P.setAll(x[0], y[0], 1, x[1], y[1], 1, x[2], y[2], 1);
479 SkAssertResult(P.invert(&P));
480 SkPoint3 weq{quad.w(0), quad.w(1), quad.w(2)};
481 P.mapHomogeneousPoints(&weq, &weq, 1);
482
483 Sk4f a, b, c;
484 compute_quad_edges_and_outset_vertices(&x, &y, &a, &b, &c);
485
486 // Compute new w values for the output vertices;
487 auto w = Sk4f(weq.fX) * x + Sk4f(weq.fY) * y + Sk4f(weq.fZ);
488 x *= w;
489 y *= w;
490
491 for (int i = 0; i < 4; ++i) {
492 vertices[i].fPosition = {x[i], y[i], w[i]};
493 for (int j = 0; j < 4; ++j) {
494 vertices[i].fEdges[j] = {a[j], b[j], c[j]};
495 }
496 }
497
498 AssignTexCoords(vertices, quad, texRect);
499 }
500
501private:
Brian Salomon86c40012018-05-22 10:48:49 -0400502 static void AssignTexCoords(V* vertices, const GrPerspQuad& quad, const SkRect& tex) {
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400503 SkMatrix q = SkMatrix::MakeAll(quad.x(0), quad.x(1), quad.x(2),
504 quad.y(0), quad.y(1), quad.y(2),
505 quad.w(0), quad.w(1), quad.w(2));
506 SkMatrix qinv;
507 if (!q.invert(&qinv)) {
508 return;
509 }
510 SkMatrix t = SkMatrix::MakeAll(tex.fLeft, tex.fLeft, tex.fRight,
511 tex.fTop, tex.fBottom, tex.fTop,
512 1.f, 1.f, 1.f);
513 SkMatrix map;
514 map.setConcat(t, qinv);
515 SkPoint3 tempTexCoords[4];
516 SkMatrixPriv::MapHomogeneousPointsWithStride(map, tempTexCoords, sizeof(SkPoint3),
Brian Salomon86c40012018-05-22 10:48:49 -0400517 &vertices[0].fPosition, sizeof(V), 4);
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400518 for (int i = 0; i < 4; ++i) {
519 auto invW = 1.f / tempTexCoords[i].fZ;
520 vertices[i].fTextureCoords.fX = tempTexCoords[i].fX * invW;
521 vertices[i].fTextureCoords.fY = tempTexCoords[i].fY * invW;
522 }
523 }
524};
525
Brian Salomon17031a72018-05-22 14:14:07 -0400526template <typename V, MultiTexture MT = V::kMultiTexture> struct TexIdAssigner;
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500527
Brian Salomon17031a72018-05-22 14:14:07 -0400528template <typename V> struct TexIdAssigner<V, MultiTexture::kYes> {
Brian Salomon86c40012018-05-22 10:48:49 -0400529 static void Assign(V* vertices, int textureIdx) {
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400530 for (int i = 0; i < 4; ++i) {
531 vertices[i].fTextureIdx = textureIdx;
532 }
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500533 }
534};
535
Brian Salomon17031a72018-05-22 14:14:07 -0400536template <typename V> struct TexIdAssigner<V, MultiTexture::kNo> {
Brian Salomon86c40012018-05-22 10:48:49 -0400537 static void Assign(V* vertices, int textureIdx) {}
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500538};
Brian Salomonb80ffee2018-05-23 16:39:39 -0400539
540template <typename V, Domain D = V::kDomain> struct DomainAssigner;
541
542template <typename V> struct DomainAssigner<V, Domain::kYes> {
543 static void Assign(V* vertices, Domain domain, GrSamplerState::Filter filter,
544 const SkRect& srcRect, GrSurfaceOrigin origin, float iw, float ih) {
545 static constexpr SkRect kLargeRect = {-2, -2, 2, 2};
546 SkRect domainRect;
547 if (domain == Domain::kYes) {
548 auto ltrb = Sk4f::Load(&srcRect);
549 if (filter == GrSamplerState::Filter::kBilerp) {
550 auto rblt = SkNx_shuffle<2, 3, 0, 1>(ltrb);
551 auto whwh = (rblt - ltrb).abs();
552 auto c = (rblt + ltrb) * 0.5f;
553 static const Sk4f kOffsets = {0.5f, 0.5f, -0.5f, -0.5f};
554 ltrb = (whwh < 1.f).thenElse(c, ltrb + kOffsets);
555 }
556 ltrb *= Sk4f(iw, ih, iw, ih);
557 if (origin == kBottomLeft_GrSurfaceOrigin) {
558 static const Sk4f kMul = {1.f, -1.f, 1.f, -1.f};
559 static const Sk4f kAdd = {0.f, 1.f, 0.f, 1.f};
560 ltrb = SkNx_shuffle<0, 3, 2, 1>(kMul * ltrb + kAdd);
561 }
562 ltrb.store(&domainRect);
563 } else {
564 domainRect = kLargeRect;
565 }
566 for (int i = 0; i < 4; ++i) {
567 vertices[i].fTextureDomain = domainRect;
568 }
569 }
570};
571
572template <typename V> struct DomainAssigner<V, Domain::kNo> {
573 static void Assign(V*, Domain domain, GrSamplerState::Filter, const SkRect&, GrSurfaceOrigin,
574 float iw, float ih) {
575 SkASSERT(domain == Domain::kNo);
576 }
577};
578
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500579} // anonymous namespace
580
Brian Salomon86c40012018-05-22 10:48:49 -0400581template <typename V>
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400582static void tessellate_quad(const GrPerspQuad& devQuad, const SkRect& srcRect, GrColor color,
Brian Salomonb80ffee2018-05-23 16:39:39 -0400583 GrSurfaceOrigin origin, GrSamplerState::Filter filter, V* vertices,
584 SkScalar iw, SkScalar ih, int textureIdx, Domain domain) {
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500585 SkRect texRect = {
586 iw * srcRect.fLeft,
587 ih * srcRect.fTop,
588 iw * srcRect.fRight,
589 ih * srcRect.fBottom
590 };
591 if (origin == kBottomLeft_GrSurfaceOrigin) {
592 texRect.fTop = 1.f - texRect.fTop;
593 texRect.fBottom = 1.f - texRect.fBottom;
594 }
Brian Salomon86c40012018-05-22 10:48:49 -0400595 VertexAAHandler<V>::AssignPositionsAndTexCoords(vertices, devQuad, texRect);
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500596 vertices[0].fColor = color;
597 vertices[1].fColor = color;
598 vertices[2].fColor = color;
599 vertices[3].fColor = color;
Brian Salomon86c40012018-05-22 10:48:49 -0400600 TexIdAssigner<V>::Assign(vertices, textureIdx);
Brian Salomonb80ffee2018-05-23 16:39:39 -0400601 DomainAssigner<V>::Assign(vertices, domain, filter, srcRect, origin, iw, ih);
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500602}
Brian Salomon17031a72018-05-22 14:14:07 -0400603
Brian Salomon34169692017-08-28 15:32:01 -0400604/**
605 * Op that implements GrTextureOp::Make. It draws textured quads. Each quad can modulate against a
606 * the texture by color. The blend with the destination is always src-over. The edges are non-AA.
607 */
608class TextureOp final : public GrMeshDrawOp {
609public:
610 static std::unique_ptr<GrDrawOp> Make(sk_sp<GrTextureProxy> proxy,
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400611 GrSamplerState::Filter filter, GrColor color,
Brian Salomon485b8c62018-01-12 15:11:06 -0500612 const SkRect& srcRect, const SkRect& dstRect,
Brian Salomonb80ffee2018-05-23 16:39:39 -0400613 GrAAType aaType, SkCanvas::SrcRectConstraint constraint,
Brian Osman2b23c4b2018-06-01 12:25:08 -0400614 const SkMatrix& viewMatrix,
615 sk_sp<GrColorSpaceXform> csxf) {
Brian Salomon34169692017-08-28 15:32:01 -0400616 return std::unique_ptr<GrDrawOp>(new TextureOp(std::move(proxy), filter, color, srcRect,
Brian Salomonb80ffee2018-05-23 16:39:39 -0400617 dstRect, aaType, constraint, viewMatrix,
Brian Osman2b23c4b2018-06-01 12:25:08 -0400618 std::move(csxf)));
Brian Salomon34169692017-08-28 15:32:01 -0400619 }
620
Brian Salomon336ce7b2017-09-08 08:23:58 -0400621 ~TextureOp() override {
622 if (fFinalized) {
623 auto proxies = this->proxies();
624 for (int i = 0; i < fProxyCnt; ++i) {
625 proxies[i]->completedRead();
626 }
627 if (fProxyCnt > 1) {
628 delete[] reinterpret_cast<const char*>(proxies);
629 }
630 } else {
631 SkASSERT(1 == fProxyCnt);
632 fProxy0->unref();
633 }
634 }
Brian Salomon34169692017-08-28 15:32:01 -0400635
636 const char* name() const override { return "TextureOp"; }
637
Robert Phillipsf1748f52017-09-14 14:11:24 -0400638 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillipsb493eeb2017-09-13 13:10:52 -0400639 auto proxies = this->proxies();
640 for (int i = 0; i < fProxyCnt; ++i) {
641 func(proxies[i]);
642 }
643 }
644
Brian Salomon34169692017-08-28 15:32:01 -0400645 SkString dumpInfo() const override {
646 SkString str;
Brian Salomon34169692017-08-28 15:32:01 -0400647 str.appendf("# draws: %d\n", fDraws.count());
Brian Salomon336ce7b2017-09-08 08:23:58 -0400648 auto proxies = this->proxies();
649 for (int i = 0; i < fProxyCnt; ++i) {
650 str.appendf("Proxy ID %d: %d, Filter: %d\n", i, proxies[i]->uniqueID().asUInt(),
651 static_cast<int>(this->filters()[i]));
652 }
Brian Salomon34169692017-08-28 15:32:01 -0400653 for (int i = 0; i < fDraws.count(); ++i) {
654 const Draw& draw = fDraws[i];
655 str.appendf(
Brian Salomon336ce7b2017-09-08 08:23:58 -0400656 "%d: Color: 0x%08x, ProxyIdx: %d, TexRect [L: %.2f, T: %.2f, R: %.2f, B: %.2f] "
657 "Quad [(%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f)]\n",
Brian Salomonb80ffee2018-05-23 16:39:39 -0400658 i, draw.color(), draw.textureIdx(), draw.srcRect().fLeft, draw.srcRect().fTop,
659 draw.srcRect().fRight, draw.srcRect().fBottom, draw.quad().point(0).fX,
660 draw.quad().point(0).fY, draw.quad().point(1).fX, draw.quad().point(1).fY,
661 draw.quad().point(2).fX, draw.quad().point(2).fY, draw.quad().point(3).fX,
662 draw.quad().point(3).fY);
Brian Salomon34169692017-08-28 15:32:01 -0400663 }
664 str += INHERITED::dumpInfo();
665 return str;
666 }
667
Brian Osman9a725dd2017-09-20 09:53:22 -0400668 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip,
669 GrPixelConfigIsClamped dstIsClamped) override {
Brian Salomon34169692017-08-28 15:32:01 -0400670 SkASSERT(!fFinalized);
Brian Salomon336ce7b2017-09-08 08:23:58 -0400671 SkASSERT(1 == fProxyCnt);
Brian Salomon34169692017-08-28 15:32:01 -0400672 fFinalized = true;
Brian Salomon336ce7b2017-09-08 08:23:58 -0400673 fProxy0->addPendingRead();
674 fProxy0->unref();
Brian Salomon34169692017-08-28 15:32:01 -0400675 return RequiresDstTexture::kNo;
676 }
677
Brian Salomon485b8c62018-01-12 15:11:06 -0500678 FixedFunctionFlags fixedFunctionFlags() const override {
679 return this->aaType() == GrAAType::kMSAA ? FixedFunctionFlags::kUsesHWAA
680 : FixedFunctionFlags::kNone;
681 }
Brian Salomon34169692017-08-28 15:32:01 -0400682
683 DEFINE_OP_CLASS_ID
684
685private:
Brian Salomon762d5e72017-12-01 10:25:08 -0500686
687 // This is used in a heursitic for choosing a code path. We don't care what happens with
688 // really large rects, infs, nans, etc.
689#if defined(__clang__) && (__clang_major__ * 1000 + __clang_minor__) >= 3007
690__attribute__((no_sanitize("float-cast-overflow")))
691#endif
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500692 size_t RectSizeAsSizeT(const SkRect& rect) {;
Brian Salomon762d5e72017-12-01 10:25:08 -0500693 return static_cast<size_t>(SkTMax(rect.width(), 1.f) * SkTMax(rect.height(), 1.f));
694 }
695
Brian Salomon336ce7b2017-09-08 08:23:58 -0400696 static constexpr int kMaxTextures = TextureGeometryProcessor::kMaxTextures;
697
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400698 TextureOp(sk_sp<GrTextureProxy> proxy, GrSamplerState::Filter filter, GrColor color,
Brian Salomon485b8c62018-01-12 15:11:06 -0500699 const SkRect& srcRect, const SkRect& dstRect, GrAAType aaType,
Brian Salomonb80ffee2018-05-23 16:39:39 -0400700 SkCanvas::SrcRectConstraint constraint, const SkMatrix& viewMatrix,
Brian Osman2b23c4b2018-06-01 12:25:08 -0400701 sk_sp<GrColorSpaceXform> csxf)
Brian Salomon34169692017-08-28 15:32:01 -0400702 : INHERITED(ClassID())
Brian Salomon34169692017-08-28 15:32:01 -0400703 , fColorSpaceXform(std::move(csxf))
Brian Salomon336ce7b2017-09-08 08:23:58 -0400704 , fProxy0(proxy.release())
705 , fFilter0(filter)
706 , fProxyCnt(1)
Brian Salomon485b8c62018-01-12 15:11:06 -0500707 , fAAType(static_cast<unsigned>(aaType))
Brian Osman2b23c4b2018-06-01 12:25:08 -0400708 , fFinalized(0) {
Brian Salomon485b8c62018-01-12 15:11:06 -0500709 SkASSERT(aaType != GrAAType::kMixedSamples);
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400710 fPerspective = viewMatrix.hasPerspective();
Brian Salomon594b64c2018-05-29 12:47:57 -0400711 auto quad = GrPerspQuad(dstRect, viewMatrix);
712 auto bounds = quad.bounds();
713#ifndef SK_DONT_DROP_UNNECESSARY_AA_IN_TEXTURE_OP
714 if (GrAAType::kCoverage == this->aaType() && viewMatrix.rectStaysRect()) {
715 // Disable coverage AA when rect falls on integers in device space.
716 auto is_int = [](float f) { return f == sk_float_floor(f); };
717 if (is_int(bounds.fLeft) && is_int(bounds.fTop) && is_int(bounds.fRight) &&
718 is_int(bounds.fBottom)) {
719 fAAType = static_cast<unsigned>(GrAAType::kNone);
720 // We may have had a strict constraint with nearest filter soley due to possible AA
721 // bloat. In that case it's no longer necessary.
722 if (constraint == SkCanvas::kStrict_SrcRectConstraint &&
723 filter == GrSamplerState::Filter::kNearest) {
724 constraint = SkCanvas::kFast_SrcRectConstraint;
725 }
726 }
727 }
728#endif
729 const auto& draw = fDraws.emplace_back(srcRect, 0, quad, constraint, color);
Brian Salomon34169692017-08-28 15:32:01 -0400730 this->setBounds(bounds, HasAABloat::kNo, IsZeroArea::kNo);
Brian Salomon594b64c2018-05-29 12:47:57 -0400731 fDomain = static_cast<bool>(draw.domain());
Brian Salomon762d5e72017-12-01 10:25:08 -0500732 fMaxApproxDstPixelArea = RectSizeAsSizeT(bounds);
Brian Salomon34169692017-08-28 15:32:01 -0400733 }
734
Brian Salomonb80ffee2018-05-23 16:39:39 -0400735 template <typename Pos, MultiTexture MT, Domain D, GrAA AA>
Brian Salomon17031a72018-05-22 14:14:07 -0400736 void tess(void* v, const float iw[], const float ih[], const GrGeometryProcessor* gp) {
Brian Salomonb80ffee2018-05-23 16:39:39 -0400737 using Vertex = TextureGeometryProcessor::Vertex<Pos, MT, D, AA>;
Brian Salomon17031a72018-05-22 14:14:07 -0400738 SkASSERT(gp->getVertexStride() == sizeof(Vertex));
739 auto vertices = static_cast<Vertex*>(v);
740 auto proxies = this->proxies();
Brian Salomonb80ffee2018-05-23 16:39:39 -0400741 auto filters = this->filters();
Brian Salomon17031a72018-05-22 14:14:07 -0400742 for (const auto& draw : fDraws) {
Brian Salomonb80ffee2018-05-23 16:39:39 -0400743 auto textureIdx = draw.textureIdx();
744 auto origin = proxies[textureIdx]->origin();
745 tessellate_quad<Vertex>(draw.quad(), draw.srcRect(), draw.color(), origin,
746 filters[textureIdx], vertices, iw[textureIdx], ih[textureIdx],
747 textureIdx, draw.domain());
Brian Salomon17031a72018-05-22 14:14:07 -0400748 vertices += 4;
749 }
750 }
751
Brian Salomon34169692017-08-28 15:32:01 -0400752 void onPrepareDraws(Target* target) override {
Brian Salomon336ce7b2017-09-08 08:23:58 -0400753 sk_sp<GrTextureProxy> proxiesSPs[kMaxTextures];
754 auto proxies = this->proxies();
755 auto filters = this->filters();
756 for (int i = 0; i < fProxyCnt; ++i) {
757 if (!proxies[i]->instantiate(target->resourceProvider())) {
758 return;
759 }
760 proxiesSPs[i] = sk_ref_sp(proxies[i]);
Brian Salomon34169692017-08-28 15:32:01 -0400761 }
Brian Salomon336ce7b2017-09-08 08:23:58 -0400762
Brian Salomonb80ffee2018-05-23 16:39:39 -0400763 Domain domain = fDomain ? Domain::kYes : Domain::kNo;
Brian Salomon485b8c62018-01-12 15:11:06 -0500764 bool coverageAA = GrAAType::kCoverage == this->aaType();
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400765 sk_sp<GrGeometryProcessor> gp = TextureGeometryProcessor::Make(
766 proxiesSPs, fProxyCnt, std::move(fColorSpaceXform), coverageAA, fPerspective,
Brian Salomonb80ffee2018-05-23 16:39:39 -0400767 domain, filters, *target->caps().shaderCaps());
Brian Salomon34169692017-08-28 15:32:01 -0400768 GrPipeline::InitArgs args;
769 args.fProxy = target->proxy();
770 args.fCaps = &target->caps();
771 args.fResourceProvider = target->resourceProvider();
Brian Salomon485b8c62018-01-12 15:11:06 -0500772 args.fFlags = 0;
Brian Salomon485b8c62018-01-12 15:11:06 -0500773 if (GrAAType::kMSAA == this->aaType()) {
774 args.fFlags |= GrPipeline::kHWAntialias_Flag;
775 }
776
Brian Salomon34169692017-08-28 15:32:01 -0400777 const GrPipeline* pipeline = target->allocPipeline(args, GrProcessorSet::MakeEmptySet(),
778 target->detachAppliedClip());
Brian Salomon34169692017-08-28 15:32:01 -0400779 int vstart;
780 const GrBuffer* vbuffer;
Brian Salomon336ce7b2017-09-08 08:23:58 -0400781 void* vdata = target->makeVertexSpace(gp->getVertexStride(), 4 * fDraws.count(), &vbuffer,
782 &vstart);
783 if (!vdata) {
Brian Salomon34169692017-08-28 15:32:01 -0400784 SkDebugf("Could not allocate vertices\n");
785 return;
786 }
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400787
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400788 float iw[kMaxTextures];
789 float ih[kMaxTextures];
790 for (int t = 0; t < fProxyCnt; ++t) {
791 const auto* texture = proxies[t]->priv().peekTexture();
792 iw[t] = 1.f / texture->width();
793 ih[t] = 1.f / texture->height();
794 }
795
Brian Salomon16b1eab2018-05-24 16:08:28 -0400796#if defined(_MSC_VER) && _MSC_VER <= 1910
797# define MAYBE_CONSTEXPR const
798#else
799# define MAYBE_CONSTEXPR constexpr
800#endif
Brian Salomonb80ffee2018-05-23 16:39:39 -0400801 using TessFn =
802 decltype(&TextureOp::tess<SkPoint, MultiTexture::kNo, Domain::kNo, GrAA::kNo>);
Brian Salomon16b1eab2018-05-24 16:08:28 -0400803 static MAYBE_CONSTEXPR TessFn kTessFns[] = {
Brian Salomonb80ffee2018-05-23 16:39:39 -0400804 &TextureOp::tess<SkPoint, MultiTexture::kNo, Domain::kNo, GrAA::kNo>,
805 &TextureOp::tess<SkPoint, MultiTexture::kNo, Domain::kNo, GrAA::kYes>,
806 &TextureOp::tess<SkPoint, MultiTexture::kNo, Domain::kYes, GrAA::kNo>,
807 &TextureOp::tess<SkPoint, MultiTexture::kNo, Domain::kYes, GrAA::kYes>,
808 &TextureOp::tess<SkPoint, MultiTexture::kYes, Domain::kNo, GrAA::kNo>,
809 &TextureOp::tess<SkPoint, MultiTexture::kYes, Domain::kNo, GrAA::kYes>,
810 &TextureOp::tess<SkPoint, MultiTexture::kYes, Domain::kYes, GrAA::kNo>,
811 &TextureOp::tess<SkPoint, MultiTexture::kYes, Domain::kYes, GrAA::kYes>,
812 &TextureOp::tess<SkPoint3, MultiTexture::kNo, Domain::kNo, GrAA::kNo>,
813 &TextureOp::tess<SkPoint3, MultiTexture::kNo, Domain::kNo, GrAA::kYes>,
814 &TextureOp::tess<SkPoint3, MultiTexture::kNo, Domain::kYes, GrAA::kNo>,
815 &TextureOp::tess<SkPoint3, MultiTexture::kNo, Domain::kYes, GrAA::kYes>,
816 &TextureOp::tess<SkPoint3, MultiTexture::kYes, Domain::kNo, GrAA::kNo>,
817 &TextureOp::tess<SkPoint3, MultiTexture::kYes, Domain::kNo, GrAA::kYes>,
818 &TextureOp::tess<SkPoint3, MultiTexture::kYes, Domain::kYes, GrAA::kNo>,
819 &TextureOp::tess<SkPoint3, MultiTexture::kYes, Domain::kYes, GrAA::kYes>,
820 };
Brian Salomon16b1eab2018-05-24 16:08:28 -0400821#undef MAYBE_CONSTEXPR
Brian Salomonb80ffee2018-05-23 16:39:39 -0400822 int tessFnIdx = 0;
823 tessFnIdx |= coverageAA ? 0x1 : 0x0;
824 tessFnIdx |= fDomain ? 0x2 : 0x0;
825 tessFnIdx |= (fProxyCnt > 1) ? 0x4 : 0x0;
826 tessFnIdx |= fPerspective ? 0x8 : 0x0;
827 (this->*(kTessFns[tessFnIdx]))(vdata, iw, ih, gp.get());
828
Brian Salomon57caa662017-10-18 12:21:05 +0000829 GrPrimitiveType primitiveType =
830 fDraws.count() > 1 ? GrPrimitiveType::kTriangles : GrPrimitiveType::kTriangleStrip;
831 GrMesh mesh(primitiveType);
Brian Salomon34169692017-08-28 15:32:01 -0400832 if (fDraws.count() > 1) {
Brian Salomon57caa662017-10-18 12:21:05 +0000833 sk_sp<const GrBuffer> ibuffer = target->resourceProvider()->refQuadIndexBuffer();
Brian Salomon34169692017-08-28 15:32:01 -0400834 if (!ibuffer) {
835 SkDebugf("Could not allocate quad indices\n");
836 return;
837 }
Brian Salomon34169692017-08-28 15:32:01 -0400838 mesh.setIndexedPatterned(ibuffer.get(), 6, 4, fDraws.count(),
839 GrResourceProvider::QuadCountOfQuadBuffer());
Brian Salomon34169692017-08-28 15:32:01 -0400840 } else {
Brian Salomon34169692017-08-28 15:32:01 -0400841 mesh.setNonIndexedNonInstanced(4);
Brian Salomon34169692017-08-28 15:32:01 -0400842 }
Brian Salomon57caa662017-10-18 12:21:05 +0000843 mesh.setVertexData(vbuffer, vstart);
844 target->draw(gp.get(), pipeline, mesh);
Brian Salomon34169692017-08-28 15:32:01 -0400845 }
846
847 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
848 const auto* that = t->cast<TextureOp>();
Brian Salomon762d5e72017-12-01 10:25:08 -0500849 const auto& shaderCaps = *caps.shaderCaps();
Brian Salomon336ce7b2017-09-08 08:23:58 -0400850 if (!GrColorSpaceXform::Equals(fColorSpaceXform.get(), that->fColorSpaceXform.get())) {
Brian Salomon34169692017-08-28 15:32:01 -0400851 return false;
852 }
Brian Salomon485b8c62018-01-12 15:11:06 -0500853 if (this->aaType() != that->aaType()) {
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500854 return false;
855 }
Brian Salomon336ce7b2017-09-08 08:23:58 -0400856 // Because of an issue where GrColorSpaceXform adds the same function every time it is used
857 // in a texture lookup, we only allow multiple textures when there is no transform.
Brian Salomon762d5e72017-12-01 10:25:08 -0500858 if (TextureGeometryProcessor::SupportsMultitexture(shaderCaps) && !fColorSpaceXform &&
859 fMaxApproxDstPixelArea <= shaderCaps.disableImageMultitexturingDstRectAreaThreshold() &&
860 that->fMaxApproxDstPixelArea <=
861 shaderCaps.disableImageMultitexturingDstRectAreaThreshold()) {
Brian Salomon336ce7b2017-09-08 08:23:58 -0400862 int map[kMaxTextures];
Brian Salomon762d5e72017-12-01 10:25:08 -0500863 int numNewProxies = this->mergeProxies(that, map, shaderCaps);
Brian Salomon336ce7b2017-09-08 08:23:58 -0400864 if (numNewProxies < 0) {
865 return false;
866 }
867 if (1 == fProxyCnt && numNewProxies) {
868 void* mem = new char[(sizeof(GrSamplerState::Filter) + sizeof(GrTextureProxy*)) *
869 kMaxTextures];
870 auto proxies = reinterpret_cast<GrTextureProxy**>(mem);
871 auto filters = reinterpret_cast<GrSamplerState::Filter*>(proxies + kMaxTextures);
872 proxies[0] = fProxy0;
873 filters[0] = fFilter0;
874 fProxyArray = proxies;
875 }
876 fProxyCnt += numNewProxies;
877 auto thisProxies = fProxyArray;
878 auto thatProxies = that->proxies();
879 auto thatFilters = that->filters();
880 auto thisFilters = reinterpret_cast<GrSamplerState::Filter*>(thisProxies +
881 kMaxTextures);
882 for (int i = 0; i < that->fProxyCnt; ++i) {
883 if (map[i] < 0) {
884 thatProxies[i]->addPendingRead();
Robert Phillipsb493eeb2017-09-13 13:10:52 -0400885
Brian Salomon336ce7b2017-09-08 08:23:58 -0400886 thisProxies[-map[i]] = thatProxies[i];
887 thisFilters[-map[i]] = thatFilters[i];
888 map[i] = -map[i];
889 }
890 }
891 int firstNewDraw = fDraws.count();
892 fDraws.push_back_n(that->fDraws.count(), that->fDraws.begin());
893 for (int i = firstNewDraw; i < fDraws.count(); ++i) {
Brian Salomonb80ffee2018-05-23 16:39:39 -0400894 fDraws[i].setTextureIdx(map[fDraws[i].textureIdx()]);
Brian Salomon336ce7b2017-09-08 08:23:58 -0400895 }
896 } else {
Brian Salomonbbf05752017-11-30 11:30:48 -0500897 // We can get here when one of the ops is already multitextured but the other cannot
898 // be because of the dst rect size.
899 if (fProxyCnt > 1 || that->fProxyCnt > 1) {
900 return false;
901 }
Brian Salomon336ce7b2017-09-08 08:23:58 -0400902 if (fProxy0->uniqueID() != that->fProxy0->uniqueID() || fFilter0 != that->fFilter0) {
903 return false;
904 }
905 fDraws.push_back_n(that->fDraws.count(), that->fDraws.begin());
906 }
Brian Salomon34169692017-08-28 15:32:01 -0400907 this->joinBounds(*that);
Brian Salomon762d5e72017-12-01 10:25:08 -0500908 fMaxApproxDstPixelArea = SkTMax(that->fMaxApproxDstPixelArea, fMaxApproxDstPixelArea);
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400909 fPerspective |= that->fPerspective;
Brian Salomonb80ffee2018-05-23 16:39:39 -0400910 fDomain |= that->fDomain;
Brian Salomon34169692017-08-28 15:32:01 -0400911 return true;
912 }
913
Brian Salomon336ce7b2017-09-08 08:23:58 -0400914 /**
915 * Determines a mapping of indices from that's proxy array to this's proxy array. A negative map
916 * value means that's proxy should be added to this's proxy array at the absolute value of
917 * the map entry. If it is determined that the ops shouldn't combine their proxies then a
918 * negative value is returned. Otherwise, return value indicates the number of proxies that have
919 * to be added to this op or, equivalently, the number of negative entries in map.
920 */
921 int mergeProxies(const TextureOp* that, int map[kMaxTextures], const GrShaderCaps& caps) const {
922 std::fill_n(map, kMaxTextures, -kMaxTextures);
923 int sharedProxyCnt = 0;
924 auto thisProxies = this->proxies();
925 auto thisFilters = this->filters();
926 auto thatProxies = that->proxies();
927 auto thatFilters = that->filters();
928 for (int i = 0; i < fProxyCnt; ++i) {
929 for (int j = 0; j < that->fProxyCnt; ++j) {
930 if (thisProxies[i]->uniqueID() == thatProxies[j]->uniqueID()) {
931 if (thisFilters[i] != thatFilters[j]) {
932 // In GL we don't currently support using the same texture with different
933 // samplers. If we added support for sampler objects and a cap bit to know
934 // it's ok to use different filter modes then we could support this.
935 // Otherwise, we could also only allow a single filter mode for each op
936 // instance.
937 return -1;
938 }
939 map[j] = i;
940 ++sharedProxyCnt;
941 break;
942 }
943 }
944 }
Brian Salomon2b6f6142017-11-13 11:49:13 -0500945 int actualMaxTextures = SkTMin(caps.maxFragmentSamplers(), kMaxTextures);
Brian Salomon336ce7b2017-09-08 08:23:58 -0400946 int newProxyCnt = that->fProxyCnt - sharedProxyCnt;
947 if (newProxyCnt + fProxyCnt > actualMaxTextures) {
948 return -1;
949 }
950 GrPixelConfig config = thisProxies[0]->config();
951 int nextSlot = fProxyCnt;
952 for (int j = 0; j < that->fProxyCnt; ++j) {
953 // We want to avoid making many shaders because of different permutations of shader
954 // based swizzle and sampler types. The approach taken here is to require the configs to
955 // be the same and to only allow already instantiated proxies that have the most
956 // common sampler type. Otherwise we don't merge.
957 if (thatProxies[j]->config() != config) {
958 return -1;
959 }
960 if (GrTexture* tex = thatProxies[j]->priv().peekTexture()) {
961 if (tex->texturePriv().samplerType() != kTexture2DSampler_GrSLType) {
962 return -1;
963 }
964 }
965 if (map[j] < 0) {
966 map[j] = -(nextSlot++);
967 }
968 }
969 return newProxyCnt;
970 }
971
Brian Salomon485b8c62018-01-12 15:11:06 -0500972 GrAAType aaType() const { return static_cast<GrAAType>(fAAType); }
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500973
Brian Salomon336ce7b2017-09-08 08:23:58 -0400974 GrTextureProxy* const* proxies() const { return fProxyCnt > 1 ? fProxyArray : &fProxy0; }
975
976 const GrSamplerState::Filter* filters() const {
977 if (fProxyCnt > 1) {
978 return reinterpret_cast<const GrSamplerState::Filter*>(fProxyArray + kMaxTextures);
979 }
980 return &fFilter0;
981 }
982
Brian Salomonb80ffee2018-05-23 16:39:39 -0400983 class Draw {
984 public:
985 Draw(const SkRect& srcRect, int textureIdx, const GrPerspQuad& quad,
986 SkCanvas::SrcRectConstraint constraint, GrColor color)
987 : fSrcRect(srcRect)
988 , fHasDomain(constraint == SkCanvas::kStrict_SrcRectConstraint)
989 , fTextureIdx(SkToUInt(textureIdx))
990 , fQuad(quad)
991 , fColor(color) {}
992 const GrPerspQuad& quad() const { return fQuad; }
993 int textureIdx() const { return SkToInt(fTextureIdx); }
994 const SkRect& srcRect() const { return fSrcRect; }
995 GrColor color() const { return fColor; }
996 Domain domain() const { return Domain(fHasDomain); }
997 void setTextureIdx(int i) { fTextureIdx = SkToUInt(i); }
998
999 private:
Brian Salomon34169692017-08-28 15:32:01 -04001000 SkRect fSrcRect;
Brian Salomonb80ffee2018-05-23 16:39:39 -04001001 unsigned fHasDomain : 1;
1002 unsigned fTextureIdx : 31;
Brian Salomonbe3c1d22018-05-21 12:54:39 -04001003 GrPerspQuad fQuad;
Brian Salomon34169692017-08-28 15:32:01 -04001004 GrColor fColor;
1005 };
1006 SkSTArray<1, Draw, true> fDraws;
Brian Salomon34169692017-08-28 15:32:01 -04001007 sk_sp<GrColorSpaceXform> fColorSpaceXform;
Brian Salomon336ce7b2017-09-08 08:23:58 -04001008 // Initially we store a single proxy ptr and a single filter. If we grow to have more than
1009 // one proxy we instead store pointers to dynamically allocated arrays of size kMaxTextures
1010 // followed by kMaxTextures filters.
1011 union {
1012 GrTextureProxy* fProxy0;
1013 GrTextureProxy** fProxyArray;
1014 };
Brian Salomonbbf05752017-11-30 11:30:48 -05001015 size_t fMaxApproxDstPixelArea;
Brian Salomon336ce7b2017-09-08 08:23:58 -04001016 GrSamplerState::Filter fFilter0;
1017 uint8_t fProxyCnt;
Brian Salomon485b8c62018-01-12 15:11:06 -05001018 unsigned fAAType : 2;
Brian Salomonbe3c1d22018-05-21 12:54:39 -04001019 unsigned fPerspective : 1;
Brian Salomonb80ffee2018-05-23 16:39:39 -04001020 unsigned fDomain : 1;
Brian Salomon34169692017-08-28 15:32:01 -04001021 // Used to track whether fProxy is ref'ed or has a pending IO after finalize() is called.
Brian Salomonb5ef1f92018-01-11 11:46:21 -05001022 unsigned fFinalized : 1;
Brian Salomon336ce7b2017-09-08 08:23:58 -04001023
Brian Salomon34169692017-08-28 15:32:01 -04001024 typedef GrMeshDrawOp INHERITED;
1025};
1026
Brian Salomon336ce7b2017-09-08 08:23:58 -04001027constexpr int TextureGeometryProcessor::kMaxTextures;
1028constexpr int TextureOp::kMaxTextures;
1029
Brian Salomon34169692017-08-28 15:32:01 -04001030} // anonymous namespace
1031
1032namespace GrTextureOp {
1033
Brian Salomon2bbdcc42017-09-07 12:36:34 -04001034std::unique_ptr<GrDrawOp> Make(sk_sp<GrTextureProxy> proxy, GrSamplerState::Filter filter,
Brian Salomon485b8c62018-01-12 15:11:06 -05001035 GrColor color, const SkRect& srcRect, const SkRect& dstRect,
Brian Salomonb80ffee2018-05-23 16:39:39 -04001036 GrAAType aaType, SkCanvas::SrcRectConstraint constraint,
Brian Osman2b23c4b2018-06-01 12:25:08 -04001037 const SkMatrix& viewMatrix, sk_sp<GrColorSpaceXform> csxf) {
Brian Salomonb80ffee2018-05-23 16:39:39 -04001038 return TextureOp::Make(std::move(proxy), filter, color, srcRect, dstRect, aaType, constraint,
Brian Osman2b23c4b2018-06-01 12:25:08 -04001039 viewMatrix, std::move(csxf));
Brian Salomon34169692017-08-28 15:32:01 -04001040}
1041
1042} // namespace GrTextureOp
1043
1044#if GR_TEST_UTILS
1045#include "GrContext.h"
Robert Phillips1afd4cd2018-01-08 13:40:32 -05001046#include "GrContextPriv.h"
Robert Phillips0bd24dc2018-01-16 08:06:32 -05001047#include "GrProxyProvider.h"
Brian Salomon34169692017-08-28 15:32:01 -04001048
1049GR_DRAW_OP_TEST_DEFINE(TextureOp) {
1050 GrSurfaceDesc desc;
1051 desc.fConfig = kRGBA_8888_GrPixelConfig;
1052 desc.fHeight = random->nextULessThan(90) + 10;
1053 desc.fWidth = random->nextULessThan(90) + 10;
Brian Salomon2a4f9832018-03-03 22:43:43 -05001054 auto origin = random->nextBool() ? kTopLeft_GrSurfaceOrigin : kBottomLeft_GrSurfaceOrigin;
Brian Salomon34169692017-08-28 15:32:01 -04001055 SkBackingFit fit = random->nextBool() ? SkBackingFit::kApprox : SkBackingFit::kExact;
Robert Phillips0bd24dc2018-01-16 08:06:32 -05001056
1057 GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider();
Brian Salomon2a4f9832018-03-03 22:43:43 -05001058 sk_sp<GrTextureProxy> proxy = proxyProvider->createProxy(desc, origin, fit, SkBudgeted::kNo);
Robert Phillips0bd24dc2018-01-16 08:06:32 -05001059
Brian Salomon34169692017-08-28 15:32:01 -04001060 SkRect rect = GrTest::TestRect(random);
1061 SkRect srcRect;
1062 srcRect.fLeft = random->nextRangeScalar(0.f, proxy->width() / 2.f);
1063 srcRect.fRight = random->nextRangeScalar(0.f, proxy->width()) + proxy->width() / 2.f;
1064 srcRect.fTop = random->nextRangeScalar(0.f, proxy->height() / 2.f);
1065 srcRect.fBottom = random->nextRangeScalar(0.f, proxy->height()) + proxy->height() / 2.f;
1066 SkMatrix viewMatrix = GrTest::TestMatrixPreservesRightAngles(random);
1067 GrColor color = SkColorToPremulGrColor(random->nextU());
Brian Salomon2bbdcc42017-09-07 12:36:34 -04001068 GrSamplerState::Filter filter = (GrSamplerState::Filter)random->nextULessThan(
1069 static_cast<uint32_t>(GrSamplerState::Filter::kMipMap) + 1);
Brian Salomon34169692017-08-28 15:32:01 -04001070 auto csxf = GrTest::TestColorXform(random);
Brian Salomon485b8c62018-01-12 15:11:06 -05001071 GrAAType aaType = GrAAType::kNone;
1072 if (random->nextBool()) {
1073 aaType = (fsaaType == GrFSAAType::kUnifiedMSAA) ? GrAAType::kMSAA : GrAAType::kCoverage;
1074 }
Brian Salomonb80ffee2018-05-23 16:39:39 -04001075 auto constraint = random->nextBool() ? SkCanvas::kStrict_SrcRectConstraint
1076 : SkCanvas::kFast_SrcRectConstraint;
1077 return GrTextureOp::Make(std::move(proxy), filter, color, srcRect, rect, aaType, constraint,
Brian Osman2b23c4b2018-06-01 12:25:08 -04001078 viewMatrix, std::move(csxf));
Brian Salomon34169692017-08-28 15:32:01 -04001079}
1080
1081#endif