blob: 108ee4294a24f197b395b422c0c91e10844df040 [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 Salomon1b112cc2018-07-10 20:34:16 +0000206 const char* aaDistName = nullptr;
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400207 bool mulByFragCoordW = false;
Brian Salomon1b112cc2018-07-10 20:34:16 +0000208 // When interpolation is inaccurate we perform the evaluation of the edge
209 // equations in the fragment shader rather than interpolating values computed
210 // in the vertex shader.
211 if (!args.fShaderCaps->interpolantsAreInaccurate()) {
212 GrGLSLVarying aaDistVarying(kFloat4_GrSLType,
213 GrGLSLVarying::Scope::kVertToFrag);
214 if (kFloat3_GrVertexAttribType == textureGP.fPositions.type()) {
215 args.fVaryingHandler->addVarying("aaDists", &aaDistVarying);
216 // The distance from edge equation e to homogenous point p=sk_Position
217 // is e.x*p.x/p.wx + e.y*p.y/p.w + e.z. However, we want screen space
218 // interpolation of this distance. We can do this by multiplying the
219 // varying in the VS by p.w and then multiplying by sk_FragCoord.w in
220 // the FS. So we output e.x*p.x + e.y*p.y + e.z * p.w
221 args.fVertBuilder->codeAppendf(
222 R"(%s = float4(dot(aaEdge0, %s), dot(aaEdge1, %s),
223 dot(aaEdge2, %s), dot(aaEdge3, %s));)",
224 aaDistVarying.vsOut(), textureGP.fPositions.name(),
225 textureGP.fPositions.name(), textureGP.fPositions.name(),
226 textureGP.fPositions.name());
227 mulByFragCoordW = true;
228 } else {
229 args.fVaryingHandler->addVarying("aaDists", &aaDistVarying);
230 args.fVertBuilder->codeAppendf(
231 R"(%s = float4(dot(aaEdge0.xy, %s.xy) + aaEdge0.z,
232 dot(aaEdge1.xy, %s.xy) + aaEdge1.z,
233 dot(aaEdge2.xy, %s.xy) + aaEdge2.z,
234 dot(aaEdge3.xy, %s.xy) + aaEdge3.z);)",
235 aaDistVarying.vsOut(), textureGP.fPositions.name(),
236 textureGP.fPositions.name(), textureGP.fPositions.name(),
237 textureGP.fPositions.name());
238 }
239 aaDistName = aaDistVarying.fsIn();
Brian Salomondba65f92018-01-22 08:43:38 -0500240 } else {
Brian Salomon1b112cc2018-07-10 20:34:16 +0000241 GrGLSLVarying aaEdgeVarying[4]{
242 {kFloat3_GrSLType, GrGLSLVarying::Scope::kVertToFrag},
243 {kFloat3_GrSLType, GrGLSLVarying::Scope::kVertToFrag},
244 {kFloat3_GrSLType, GrGLSLVarying::Scope::kVertToFrag},
245 {kFloat3_GrSLType, GrGLSLVarying::Scope::kVertToFrag}
246 };
247 for (int i = 0; i < 4; ++i) {
248 SkString name;
249 name.printf("aaEdge%d", i);
250 args.fVaryingHandler->addVarying(name.c_str(), &aaEdgeVarying[i],
251 Interpolation::kCanBeFlat);
252 args.fVertBuilder->codeAppendf(
253 "%s = aaEdge%d;", aaEdgeVarying[i].vsOut(), i);
254 }
255 args.fFragBuilder->codeAppendf(
256 R"(float4 aaDists = float4(dot(%s.xy, sk_FragCoord.xy) + %s.z,
257 dot(%s.xy, sk_FragCoord.xy) + %s.z,
258 dot(%s.xy, sk_FragCoord.xy) + %s.z,
259 dot(%s.xy, sk_FragCoord.xy) + %s.z);)",
260 aaEdgeVarying[0].fsIn(), aaEdgeVarying[0].fsIn(),
261 aaEdgeVarying[1].fsIn(), aaEdgeVarying[1].fsIn(),
262 aaEdgeVarying[2].fsIn(), aaEdgeVarying[2].fsIn(),
263 aaEdgeVarying[3].fsIn(), aaEdgeVarying[3].fsIn());
264 aaDistName = "aaDists";
Brian Salomondba65f92018-01-22 08:43:38 -0500265 }
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500266 args.fFragBuilder->codeAppendf(
267 "float mindist = min(min(%s.x, %s.y), min(%s.z, %s.w));",
Brian Salomon1b112cc2018-07-10 20:34:16 +0000268 aaDistName, aaDistName, aaDistName, aaDistName);
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400269 if (mulByFragCoordW) {
270 args.fFragBuilder->codeAppend("mindist *= sk_FragCoord.w;");
271 }
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500272 args.fFragBuilder->codeAppendf("%s = float4(clamp(mindist, 0, 1));",
273 args.fOutputCoverage);
274 } else {
275 args.fFragBuilder->codeAppendf("%s = float4(1);", args.fOutputCoverage);
276 }
Brian Salomon34169692017-08-28 15:32:01 -0400277 }
278 GrGLSLColorSpaceXformHelper fColorSpaceXformHelper;
279 };
280 return new GLSLProcessor;
281 }
282
Brian Salomon485b8c62018-01-12 15:11:06 -0500283 bool usesCoverageEdgeAA() const { return SkToBool(fAAEdges[0].isInitialized()); }
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500284
Brian Salomon34169692017-08-28 15:32:01 -0400285private:
Brian Salomon336ce7b2017-09-08 08:23:58 -0400286 // This exists to reduce the number of shaders generated. It does some rounding of sampler
287 // counts.
288 static int NumSamplersToUse(int numRealProxies, const GrShaderCaps& caps) {
289 SkASSERT(numRealProxies > 0 && numRealProxies <= kMaxTextures &&
290 numRealProxies <= caps.maxFragmentSamplers());
291 if (1 == numRealProxies) {
292 return 1;
293 }
294 if (numRealProxies <= 4) {
295 return 4;
296 }
297 // Round to the next power of 2 and then clamp to kMaxTextures and the max allowed by caps.
298 return SkTMin(SkNextPow2(numRealProxies), SkTMin(kMaxTextures, caps.maxFragmentSamplers()));
299 }
300
301 TextureGeometryProcessor(sk_sp<GrTextureProxy> proxies[], int proxyCnt, int samplerCnt,
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400302 sk_sp<GrColorSpaceXform> csxf, bool coverageAA, bool perspective,
Brian Salomonb80ffee2018-05-23 16:39:39 -0400303 Domain domain, const GrSamplerState::Filter filters[],
304 const GrShaderCaps& caps)
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500305 : INHERITED(kTextureGeometryProcessor_ClassID), fColorSpaceXform(std::move(csxf)) {
Brian Salomon336ce7b2017-09-08 08:23:58 -0400306 SkASSERT(proxyCnt > 0 && samplerCnt >= proxyCnt);
Brian Salomon336ce7b2017-09-08 08:23:58 -0400307 fSamplers[0].reset(std::move(proxies[0]), filters[0]);
308 this->addTextureSampler(&fSamplers[0]);
309 for (int i = 1; i < proxyCnt; ++i) {
310 // This class has one sampler built in, the rest come from memory this processor was
311 // placement-newed into and so haven't been constructed.
312 new (&fSamplers[i]) TextureSampler(std::move(proxies[i]), filters[i]);
313 this->addTextureSampler(&fSamplers[i]);
314 }
Brian Salomon30e1a5e2018-05-18 12:32:32 -0400315
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400316 if (perspective) {
Brian Salomon92be2f72018-06-19 14:33:47 -0400317 fPositions = {"position", kFloat3_GrVertexAttribType};
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400318 } else {
Brian Salomon92be2f72018-06-19 14:33:47 -0400319 fPositions = {"position", kFloat2_GrVertexAttribType};
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400320 }
Brian Salomon92be2f72018-06-19 14:33:47 -0400321 fColors = {"color", kUByte4_norm_GrVertexAttribType};
322 fTextureCoords = {"textureCoords", kFloat2_GrVertexAttribType};
323 int vertexAttributeCnt = 3;
Brian Salomon30e1a5e2018-05-18 12:32:32 -0400324
Brian Salomon336ce7b2017-09-08 08:23:58 -0400325 if (samplerCnt > 1) {
326 // Here we initialize any extra samplers by repeating the last one samplerCnt - proxyCnt
327 // times.
328 GrTextureProxy* dupeProxy = fSamplers[proxyCnt - 1].proxy();
329 for (int i = proxyCnt; i < samplerCnt; ++i) {
330 new (&fSamplers[i]) TextureSampler(sk_ref_sp(dupeProxy), filters[proxyCnt - 1]);
331 this->addTextureSampler(&fSamplers[i]);
332 }
333 SkASSERT(caps.integerSupport());
Brian Salomon92be2f72018-06-19 14:33:47 -0400334 fTextureIdx = {"textureIdx", kInt_GrVertexAttribType};
335 ++vertexAttributeCnt;
Brian Salomon336ce7b2017-09-08 08:23:58 -0400336 }
Brian Salomonb80ffee2018-05-23 16:39:39 -0400337 if (domain == Domain::kYes) {
Brian Salomon92be2f72018-06-19 14:33:47 -0400338 fDomain = {"domain", kFloat4_GrVertexAttribType};
339 ++vertexAttributeCnt;
Brian Salomonb80ffee2018-05-23 16:39:39 -0400340 }
Brian Salomon485b8c62018-01-12 15:11:06 -0500341 if (coverageAA) {
Brian Salomon92be2f72018-06-19 14:33:47 -0400342 fAAEdges[0] = {"aaEdge0", kFloat3_GrVertexAttribType};
343 fAAEdges[1] = {"aaEdge1", kFloat3_GrVertexAttribType};
344 fAAEdges[2] = {"aaEdge2", kFloat3_GrVertexAttribType};
345 fAAEdges[3] = {"aaEdge3", kFloat3_GrVertexAttribType};
346 vertexAttributeCnt += 4;
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500347 }
Brian Salomon92be2f72018-06-19 14:33:47 -0400348 this->setVertexAttributeCnt(vertexAttributeCnt);
349 }
350
351 const Attribute& onVertexAttribute(int i) const override {
352 return IthInitializedAttribute(i, fPositions, fColors, fTextureCoords, fTextureIdx, fDomain,
353 fAAEdges[0], fAAEdges[1], fAAEdges[2], fAAEdges[3]);
Brian Salomon34169692017-08-28 15:32:01 -0400354 }
355
356 Attribute fPositions;
Brian Salomon34169692017-08-28 15:32:01 -0400357 Attribute fColors;
Brian Salomon30e1a5e2018-05-18 12:32:32 -0400358 Attribute fTextureCoords;
359 Attribute fTextureIdx;
Brian Salomonb80ffee2018-05-23 16:39:39 -0400360 Attribute fDomain;
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500361 Attribute fAAEdges[4];
Brian Salomon34169692017-08-28 15:32:01 -0400362 sk_sp<GrColorSpaceXform> fColorSpaceXform;
Brian Salomon336ce7b2017-09-08 08:23:58 -0400363 TextureSampler fSamplers[1];
Ethan Nicholasabff9562017-10-09 10:54:08 -0400364
365 typedef GrGeometryProcessor INHERITED;
Brian Salomon34169692017-08-28 15:32:01 -0400366};
367
Brian Salomon6872e942018-05-18 10:29:54 -0400368// This computes the four edge equations for a quad, then outsets them and computes a new quad
369// as the intersection points of the outset edges. 'x' and 'y' contain the original points as input
370// and the outset points as output. 'a', 'b', and 'c' are the edge equation coefficients on output.
371static void compute_quad_edges_and_outset_vertices(Sk4f* x, Sk4f* y, Sk4f* a, Sk4f* b, Sk4f* c) {
372 static constexpr auto fma = SkNx_fma<4, float>;
373 // These rotate the points/edge values either clockwise or counterclockwise assuming tri strip
374 // order.
375 auto nextCW = [](const Sk4f& v) { return SkNx_shuffle<2, 0, 3, 1>(v); };
376 auto nextCCW = [](const Sk4f& v) { return SkNx_shuffle<1, 3, 0, 2>(v); };
377
378 auto xnext = nextCCW(*x);
379 auto ynext = nextCCW(*y);
380 *a = ynext - *y;
381 *b = *x - xnext;
382 *c = fma(xnext, *y, -ynext * *x);
383 Sk4f invNormLengths = (*a * *a + *b * *b).rsqrt();
384 // Make sure the edge equations have their normals facing into the quad in device space.
385 auto test = fma(*a, nextCW(*x), fma(*b, nextCW(*y), *c));
386 if ((test < Sk4f(0)).anyTrue()) {
387 invNormLengths = -invNormLengths;
388 }
389 *a *= invNormLengths;
390 *b *= invNormLengths;
391 *c *= invNormLengths;
392
393 // Here is the outset. This makes our edge equations compute coverage without requiring a
394 // half pixel offset and is also used to compute the bloated quad that will cover all
395 // pixels.
396 *c += Sk4f(0.5f);
397
398 // Reverse the process to compute the points of the bloated quad from the edge equations.
399 // This time the inputs don't have 1s as their third coord and we want to homogenize rather
400 // than normalize.
401 auto anext = nextCW(*a);
402 auto bnext = nextCW(*b);
403 auto cnext = nextCW(*c);
404 *x = fma(bnext, *c, -*b * cnext);
405 *y = fma(*a, cnext, -anext * *c);
406 auto ic = (fma(anext, *b, -bnext * *a)).invert();
407 *x *= ic;
408 *y *= ic;
409}
410
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500411namespace {
412// This is a class soley so it can be partially specialized (functions cannot be).
Brian Salomon86c40012018-05-22 10:48:49 -0400413template <typename V, GrAA AA = V::kAA, typename Position = typename V::Position>
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400414class VertexAAHandler;
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500415
Brian Salomon86c40012018-05-22 10:48:49 -0400416template<typename V> class VertexAAHandler<V, GrAA::kNo, SkPoint> {
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 Salomonbe3c1d22018-05-21 12:54:39 -0400420 SkASSERT((quad.w4f() == Sk4f(1.f)).allTrue());
Brian Salomon86c40012018-05-22 10:48:49 -0400421 SkPointPriv::SetRectTriStrip(&vertices[0].fTextureCoords, texRect, sizeof(V));
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400422 for (int i = 0; i < 4; ++i) {
423 vertices[i].fPosition = {quad.x(i), quad.y(i)};
424 }
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500425 }
426};
427
Brian Salomon86c40012018-05-22 10:48:49 -0400428template<typename V> class VertexAAHandler<V, GrAA::kNo, SkPoint3> {
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500429public:
Brian Salomon86c40012018-05-22 10:48:49 -0400430 static void AssignPositionsAndTexCoords(V* vertices, const GrPerspQuad& quad,
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500431 const SkRect& texRect) {
Brian Salomon86c40012018-05-22 10:48:49 -0400432 SkPointPriv::SetRectTriStrip(&vertices[0].fTextureCoords, texRect, sizeof(V));
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400433 for (int i = 0; i < 4; ++i) {
434 vertices[i].fPosition = quad.point(i);
435 }
436 }
437};
438
Brian Salomon86c40012018-05-22 10:48:49 -0400439template<typename V> class VertexAAHandler<V, GrAA::kYes, SkPoint> {
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400440public:
Brian Salomon86c40012018-05-22 10:48:49 -0400441 static void AssignPositionsAndTexCoords(V* vertices, const GrPerspQuad& quad,
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400442 const SkRect& texRect) {
443 SkASSERT((quad.w4f() == Sk4f(1.f)).allTrue());
Brian Salomon6872e942018-05-18 10:29:54 -0400444 auto x = quad.x4f();
445 auto y = quad.y4f();
446 Sk4f a, b, c;
447 compute_quad_edges_and_outset_vertices(&x, &y, &a, &b, &c);
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500448
449 for (int i = 0; i < 4; ++i) {
Brian Salomon6872e942018-05-18 10:29:54 -0400450 vertices[i].fPosition = {x[i], y[i]};
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500451 for (int j = 0; j < 4; ++j) {
Brian Salomon6872e942018-05-18 10:29:54 -0400452 vertices[i].fEdges[j] = {a[j], b[j], c[j]};
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500453 }
454 }
455
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500456 AssignTexCoords(vertices, quad, texRect);
457 }
458
459private:
Brian Salomon86c40012018-05-22 10:48:49 -0400460 static void AssignTexCoords(V* vertices, const GrPerspQuad& quad, const SkRect& tex) {
Brian Salomona33b67c2018-05-17 10:42:14 -0400461 SkMatrix q = SkMatrix::MakeAll(quad.x(0), quad.x(1), quad.x(2),
462 quad.y(0), quad.y(1), quad.y(2),
463 1.f, 1.f, 1.f);
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500464 SkMatrix qinv;
465 if (!q.invert(&qinv)) {
466 return;
467 }
468 SkMatrix t = SkMatrix::MakeAll(tex.fLeft, tex.fLeft, tex.fRight,
469 tex.fTop, tex.fBottom, tex.fTop,
470 1.f, 1.f, 1.f);
471 SkMatrix map;
472 map.setConcat(t, qinv);
Brian Salomon86c40012018-05-22 10:48:49 -0400473 SkMatrixPriv::MapPointsWithStride(map, &vertices[0].fTextureCoords, sizeof(V),
474 &vertices[0].fPosition, sizeof(V), 4);
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500475 }
476};
477
Brian Salomon86c40012018-05-22 10:48:49 -0400478template<typename V> class VertexAAHandler<V, GrAA::kYes, SkPoint3> {
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400479public:
Brian Salomon86c40012018-05-22 10:48:49 -0400480 static void AssignPositionsAndTexCoords(V* vertices, const GrPerspQuad& quad,
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400481 const SkRect& texRect) {
482 auto x = quad.x4f();
483 auto y = quad.y4f();
484 auto iw = quad.iw4f();
485 x *= iw;
486 y *= iw;
487
488 // Get an equation for w from device space coords.
489 SkMatrix P;
490 P.setAll(x[0], y[0], 1, x[1], y[1], 1, x[2], y[2], 1);
491 SkAssertResult(P.invert(&P));
492 SkPoint3 weq{quad.w(0), quad.w(1), quad.w(2)};
493 P.mapHomogeneousPoints(&weq, &weq, 1);
494
495 Sk4f a, b, c;
496 compute_quad_edges_and_outset_vertices(&x, &y, &a, &b, &c);
497
498 // Compute new w values for the output vertices;
499 auto w = Sk4f(weq.fX) * x + Sk4f(weq.fY) * y + Sk4f(weq.fZ);
500 x *= w;
501 y *= w;
502
503 for (int i = 0; i < 4; ++i) {
504 vertices[i].fPosition = {x[i], y[i], w[i]};
505 for (int j = 0; j < 4; ++j) {
506 vertices[i].fEdges[j] = {a[j], b[j], c[j]};
507 }
508 }
509
510 AssignTexCoords(vertices, quad, texRect);
511 }
512
513private:
Brian Salomon86c40012018-05-22 10:48:49 -0400514 static void AssignTexCoords(V* vertices, const GrPerspQuad& quad, const SkRect& tex) {
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400515 SkMatrix q = SkMatrix::MakeAll(quad.x(0), quad.x(1), quad.x(2),
516 quad.y(0), quad.y(1), quad.y(2),
517 quad.w(0), quad.w(1), quad.w(2));
518 SkMatrix qinv;
519 if (!q.invert(&qinv)) {
520 return;
521 }
522 SkMatrix t = SkMatrix::MakeAll(tex.fLeft, tex.fLeft, tex.fRight,
523 tex.fTop, tex.fBottom, tex.fTop,
524 1.f, 1.f, 1.f);
525 SkMatrix map;
526 map.setConcat(t, qinv);
527 SkPoint3 tempTexCoords[4];
528 SkMatrixPriv::MapHomogeneousPointsWithStride(map, tempTexCoords, sizeof(SkPoint3),
Brian Salomon86c40012018-05-22 10:48:49 -0400529 &vertices[0].fPosition, sizeof(V), 4);
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400530 for (int i = 0; i < 4; ++i) {
531 auto invW = 1.f / tempTexCoords[i].fZ;
532 vertices[i].fTextureCoords.fX = tempTexCoords[i].fX * invW;
533 vertices[i].fTextureCoords.fY = tempTexCoords[i].fY * invW;
534 }
535 }
536};
537
Brian Salomon17031a72018-05-22 14:14:07 -0400538template <typename V, MultiTexture MT = V::kMultiTexture> struct TexIdAssigner;
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500539
Brian Salomon17031a72018-05-22 14:14:07 -0400540template <typename V> struct TexIdAssigner<V, MultiTexture::kYes> {
Brian Salomon86c40012018-05-22 10:48:49 -0400541 static void Assign(V* vertices, int textureIdx) {
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400542 for (int i = 0; i < 4; ++i) {
543 vertices[i].fTextureIdx = textureIdx;
544 }
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500545 }
546};
547
Brian Salomon17031a72018-05-22 14:14:07 -0400548template <typename V> struct TexIdAssigner<V, MultiTexture::kNo> {
Brian Salomon86c40012018-05-22 10:48:49 -0400549 static void Assign(V* vertices, int textureIdx) {}
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500550};
Brian Salomonb80ffee2018-05-23 16:39:39 -0400551
552template <typename V, Domain D = V::kDomain> struct DomainAssigner;
553
554template <typename V> struct DomainAssigner<V, Domain::kYes> {
555 static void Assign(V* vertices, Domain domain, GrSamplerState::Filter filter,
556 const SkRect& srcRect, GrSurfaceOrigin origin, float iw, float ih) {
557 static constexpr SkRect kLargeRect = {-2, -2, 2, 2};
558 SkRect domainRect;
559 if (domain == Domain::kYes) {
560 auto ltrb = Sk4f::Load(&srcRect);
561 if (filter == GrSamplerState::Filter::kBilerp) {
562 auto rblt = SkNx_shuffle<2, 3, 0, 1>(ltrb);
563 auto whwh = (rblt - ltrb).abs();
564 auto c = (rblt + ltrb) * 0.5f;
565 static const Sk4f kOffsets = {0.5f, 0.5f, -0.5f, -0.5f};
566 ltrb = (whwh < 1.f).thenElse(c, ltrb + kOffsets);
567 }
568 ltrb *= Sk4f(iw, ih, iw, ih);
569 if (origin == kBottomLeft_GrSurfaceOrigin) {
570 static const Sk4f kMul = {1.f, -1.f, 1.f, -1.f};
571 static const Sk4f kAdd = {0.f, 1.f, 0.f, 1.f};
572 ltrb = SkNx_shuffle<0, 3, 2, 1>(kMul * ltrb + kAdd);
573 }
574 ltrb.store(&domainRect);
575 } else {
576 domainRect = kLargeRect;
577 }
578 for (int i = 0; i < 4; ++i) {
579 vertices[i].fTextureDomain = domainRect;
580 }
581 }
582};
583
584template <typename V> struct DomainAssigner<V, Domain::kNo> {
585 static void Assign(V*, Domain domain, GrSamplerState::Filter, const SkRect&, GrSurfaceOrigin,
586 float iw, float ih) {
587 SkASSERT(domain == Domain::kNo);
588 }
589};
590
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500591} // anonymous namespace
592
Brian Salomon86c40012018-05-22 10:48:49 -0400593template <typename V>
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400594static void tessellate_quad(const GrPerspQuad& devQuad, const SkRect& srcRect, GrColor color,
Brian Salomonb80ffee2018-05-23 16:39:39 -0400595 GrSurfaceOrigin origin, GrSamplerState::Filter filter, V* vertices,
596 SkScalar iw, SkScalar ih, int textureIdx, Domain domain) {
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500597 SkRect texRect = {
598 iw * srcRect.fLeft,
599 ih * srcRect.fTop,
600 iw * srcRect.fRight,
601 ih * srcRect.fBottom
602 };
603 if (origin == kBottomLeft_GrSurfaceOrigin) {
604 texRect.fTop = 1.f - texRect.fTop;
605 texRect.fBottom = 1.f - texRect.fBottom;
606 }
Brian Salomon86c40012018-05-22 10:48:49 -0400607 VertexAAHandler<V>::AssignPositionsAndTexCoords(vertices, devQuad, texRect);
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500608 vertices[0].fColor = color;
609 vertices[1].fColor = color;
610 vertices[2].fColor = color;
611 vertices[3].fColor = color;
Brian Salomon86c40012018-05-22 10:48:49 -0400612 TexIdAssigner<V>::Assign(vertices, textureIdx);
Brian Salomonb80ffee2018-05-23 16:39:39 -0400613 DomainAssigner<V>::Assign(vertices, domain, filter, srcRect, origin, iw, ih);
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500614}
Brian Salomon17031a72018-05-22 14:14:07 -0400615
Brian Salomon34169692017-08-28 15:32:01 -0400616/**
617 * Op that implements GrTextureOp::Make. It draws textured quads. Each quad can modulate against a
618 * the texture by color. The blend with the destination is always src-over. The edges are non-AA.
619 */
620class TextureOp final : public GrMeshDrawOp {
621public:
Robert Phillips7c525e62018-06-12 10:11:12 -0400622 static std::unique_ptr<GrDrawOp> Make(GrContext* context,
623 sk_sp<GrTextureProxy> proxy,
624 GrSamplerState::Filter filter,
625 GrColor color,
626 const SkRect& srcRect,
627 const SkRect& dstRect,
628 GrAAType aaType,
629 SkCanvas::SrcRectConstraint constraint,
Brian Osman2b23c4b2018-06-01 12:25:08 -0400630 const SkMatrix& viewMatrix,
631 sk_sp<GrColorSpaceXform> csxf) {
Robert Phillipsc994a932018-06-19 13:09:54 -0400632 GrOpMemoryPool* pool = context->contextPriv().opMemoryPool();
633
634 return pool->allocate<TextureOp>(std::move(proxy), filter, color,
635 srcRect, dstRect, aaType, constraint,
636 viewMatrix, std::move(csxf));
Brian Salomon34169692017-08-28 15:32:01 -0400637 }
638
Brian Salomon336ce7b2017-09-08 08:23:58 -0400639 ~TextureOp() override {
640 if (fFinalized) {
641 auto proxies = this->proxies();
642 for (int i = 0; i < fProxyCnt; ++i) {
643 proxies[i]->completedRead();
644 }
645 if (fProxyCnt > 1) {
646 delete[] reinterpret_cast<const char*>(proxies);
647 }
648 } else {
649 SkASSERT(1 == fProxyCnt);
650 fProxy0->unref();
651 }
652 }
Brian Salomon34169692017-08-28 15:32:01 -0400653
654 const char* name() const override { return "TextureOp"; }
655
Robert Phillipsf1748f52017-09-14 14:11:24 -0400656 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillipsb493eeb2017-09-13 13:10:52 -0400657 auto proxies = this->proxies();
658 for (int i = 0; i < fProxyCnt; ++i) {
659 func(proxies[i]);
660 }
661 }
662
Brian Salomon34169692017-08-28 15:32:01 -0400663 SkString dumpInfo() const override {
664 SkString str;
Brian Salomon34169692017-08-28 15:32:01 -0400665 str.appendf("# draws: %d\n", fDraws.count());
Brian Salomon336ce7b2017-09-08 08:23:58 -0400666 auto proxies = this->proxies();
667 for (int i = 0; i < fProxyCnt; ++i) {
668 str.appendf("Proxy ID %d: %d, Filter: %d\n", i, proxies[i]->uniqueID().asUInt(),
669 static_cast<int>(this->filters()[i]));
670 }
Brian Salomon34169692017-08-28 15:32:01 -0400671 for (int i = 0; i < fDraws.count(); ++i) {
672 const Draw& draw = fDraws[i];
673 str.appendf(
Brian Salomon336ce7b2017-09-08 08:23:58 -0400674 "%d: Color: 0x%08x, ProxyIdx: %d, TexRect [L: %.2f, T: %.2f, R: %.2f, B: %.2f] "
675 "Quad [(%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f)]\n",
Brian Salomonb80ffee2018-05-23 16:39:39 -0400676 i, draw.color(), draw.textureIdx(), draw.srcRect().fLeft, draw.srcRect().fTop,
677 draw.srcRect().fRight, draw.srcRect().fBottom, draw.quad().point(0).fX,
678 draw.quad().point(0).fY, draw.quad().point(1).fX, draw.quad().point(1).fY,
679 draw.quad().point(2).fX, draw.quad().point(2).fY, draw.quad().point(3).fX,
680 draw.quad().point(3).fY);
Brian Salomon34169692017-08-28 15:32:01 -0400681 }
682 str += INHERITED::dumpInfo();
683 return str;
684 }
685
Brian Osman532b3f92018-07-11 10:02:07 -0400686 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip) override {
Brian Salomon34169692017-08-28 15:32:01 -0400687 SkASSERT(!fFinalized);
Brian Salomon336ce7b2017-09-08 08:23:58 -0400688 SkASSERT(1 == fProxyCnt);
Brian Salomon34169692017-08-28 15:32:01 -0400689 fFinalized = true;
Brian Salomon336ce7b2017-09-08 08:23:58 -0400690 fProxy0->addPendingRead();
691 fProxy0->unref();
Brian Salomon34169692017-08-28 15:32:01 -0400692 return RequiresDstTexture::kNo;
693 }
694
Brian Salomon485b8c62018-01-12 15:11:06 -0500695 FixedFunctionFlags fixedFunctionFlags() const override {
696 return this->aaType() == GrAAType::kMSAA ? FixedFunctionFlags::kUsesHWAA
697 : FixedFunctionFlags::kNone;
698 }
Brian Salomon34169692017-08-28 15:32:01 -0400699
700 DEFINE_OP_CLASS_ID
701
702private:
Robert Phillips7c525e62018-06-12 10:11:12 -0400703 friend class ::GrOpMemoryPool;
Brian Salomon762d5e72017-12-01 10:25:08 -0500704
705 // This is used in a heursitic for choosing a code path. We don't care what happens with
706 // really large rects, infs, nans, etc.
707#if defined(__clang__) && (__clang_major__ * 1000 + __clang_minor__) >= 3007
708__attribute__((no_sanitize("float-cast-overflow")))
709#endif
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500710 size_t RectSizeAsSizeT(const SkRect& rect) {;
Brian Salomon762d5e72017-12-01 10:25:08 -0500711 return static_cast<size_t>(SkTMax(rect.width(), 1.f) * SkTMax(rect.height(), 1.f));
712 }
713
Brian Salomon336ce7b2017-09-08 08:23:58 -0400714 static constexpr int kMaxTextures = TextureGeometryProcessor::kMaxTextures;
715
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400716 TextureOp(sk_sp<GrTextureProxy> proxy, GrSamplerState::Filter filter, GrColor color,
Brian Salomon485b8c62018-01-12 15:11:06 -0500717 const SkRect& srcRect, const SkRect& dstRect, GrAAType aaType,
Brian Salomonb80ffee2018-05-23 16:39:39 -0400718 SkCanvas::SrcRectConstraint constraint, const SkMatrix& viewMatrix,
Brian Osman2b23c4b2018-06-01 12:25:08 -0400719 sk_sp<GrColorSpaceXform> csxf)
Brian Salomon34169692017-08-28 15:32:01 -0400720 : INHERITED(ClassID())
Brian Salomon34169692017-08-28 15:32:01 -0400721 , fColorSpaceXform(std::move(csxf))
Brian Salomon336ce7b2017-09-08 08:23:58 -0400722 , fProxy0(proxy.release())
723 , fFilter0(filter)
724 , fProxyCnt(1)
Brian Salomon485b8c62018-01-12 15:11:06 -0500725 , fAAType(static_cast<unsigned>(aaType))
Brian Osman2b23c4b2018-06-01 12:25:08 -0400726 , fFinalized(0) {
Brian Salomon485b8c62018-01-12 15:11:06 -0500727 SkASSERT(aaType != GrAAType::kMixedSamples);
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400728 fPerspective = viewMatrix.hasPerspective();
Brian Salomon594b64c2018-05-29 12:47:57 -0400729 auto quad = GrPerspQuad(dstRect, viewMatrix);
730 auto bounds = quad.bounds();
731#ifndef SK_DONT_DROP_UNNECESSARY_AA_IN_TEXTURE_OP
732 if (GrAAType::kCoverage == this->aaType() && viewMatrix.rectStaysRect()) {
733 // Disable coverage AA when rect falls on integers in device space.
734 auto is_int = [](float f) { return f == sk_float_floor(f); };
735 if (is_int(bounds.fLeft) && is_int(bounds.fTop) && is_int(bounds.fRight) &&
736 is_int(bounds.fBottom)) {
737 fAAType = static_cast<unsigned>(GrAAType::kNone);
738 // We may have had a strict constraint with nearest filter soley due to possible AA
739 // bloat. In that case it's no longer necessary.
740 if (constraint == SkCanvas::kStrict_SrcRectConstraint &&
741 filter == GrSamplerState::Filter::kNearest) {
742 constraint = SkCanvas::kFast_SrcRectConstraint;
743 }
744 }
745 }
746#endif
747 const auto& draw = fDraws.emplace_back(srcRect, 0, quad, constraint, color);
Brian Salomon34169692017-08-28 15:32:01 -0400748 this->setBounds(bounds, HasAABloat::kNo, IsZeroArea::kNo);
Brian Salomon594b64c2018-05-29 12:47:57 -0400749 fDomain = static_cast<bool>(draw.domain());
Brian Salomon762d5e72017-12-01 10:25:08 -0500750 fMaxApproxDstPixelArea = RectSizeAsSizeT(bounds);
Brian Salomon34169692017-08-28 15:32:01 -0400751 }
752
Brian Salomonb80ffee2018-05-23 16:39:39 -0400753 template <typename Pos, MultiTexture MT, Domain D, GrAA AA>
Brian Salomon17031a72018-05-22 14:14:07 -0400754 void tess(void* v, const float iw[], const float ih[], const GrGeometryProcessor* gp) {
Brian Salomonb80ffee2018-05-23 16:39:39 -0400755 using Vertex = TextureGeometryProcessor::Vertex<Pos, MT, D, AA>;
Brian Salomon92be2f72018-06-19 14:33:47 -0400756 SkASSERT(gp->debugOnly_vertexStride() == sizeof(Vertex));
Brian Salomon17031a72018-05-22 14:14:07 -0400757 auto vertices = static_cast<Vertex*>(v);
758 auto proxies = this->proxies();
Brian Salomonb80ffee2018-05-23 16:39:39 -0400759 auto filters = this->filters();
Brian Salomon17031a72018-05-22 14:14:07 -0400760 for (const auto& draw : fDraws) {
Brian Salomonb80ffee2018-05-23 16:39:39 -0400761 auto textureIdx = draw.textureIdx();
762 auto origin = proxies[textureIdx]->origin();
763 tessellate_quad<Vertex>(draw.quad(), draw.srcRect(), draw.color(), origin,
764 filters[textureIdx], vertices, iw[textureIdx], ih[textureIdx],
765 textureIdx, draw.domain());
Brian Salomon17031a72018-05-22 14:14:07 -0400766 vertices += 4;
767 }
768 }
769
Brian Salomon34169692017-08-28 15:32:01 -0400770 void onPrepareDraws(Target* target) override {
Brian Salomon336ce7b2017-09-08 08:23:58 -0400771 sk_sp<GrTextureProxy> proxiesSPs[kMaxTextures];
772 auto proxies = this->proxies();
773 auto filters = this->filters();
774 for (int i = 0; i < fProxyCnt; ++i) {
775 if (!proxies[i]->instantiate(target->resourceProvider())) {
776 return;
777 }
778 proxiesSPs[i] = sk_ref_sp(proxies[i]);
Brian Salomon34169692017-08-28 15:32:01 -0400779 }
Brian Salomon336ce7b2017-09-08 08:23:58 -0400780
Brian Salomonb80ffee2018-05-23 16:39:39 -0400781 Domain domain = fDomain ? Domain::kYes : Domain::kNo;
Brian Salomon485b8c62018-01-12 15:11:06 -0500782 bool coverageAA = GrAAType::kCoverage == this->aaType();
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400783 sk_sp<GrGeometryProcessor> gp = TextureGeometryProcessor::Make(
784 proxiesSPs, fProxyCnt, std::move(fColorSpaceXform), coverageAA, fPerspective,
Brian Salomonb80ffee2018-05-23 16:39:39 -0400785 domain, filters, *target->caps().shaderCaps());
Brian Salomon34169692017-08-28 15:32:01 -0400786 GrPipeline::InitArgs args;
787 args.fProxy = target->proxy();
788 args.fCaps = &target->caps();
789 args.fResourceProvider = target->resourceProvider();
Brian Salomon485b8c62018-01-12 15:11:06 -0500790 args.fFlags = 0;
Brian Salomon485b8c62018-01-12 15:11:06 -0500791 if (GrAAType::kMSAA == this->aaType()) {
792 args.fFlags |= GrPipeline::kHWAntialias_Flag;
793 }
794
Brian Salomon49348902018-06-26 09:12:38 -0400795 auto clip = target->detachAppliedClip();
796 const auto* fixedDynamicState = target->allocFixedDynamicState(clip.scissorState().rect());
797 const auto* pipeline =
798 target->allocPipeline(args, GrProcessorSet::MakeEmptySet(), std::move(clip));
Brian Salomon92be2f72018-06-19 14:33:47 -0400799 using TessFn =
800 decltype(&TextureOp::tess<SkPoint, MultiTexture::kNo, Domain::kNo, GrAA::kNo>);
801#define TESS_FN_AND_VERTEX_SIZE(Point, MT, Domain, AA) \
802 { \
803 &TextureOp::tess<Point, MT, Domain, AA>, \
804 sizeof(TextureGeometryProcessor::Vertex<Point, MT, Domain, AA>) \
805 }
806 static constexpr struct {
807 TessFn fTessFn;
808 size_t fVertexSize;
809 } kTessFnsAndVertexSizes[] = {
810 TESS_FN_AND_VERTEX_SIZE(SkPoint, MultiTexture::kNo, Domain::kNo, GrAA::kNo),
811 TESS_FN_AND_VERTEX_SIZE(SkPoint, MultiTexture::kNo, Domain::kNo, GrAA::kYes),
812 TESS_FN_AND_VERTEX_SIZE(SkPoint, MultiTexture::kNo, Domain::kYes, GrAA::kNo),
813 TESS_FN_AND_VERTEX_SIZE(SkPoint, MultiTexture::kNo, Domain::kYes, GrAA::kYes),
814 TESS_FN_AND_VERTEX_SIZE(SkPoint, MultiTexture::kYes, Domain::kNo, GrAA::kNo),
815 TESS_FN_AND_VERTEX_SIZE(SkPoint, MultiTexture::kYes, Domain::kNo, GrAA::kYes),
816 TESS_FN_AND_VERTEX_SIZE(SkPoint, MultiTexture::kYes, Domain::kYes, GrAA::kNo),
817 TESS_FN_AND_VERTEX_SIZE(SkPoint, MultiTexture::kYes, Domain::kYes, GrAA::kYes),
818 TESS_FN_AND_VERTEX_SIZE(SkPoint3, MultiTexture::kNo, Domain::kNo, GrAA::kNo),
819 TESS_FN_AND_VERTEX_SIZE(SkPoint3, MultiTexture::kNo, Domain::kNo, GrAA::kYes),
820 TESS_FN_AND_VERTEX_SIZE(SkPoint3, MultiTexture::kNo, Domain::kYes, GrAA::kNo),
821 TESS_FN_AND_VERTEX_SIZE(SkPoint3, MultiTexture::kNo, Domain::kYes, GrAA::kYes),
822 TESS_FN_AND_VERTEX_SIZE(SkPoint3, MultiTexture::kYes, Domain::kNo, GrAA::kNo),
823 TESS_FN_AND_VERTEX_SIZE(SkPoint3, MultiTexture::kYes, Domain::kNo, GrAA::kYes),
824 TESS_FN_AND_VERTEX_SIZE(SkPoint3, MultiTexture::kYes, Domain::kYes, GrAA::kNo),
825 TESS_FN_AND_VERTEX_SIZE(SkPoint3, MultiTexture::kYes, Domain::kYes, GrAA::kYes),
826 };
827#undef TESS_FN_AND_VERTEX_SIZE
828 int tessFnIdx = 0;
829 tessFnIdx |= coverageAA ? 0x1 : 0x0;
830 tessFnIdx |= fDomain ? 0x2 : 0x0;
831 tessFnIdx |= (fProxyCnt > 1) ? 0x4 : 0x0;
832 tessFnIdx |= fPerspective ? 0x8 : 0x0;
833
834 SkASSERT(kTessFnsAndVertexSizes[tessFnIdx].fVertexSize == gp->debugOnly_vertexStride());
835
Brian Salomon34169692017-08-28 15:32:01 -0400836 int vstart;
837 const GrBuffer* vbuffer;
Brian Salomon92be2f72018-06-19 14:33:47 -0400838 void* vdata = target->makeVertexSpace(kTessFnsAndVertexSizes[tessFnIdx].fVertexSize,
839 4 * fDraws.count(), &vbuffer, &vstart);
Brian Salomon336ce7b2017-09-08 08:23:58 -0400840 if (!vdata) {
Brian Salomon34169692017-08-28 15:32:01 -0400841 SkDebugf("Could not allocate vertices\n");
842 return;
843 }
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400844
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400845 float iw[kMaxTextures];
846 float ih[kMaxTextures];
847 for (int t = 0; t < fProxyCnt; ++t) {
848 const auto* texture = proxies[t]->priv().peekTexture();
849 iw[t] = 1.f / texture->width();
850 ih[t] = 1.f / texture->height();
851 }
852
Brian Salomon92be2f72018-06-19 14:33:47 -0400853 (this->*(kTessFnsAndVertexSizes[tessFnIdx].fTessFn))(vdata, iw, ih, gp.get());
Brian Salomonb80ffee2018-05-23 16:39:39 -0400854
Brian Salomon57caa662017-10-18 12:21:05 +0000855 GrPrimitiveType primitiveType =
856 fDraws.count() > 1 ? GrPrimitiveType::kTriangles : GrPrimitiveType::kTriangleStrip;
857 GrMesh mesh(primitiveType);
Brian Salomon34169692017-08-28 15:32:01 -0400858 if (fDraws.count() > 1) {
Brian Salomon57caa662017-10-18 12:21:05 +0000859 sk_sp<const GrBuffer> ibuffer = target->resourceProvider()->refQuadIndexBuffer();
Brian Salomon34169692017-08-28 15:32:01 -0400860 if (!ibuffer) {
861 SkDebugf("Could not allocate quad indices\n");
862 return;
863 }
Brian Salomon34169692017-08-28 15:32:01 -0400864 mesh.setIndexedPatterned(ibuffer.get(), 6, 4, fDraws.count(),
865 GrResourceProvider::QuadCountOfQuadBuffer());
Brian Salomon34169692017-08-28 15:32:01 -0400866 } else {
Brian Salomon34169692017-08-28 15:32:01 -0400867 mesh.setNonIndexedNonInstanced(4);
Brian Salomon34169692017-08-28 15:32:01 -0400868 }
Brian Salomon57caa662017-10-18 12:21:05 +0000869 mesh.setVertexData(vbuffer, vstart);
Brian Salomon49348902018-06-26 09:12:38 -0400870 target->draw(gp.get(), pipeline, fixedDynamicState, mesh);
Brian Salomon34169692017-08-28 15:32:01 -0400871 }
872
873 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
874 const auto* that = t->cast<TextureOp>();
Brian Salomon762d5e72017-12-01 10:25:08 -0500875 const auto& shaderCaps = *caps.shaderCaps();
Brian Salomon336ce7b2017-09-08 08:23:58 -0400876 if (!GrColorSpaceXform::Equals(fColorSpaceXform.get(), that->fColorSpaceXform.get())) {
Brian Salomon34169692017-08-28 15:32:01 -0400877 return false;
878 }
Brian Salomon485b8c62018-01-12 15:11:06 -0500879 if (this->aaType() != that->aaType()) {
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500880 return false;
881 }
Brian Salomon336ce7b2017-09-08 08:23:58 -0400882 // Because of an issue where GrColorSpaceXform adds the same function every time it is used
883 // in a texture lookup, we only allow multiple textures when there is no transform.
Brian Salomon762d5e72017-12-01 10:25:08 -0500884 if (TextureGeometryProcessor::SupportsMultitexture(shaderCaps) && !fColorSpaceXform &&
885 fMaxApproxDstPixelArea <= shaderCaps.disableImageMultitexturingDstRectAreaThreshold() &&
886 that->fMaxApproxDstPixelArea <=
887 shaderCaps.disableImageMultitexturingDstRectAreaThreshold()) {
Brian Salomon336ce7b2017-09-08 08:23:58 -0400888 int map[kMaxTextures];
Brian Salomon762d5e72017-12-01 10:25:08 -0500889 int numNewProxies = this->mergeProxies(that, map, shaderCaps);
Brian Salomon336ce7b2017-09-08 08:23:58 -0400890 if (numNewProxies < 0) {
891 return false;
892 }
893 if (1 == fProxyCnt && numNewProxies) {
894 void* mem = new char[(sizeof(GrSamplerState::Filter) + sizeof(GrTextureProxy*)) *
895 kMaxTextures];
896 auto proxies = reinterpret_cast<GrTextureProxy**>(mem);
897 auto filters = reinterpret_cast<GrSamplerState::Filter*>(proxies + kMaxTextures);
898 proxies[0] = fProxy0;
899 filters[0] = fFilter0;
900 fProxyArray = proxies;
901 }
902 fProxyCnt += numNewProxies;
903 auto thisProxies = fProxyArray;
904 auto thatProxies = that->proxies();
905 auto thatFilters = that->filters();
906 auto thisFilters = reinterpret_cast<GrSamplerState::Filter*>(thisProxies +
907 kMaxTextures);
908 for (int i = 0; i < that->fProxyCnt; ++i) {
909 if (map[i] < 0) {
910 thatProxies[i]->addPendingRead();
Robert Phillipsb493eeb2017-09-13 13:10:52 -0400911
Brian Salomon336ce7b2017-09-08 08:23:58 -0400912 thisProxies[-map[i]] = thatProxies[i];
913 thisFilters[-map[i]] = thatFilters[i];
914 map[i] = -map[i];
915 }
916 }
917 int firstNewDraw = fDraws.count();
918 fDraws.push_back_n(that->fDraws.count(), that->fDraws.begin());
919 for (int i = firstNewDraw; i < fDraws.count(); ++i) {
Brian Salomonb80ffee2018-05-23 16:39:39 -0400920 fDraws[i].setTextureIdx(map[fDraws[i].textureIdx()]);
Brian Salomon336ce7b2017-09-08 08:23:58 -0400921 }
922 } else {
Brian Salomonbbf05752017-11-30 11:30:48 -0500923 // We can get here when one of the ops is already multitextured but the other cannot
924 // be because of the dst rect size.
925 if (fProxyCnt > 1 || that->fProxyCnt > 1) {
926 return false;
927 }
Brian Salomon336ce7b2017-09-08 08:23:58 -0400928 if (fProxy0->uniqueID() != that->fProxy0->uniqueID() || fFilter0 != that->fFilter0) {
929 return false;
930 }
931 fDraws.push_back_n(that->fDraws.count(), that->fDraws.begin());
932 }
Brian Salomon34169692017-08-28 15:32:01 -0400933 this->joinBounds(*that);
Brian Salomon762d5e72017-12-01 10:25:08 -0500934 fMaxApproxDstPixelArea = SkTMax(that->fMaxApproxDstPixelArea, fMaxApproxDstPixelArea);
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400935 fPerspective |= that->fPerspective;
Brian Salomonb80ffee2018-05-23 16:39:39 -0400936 fDomain |= that->fDomain;
Brian Salomon34169692017-08-28 15:32:01 -0400937 return true;
938 }
939
Brian Salomon336ce7b2017-09-08 08:23:58 -0400940 /**
941 * Determines a mapping of indices from that's proxy array to this's proxy array. A negative map
942 * value means that's proxy should be added to this's proxy array at the absolute value of
943 * the map entry. If it is determined that the ops shouldn't combine their proxies then a
944 * negative value is returned. Otherwise, return value indicates the number of proxies that have
945 * to be added to this op or, equivalently, the number of negative entries in map.
946 */
947 int mergeProxies(const TextureOp* that, int map[kMaxTextures], const GrShaderCaps& caps) const {
948 std::fill_n(map, kMaxTextures, -kMaxTextures);
949 int sharedProxyCnt = 0;
950 auto thisProxies = this->proxies();
951 auto thisFilters = this->filters();
952 auto thatProxies = that->proxies();
953 auto thatFilters = that->filters();
954 for (int i = 0; i < fProxyCnt; ++i) {
955 for (int j = 0; j < that->fProxyCnt; ++j) {
956 if (thisProxies[i]->uniqueID() == thatProxies[j]->uniqueID()) {
957 if (thisFilters[i] != thatFilters[j]) {
958 // In GL we don't currently support using the same texture with different
959 // samplers. If we added support for sampler objects and a cap bit to know
960 // it's ok to use different filter modes then we could support this.
961 // Otherwise, we could also only allow a single filter mode for each op
962 // instance.
963 return -1;
964 }
965 map[j] = i;
966 ++sharedProxyCnt;
967 break;
968 }
969 }
970 }
Brian Salomon2b6f6142017-11-13 11:49:13 -0500971 int actualMaxTextures = SkTMin(caps.maxFragmentSamplers(), kMaxTextures);
Brian Salomon336ce7b2017-09-08 08:23:58 -0400972 int newProxyCnt = that->fProxyCnt - sharedProxyCnt;
973 if (newProxyCnt + fProxyCnt > actualMaxTextures) {
974 return -1;
975 }
976 GrPixelConfig config = thisProxies[0]->config();
977 int nextSlot = fProxyCnt;
978 for (int j = 0; j < that->fProxyCnt; ++j) {
979 // We want to avoid making many shaders because of different permutations of shader
980 // based swizzle and sampler types. The approach taken here is to require the configs to
981 // be the same and to only allow already instantiated proxies that have the most
982 // common sampler type. Otherwise we don't merge.
983 if (thatProxies[j]->config() != config) {
984 return -1;
985 }
986 if (GrTexture* tex = thatProxies[j]->priv().peekTexture()) {
987 if (tex->texturePriv().samplerType() != kTexture2DSampler_GrSLType) {
988 return -1;
989 }
990 }
991 if (map[j] < 0) {
992 map[j] = -(nextSlot++);
993 }
994 }
995 return newProxyCnt;
996 }
997
Brian Salomon485b8c62018-01-12 15:11:06 -0500998 GrAAType aaType() const { return static_cast<GrAAType>(fAAType); }
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500999
Brian Salomon336ce7b2017-09-08 08:23:58 -04001000 GrTextureProxy* const* proxies() const { return fProxyCnt > 1 ? fProxyArray : &fProxy0; }
1001
1002 const GrSamplerState::Filter* filters() const {
1003 if (fProxyCnt > 1) {
1004 return reinterpret_cast<const GrSamplerState::Filter*>(fProxyArray + kMaxTextures);
1005 }
1006 return &fFilter0;
1007 }
1008
Brian Salomonb80ffee2018-05-23 16:39:39 -04001009 class Draw {
1010 public:
1011 Draw(const SkRect& srcRect, int textureIdx, const GrPerspQuad& quad,
1012 SkCanvas::SrcRectConstraint constraint, GrColor color)
1013 : fSrcRect(srcRect)
1014 , fHasDomain(constraint == SkCanvas::kStrict_SrcRectConstraint)
1015 , fTextureIdx(SkToUInt(textureIdx))
1016 , fQuad(quad)
1017 , fColor(color) {}
1018 const GrPerspQuad& quad() const { return fQuad; }
1019 int textureIdx() const { return SkToInt(fTextureIdx); }
1020 const SkRect& srcRect() const { return fSrcRect; }
1021 GrColor color() const { return fColor; }
1022 Domain domain() const { return Domain(fHasDomain); }
1023 void setTextureIdx(int i) { fTextureIdx = SkToUInt(i); }
1024
1025 private:
Brian Salomon34169692017-08-28 15:32:01 -04001026 SkRect fSrcRect;
Brian Salomonb80ffee2018-05-23 16:39:39 -04001027 unsigned fHasDomain : 1;
1028 unsigned fTextureIdx : 31;
Brian Salomonbe3c1d22018-05-21 12:54:39 -04001029 GrPerspQuad fQuad;
Brian Salomon34169692017-08-28 15:32:01 -04001030 GrColor fColor;
1031 };
1032 SkSTArray<1, Draw, true> fDraws;
Brian Salomon34169692017-08-28 15:32:01 -04001033 sk_sp<GrColorSpaceXform> fColorSpaceXform;
Brian Salomon336ce7b2017-09-08 08:23:58 -04001034 // Initially we store a single proxy ptr and a single filter. If we grow to have more than
1035 // one proxy we instead store pointers to dynamically allocated arrays of size kMaxTextures
1036 // followed by kMaxTextures filters.
1037 union {
1038 GrTextureProxy* fProxy0;
1039 GrTextureProxy** fProxyArray;
1040 };
Brian Salomonbbf05752017-11-30 11:30:48 -05001041 size_t fMaxApproxDstPixelArea;
Brian Salomon336ce7b2017-09-08 08:23:58 -04001042 GrSamplerState::Filter fFilter0;
1043 uint8_t fProxyCnt;
Brian Salomon485b8c62018-01-12 15:11:06 -05001044 unsigned fAAType : 2;
Brian Salomonbe3c1d22018-05-21 12:54:39 -04001045 unsigned fPerspective : 1;
Brian Salomonb80ffee2018-05-23 16:39:39 -04001046 unsigned fDomain : 1;
Brian Salomon34169692017-08-28 15:32:01 -04001047 // Used to track whether fProxy is ref'ed or has a pending IO after finalize() is called.
Brian Salomonb5ef1f92018-01-11 11:46:21 -05001048 unsigned fFinalized : 1;
Brian Salomon336ce7b2017-09-08 08:23:58 -04001049
Brian Salomon34169692017-08-28 15:32:01 -04001050 typedef GrMeshDrawOp INHERITED;
1051};
1052
Brian Salomon336ce7b2017-09-08 08:23:58 -04001053constexpr int TextureGeometryProcessor::kMaxTextures;
1054constexpr int TextureOp::kMaxTextures;
1055
Brian Salomon34169692017-08-28 15:32:01 -04001056} // anonymous namespace
1057
1058namespace GrTextureOp {
1059
Robert Phillips7c525e62018-06-12 10:11:12 -04001060std::unique_ptr<GrDrawOp> Make(GrContext* context,
1061 sk_sp<GrTextureProxy> proxy,
1062 GrSamplerState::Filter filter,
1063 GrColor color,
1064 const SkRect& srcRect,
1065 const SkRect& dstRect,
1066 GrAAType aaType,
1067 SkCanvas::SrcRectConstraint constraint,
1068 const SkMatrix& viewMatrix,
1069 sk_sp<GrColorSpaceXform> csxf) {
1070 return TextureOp::Make(context, std::move(proxy), filter, color, srcRect, dstRect, aaType,
1071 constraint, viewMatrix, std::move(csxf));
Brian Salomon34169692017-08-28 15:32:01 -04001072}
1073
1074} // namespace GrTextureOp
1075
1076#if GR_TEST_UTILS
1077#include "GrContext.h"
Robert Phillips1afd4cd2018-01-08 13:40:32 -05001078#include "GrContextPriv.h"
Robert Phillips0bd24dc2018-01-16 08:06:32 -05001079#include "GrProxyProvider.h"
Brian Salomon34169692017-08-28 15:32:01 -04001080
1081GR_DRAW_OP_TEST_DEFINE(TextureOp) {
1082 GrSurfaceDesc desc;
1083 desc.fConfig = kRGBA_8888_GrPixelConfig;
1084 desc.fHeight = random->nextULessThan(90) + 10;
1085 desc.fWidth = random->nextULessThan(90) + 10;
Brian Salomon2a4f9832018-03-03 22:43:43 -05001086 auto origin = random->nextBool() ? kTopLeft_GrSurfaceOrigin : kBottomLeft_GrSurfaceOrigin;
Greg Daniel09c94002018-06-08 22:11:51 +00001087 GrMipMapped mipMapped = random->nextBool() ? GrMipMapped::kYes : GrMipMapped::kNo;
1088 SkBackingFit fit = SkBackingFit::kExact;
1089 if (mipMapped == GrMipMapped::kNo) {
1090 fit = random->nextBool() ? SkBackingFit::kApprox : SkBackingFit::kExact;
1091 }
Robert Phillips0bd24dc2018-01-16 08:06:32 -05001092
1093 GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider();
Greg Daniel09c94002018-06-08 22:11:51 +00001094 sk_sp<GrTextureProxy> proxy = proxyProvider->createProxy(desc, origin, mipMapped, fit,
1095 SkBudgeted::kNo,
1096 GrInternalSurfaceFlags::kNone);
Robert Phillips0bd24dc2018-01-16 08:06:32 -05001097
Brian Salomon34169692017-08-28 15:32:01 -04001098 SkRect rect = GrTest::TestRect(random);
1099 SkRect srcRect;
1100 srcRect.fLeft = random->nextRangeScalar(0.f, proxy->width() / 2.f);
1101 srcRect.fRight = random->nextRangeScalar(0.f, proxy->width()) + proxy->width() / 2.f;
1102 srcRect.fTop = random->nextRangeScalar(0.f, proxy->height() / 2.f);
1103 srcRect.fBottom = random->nextRangeScalar(0.f, proxy->height()) + proxy->height() / 2.f;
1104 SkMatrix viewMatrix = GrTest::TestMatrixPreservesRightAngles(random);
1105 GrColor color = SkColorToPremulGrColor(random->nextU());
Brian Salomon2bbdcc42017-09-07 12:36:34 -04001106 GrSamplerState::Filter filter = (GrSamplerState::Filter)random->nextULessThan(
1107 static_cast<uint32_t>(GrSamplerState::Filter::kMipMap) + 1);
Greg Daniel09c94002018-06-08 22:11:51 +00001108 while (mipMapped == GrMipMapped::kNo && filter == GrSamplerState::Filter::kMipMap) {
1109 filter = (GrSamplerState::Filter)random->nextULessThan(
1110 static_cast<uint32_t>(GrSamplerState::Filter::kMipMap) + 1);
1111 }
Brian Salomon34169692017-08-28 15:32:01 -04001112 auto csxf = GrTest::TestColorXform(random);
Brian Salomon485b8c62018-01-12 15:11:06 -05001113 GrAAType aaType = GrAAType::kNone;
1114 if (random->nextBool()) {
1115 aaType = (fsaaType == GrFSAAType::kUnifiedMSAA) ? GrAAType::kMSAA : GrAAType::kCoverage;
1116 }
Brian Salomonb80ffee2018-05-23 16:39:39 -04001117 auto constraint = random->nextBool() ? SkCanvas::kStrict_SrcRectConstraint
1118 : SkCanvas::kFast_SrcRectConstraint;
Robert Phillips7c525e62018-06-12 10:11:12 -04001119 return GrTextureOp::Make(context, std::move(proxy), filter, color, srcRect, rect, aaType,
1120 constraint, viewMatrix, std::move(csxf));
Brian Salomon34169692017-08-28 15:32:01 -04001121}
1122
1123#endif