blob: 99ecdf8101e22cc425b6e0fdc6bf53ec1254f515 [file] [log] [blame]
Brian Salomon34169692017-08-28 15:32:01 -04001/*
2 * Copyright 2017 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "GrTextureOp.h"
Hal Canaryc640d0d2018-06-13 09:59:02 -04009
Brian Salomon34169692017-08-28 15:32:01 -040010#include "GrAppliedClip.h"
Brian Salomon336ce7b2017-09-08 08:23:58 -040011#include "GrCaps.h"
Robert Phillips7c525e62018-06-12 10:11:12 -040012#include "GrContext.h"
13#include "GrContextPriv.h"
Brian Salomon34169692017-08-28 15:32:01 -040014#include "GrDrawOpTest.h"
15#include "GrGeometryProcessor.h"
Robert Phillips7c525e62018-06-12 10:11:12 -040016#include "GrMemoryPool.h"
Brian Salomon34169692017-08-28 15:32:01 -040017#include "GrMeshDrawOp.h"
18#include "GrOpFlushState.h"
19#include "GrQuad.h"
20#include "GrResourceProvider.h"
21#include "GrShaderCaps.h"
22#include "GrTexture.h"
Brian Salomon336ce7b2017-09-08 08:23:58 -040023#include "GrTexturePriv.h"
Brian Salomon34169692017-08-28 15:32:01 -040024#include "GrTextureProxy.h"
25#include "SkGr.h"
Brian Salomon336ce7b2017-09-08 08:23:58 -040026#include "SkMathPriv.h"
Brian Salomona33b67c2018-05-17 10:42:14 -040027#include "SkMatrixPriv.h"
Brian Salomonb5ef1f92018-01-11 11:46:21 -050028#include "SkPoint.h"
29#include "SkPoint3.h"
Hal Canaryc640d0d2018-06-13 09:59:02 -040030#include "SkTo.h"
Brian Salomon34169692017-08-28 15:32:01 -040031#include "glsl/GrGLSLColorSpaceXformHelper.h"
Brian Salomonb5ef1f92018-01-11 11:46:21 -050032#include "glsl/GrGLSLFragmentShaderBuilder.h"
Brian Salomon34169692017-08-28 15:32:01 -040033#include "glsl/GrGLSLGeometryProcessor.h"
34#include "glsl/GrGLSLVarying.h"
Brian Salomonb5ef1f92018-01-11 11:46:21 -050035#include "glsl/GrGLSLVertexGeoBuilder.h"
Mike Klein79aea6a2018-06-11 10:45:26 -040036#include <new>
Brian Salomon34169692017-08-28 15:32:01 -040037
38namespace {
39
Brian Salomon17031a72018-05-22 14:14:07 -040040enum class MultiTexture : bool { kNo = false, kYes = true };
41
Brian Salomonb80ffee2018-05-23 16:39:39 -040042enum class Domain : bool { kNo = false, kYes = true };
43
Brian Salomon34169692017-08-28 15:32:01 -040044/**
45 * Geometry Processor that draws a texture modulated by a vertex color (though, this is meant to be
46 * the same value across all vertices of a quad and uses flat interpolation when available). This is
47 * used by TextureOp below.
48 */
49class TextureGeometryProcessor : public GrGeometryProcessor {
50public:
Brian Salomon17031a72018-05-22 14:14:07 -040051 template <typename Pos> struct VertexCommon {
52 using Position = Pos;
53 Position fPosition;
Brian Salomon34169692017-08-28 15:32:01 -040054 GrColor fColor;
Brian Salomon17031a72018-05-22 14:14:07 -040055 SkPoint fTextureCoords;
Brian Salomon34169692017-08-28 15:32:01 -040056 };
Brian Salomon17031a72018-05-22 14:14:07 -040057
58 template <typename Pos, MultiTexture MT> struct OptionalMultiTextureVertex;
59 template <typename Pos>
60 struct OptionalMultiTextureVertex<Pos, MultiTexture::kNo> : VertexCommon<Pos> {
61 static constexpr MultiTexture kMultiTexture = MultiTexture::kNo;
Brian Salomonb5ef1f92018-01-11 11:46:21 -050062 };
Brian Salomon17031a72018-05-22 14:14:07 -040063 template <typename Pos>
64 struct OptionalMultiTextureVertex<Pos, MultiTexture::kYes> : VertexCommon<Pos> {
65 static constexpr MultiTexture kMultiTexture = MultiTexture::kYes;
Brian Salomon336ce7b2017-09-08 08:23:58 -040066 int fTextureIdx;
Brian Salomon336ce7b2017-09-08 08:23:58 -040067 };
Brian Salomon17031a72018-05-22 14:14:07 -040068
Brian Salomonb80ffee2018-05-23 16:39:39 -040069 template <typename Pos, MultiTexture MT, Domain D> struct OptionalDomainVertex;
Brian Salomon17031a72018-05-22 14:14:07 -040070 template <typename Pos, MultiTexture MT>
Brian Salomonb80ffee2018-05-23 16:39:39 -040071 struct OptionalDomainVertex<Pos, MT, Domain::kNo> : OptionalMultiTextureVertex<Pos, MT> {
72 static constexpr Domain kDomain = Domain::kNo;
Brian Salomona0047bc2018-05-23 16:39:39 -040073 };
Stephen White633f20e2018-05-26 18:07:27 +000074 template <typename Pos, MultiTexture MT>
Brian Salomonb80ffee2018-05-23 16:39:39 -040075 struct OptionalDomainVertex<Pos, MT, Domain::kYes> : OptionalMultiTextureVertex<Pos, MT> {
76 static constexpr Domain kDomain = Domain::kYes;
77 SkRect fTextureDomain;
78 };
79
80 template <typename Pos, MultiTexture MT, Domain D, GrAA> struct OptionalAAVertex;
81 template <typename Pos, MultiTexture MT, Domain D>
82 struct OptionalAAVertex<Pos, MT, D, GrAA::kNo> : OptionalDomainVertex<Pos, MT, D> {
83 static constexpr GrAA kAA = GrAA::kNo;
84 };
85 template <typename Pos, MultiTexture MT, Domain D>
86 struct OptionalAAVertex<Pos, MT, D, GrAA::kYes> : OptionalDomainVertex<Pos, MT, D> {
Brian Salomonbe3c1d22018-05-21 12:54:39 -040087 static constexpr GrAA kAA = GrAA::kYes;
Brian Salomonb5ef1f92018-01-11 11:46:21 -050088 SkPoint3 fEdges[4];
Brian Salomonb5ef1f92018-01-11 11:46:21 -050089 };
Brian Salomon336ce7b2017-09-08 08:23:58 -040090
Brian Salomonb80ffee2018-05-23 16:39:39 -040091 template <typename Pos, MultiTexture MT, Domain D, GrAA AA>
92 using Vertex = OptionalAAVertex<Pos, MT, D, AA>;
Brian Salomon17031a72018-05-22 14:14:07 -040093
Brian Salomon336ce7b2017-09-08 08:23:58 -040094 // Maximum number of textures supported by this op. Must also be checked against the caps
95 // limit. These numbers were based on some limited experiments on a HP Z840 and Pixel XL 2016
96 // and could probably use more tuning.
97#ifdef SK_BUILD_FOR_ANDROID
98 static constexpr int kMaxTextures = 4;
99#else
100 static constexpr int kMaxTextures = 8;
101#endif
102
Brian Salomon0b4d8aa2017-10-11 15:34:27 -0400103 static int SupportsMultitexture(const GrShaderCaps& caps) {
Brian Salomon762d5e72017-12-01 10:25:08 -0500104 return caps.integerSupport() && caps.maxFragmentSamplers() > 1;
Brian Salomon0b4d8aa2017-10-11 15:34:27 -0400105 }
Brian Salomon336ce7b2017-09-08 08:23:58 -0400106
107 static sk_sp<GrGeometryProcessor> Make(sk_sp<GrTextureProxy> proxies[], int proxyCnt,
Brian Salomon485b8c62018-01-12 15:11:06 -0500108 sk_sp<GrColorSpaceXform> csxf, bool coverageAA,
Brian Salomonb80ffee2018-05-23 16:39:39 -0400109 bool perspective, Domain domain,
110 const GrSamplerState::Filter filters[],
Brian Salomon336ce7b2017-09-08 08:23:58 -0400111 const GrShaderCaps& caps) {
112 // We use placement new to avoid always allocating space for kMaxTextures TextureSampler
113 // instances.
114 int samplerCnt = NumSamplersToUse(proxyCnt, caps);
115 size_t size = sizeof(TextureGeometryProcessor) + sizeof(TextureSampler) * (samplerCnt - 1);
116 void* mem = GrGeometryProcessor::operator new(size);
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400117 return sk_sp<TextureGeometryProcessor>(
118 new (mem) TextureGeometryProcessor(proxies, proxyCnt, samplerCnt, std::move(csxf),
Brian Salomonb80ffee2018-05-23 16:39:39 -0400119 coverageAA, perspective, domain, filters, caps));
Brian Salomon336ce7b2017-09-08 08:23:58 -0400120 }
121
122 ~TextureGeometryProcessor() override {
123 int cnt = this->numTextureSamplers();
124 for (int i = 1; i < cnt; ++i) {
125 fSamplers[i].~TextureSampler();
126 }
Brian Salomon34169692017-08-28 15:32:01 -0400127 }
128
129 const char* name() const override { return "TextureGeometryProcessor"; }
130
131 void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const override {
132 b->add32(GrColorSpaceXform::XformKey(fColorSpaceXform.get()));
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400133 uint32_t x = this->usesCoverageEdgeAA() ? 0 : 1;
Brian Salomon70132d02018-05-29 15:33:06 -0400134 x |= kFloat3_GrVertexAttribType == fPositions.type() ? 0 : 2;
Brian Salomonb80ffee2018-05-23 16:39:39 -0400135 x |= fDomain.isInitialized() ? 4 : 0;
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400136 b->add32(x);
Brian Salomon34169692017-08-28 15:32:01 -0400137 }
138
139 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps& caps) const override {
140 class GLSLProcessor : public GrGLSLGeometryProcessor {
141 public:
142 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& proc,
143 FPCoordTransformIter&& transformIter) override {
144 const auto& textureGP = proc.cast<TextureGeometryProcessor>();
145 this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
Brian Osmanc891b102018-06-14 14:50:17 -0400146 fColorSpaceXformHelper.setData(pdman, textureGP.fColorSpaceXform.get());
Brian Salomon34169692017-08-28 15:32:01 -0400147 }
148
149 private:
150 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
Chris Dalton7b046312018-02-02 11:06:30 -0700151 using Interpolation = GrGLSLVaryingHandler::Interpolation;
Brian Salomon34169692017-08-28 15:32:01 -0400152 const auto& textureGP = args.fGP.cast<TextureGeometryProcessor>();
153 fColorSpaceXformHelper.emitCode(
154 args.fUniformHandler, textureGP.fColorSpaceXform.get());
Brian Salomon70132d02018-05-29 15:33:06 -0400155 if (kFloat2_GrVertexAttribType == textureGP.fPositions.type()) {
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400156 args.fVaryingHandler->setNoPerspective();
157 }
Brian Salomon34169692017-08-28 15:32:01 -0400158 args.fVaryingHandler->emitAttributes(textureGP);
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400159 gpArgs->fPositionVar = textureGP.fPositions.asShaderVar();
160
Brian Salomon34169692017-08-28 15:32:01 -0400161 this->emitTransforms(args.fVertBuilder,
162 args.fVaryingHandler,
163 args.fUniformHandler,
Brian Salomon04460cc2017-12-06 14:47:42 -0500164 textureGP.fTextureCoords.asShaderVar(),
Brian Salomon34169692017-08-28 15:32:01 -0400165 args.fFPCoordTransformHandler);
Brian Salomon92be2f72018-06-19 14:33:47 -0400166 args.fVaryingHandler->addPassThroughAttribute(
167 textureGP.fColors, args.fOutputColor, Interpolation::kCanBeFlat);
Ethan Nicholas8aa45692017-09-20 11:24:15 -0400168 args.fFragBuilder->codeAppend("float2 texCoord;");
Brian Salomon92be2f72018-06-19 14:33:47 -0400169 args.fVaryingHandler->addPassThroughAttribute(textureGP.fTextureCoords, "texCoord");
Brian Salomonb80ffee2018-05-23 16:39:39 -0400170 if (textureGP.fDomain.isInitialized()) {
171 args.fFragBuilder->codeAppend("float4 domain;");
172 args.fVaryingHandler->addPassThroughAttribute(
Brian Salomon92be2f72018-06-19 14:33:47 -0400173 textureGP.fDomain, "domain",
Brian Salomonb80ffee2018-05-23 16:39:39 -0400174 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
175 args.fFragBuilder->codeAppend(
176 "texCoord = clamp(texCoord, domain.xy, domain.zw);");
177 }
Brian Salomon336ce7b2017-09-08 08:23:58 -0400178 if (textureGP.numTextureSamplers() > 1) {
Chris Dalton7b046312018-02-02 11:06:30 -0700179 // If this changes to float, reconsider Interpolation::kMustBeFlat.
Brian Salomon70132d02018-05-29 15:33:06 -0400180 SkASSERT(kInt_GrVertexAttribType == textureGP.fTextureIdx.type());
Brian Salomon336ce7b2017-09-08 08:23:58 -0400181 SkASSERT(args.fShaderCaps->integerSupport());
182 args.fFragBuilder->codeAppend("int texIdx;");
Brian Salomon92be2f72018-06-19 14:33:47 -0400183 args.fVaryingHandler->addPassThroughAttribute(textureGP.fTextureIdx, "texIdx",
Chris Dalton7b046312018-02-02 11:06:30 -0700184 Interpolation::kMustBeFlat);
Brian Salomon336ce7b2017-09-08 08:23:58 -0400185 args.fFragBuilder->codeAppend("switch (texIdx) {");
186 for (int i = 0; i < textureGP.numTextureSamplers(); ++i) {
187 args.fFragBuilder->codeAppendf("case %d: %s = ", i, args.fOutputColor);
188 args.fFragBuilder->appendTextureLookupAndModulate(args.fOutputColor,
189 args.fTexSamplers[i],
190 "texCoord",
Ethan Nicholas8aa45692017-09-20 11:24:15 -0400191 kFloat2_GrSLType,
Brian Salomon336ce7b2017-09-08 08:23:58 -0400192 &fColorSpaceXformHelper);
193 args.fFragBuilder->codeAppend("; break;");
194 }
195 args.fFragBuilder->codeAppend("}");
196 } else {
197 args.fFragBuilder->codeAppendf("%s = ", args.fOutputColor);
198 args.fFragBuilder->appendTextureLookupAndModulate(args.fOutputColor,
199 args.fTexSamplers[0],
200 "texCoord",
Ethan Nicholas8aa45692017-09-20 11:24:15 -0400201 kFloat2_GrSLType,
Brian Salomon336ce7b2017-09-08 08:23:58 -0400202 &fColorSpaceXformHelper);
203 }
Brian Salomon34169692017-08-28 15:32:01 -0400204 args.fFragBuilder->codeAppend(";");
Brian Salomon485b8c62018-01-12 15:11:06 -0500205 if (textureGP.usesCoverageEdgeAA()) {
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400206 bool mulByFragCoordW = false;
Brian Salomon9b4bd592018-07-10 09:23:40 -0400207 GrGLSLVarying aaDistVarying(kFloat4_GrSLType,
208 GrGLSLVarying::Scope::kVertToFrag);
209 if (kFloat3_GrVertexAttribType == textureGP.fPositions.type()) {
210 args.fVaryingHandler->addVarying("aaDists", &aaDistVarying);
211 // The distance from edge equation e to homogenous point p=sk_Position
212 // is e.x*p.x/p.wx + e.y*p.y/p.w + e.z. However, we want screen space
213 // interpolation of this distance. We can do this by multiplying the
214 // varying in the VS by p.w and then multiplying by sk_FragCoord.w in
215 // the FS. So we output e.x*p.x + e.y*p.y + e.z * p.w
216 args.fVertBuilder->codeAppendf(
217 R"(%s = float4(dot(aaEdge0, %s), dot(aaEdge1, %s),
218 dot(aaEdge2, %s), dot(aaEdge3, %s));)",
219 aaDistVarying.vsOut(), textureGP.fPositions.name(),
220 textureGP.fPositions.name(), textureGP.fPositions.name(),
221 textureGP.fPositions.name());
222 mulByFragCoordW = true;
Brian Salomondba65f92018-01-22 08:43:38 -0500223 } else {
Brian Salomon9b4bd592018-07-10 09:23:40 -0400224 args.fVaryingHandler->addVarying("aaDists", &aaDistVarying);
225 args.fVertBuilder->codeAppendf(
226 R"(%s = float4(dot(aaEdge0.xy, %s.xy) + aaEdge0.z,
227 dot(aaEdge1.xy, %s.xy) + aaEdge1.z,
228 dot(aaEdge2.xy, %s.xy) + aaEdge2.z,
229 dot(aaEdge3.xy, %s.xy) + aaEdge3.z);)",
230 aaDistVarying.vsOut(), textureGP.fPositions.name(),
231 textureGP.fPositions.name(), textureGP.fPositions.name(),
232 textureGP.fPositions.name());
Brian Salomondba65f92018-01-22 08:43:38 -0500233 }
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500234 args.fFragBuilder->codeAppendf(
235 "float mindist = min(min(%s.x, %s.y), min(%s.z, %s.w));",
Brian Salomon9b4bd592018-07-10 09:23:40 -0400236 aaDistVarying.fsIn(), aaDistVarying.fsIn(), aaDistVarying.fsIn(),
237 aaDistVarying.fsIn());
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400238 if (mulByFragCoordW) {
239 args.fFragBuilder->codeAppend("mindist *= sk_FragCoord.w;");
240 }
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500241 args.fFragBuilder->codeAppendf("%s = float4(clamp(mindist, 0, 1));",
242 args.fOutputCoverage);
243 } else {
244 args.fFragBuilder->codeAppendf("%s = float4(1);", args.fOutputCoverage);
245 }
Brian Salomon34169692017-08-28 15:32:01 -0400246 }
247 GrGLSLColorSpaceXformHelper fColorSpaceXformHelper;
248 };
249 return new GLSLProcessor;
250 }
251
Brian Salomon485b8c62018-01-12 15:11:06 -0500252 bool usesCoverageEdgeAA() const { return SkToBool(fAAEdges[0].isInitialized()); }
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500253
Brian Salomon34169692017-08-28 15:32:01 -0400254private:
Brian Salomon336ce7b2017-09-08 08:23:58 -0400255 // This exists to reduce the number of shaders generated. It does some rounding of sampler
256 // counts.
257 static int NumSamplersToUse(int numRealProxies, const GrShaderCaps& caps) {
258 SkASSERT(numRealProxies > 0 && numRealProxies <= kMaxTextures &&
259 numRealProxies <= caps.maxFragmentSamplers());
260 if (1 == numRealProxies) {
261 return 1;
262 }
263 if (numRealProxies <= 4) {
264 return 4;
265 }
266 // Round to the next power of 2 and then clamp to kMaxTextures and the max allowed by caps.
267 return SkTMin(SkNextPow2(numRealProxies), SkTMin(kMaxTextures, caps.maxFragmentSamplers()));
268 }
269
270 TextureGeometryProcessor(sk_sp<GrTextureProxy> proxies[], int proxyCnt, int samplerCnt,
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400271 sk_sp<GrColorSpaceXform> csxf, bool coverageAA, bool perspective,
Brian Salomonb80ffee2018-05-23 16:39:39 -0400272 Domain domain, const GrSamplerState::Filter filters[],
273 const GrShaderCaps& caps)
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500274 : INHERITED(kTextureGeometryProcessor_ClassID), fColorSpaceXform(std::move(csxf)) {
Brian Salomon336ce7b2017-09-08 08:23:58 -0400275 SkASSERT(proxyCnt > 0 && samplerCnt >= proxyCnt);
Brian Salomon336ce7b2017-09-08 08:23:58 -0400276 fSamplers[0].reset(std::move(proxies[0]), filters[0]);
277 this->addTextureSampler(&fSamplers[0]);
278 for (int i = 1; i < proxyCnt; ++i) {
279 // This class has one sampler built in, the rest come from memory this processor was
280 // placement-newed into and so haven't been constructed.
281 new (&fSamplers[i]) TextureSampler(std::move(proxies[i]), filters[i]);
282 this->addTextureSampler(&fSamplers[i]);
283 }
Brian Salomon30e1a5e2018-05-18 12:32:32 -0400284
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400285 if (perspective) {
Brian Salomon92be2f72018-06-19 14:33:47 -0400286 fPositions = {"position", kFloat3_GrVertexAttribType};
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400287 } else {
Brian Salomon92be2f72018-06-19 14:33:47 -0400288 fPositions = {"position", kFloat2_GrVertexAttribType};
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400289 }
Brian Salomon92be2f72018-06-19 14:33:47 -0400290 fColors = {"color", kUByte4_norm_GrVertexAttribType};
291 fTextureCoords = {"textureCoords", kFloat2_GrVertexAttribType};
292 int vertexAttributeCnt = 3;
Brian Salomon30e1a5e2018-05-18 12:32:32 -0400293
Brian Salomon336ce7b2017-09-08 08:23:58 -0400294 if (samplerCnt > 1) {
295 // Here we initialize any extra samplers by repeating the last one samplerCnt - proxyCnt
296 // times.
297 GrTextureProxy* dupeProxy = fSamplers[proxyCnt - 1].proxy();
298 for (int i = proxyCnt; i < samplerCnt; ++i) {
299 new (&fSamplers[i]) TextureSampler(sk_ref_sp(dupeProxy), filters[proxyCnt - 1]);
300 this->addTextureSampler(&fSamplers[i]);
301 }
302 SkASSERT(caps.integerSupport());
Brian Salomon92be2f72018-06-19 14:33:47 -0400303 fTextureIdx = {"textureIdx", kInt_GrVertexAttribType};
304 ++vertexAttributeCnt;
Brian Salomon336ce7b2017-09-08 08:23:58 -0400305 }
Brian Salomonb80ffee2018-05-23 16:39:39 -0400306 if (domain == Domain::kYes) {
Brian Salomon92be2f72018-06-19 14:33:47 -0400307 fDomain = {"domain", kFloat4_GrVertexAttribType};
308 ++vertexAttributeCnt;
Brian Salomonb80ffee2018-05-23 16:39:39 -0400309 }
Brian Salomon485b8c62018-01-12 15:11:06 -0500310 if (coverageAA) {
Brian Salomon92be2f72018-06-19 14:33:47 -0400311 fAAEdges[0] = {"aaEdge0", kFloat3_GrVertexAttribType};
312 fAAEdges[1] = {"aaEdge1", kFloat3_GrVertexAttribType};
313 fAAEdges[2] = {"aaEdge2", kFloat3_GrVertexAttribType};
314 fAAEdges[3] = {"aaEdge3", kFloat3_GrVertexAttribType};
315 vertexAttributeCnt += 4;
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500316 }
Brian Salomon92be2f72018-06-19 14:33:47 -0400317 this->setVertexAttributeCnt(vertexAttributeCnt);
318 }
319
320 const Attribute& onVertexAttribute(int i) const override {
321 return IthInitializedAttribute(i, fPositions, fColors, fTextureCoords, fTextureIdx, fDomain,
322 fAAEdges[0], fAAEdges[1], fAAEdges[2], fAAEdges[3]);
Brian Salomon34169692017-08-28 15:32:01 -0400323 }
324
325 Attribute fPositions;
Brian Salomon34169692017-08-28 15:32:01 -0400326 Attribute fColors;
Brian Salomon30e1a5e2018-05-18 12:32:32 -0400327 Attribute fTextureCoords;
328 Attribute fTextureIdx;
Brian Salomonb80ffee2018-05-23 16:39:39 -0400329 Attribute fDomain;
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500330 Attribute fAAEdges[4];
Brian Salomon34169692017-08-28 15:32:01 -0400331 sk_sp<GrColorSpaceXform> fColorSpaceXform;
Brian Salomon336ce7b2017-09-08 08:23:58 -0400332 TextureSampler fSamplers[1];
Ethan Nicholasabff9562017-10-09 10:54:08 -0400333
334 typedef GrGeometryProcessor INHERITED;
Brian Salomon34169692017-08-28 15:32:01 -0400335};
336
Brian Salomon6872e942018-05-18 10:29:54 -0400337// This computes the four edge equations for a quad, then outsets them and computes a new quad
338// as the intersection points of the outset edges. 'x' and 'y' contain the original points as input
339// and the outset points as output. 'a', 'b', and 'c' are the edge equation coefficients on output.
340static void compute_quad_edges_and_outset_vertices(Sk4f* x, Sk4f* y, Sk4f* a, Sk4f* b, Sk4f* c) {
341 static constexpr auto fma = SkNx_fma<4, float>;
342 // These rotate the points/edge values either clockwise or counterclockwise assuming tri strip
343 // order.
344 auto nextCW = [](const Sk4f& v) { return SkNx_shuffle<2, 0, 3, 1>(v); };
345 auto nextCCW = [](const Sk4f& v) { return SkNx_shuffle<1, 3, 0, 2>(v); };
346
347 auto xnext = nextCCW(*x);
348 auto ynext = nextCCW(*y);
349 *a = ynext - *y;
350 *b = *x - xnext;
351 *c = fma(xnext, *y, -ynext * *x);
352 Sk4f invNormLengths = (*a * *a + *b * *b).rsqrt();
353 // Make sure the edge equations have their normals facing into the quad in device space.
354 auto test = fma(*a, nextCW(*x), fma(*b, nextCW(*y), *c));
355 if ((test < Sk4f(0)).anyTrue()) {
356 invNormLengths = -invNormLengths;
357 }
358 *a *= invNormLengths;
359 *b *= invNormLengths;
360 *c *= invNormLengths;
361
362 // Here is the outset. This makes our edge equations compute coverage without requiring a
363 // half pixel offset and is also used to compute the bloated quad that will cover all
364 // pixels.
365 *c += Sk4f(0.5f);
366
367 // Reverse the process to compute the points of the bloated quad from the edge equations.
368 // This time the inputs don't have 1s as their third coord and we want to homogenize rather
369 // than normalize.
370 auto anext = nextCW(*a);
371 auto bnext = nextCW(*b);
372 auto cnext = nextCW(*c);
373 *x = fma(bnext, *c, -*b * cnext);
374 *y = fma(*a, cnext, -anext * *c);
375 auto ic = (fma(anext, *b, -bnext * *a)).invert();
376 *x *= ic;
377 *y *= ic;
378}
379
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500380namespace {
381// This is a class soley so it can be partially specialized (functions cannot be).
Brian Salomon86c40012018-05-22 10:48:49 -0400382template <typename V, GrAA AA = V::kAA, typename Position = typename V::Position>
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400383class VertexAAHandler;
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500384
Brian Salomon86c40012018-05-22 10:48:49 -0400385template<typename V> class VertexAAHandler<V, GrAA::kNo, SkPoint> {
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500386public:
Brian Salomon86c40012018-05-22 10:48:49 -0400387 static void AssignPositionsAndTexCoords(V* vertices, const GrPerspQuad& quad,
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500388 const SkRect& texRect) {
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400389 SkASSERT((quad.w4f() == Sk4f(1.f)).allTrue());
Brian Salomon86c40012018-05-22 10:48:49 -0400390 SkPointPriv::SetRectTriStrip(&vertices[0].fTextureCoords, texRect, sizeof(V));
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400391 for (int i = 0; i < 4; ++i) {
392 vertices[i].fPosition = {quad.x(i), quad.y(i)};
393 }
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500394 }
395};
396
Brian Salomon86c40012018-05-22 10:48:49 -0400397template<typename V> class VertexAAHandler<V, GrAA::kNo, SkPoint3> {
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500398public:
Brian Salomon86c40012018-05-22 10:48:49 -0400399 static void AssignPositionsAndTexCoords(V* vertices, const GrPerspQuad& quad,
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500400 const SkRect& texRect) {
Brian Salomon86c40012018-05-22 10:48:49 -0400401 SkPointPriv::SetRectTriStrip(&vertices[0].fTextureCoords, texRect, sizeof(V));
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400402 for (int i = 0; i < 4; ++i) {
403 vertices[i].fPosition = quad.point(i);
404 }
405 }
406};
407
Brian Salomon86c40012018-05-22 10:48:49 -0400408template<typename V> class VertexAAHandler<V, GrAA::kYes, SkPoint> {
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400409public:
Brian Salomon86c40012018-05-22 10:48:49 -0400410 static void AssignPositionsAndTexCoords(V* vertices, const GrPerspQuad& quad,
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400411 const SkRect& texRect) {
412 SkASSERT((quad.w4f() == Sk4f(1.f)).allTrue());
Brian Salomon6872e942018-05-18 10:29:54 -0400413 auto x = quad.x4f();
414 auto y = quad.y4f();
415 Sk4f a, b, c;
416 compute_quad_edges_and_outset_vertices(&x, &y, &a, &b, &c);
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500417
418 for (int i = 0; i < 4; ++i) {
Brian Salomon6872e942018-05-18 10:29:54 -0400419 vertices[i].fPosition = {x[i], y[i]};
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500420 for (int j = 0; j < 4; ++j) {
Brian Salomon6872e942018-05-18 10:29:54 -0400421 vertices[i].fEdges[j] = {a[j], b[j], c[j]};
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500422 }
423 }
424
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500425 AssignTexCoords(vertices, quad, texRect);
426 }
427
428private:
Brian Salomon86c40012018-05-22 10:48:49 -0400429 static void AssignTexCoords(V* vertices, const GrPerspQuad& quad, const SkRect& tex) {
Brian Salomona33b67c2018-05-17 10:42:14 -0400430 SkMatrix q = SkMatrix::MakeAll(quad.x(0), quad.x(1), quad.x(2),
431 quad.y(0), quad.y(1), quad.y(2),
432 1.f, 1.f, 1.f);
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500433 SkMatrix qinv;
434 if (!q.invert(&qinv)) {
435 return;
436 }
437 SkMatrix t = SkMatrix::MakeAll(tex.fLeft, tex.fLeft, tex.fRight,
438 tex.fTop, tex.fBottom, tex.fTop,
439 1.f, 1.f, 1.f);
440 SkMatrix map;
441 map.setConcat(t, qinv);
Brian Salomon86c40012018-05-22 10:48:49 -0400442 SkMatrixPriv::MapPointsWithStride(map, &vertices[0].fTextureCoords, sizeof(V),
443 &vertices[0].fPosition, sizeof(V), 4);
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500444 }
445};
446
Brian Salomon86c40012018-05-22 10:48:49 -0400447template<typename V> class VertexAAHandler<V, GrAA::kYes, SkPoint3> {
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400448public:
Brian Salomon86c40012018-05-22 10:48:49 -0400449 static void AssignPositionsAndTexCoords(V* vertices, const GrPerspQuad& quad,
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400450 const SkRect& texRect) {
451 auto x = quad.x4f();
452 auto y = quad.y4f();
453 auto iw = quad.iw4f();
454 x *= iw;
455 y *= iw;
456
457 // Get an equation for w from device space coords.
458 SkMatrix P;
459 P.setAll(x[0], y[0], 1, x[1], y[1], 1, x[2], y[2], 1);
460 SkAssertResult(P.invert(&P));
461 SkPoint3 weq{quad.w(0), quad.w(1), quad.w(2)};
462 P.mapHomogeneousPoints(&weq, &weq, 1);
463
464 Sk4f a, b, c;
465 compute_quad_edges_and_outset_vertices(&x, &y, &a, &b, &c);
466
467 // Compute new w values for the output vertices;
468 auto w = Sk4f(weq.fX) * x + Sk4f(weq.fY) * y + Sk4f(weq.fZ);
469 x *= w;
470 y *= w;
471
472 for (int i = 0; i < 4; ++i) {
473 vertices[i].fPosition = {x[i], y[i], w[i]};
474 for (int j = 0; j < 4; ++j) {
475 vertices[i].fEdges[j] = {a[j], b[j], c[j]};
476 }
477 }
478
479 AssignTexCoords(vertices, quad, texRect);
480 }
481
482private:
Brian Salomon86c40012018-05-22 10:48:49 -0400483 static void AssignTexCoords(V* vertices, const GrPerspQuad& quad, const SkRect& tex) {
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400484 SkMatrix q = SkMatrix::MakeAll(quad.x(0), quad.x(1), quad.x(2),
485 quad.y(0), quad.y(1), quad.y(2),
486 quad.w(0), quad.w(1), quad.w(2));
487 SkMatrix qinv;
488 if (!q.invert(&qinv)) {
489 return;
490 }
491 SkMatrix t = SkMatrix::MakeAll(tex.fLeft, tex.fLeft, tex.fRight,
492 tex.fTop, tex.fBottom, tex.fTop,
493 1.f, 1.f, 1.f);
494 SkMatrix map;
495 map.setConcat(t, qinv);
496 SkPoint3 tempTexCoords[4];
497 SkMatrixPriv::MapHomogeneousPointsWithStride(map, tempTexCoords, sizeof(SkPoint3),
Brian Salomon86c40012018-05-22 10:48:49 -0400498 &vertices[0].fPosition, sizeof(V), 4);
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400499 for (int i = 0; i < 4; ++i) {
500 auto invW = 1.f / tempTexCoords[i].fZ;
501 vertices[i].fTextureCoords.fX = tempTexCoords[i].fX * invW;
502 vertices[i].fTextureCoords.fY = tempTexCoords[i].fY * invW;
503 }
504 }
505};
506
Brian Salomon17031a72018-05-22 14:14:07 -0400507template <typename V, MultiTexture MT = V::kMultiTexture> struct TexIdAssigner;
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500508
Brian Salomon17031a72018-05-22 14:14:07 -0400509template <typename V> struct TexIdAssigner<V, MultiTexture::kYes> {
Brian Salomon86c40012018-05-22 10:48:49 -0400510 static void Assign(V* vertices, int textureIdx) {
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400511 for (int i = 0; i < 4; ++i) {
512 vertices[i].fTextureIdx = textureIdx;
513 }
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500514 }
515};
516
Brian Salomon17031a72018-05-22 14:14:07 -0400517template <typename V> struct TexIdAssigner<V, MultiTexture::kNo> {
Brian Salomon86c40012018-05-22 10:48:49 -0400518 static void Assign(V* vertices, int textureIdx) {}
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500519};
Brian Salomonb80ffee2018-05-23 16:39:39 -0400520
521template <typename V, Domain D = V::kDomain> struct DomainAssigner;
522
523template <typename V> struct DomainAssigner<V, Domain::kYes> {
524 static void Assign(V* vertices, Domain domain, GrSamplerState::Filter filter,
525 const SkRect& srcRect, GrSurfaceOrigin origin, float iw, float ih) {
526 static constexpr SkRect kLargeRect = {-2, -2, 2, 2};
527 SkRect domainRect;
528 if (domain == Domain::kYes) {
529 auto ltrb = Sk4f::Load(&srcRect);
530 if (filter == GrSamplerState::Filter::kBilerp) {
531 auto rblt = SkNx_shuffle<2, 3, 0, 1>(ltrb);
532 auto whwh = (rblt - ltrb).abs();
533 auto c = (rblt + ltrb) * 0.5f;
534 static const Sk4f kOffsets = {0.5f, 0.5f, -0.5f, -0.5f};
535 ltrb = (whwh < 1.f).thenElse(c, ltrb + kOffsets);
536 }
537 ltrb *= Sk4f(iw, ih, iw, ih);
538 if (origin == kBottomLeft_GrSurfaceOrigin) {
539 static const Sk4f kMul = {1.f, -1.f, 1.f, -1.f};
540 static const Sk4f kAdd = {0.f, 1.f, 0.f, 1.f};
541 ltrb = SkNx_shuffle<0, 3, 2, 1>(kMul * ltrb + kAdd);
542 }
543 ltrb.store(&domainRect);
544 } else {
545 domainRect = kLargeRect;
546 }
547 for (int i = 0; i < 4; ++i) {
548 vertices[i].fTextureDomain = domainRect;
549 }
550 }
551};
552
553template <typename V> struct DomainAssigner<V, Domain::kNo> {
554 static void Assign(V*, Domain domain, GrSamplerState::Filter, const SkRect&, GrSurfaceOrigin,
555 float iw, float ih) {
556 SkASSERT(domain == Domain::kNo);
557 }
558};
559
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500560} // anonymous namespace
561
Brian Salomon86c40012018-05-22 10:48:49 -0400562template <typename V>
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400563static void tessellate_quad(const GrPerspQuad& devQuad, const SkRect& srcRect, GrColor color,
Brian Salomonb80ffee2018-05-23 16:39:39 -0400564 GrSurfaceOrigin origin, GrSamplerState::Filter filter, V* vertices,
565 SkScalar iw, SkScalar ih, int textureIdx, Domain domain) {
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500566 SkRect texRect = {
567 iw * srcRect.fLeft,
568 ih * srcRect.fTop,
569 iw * srcRect.fRight,
570 ih * srcRect.fBottom
571 };
572 if (origin == kBottomLeft_GrSurfaceOrigin) {
573 texRect.fTop = 1.f - texRect.fTop;
574 texRect.fBottom = 1.f - texRect.fBottom;
575 }
Brian Salomon86c40012018-05-22 10:48:49 -0400576 VertexAAHandler<V>::AssignPositionsAndTexCoords(vertices, devQuad, texRect);
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500577 vertices[0].fColor = color;
578 vertices[1].fColor = color;
579 vertices[2].fColor = color;
580 vertices[3].fColor = color;
Brian Salomon86c40012018-05-22 10:48:49 -0400581 TexIdAssigner<V>::Assign(vertices, textureIdx);
Brian Salomonb80ffee2018-05-23 16:39:39 -0400582 DomainAssigner<V>::Assign(vertices, domain, filter, srcRect, origin, iw, ih);
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500583}
Brian Salomon17031a72018-05-22 14:14:07 -0400584
Brian Salomon34169692017-08-28 15:32:01 -0400585/**
586 * Op that implements GrTextureOp::Make. It draws textured quads. Each quad can modulate against a
587 * the texture by color. The blend with the destination is always src-over. The edges are non-AA.
588 */
589class TextureOp final : public GrMeshDrawOp {
590public:
Robert Phillips7c525e62018-06-12 10:11:12 -0400591 static std::unique_ptr<GrDrawOp> Make(GrContext* context,
592 sk_sp<GrTextureProxy> proxy,
593 GrSamplerState::Filter filter,
594 GrColor color,
595 const SkRect& srcRect,
596 const SkRect& dstRect,
597 GrAAType aaType,
598 SkCanvas::SrcRectConstraint constraint,
Brian Osman2b23c4b2018-06-01 12:25:08 -0400599 const SkMatrix& viewMatrix,
600 sk_sp<GrColorSpaceXform> csxf) {
Robert Phillipsc994a932018-06-19 13:09:54 -0400601 GrOpMemoryPool* pool = context->contextPriv().opMemoryPool();
602
603 return pool->allocate<TextureOp>(std::move(proxy), filter, color,
604 srcRect, dstRect, aaType, constraint,
605 viewMatrix, std::move(csxf));
Brian Salomon34169692017-08-28 15:32:01 -0400606 }
607
Brian Salomon336ce7b2017-09-08 08:23:58 -0400608 ~TextureOp() override {
609 if (fFinalized) {
610 auto proxies = this->proxies();
611 for (int i = 0; i < fProxyCnt; ++i) {
612 proxies[i]->completedRead();
613 }
614 if (fProxyCnt > 1) {
615 delete[] reinterpret_cast<const char*>(proxies);
616 }
617 } else {
618 SkASSERT(1 == fProxyCnt);
619 fProxy0->unref();
620 }
621 }
Brian Salomon34169692017-08-28 15:32:01 -0400622
623 const char* name() const override { return "TextureOp"; }
624
Robert Phillipsf1748f52017-09-14 14:11:24 -0400625 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillipsb493eeb2017-09-13 13:10:52 -0400626 auto proxies = this->proxies();
627 for (int i = 0; i < fProxyCnt; ++i) {
628 func(proxies[i]);
629 }
630 }
631
Brian Salomon34169692017-08-28 15:32:01 -0400632 SkString dumpInfo() const override {
633 SkString str;
Brian Salomon34169692017-08-28 15:32:01 -0400634 str.appendf("# draws: %d\n", fDraws.count());
Brian Salomon336ce7b2017-09-08 08:23:58 -0400635 auto proxies = this->proxies();
636 for (int i = 0; i < fProxyCnt; ++i) {
637 str.appendf("Proxy ID %d: %d, Filter: %d\n", i, proxies[i]->uniqueID().asUInt(),
638 static_cast<int>(this->filters()[i]));
639 }
Brian Salomon34169692017-08-28 15:32:01 -0400640 for (int i = 0; i < fDraws.count(); ++i) {
641 const Draw& draw = fDraws[i];
642 str.appendf(
Brian Salomon336ce7b2017-09-08 08:23:58 -0400643 "%d: Color: 0x%08x, ProxyIdx: %d, TexRect [L: %.2f, T: %.2f, R: %.2f, B: %.2f] "
644 "Quad [(%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f)]\n",
Brian Salomonb80ffee2018-05-23 16:39:39 -0400645 i, draw.color(), draw.textureIdx(), draw.srcRect().fLeft, draw.srcRect().fTop,
646 draw.srcRect().fRight, draw.srcRect().fBottom, draw.quad().point(0).fX,
647 draw.quad().point(0).fY, draw.quad().point(1).fX, draw.quad().point(1).fY,
648 draw.quad().point(2).fX, draw.quad().point(2).fY, draw.quad().point(3).fX,
649 draw.quad().point(3).fY);
Brian Salomon34169692017-08-28 15:32:01 -0400650 }
651 str += INHERITED::dumpInfo();
652 return str;
653 }
654
Brian Osman532b3f92018-07-11 10:02:07 -0400655 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip) override {
Brian Salomon34169692017-08-28 15:32:01 -0400656 SkASSERT(!fFinalized);
Brian Salomon336ce7b2017-09-08 08:23:58 -0400657 SkASSERT(1 == fProxyCnt);
Brian Salomon34169692017-08-28 15:32:01 -0400658 fFinalized = true;
Brian Salomon336ce7b2017-09-08 08:23:58 -0400659 fProxy0->addPendingRead();
660 fProxy0->unref();
Brian Salomon34169692017-08-28 15:32:01 -0400661 return RequiresDstTexture::kNo;
662 }
663
Brian Salomon485b8c62018-01-12 15:11:06 -0500664 FixedFunctionFlags fixedFunctionFlags() const override {
665 return this->aaType() == GrAAType::kMSAA ? FixedFunctionFlags::kUsesHWAA
666 : FixedFunctionFlags::kNone;
667 }
Brian Salomon34169692017-08-28 15:32:01 -0400668
669 DEFINE_OP_CLASS_ID
670
671private:
Robert Phillips7c525e62018-06-12 10:11:12 -0400672 friend class ::GrOpMemoryPool;
Brian Salomon762d5e72017-12-01 10:25:08 -0500673
674 // This is used in a heursitic for choosing a code path. We don't care what happens with
675 // really large rects, infs, nans, etc.
676#if defined(__clang__) && (__clang_major__ * 1000 + __clang_minor__) >= 3007
677__attribute__((no_sanitize("float-cast-overflow")))
678#endif
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500679 size_t RectSizeAsSizeT(const SkRect& rect) {;
Brian Salomon762d5e72017-12-01 10:25:08 -0500680 return static_cast<size_t>(SkTMax(rect.width(), 1.f) * SkTMax(rect.height(), 1.f));
681 }
682
Brian Salomon336ce7b2017-09-08 08:23:58 -0400683 static constexpr int kMaxTextures = TextureGeometryProcessor::kMaxTextures;
684
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400685 TextureOp(sk_sp<GrTextureProxy> proxy, GrSamplerState::Filter filter, GrColor color,
Brian Salomon485b8c62018-01-12 15:11:06 -0500686 const SkRect& srcRect, const SkRect& dstRect, GrAAType aaType,
Brian Salomonb80ffee2018-05-23 16:39:39 -0400687 SkCanvas::SrcRectConstraint constraint, const SkMatrix& viewMatrix,
Brian Osman2b23c4b2018-06-01 12:25:08 -0400688 sk_sp<GrColorSpaceXform> csxf)
Brian Salomon34169692017-08-28 15:32:01 -0400689 : INHERITED(ClassID())
Brian Salomon34169692017-08-28 15:32:01 -0400690 , fColorSpaceXform(std::move(csxf))
Brian Salomon336ce7b2017-09-08 08:23:58 -0400691 , fProxy0(proxy.release())
692 , fFilter0(filter)
693 , fProxyCnt(1)
Brian Salomon485b8c62018-01-12 15:11:06 -0500694 , fAAType(static_cast<unsigned>(aaType))
Brian Osman2b23c4b2018-06-01 12:25:08 -0400695 , fFinalized(0) {
Brian Salomon485b8c62018-01-12 15:11:06 -0500696 SkASSERT(aaType != GrAAType::kMixedSamples);
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400697 fPerspective = viewMatrix.hasPerspective();
Brian Salomon594b64c2018-05-29 12:47:57 -0400698 auto quad = GrPerspQuad(dstRect, viewMatrix);
699 auto bounds = quad.bounds();
700#ifndef SK_DONT_DROP_UNNECESSARY_AA_IN_TEXTURE_OP
701 if (GrAAType::kCoverage == this->aaType() && viewMatrix.rectStaysRect()) {
702 // Disable coverage AA when rect falls on integers in device space.
703 auto is_int = [](float f) { return f == sk_float_floor(f); };
704 if (is_int(bounds.fLeft) && is_int(bounds.fTop) && is_int(bounds.fRight) &&
705 is_int(bounds.fBottom)) {
706 fAAType = static_cast<unsigned>(GrAAType::kNone);
707 // We may have had a strict constraint with nearest filter soley due to possible AA
708 // bloat. In that case it's no longer necessary.
709 if (constraint == SkCanvas::kStrict_SrcRectConstraint &&
710 filter == GrSamplerState::Filter::kNearest) {
711 constraint = SkCanvas::kFast_SrcRectConstraint;
712 }
713 }
714 }
715#endif
716 const auto& draw = fDraws.emplace_back(srcRect, 0, quad, constraint, color);
Brian Salomon34169692017-08-28 15:32:01 -0400717 this->setBounds(bounds, HasAABloat::kNo, IsZeroArea::kNo);
Brian Salomon594b64c2018-05-29 12:47:57 -0400718 fDomain = static_cast<bool>(draw.domain());
Brian Salomon762d5e72017-12-01 10:25:08 -0500719 fMaxApproxDstPixelArea = RectSizeAsSizeT(bounds);
Brian Salomon34169692017-08-28 15:32:01 -0400720 }
721
Brian Salomonb80ffee2018-05-23 16:39:39 -0400722 template <typename Pos, MultiTexture MT, Domain D, GrAA AA>
Brian Salomon17031a72018-05-22 14:14:07 -0400723 void tess(void* v, const float iw[], const float ih[], const GrGeometryProcessor* gp) {
Brian Salomonb80ffee2018-05-23 16:39:39 -0400724 using Vertex = TextureGeometryProcessor::Vertex<Pos, MT, D, AA>;
Brian Salomon92be2f72018-06-19 14:33:47 -0400725 SkASSERT(gp->debugOnly_vertexStride() == sizeof(Vertex));
Brian Salomon17031a72018-05-22 14:14:07 -0400726 auto vertices = static_cast<Vertex*>(v);
727 auto proxies = this->proxies();
Brian Salomonb80ffee2018-05-23 16:39:39 -0400728 auto filters = this->filters();
Brian Salomon17031a72018-05-22 14:14:07 -0400729 for (const auto& draw : fDraws) {
Brian Salomonb80ffee2018-05-23 16:39:39 -0400730 auto textureIdx = draw.textureIdx();
731 auto origin = proxies[textureIdx]->origin();
732 tessellate_quad<Vertex>(draw.quad(), draw.srcRect(), draw.color(), origin,
733 filters[textureIdx], vertices, iw[textureIdx], ih[textureIdx],
734 textureIdx, draw.domain());
Brian Salomon17031a72018-05-22 14:14:07 -0400735 vertices += 4;
736 }
737 }
738
Brian Salomon34169692017-08-28 15:32:01 -0400739 void onPrepareDraws(Target* target) override {
Brian Salomon336ce7b2017-09-08 08:23:58 -0400740 sk_sp<GrTextureProxy> proxiesSPs[kMaxTextures];
741 auto proxies = this->proxies();
742 auto filters = this->filters();
743 for (int i = 0; i < fProxyCnt; ++i) {
744 if (!proxies[i]->instantiate(target->resourceProvider())) {
745 return;
746 }
747 proxiesSPs[i] = sk_ref_sp(proxies[i]);
Brian Salomon34169692017-08-28 15:32:01 -0400748 }
Brian Salomon336ce7b2017-09-08 08:23:58 -0400749
Brian Salomonb80ffee2018-05-23 16:39:39 -0400750 Domain domain = fDomain ? Domain::kYes : Domain::kNo;
Brian Salomon485b8c62018-01-12 15:11:06 -0500751 bool coverageAA = GrAAType::kCoverage == this->aaType();
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400752 sk_sp<GrGeometryProcessor> gp = TextureGeometryProcessor::Make(
753 proxiesSPs, fProxyCnt, std::move(fColorSpaceXform), coverageAA, fPerspective,
Brian Salomonb80ffee2018-05-23 16:39:39 -0400754 domain, filters, *target->caps().shaderCaps());
Brian Salomon34169692017-08-28 15:32:01 -0400755 GrPipeline::InitArgs args;
756 args.fProxy = target->proxy();
757 args.fCaps = &target->caps();
758 args.fResourceProvider = target->resourceProvider();
Brian Salomon485b8c62018-01-12 15:11:06 -0500759 args.fFlags = 0;
Brian Salomon485b8c62018-01-12 15:11:06 -0500760 if (GrAAType::kMSAA == this->aaType()) {
761 args.fFlags |= GrPipeline::kHWAntialias_Flag;
762 }
763
Brian Salomon49348902018-06-26 09:12:38 -0400764 auto clip = target->detachAppliedClip();
765 const auto* fixedDynamicState = target->allocFixedDynamicState(clip.scissorState().rect());
766 const auto* pipeline =
767 target->allocPipeline(args, GrProcessorSet::MakeEmptySet(), std::move(clip));
Brian Salomon92be2f72018-06-19 14:33:47 -0400768 using TessFn =
769 decltype(&TextureOp::tess<SkPoint, MultiTexture::kNo, Domain::kNo, GrAA::kNo>);
770#define TESS_FN_AND_VERTEX_SIZE(Point, MT, Domain, AA) \
771 { \
772 &TextureOp::tess<Point, MT, Domain, AA>, \
773 sizeof(TextureGeometryProcessor::Vertex<Point, MT, Domain, AA>) \
774 }
775 static constexpr struct {
776 TessFn fTessFn;
777 size_t fVertexSize;
778 } kTessFnsAndVertexSizes[] = {
779 TESS_FN_AND_VERTEX_SIZE(SkPoint, MultiTexture::kNo, Domain::kNo, GrAA::kNo),
780 TESS_FN_AND_VERTEX_SIZE(SkPoint, MultiTexture::kNo, Domain::kNo, GrAA::kYes),
781 TESS_FN_AND_VERTEX_SIZE(SkPoint, MultiTexture::kNo, Domain::kYes, GrAA::kNo),
782 TESS_FN_AND_VERTEX_SIZE(SkPoint, MultiTexture::kNo, Domain::kYes, GrAA::kYes),
783 TESS_FN_AND_VERTEX_SIZE(SkPoint, MultiTexture::kYes, Domain::kNo, GrAA::kNo),
784 TESS_FN_AND_VERTEX_SIZE(SkPoint, MultiTexture::kYes, Domain::kNo, GrAA::kYes),
785 TESS_FN_AND_VERTEX_SIZE(SkPoint, MultiTexture::kYes, Domain::kYes, GrAA::kNo),
786 TESS_FN_AND_VERTEX_SIZE(SkPoint, MultiTexture::kYes, Domain::kYes, GrAA::kYes),
787 TESS_FN_AND_VERTEX_SIZE(SkPoint3, MultiTexture::kNo, Domain::kNo, GrAA::kNo),
788 TESS_FN_AND_VERTEX_SIZE(SkPoint3, MultiTexture::kNo, Domain::kNo, GrAA::kYes),
789 TESS_FN_AND_VERTEX_SIZE(SkPoint3, MultiTexture::kNo, Domain::kYes, GrAA::kNo),
790 TESS_FN_AND_VERTEX_SIZE(SkPoint3, MultiTexture::kNo, Domain::kYes, GrAA::kYes),
791 TESS_FN_AND_VERTEX_SIZE(SkPoint3, MultiTexture::kYes, Domain::kNo, GrAA::kNo),
792 TESS_FN_AND_VERTEX_SIZE(SkPoint3, MultiTexture::kYes, Domain::kNo, GrAA::kYes),
793 TESS_FN_AND_VERTEX_SIZE(SkPoint3, MultiTexture::kYes, Domain::kYes, GrAA::kNo),
794 TESS_FN_AND_VERTEX_SIZE(SkPoint3, MultiTexture::kYes, Domain::kYes, GrAA::kYes),
795 };
796#undef TESS_FN_AND_VERTEX_SIZE
797 int tessFnIdx = 0;
798 tessFnIdx |= coverageAA ? 0x1 : 0x0;
799 tessFnIdx |= fDomain ? 0x2 : 0x0;
800 tessFnIdx |= (fProxyCnt > 1) ? 0x4 : 0x0;
801 tessFnIdx |= fPerspective ? 0x8 : 0x0;
802
803 SkASSERT(kTessFnsAndVertexSizes[tessFnIdx].fVertexSize == gp->debugOnly_vertexStride());
804
Brian Salomon34169692017-08-28 15:32:01 -0400805 int vstart;
806 const GrBuffer* vbuffer;
Brian Salomon92be2f72018-06-19 14:33:47 -0400807 void* vdata = target->makeVertexSpace(kTessFnsAndVertexSizes[tessFnIdx].fVertexSize,
808 4 * fDraws.count(), &vbuffer, &vstart);
Brian Salomon336ce7b2017-09-08 08:23:58 -0400809 if (!vdata) {
Brian Salomon34169692017-08-28 15:32:01 -0400810 SkDebugf("Could not allocate vertices\n");
811 return;
812 }
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400813
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400814 float iw[kMaxTextures];
815 float ih[kMaxTextures];
816 for (int t = 0; t < fProxyCnt; ++t) {
817 const auto* texture = proxies[t]->priv().peekTexture();
818 iw[t] = 1.f / texture->width();
819 ih[t] = 1.f / texture->height();
820 }
821
Brian Salomon92be2f72018-06-19 14:33:47 -0400822 (this->*(kTessFnsAndVertexSizes[tessFnIdx].fTessFn))(vdata, iw, ih, gp.get());
Brian Salomonb80ffee2018-05-23 16:39:39 -0400823
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);
Brian Salomon49348902018-06-26 09:12:38 -0400839 target->draw(gp.get(), pipeline, fixedDynamicState, 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()) {
Brian Salomon60dd8c72018-07-30 10:24:13 -0400956 if (tex->texturePriv().textureType() != GrTextureType::k2D) {
Brian Salomon336ce7b2017-09-08 08:23:58 -0400957 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
Robert Phillips7c525e62018-06-12 10:11:12 -04001029std::unique_ptr<GrDrawOp> Make(GrContext* context,
1030 sk_sp<GrTextureProxy> proxy,
1031 GrSamplerState::Filter filter,
1032 GrColor color,
1033 const SkRect& srcRect,
1034 const SkRect& dstRect,
1035 GrAAType aaType,
1036 SkCanvas::SrcRectConstraint constraint,
1037 const SkMatrix& viewMatrix,
1038 sk_sp<GrColorSpaceXform> csxf) {
1039 return TextureOp::Make(context, std::move(proxy), filter, color, srcRect, dstRect, aaType,
1040 constraint, viewMatrix, std::move(csxf));
Brian Salomon34169692017-08-28 15:32:01 -04001041}
1042
1043} // namespace GrTextureOp
1044
1045#if GR_TEST_UTILS
1046#include "GrContext.h"
Robert Phillips1afd4cd2018-01-08 13:40:32 -05001047#include "GrContextPriv.h"
Robert Phillips0bd24dc2018-01-16 08:06:32 -05001048#include "GrProxyProvider.h"
Brian Salomon34169692017-08-28 15:32:01 -04001049
1050GR_DRAW_OP_TEST_DEFINE(TextureOp) {
1051 GrSurfaceDesc desc;
1052 desc.fConfig = kRGBA_8888_GrPixelConfig;
1053 desc.fHeight = random->nextULessThan(90) + 10;
1054 desc.fWidth = random->nextULessThan(90) + 10;
Brian Salomon2a4f9832018-03-03 22:43:43 -05001055 auto origin = random->nextBool() ? kTopLeft_GrSurfaceOrigin : kBottomLeft_GrSurfaceOrigin;
Greg Daniel09c94002018-06-08 22:11:51 +00001056 GrMipMapped mipMapped = random->nextBool() ? GrMipMapped::kYes : GrMipMapped::kNo;
1057 SkBackingFit fit = SkBackingFit::kExact;
1058 if (mipMapped == GrMipMapped::kNo) {
1059 fit = random->nextBool() ? SkBackingFit::kApprox : SkBackingFit::kExact;
1060 }
Robert Phillips0bd24dc2018-01-16 08:06:32 -05001061
1062 GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider();
Greg Daniel09c94002018-06-08 22:11:51 +00001063 sk_sp<GrTextureProxy> proxy = proxyProvider->createProxy(desc, origin, mipMapped, fit,
1064 SkBudgeted::kNo,
1065 GrInternalSurfaceFlags::kNone);
Robert Phillips0bd24dc2018-01-16 08:06:32 -05001066
Brian Salomon34169692017-08-28 15:32:01 -04001067 SkRect rect = GrTest::TestRect(random);
1068 SkRect srcRect;
1069 srcRect.fLeft = random->nextRangeScalar(0.f, proxy->width() / 2.f);
1070 srcRect.fRight = random->nextRangeScalar(0.f, proxy->width()) + proxy->width() / 2.f;
1071 srcRect.fTop = random->nextRangeScalar(0.f, proxy->height() / 2.f);
1072 srcRect.fBottom = random->nextRangeScalar(0.f, proxy->height()) + proxy->height() / 2.f;
1073 SkMatrix viewMatrix = GrTest::TestMatrixPreservesRightAngles(random);
1074 GrColor color = SkColorToPremulGrColor(random->nextU());
Brian Salomon2bbdcc42017-09-07 12:36:34 -04001075 GrSamplerState::Filter filter = (GrSamplerState::Filter)random->nextULessThan(
1076 static_cast<uint32_t>(GrSamplerState::Filter::kMipMap) + 1);
Greg Daniel09c94002018-06-08 22:11:51 +00001077 while (mipMapped == GrMipMapped::kNo && filter == GrSamplerState::Filter::kMipMap) {
1078 filter = (GrSamplerState::Filter)random->nextULessThan(
1079 static_cast<uint32_t>(GrSamplerState::Filter::kMipMap) + 1);
1080 }
Brian Salomon34169692017-08-28 15:32:01 -04001081 auto csxf = GrTest::TestColorXform(random);
Brian Salomon485b8c62018-01-12 15:11:06 -05001082 GrAAType aaType = GrAAType::kNone;
1083 if (random->nextBool()) {
1084 aaType = (fsaaType == GrFSAAType::kUnifiedMSAA) ? GrAAType::kMSAA : GrAAType::kCoverage;
1085 }
Brian Salomonb80ffee2018-05-23 16:39:39 -04001086 auto constraint = random->nextBool() ? SkCanvas::kStrict_SrcRectConstraint
1087 : SkCanvas::kFast_SrcRectConstraint;
Robert Phillips7c525e62018-06-12 10:11:12 -04001088 return GrTextureOp::Make(context, std::move(proxy), filter, color, srcRect, rect, aaType,
1089 constraint, viewMatrix, std::move(csxf));
Brian Salomon34169692017-08-28 15:32:01 -04001090}
1091
1092#endif