blob: 516cee628fc54fe1ba2de3567878ed313d291fbc [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 Osman3ebd3542018-07-30 14:36:53 -0400108 sk_sp<GrColorSpaceXform> textureColorSpaceXform,
109 sk_sp<GrColorSpaceXform> paintColorSpaceXform,
110 bool coverageAA,
Brian Salomonb80ffee2018-05-23 16:39:39 -0400111 bool perspective, Domain domain,
112 const GrSamplerState::Filter filters[],
Brian Salomon336ce7b2017-09-08 08:23:58 -0400113 const GrShaderCaps& caps) {
114 // We use placement new to avoid always allocating space for kMaxTextures TextureSampler
115 // instances.
116 int samplerCnt = NumSamplersToUse(proxyCnt, caps);
117 size_t size = sizeof(TextureGeometryProcessor) + sizeof(TextureSampler) * (samplerCnt - 1);
118 void* mem = GrGeometryProcessor::operator new(size);
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400119 return sk_sp<TextureGeometryProcessor>(
Brian Osman3ebd3542018-07-30 14:36:53 -0400120 new (mem) TextureGeometryProcessor(proxies, proxyCnt, samplerCnt,
121 std::move(textureColorSpaceXform),
122 std::move(paintColorSpaceXform),
Brian Salomonb80ffee2018-05-23 16:39:39 -0400123 coverageAA, perspective, domain, filters, caps));
Brian Salomon336ce7b2017-09-08 08:23:58 -0400124 }
125
126 ~TextureGeometryProcessor() override {
127 int cnt = this->numTextureSamplers();
128 for (int i = 1; i < cnt; ++i) {
129 fSamplers[i].~TextureSampler();
130 }
Brian Salomon34169692017-08-28 15:32:01 -0400131 }
132
133 const char* name() const override { return "TextureGeometryProcessor"; }
134
135 void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const override {
Brian Osman3ebd3542018-07-30 14:36:53 -0400136 b->add32(GrColorSpaceXform::XformKey(fTextureColorSpaceXform.get()));
137 b->add32(GrColorSpaceXform::XformKey(fPaintColorSpaceXform.get()));
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400138 uint32_t x = this->usesCoverageEdgeAA() ? 0 : 1;
Brian Salomon70132d02018-05-29 15:33:06 -0400139 x |= kFloat3_GrVertexAttribType == fPositions.type() ? 0 : 2;
Brian Salomonb80ffee2018-05-23 16:39:39 -0400140 x |= fDomain.isInitialized() ? 4 : 0;
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400141 b->add32(x);
Brian Salomon34169692017-08-28 15:32:01 -0400142 }
143
144 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps& caps) const override {
145 class GLSLProcessor : public GrGLSLGeometryProcessor {
146 public:
147 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& proc,
148 FPCoordTransformIter&& transformIter) override {
149 const auto& textureGP = proc.cast<TextureGeometryProcessor>();
150 this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
Brian Osman3ebd3542018-07-30 14:36:53 -0400151 fTextureColorSpaceXformHelper.setData(
152 pdman, textureGP.fTextureColorSpaceXform.get());
153 fPaintColorSpaceXformHelper.setData(pdman, textureGP.fPaintColorSpaceXform.get());
Brian Salomon34169692017-08-28 15:32:01 -0400154 }
155
156 private:
157 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
Chris Dalton7b046312018-02-02 11:06:30 -0700158 using Interpolation = GrGLSLVaryingHandler::Interpolation;
Brian Salomon34169692017-08-28 15:32:01 -0400159 const auto& textureGP = args.fGP.cast<TextureGeometryProcessor>();
Brian Osman3ebd3542018-07-30 14:36:53 -0400160 fTextureColorSpaceXformHelper.emitCode(
161 args.fUniformHandler, textureGP.fTextureColorSpaceXform.get());
162 fPaintColorSpaceXformHelper.emitCode(
163 args.fUniformHandler, textureGP.fPaintColorSpaceXform.get(),
164 kVertex_GrShaderFlag);
Brian Salomon70132d02018-05-29 15:33:06 -0400165 if (kFloat2_GrVertexAttribType == textureGP.fPositions.type()) {
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400166 args.fVaryingHandler->setNoPerspective();
167 }
Brian Salomon34169692017-08-28 15:32:01 -0400168 args.fVaryingHandler->emitAttributes(textureGP);
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400169 gpArgs->fPositionVar = textureGP.fPositions.asShaderVar();
170
Brian Salomon34169692017-08-28 15:32:01 -0400171 this->emitTransforms(args.fVertBuilder,
172 args.fVaryingHandler,
173 args.fUniformHandler,
Brian Salomon04460cc2017-12-06 14:47:42 -0500174 textureGP.fTextureCoords.asShaderVar(),
Brian Salomon34169692017-08-28 15:32:01 -0400175 args.fFPCoordTransformHandler);
Brian Osman3ebd3542018-07-30 14:36:53 -0400176 if (fPaintColorSpaceXformHelper.isNoop()) {
177 args.fVaryingHandler->addPassThroughAttribute(
178 textureGP.fColors, args.fOutputColor, Interpolation::kCanBeFlat);
179 } else {
180 GrGLSLVarying varying(kHalf4_GrSLType);
181 args.fVaryingHandler->addVarying("color", &varying);
182 args.fVertBuilder->codeAppend("half4 color = ");
183 args.fVertBuilder->appendColorGamutXform(textureGP.fColors.name(),
184 &fPaintColorSpaceXformHelper);
185 args.fVertBuilder->codeAppend(";");
186 args.fVertBuilder->codeAppendf("%s = half4(color.rgb * color.a, color.a);",
187 varying.vsOut());
188 args.fFragBuilder->codeAppendf("%s = %s;", args.fOutputColor, varying.fsIn());
189 }
Ethan Nicholas8aa45692017-09-20 11:24:15 -0400190 args.fFragBuilder->codeAppend("float2 texCoord;");
Brian Salomon92be2f72018-06-19 14:33:47 -0400191 args.fVaryingHandler->addPassThroughAttribute(textureGP.fTextureCoords, "texCoord");
Brian Salomonb80ffee2018-05-23 16:39:39 -0400192 if (textureGP.fDomain.isInitialized()) {
193 args.fFragBuilder->codeAppend("float4 domain;");
194 args.fVaryingHandler->addPassThroughAttribute(
Brian Salomon92be2f72018-06-19 14:33:47 -0400195 textureGP.fDomain, "domain",
Brian Salomonb80ffee2018-05-23 16:39:39 -0400196 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
197 args.fFragBuilder->codeAppend(
198 "texCoord = clamp(texCoord, domain.xy, domain.zw);");
199 }
Brian Salomon336ce7b2017-09-08 08:23:58 -0400200 if (textureGP.numTextureSamplers() > 1) {
Chris Dalton7b046312018-02-02 11:06:30 -0700201 // If this changes to float, reconsider Interpolation::kMustBeFlat.
Brian Salomon70132d02018-05-29 15:33:06 -0400202 SkASSERT(kInt_GrVertexAttribType == textureGP.fTextureIdx.type());
Brian Salomon336ce7b2017-09-08 08:23:58 -0400203 SkASSERT(args.fShaderCaps->integerSupport());
204 args.fFragBuilder->codeAppend("int texIdx;");
Brian Salomon92be2f72018-06-19 14:33:47 -0400205 args.fVaryingHandler->addPassThroughAttribute(textureGP.fTextureIdx, "texIdx",
Chris Dalton7b046312018-02-02 11:06:30 -0700206 Interpolation::kMustBeFlat);
Brian Salomon336ce7b2017-09-08 08:23:58 -0400207 args.fFragBuilder->codeAppend("switch (texIdx) {");
208 for (int i = 0; i < textureGP.numTextureSamplers(); ++i) {
209 args.fFragBuilder->codeAppendf("case %d: %s = ", i, args.fOutputColor);
Brian Osman3ebd3542018-07-30 14:36:53 -0400210 args.fFragBuilder->appendTextureLookupAndModulate(
211 args.fOutputColor, args.fTexSamplers[i], "texCoord",
212 kFloat2_GrSLType, &fTextureColorSpaceXformHelper);
Brian Salomon336ce7b2017-09-08 08:23:58 -0400213 args.fFragBuilder->codeAppend("; break;");
214 }
215 args.fFragBuilder->codeAppend("}");
216 } else {
217 args.fFragBuilder->codeAppendf("%s = ", args.fOutputColor);
Brian Osman3ebd3542018-07-30 14:36:53 -0400218 args.fFragBuilder->appendTextureLookupAndModulate(
219 args.fOutputColor, args.fTexSamplers[0], "texCoord",
220 kFloat2_GrSLType, &fTextureColorSpaceXformHelper);
Brian Salomon336ce7b2017-09-08 08:23:58 -0400221 }
Brian Salomon34169692017-08-28 15:32:01 -0400222 args.fFragBuilder->codeAppend(";");
Brian Salomon485b8c62018-01-12 15:11:06 -0500223 if (textureGP.usesCoverageEdgeAA()) {
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400224 bool mulByFragCoordW = false;
Brian Salomon9b4bd592018-07-10 09:23:40 -0400225 GrGLSLVarying aaDistVarying(kFloat4_GrSLType,
226 GrGLSLVarying::Scope::kVertToFrag);
227 if (kFloat3_GrVertexAttribType == textureGP.fPositions.type()) {
228 args.fVaryingHandler->addVarying("aaDists", &aaDistVarying);
229 // The distance from edge equation e to homogenous point p=sk_Position
230 // is e.x*p.x/p.wx + e.y*p.y/p.w + e.z. However, we want screen space
231 // interpolation of this distance. We can do this by multiplying the
232 // varying in the VS by p.w and then multiplying by sk_FragCoord.w in
233 // the FS. So we output e.x*p.x + e.y*p.y + e.z * p.w
234 args.fVertBuilder->codeAppendf(
235 R"(%s = float4(dot(aaEdge0, %s), dot(aaEdge1, %s),
236 dot(aaEdge2, %s), dot(aaEdge3, %s));)",
237 aaDistVarying.vsOut(), textureGP.fPositions.name(),
238 textureGP.fPositions.name(), textureGP.fPositions.name(),
239 textureGP.fPositions.name());
240 mulByFragCoordW = true;
Brian Salomondba65f92018-01-22 08:43:38 -0500241 } else {
Brian Salomon9b4bd592018-07-10 09:23:40 -0400242 args.fVaryingHandler->addVarying("aaDists", &aaDistVarying);
243 args.fVertBuilder->codeAppendf(
244 R"(%s = float4(dot(aaEdge0.xy, %s.xy) + aaEdge0.z,
245 dot(aaEdge1.xy, %s.xy) + aaEdge1.z,
246 dot(aaEdge2.xy, %s.xy) + aaEdge2.z,
247 dot(aaEdge3.xy, %s.xy) + aaEdge3.z);)",
248 aaDistVarying.vsOut(), textureGP.fPositions.name(),
249 textureGP.fPositions.name(), textureGP.fPositions.name(),
250 textureGP.fPositions.name());
Brian Salomondba65f92018-01-22 08:43:38 -0500251 }
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500252 args.fFragBuilder->codeAppendf(
253 "float mindist = min(min(%s.x, %s.y), min(%s.z, %s.w));",
Brian Salomon9b4bd592018-07-10 09:23:40 -0400254 aaDistVarying.fsIn(), aaDistVarying.fsIn(), aaDistVarying.fsIn(),
255 aaDistVarying.fsIn());
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400256 if (mulByFragCoordW) {
257 args.fFragBuilder->codeAppend("mindist *= sk_FragCoord.w;");
258 }
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500259 args.fFragBuilder->codeAppendf("%s = float4(clamp(mindist, 0, 1));",
260 args.fOutputCoverage);
261 } else {
262 args.fFragBuilder->codeAppendf("%s = float4(1);", args.fOutputCoverage);
263 }
Brian Salomon34169692017-08-28 15:32:01 -0400264 }
Brian Osman3ebd3542018-07-30 14:36:53 -0400265 GrGLSLColorSpaceXformHelper fTextureColorSpaceXformHelper;
266 GrGLSLColorSpaceXformHelper fPaintColorSpaceXformHelper;
Brian Salomon34169692017-08-28 15:32:01 -0400267 };
268 return new GLSLProcessor;
269 }
270
Brian Salomon485b8c62018-01-12 15:11:06 -0500271 bool usesCoverageEdgeAA() const { return SkToBool(fAAEdges[0].isInitialized()); }
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500272
Brian Salomon34169692017-08-28 15:32:01 -0400273private:
Brian Salomon336ce7b2017-09-08 08:23:58 -0400274 // This exists to reduce the number of shaders generated. It does some rounding of sampler
275 // counts.
276 static int NumSamplersToUse(int numRealProxies, const GrShaderCaps& caps) {
277 SkASSERT(numRealProxies > 0 && numRealProxies <= kMaxTextures &&
278 numRealProxies <= caps.maxFragmentSamplers());
279 if (1 == numRealProxies) {
280 return 1;
281 }
282 if (numRealProxies <= 4) {
283 return 4;
284 }
285 // Round to the next power of 2 and then clamp to kMaxTextures and the max allowed by caps.
286 return SkTMin(SkNextPow2(numRealProxies), SkTMin(kMaxTextures, caps.maxFragmentSamplers()));
287 }
288
289 TextureGeometryProcessor(sk_sp<GrTextureProxy> proxies[], int proxyCnt, int samplerCnt,
Brian Osman3ebd3542018-07-30 14:36:53 -0400290 sk_sp<GrColorSpaceXform> textureColorSpaceXform,
291 sk_sp<GrColorSpaceXform> paintColorSpaceXform,
292 bool coverageAA, bool perspective, Domain domain,
293 const GrSamplerState::Filter filters[], const GrShaderCaps& caps)
294 : INHERITED(kTextureGeometryProcessor_ClassID)
295 , fTextureColorSpaceXform(std::move(textureColorSpaceXform))
296 , fPaintColorSpaceXform(std::move(paintColorSpaceXform)) {
Brian Salomon336ce7b2017-09-08 08:23:58 -0400297 SkASSERT(proxyCnt > 0 && samplerCnt >= proxyCnt);
Brian Salomon336ce7b2017-09-08 08:23:58 -0400298 fSamplers[0].reset(std::move(proxies[0]), filters[0]);
299 this->addTextureSampler(&fSamplers[0]);
300 for (int i = 1; i < proxyCnt; ++i) {
301 // This class has one sampler built in, the rest come from memory this processor was
302 // placement-newed into and so haven't been constructed.
303 new (&fSamplers[i]) TextureSampler(std::move(proxies[i]), filters[i]);
304 this->addTextureSampler(&fSamplers[i]);
305 }
Brian Salomon30e1a5e2018-05-18 12:32:32 -0400306
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400307 if (perspective) {
Brian Salomon92be2f72018-06-19 14:33:47 -0400308 fPositions = {"position", kFloat3_GrVertexAttribType};
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400309 } else {
Brian Salomon92be2f72018-06-19 14:33:47 -0400310 fPositions = {"position", kFloat2_GrVertexAttribType};
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400311 }
Brian Salomon92be2f72018-06-19 14:33:47 -0400312 fColors = {"color", kUByte4_norm_GrVertexAttribType};
313 fTextureCoords = {"textureCoords", kFloat2_GrVertexAttribType};
314 int vertexAttributeCnt = 3;
Brian Salomon30e1a5e2018-05-18 12:32:32 -0400315
Brian Salomon336ce7b2017-09-08 08:23:58 -0400316 if (samplerCnt > 1) {
317 // Here we initialize any extra samplers by repeating the last one samplerCnt - proxyCnt
318 // times.
319 GrTextureProxy* dupeProxy = fSamplers[proxyCnt - 1].proxy();
320 for (int i = proxyCnt; i < samplerCnt; ++i) {
321 new (&fSamplers[i]) TextureSampler(sk_ref_sp(dupeProxy), filters[proxyCnt - 1]);
322 this->addTextureSampler(&fSamplers[i]);
323 }
324 SkASSERT(caps.integerSupport());
Brian Salomon92be2f72018-06-19 14:33:47 -0400325 fTextureIdx = {"textureIdx", kInt_GrVertexAttribType};
326 ++vertexAttributeCnt;
Brian Salomon336ce7b2017-09-08 08:23:58 -0400327 }
Brian Salomonb80ffee2018-05-23 16:39:39 -0400328 if (domain == Domain::kYes) {
Brian Salomon92be2f72018-06-19 14:33:47 -0400329 fDomain = {"domain", kFloat4_GrVertexAttribType};
330 ++vertexAttributeCnt;
Brian Salomonb80ffee2018-05-23 16:39:39 -0400331 }
Brian Salomon485b8c62018-01-12 15:11:06 -0500332 if (coverageAA) {
Brian Salomon92be2f72018-06-19 14:33:47 -0400333 fAAEdges[0] = {"aaEdge0", kFloat3_GrVertexAttribType};
334 fAAEdges[1] = {"aaEdge1", kFloat3_GrVertexAttribType};
335 fAAEdges[2] = {"aaEdge2", kFloat3_GrVertexAttribType};
336 fAAEdges[3] = {"aaEdge3", kFloat3_GrVertexAttribType};
337 vertexAttributeCnt += 4;
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500338 }
Brian Salomon92be2f72018-06-19 14:33:47 -0400339 this->setVertexAttributeCnt(vertexAttributeCnt);
340 }
341
342 const Attribute& onVertexAttribute(int i) const override {
343 return IthInitializedAttribute(i, fPositions, fColors, fTextureCoords, fTextureIdx, fDomain,
344 fAAEdges[0], fAAEdges[1], fAAEdges[2], fAAEdges[3]);
Brian Salomon34169692017-08-28 15:32:01 -0400345 }
346
347 Attribute fPositions;
Brian Salomon34169692017-08-28 15:32:01 -0400348 Attribute fColors;
Brian Salomon30e1a5e2018-05-18 12:32:32 -0400349 Attribute fTextureCoords;
350 Attribute fTextureIdx;
Brian Salomonb80ffee2018-05-23 16:39:39 -0400351 Attribute fDomain;
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500352 Attribute fAAEdges[4];
Brian Osman3ebd3542018-07-30 14:36:53 -0400353 sk_sp<GrColorSpaceXform> fTextureColorSpaceXform;
354 sk_sp<GrColorSpaceXform> fPaintColorSpaceXform;
Brian Salomon336ce7b2017-09-08 08:23:58 -0400355 TextureSampler fSamplers[1];
Ethan Nicholasabff9562017-10-09 10:54:08 -0400356
357 typedef GrGeometryProcessor INHERITED;
Brian Salomon34169692017-08-28 15:32:01 -0400358};
359
Brian Salomon6872e942018-05-18 10:29:54 -0400360// This computes the four edge equations for a quad, then outsets them and computes a new quad
361// as the intersection points of the outset edges. 'x' and 'y' contain the original points as input
362// and the outset points as output. 'a', 'b', and 'c' are the edge equation coefficients on output.
363static void compute_quad_edges_and_outset_vertices(Sk4f* x, Sk4f* y, Sk4f* a, Sk4f* b, Sk4f* c) {
364 static constexpr auto fma = SkNx_fma<4, float>;
365 // These rotate the points/edge values either clockwise or counterclockwise assuming tri strip
366 // order.
367 auto nextCW = [](const Sk4f& v) { return SkNx_shuffle<2, 0, 3, 1>(v); };
368 auto nextCCW = [](const Sk4f& v) { return SkNx_shuffle<1, 3, 0, 2>(v); };
369
370 auto xnext = nextCCW(*x);
371 auto ynext = nextCCW(*y);
372 *a = ynext - *y;
373 *b = *x - xnext;
374 *c = fma(xnext, *y, -ynext * *x);
375 Sk4f invNormLengths = (*a * *a + *b * *b).rsqrt();
376 // Make sure the edge equations have their normals facing into the quad in device space.
377 auto test = fma(*a, nextCW(*x), fma(*b, nextCW(*y), *c));
378 if ((test < Sk4f(0)).anyTrue()) {
379 invNormLengths = -invNormLengths;
380 }
381 *a *= invNormLengths;
382 *b *= invNormLengths;
383 *c *= invNormLengths;
384
385 // Here is the outset. This makes our edge equations compute coverage without requiring a
386 // half pixel offset and is also used to compute the bloated quad that will cover all
387 // pixels.
388 *c += Sk4f(0.5f);
389
390 // Reverse the process to compute the points of the bloated quad from the edge equations.
391 // This time the inputs don't have 1s as their third coord and we want to homogenize rather
392 // than normalize.
393 auto anext = nextCW(*a);
394 auto bnext = nextCW(*b);
395 auto cnext = nextCW(*c);
396 *x = fma(bnext, *c, -*b * cnext);
397 *y = fma(*a, cnext, -anext * *c);
398 auto ic = (fma(anext, *b, -bnext * *a)).invert();
399 *x *= ic;
400 *y *= ic;
401}
402
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500403namespace {
404// This is a class soley so it can be partially specialized (functions cannot be).
Brian Salomon86c40012018-05-22 10:48:49 -0400405template <typename V, GrAA AA = V::kAA, typename Position = typename V::Position>
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400406class VertexAAHandler;
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500407
Brian Salomon86c40012018-05-22 10:48:49 -0400408template<typename V> class VertexAAHandler<V, GrAA::kNo, SkPoint> {
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500409public:
Brian Salomon86c40012018-05-22 10:48:49 -0400410 static void AssignPositionsAndTexCoords(V* vertices, const GrPerspQuad& quad,
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500411 const SkRect& texRect) {
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400412 SkASSERT((quad.w4f() == Sk4f(1.f)).allTrue());
Brian Salomon86c40012018-05-22 10:48:49 -0400413 SkPointPriv::SetRectTriStrip(&vertices[0].fTextureCoords, texRect, sizeof(V));
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400414 for (int i = 0; i < 4; ++i) {
415 vertices[i].fPosition = {quad.x(i), quad.y(i)};
416 }
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500417 }
418};
419
Brian Salomon86c40012018-05-22 10:48:49 -0400420template<typename V> class VertexAAHandler<V, GrAA::kNo, SkPoint3> {
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500421public:
Brian Salomon86c40012018-05-22 10:48:49 -0400422 static void AssignPositionsAndTexCoords(V* vertices, const GrPerspQuad& quad,
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500423 const SkRect& texRect) {
Brian Salomon86c40012018-05-22 10:48:49 -0400424 SkPointPriv::SetRectTriStrip(&vertices[0].fTextureCoords, texRect, sizeof(V));
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400425 for (int i = 0; i < 4; ++i) {
426 vertices[i].fPosition = quad.point(i);
427 }
428 }
429};
430
Brian Salomon86c40012018-05-22 10:48:49 -0400431template<typename V> class VertexAAHandler<V, GrAA::kYes, SkPoint> {
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400432public:
Brian Salomon86c40012018-05-22 10:48:49 -0400433 static void AssignPositionsAndTexCoords(V* vertices, const GrPerspQuad& quad,
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400434 const SkRect& texRect) {
435 SkASSERT((quad.w4f() == Sk4f(1.f)).allTrue());
Brian Salomon6872e942018-05-18 10:29:54 -0400436 auto x = quad.x4f();
437 auto y = quad.y4f();
438 Sk4f a, b, c;
439 compute_quad_edges_and_outset_vertices(&x, &y, &a, &b, &c);
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500440
441 for (int i = 0; i < 4; ++i) {
Brian Salomon6872e942018-05-18 10:29:54 -0400442 vertices[i].fPosition = {x[i], y[i]};
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500443 for (int j = 0; j < 4; ++j) {
Brian Salomon6872e942018-05-18 10:29:54 -0400444 vertices[i].fEdges[j] = {a[j], b[j], c[j]};
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500445 }
446 }
447
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500448 AssignTexCoords(vertices, quad, texRect);
449 }
450
451private:
Brian Salomon86c40012018-05-22 10:48:49 -0400452 static void AssignTexCoords(V* vertices, const GrPerspQuad& quad, const SkRect& tex) {
Brian Salomona33b67c2018-05-17 10:42:14 -0400453 SkMatrix q = SkMatrix::MakeAll(quad.x(0), quad.x(1), quad.x(2),
454 quad.y(0), quad.y(1), quad.y(2),
455 1.f, 1.f, 1.f);
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500456 SkMatrix qinv;
457 if (!q.invert(&qinv)) {
458 return;
459 }
460 SkMatrix t = SkMatrix::MakeAll(tex.fLeft, tex.fLeft, tex.fRight,
461 tex.fTop, tex.fBottom, tex.fTop,
462 1.f, 1.f, 1.f);
463 SkMatrix map;
464 map.setConcat(t, qinv);
Brian Salomon86c40012018-05-22 10:48:49 -0400465 SkMatrixPriv::MapPointsWithStride(map, &vertices[0].fTextureCoords, sizeof(V),
466 &vertices[0].fPosition, sizeof(V), 4);
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500467 }
468};
469
Brian Salomon86c40012018-05-22 10:48:49 -0400470template<typename V> class VertexAAHandler<V, GrAA::kYes, SkPoint3> {
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400471public:
Brian Salomon86c40012018-05-22 10:48:49 -0400472 static void AssignPositionsAndTexCoords(V* vertices, const GrPerspQuad& quad,
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400473 const SkRect& texRect) {
474 auto x = quad.x4f();
475 auto y = quad.y4f();
476 auto iw = quad.iw4f();
477 x *= iw;
478 y *= iw;
479
480 // Get an equation for w from device space coords.
481 SkMatrix P;
482 P.setAll(x[0], y[0], 1, x[1], y[1], 1, x[2], y[2], 1);
483 SkAssertResult(P.invert(&P));
484 SkPoint3 weq{quad.w(0), quad.w(1), quad.w(2)};
485 P.mapHomogeneousPoints(&weq, &weq, 1);
486
487 Sk4f a, b, c;
488 compute_quad_edges_and_outset_vertices(&x, &y, &a, &b, &c);
489
490 // Compute new w values for the output vertices;
491 auto w = Sk4f(weq.fX) * x + Sk4f(weq.fY) * y + Sk4f(weq.fZ);
492 x *= w;
493 y *= w;
494
495 for (int i = 0; i < 4; ++i) {
496 vertices[i].fPosition = {x[i], y[i], w[i]};
497 for (int j = 0; j < 4; ++j) {
498 vertices[i].fEdges[j] = {a[j], b[j], c[j]};
499 }
500 }
501
502 AssignTexCoords(vertices, quad, texRect);
503 }
504
505private:
Brian Salomon86c40012018-05-22 10:48:49 -0400506 static void AssignTexCoords(V* vertices, const GrPerspQuad& quad, const SkRect& tex) {
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400507 SkMatrix q = SkMatrix::MakeAll(quad.x(0), quad.x(1), quad.x(2),
508 quad.y(0), quad.y(1), quad.y(2),
509 quad.w(0), quad.w(1), quad.w(2));
510 SkMatrix qinv;
511 if (!q.invert(&qinv)) {
512 return;
513 }
514 SkMatrix t = SkMatrix::MakeAll(tex.fLeft, tex.fLeft, tex.fRight,
515 tex.fTop, tex.fBottom, tex.fTop,
516 1.f, 1.f, 1.f);
517 SkMatrix map;
518 map.setConcat(t, qinv);
519 SkPoint3 tempTexCoords[4];
520 SkMatrixPriv::MapHomogeneousPointsWithStride(map, tempTexCoords, sizeof(SkPoint3),
Brian Salomon86c40012018-05-22 10:48:49 -0400521 &vertices[0].fPosition, sizeof(V), 4);
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400522 for (int i = 0; i < 4; ++i) {
523 auto invW = 1.f / tempTexCoords[i].fZ;
524 vertices[i].fTextureCoords.fX = tempTexCoords[i].fX * invW;
525 vertices[i].fTextureCoords.fY = tempTexCoords[i].fY * invW;
526 }
527 }
528};
529
Brian Salomon17031a72018-05-22 14:14:07 -0400530template <typename V, MultiTexture MT = V::kMultiTexture> struct TexIdAssigner;
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500531
Brian Salomon17031a72018-05-22 14:14:07 -0400532template <typename V> struct TexIdAssigner<V, MultiTexture::kYes> {
Brian Salomon86c40012018-05-22 10:48:49 -0400533 static void Assign(V* vertices, int textureIdx) {
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400534 for (int i = 0; i < 4; ++i) {
535 vertices[i].fTextureIdx = textureIdx;
536 }
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500537 }
538};
539
Brian Salomon17031a72018-05-22 14:14:07 -0400540template <typename V> struct TexIdAssigner<V, MultiTexture::kNo> {
Brian Salomon86c40012018-05-22 10:48:49 -0400541 static void Assign(V* vertices, int textureIdx) {}
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500542};
Brian Salomonb80ffee2018-05-23 16:39:39 -0400543
544template <typename V, Domain D = V::kDomain> struct DomainAssigner;
545
546template <typename V> struct DomainAssigner<V, Domain::kYes> {
547 static void Assign(V* vertices, Domain domain, GrSamplerState::Filter filter,
548 const SkRect& srcRect, GrSurfaceOrigin origin, float iw, float ih) {
549 static constexpr SkRect kLargeRect = {-2, -2, 2, 2};
550 SkRect domainRect;
551 if (domain == Domain::kYes) {
552 auto ltrb = Sk4f::Load(&srcRect);
553 if (filter == GrSamplerState::Filter::kBilerp) {
554 auto rblt = SkNx_shuffle<2, 3, 0, 1>(ltrb);
555 auto whwh = (rblt - ltrb).abs();
556 auto c = (rblt + ltrb) * 0.5f;
557 static const Sk4f kOffsets = {0.5f, 0.5f, -0.5f, -0.5f};
558 ltrb = (whwh < 1.f).thenElse(c, ltrb + kOffsets);
559 }
560 ltrb *= Sk4f(iw, ih, iw, ih);
561 if (origin == kBottomLeft_GrSurfaceOrigin) {
562 static const Sk4f kMul = {1.f, -1.f, 1.f, -1.f};
563 static const Sk4f kAdd = {0.f, 1.f, 0.f, 1.f};
564 ltrb = SkNx_shuffle<0, 3, 2, 1>(kMul * ltrb + kAdd);
565 }
566 ltrb.store(&domainRect);
567 } else {
568 domainRect = kLargeRect;
569 }
570 for (int i = 0; i < 4; ++i) {
571 vertices[i].fTextureDomain = domainRect;
572 }
573 }
574};
575
576template <typename V> struct DomainAssigner<V, Domain::kNo> {
577 static void Assign(V*, Domain domain, GrSamplerState::Filter, const SkRect&, GrSurfaceOrigin,
578 float iw, float ih) {
579 SkASSERT(domain == Domain::kNo);
580 }
581};
582
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500583} // anonymous namespace
584
Brian Salomon86c40012018-05-22 10:48:49 -0400585template <typename V>
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400586static void tessellate_quad(const GrPerspQuad& devQuad, const SkRect& srcRect, GrColor color,
Brian Salomonb80ffee2018-05-23 16:39:39 -0400587 GrSurfaceOrigin origin, GrSamplerState::Filter filter, V* vertices,
588 SkScalar iw, SkScalar ih, int textureIdx, Domain domain) {
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500589 SkRect texRect = {
590 iw * srcRect.fLeft,
591 ih * srcRect.fTop,
592 iw * srcRect.fRight,
593 ih * srcRect.fBottom
594 };
595 if (origin == kBottomLeft_GrSurfaceOrigin) {
596 texRect.fTop = 1.f - texRect.fTop;
597 texRect.fBottom = 1.f - texRect.fBottom;
598 }
Brian Salomon86c40012018-05-22 10:48:49 -0400599 VertexAAHandler<V>::AssignPositionsAndTexCoords(vertices, devQuad, texRect);
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500600 vertices[0].fColor = color;
601 vertices[1].fColor = color;
602 vertices[2].fColor = color;
603 vertices[3].fColor = color;
Brian Salomon86c40012018-05-22 10:48:49 -0400604 TexIdAssigner<V>::Assign(vertices, textureIdx);
Brian Salomonb80ffee2018-05-23 16:39:39 -0400605 DomainAssigner<V>::Assign(vertices, domain, filter, srcRect, origin, iw, ih);
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500606}
Brian Salomon17031a72018-05-22 14:14:07 -0400607
Brian Salomon34169692017-08-28 15:32:01 -0400608/**
609 * Op that implements GrTextureOp::Make. It draws textured quads. Each quad can modulate against a
610 * the texture by color. The blend with the destination is always src-over. The edges are non-AA.
611 */
612class TextureOp final : public GrMeshDrawOp {
613public:
Robert Phillips7c525e62018-06-12 10:11:12 -0400614 static std::unique_ptr<GrDrawOp> Make(GrContext* context,
615 sk_sp<GrTextureProxy> proxy,
616 GrSamplerState::Filter filter,
617 GrColor color,
618 const SkRect& srcRect,
619 const SkRect& dstRect,
620 GrAAType aaType,
621 SkCanvas::SrcRectConstraint constraint,
Brian Osman2b23c4b2018-06-01 12:25:08 -0400622 const SkMatrix& viewMatrix,
Brian Osman3ebd3542018-07-30 14:36:53 -0400623 sk_sp<GrColorSpaceXform> textureColorSpaceXform,
624 sk_sp<GrColorSpaceXform> paintColorSpaceXform) {
Robert Phillipsc994a932018-06-19 13:09:54 -0400625 GrOpMemoryPool* pool = context->contextPriv().opMemoryPool();
626
627 return pool->allocate<TextureOp>(std::move(proxy), filter, color,
628 srcRect, dstRect, aaType, constraint,
Brian Osman3ebd3542018-07-30 14:36:53 -0400629 viewMatrix, std::move(textureColorSpaceXform),
630 std::move(paintColorSpaceXform));
Brian Salomon34169692017-08-28 15:32:01 -0400631 }
632
Brian Salomon336ce7b2017-09-08 08:23:58 -0400633 ~TextureOp() override {
634 if (fFinalized) {
635 auto proxies = this->proxies();
636 for (int i = 0; i < fProxyCnt; ++i) {
637 proxies[i]->completedRead();
638 }
639 if (fProxyCnt > 1) {
640 delete[] reinterpret_cast<const char*>(proxies);
641 }
642 } else {
643 SkASSERT(1 == fProxyCnt);
644 fProxy0->unref();
645 }
646 }
Brian Salomon34169692017-08-28 15:32:01 -0400647
648 const char* name() const override { return "TextureOp"; }
649
Robert Phillipsf1748f52017-09-14 14:11:24 -0400650 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillipsb493eeb2017-09-13 13:10:52 -0400651 auto proxies = this->proxies();
652 for (int i = 0; i < fProxyCnt; ++i) {
653 func(proxies[i]);
654 }
655 }
656
Brian Salomon34169692017-08-28 15:32:01 -0400657 SkString dumpInfo() const override {
658 SkString str;
Brian Salomon34169692017-08-28 15:32:01 -0400659 str.appendf("# draws: %d\n", fDraws.count());
Brian Salomon336ce7b2017-09-08 08:23:58 -0400660 auto proxies = this->proxies();
661 for (int i = 0; i < fProxyCnt; ++i) {
662 str.appendf("Proxy ID %d: %d, Filter: %d\n", i, proxies[i]->uniqueID().asUInt(),
663 static_cast<int>(this->filters()[i]));
664 }
Brian Salomon34169692017-08-28 15:32:01 -0400665 for (int i = 0; i < fDraws.count(); ++i) {
666 const Draw& draw = fDraws[i];
667 str.appendf(
Brian Salomon336ce7b2017-09-08 08:23:58 -0400668 "%d: Color: 0x%08x, ProxyIdx: %d, TexRect [L: %.2f, T: %.2f, R: %.2f, B: %.2f] "
669 "Quad [(%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f)]\n",
Brian Salomonb80ffee2018-05-23 16:39:39 -0400670 i, draw.color(), draw.textureIdx(), draw.srcRect().fLeft, draw.srcRect().fTop,
671 draw.srcRect().fRight, draw.srcRect().fBottom, draw.quad().point(0).fX,
672 draw.quad().point(0).fY, draw.quad().point(1).fX, draw.quad().point(1).fY,
673 draw.quad().point(2).fX, draw.quad().point(2).fY, draw.quad().point(3).fX,
674 draw.quad().point(3).fY);
Brian Salomon34169692017-08-28 15:32:01 -0400675 }
676 str += INHERITED::dumpInfo();
677 return str;
678 }
679
Brian Osman532b3f92018-07-11 10:02:07 -0400680 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip) override {
Brian Salomon34169692017-08-28 15:32:01 -0400681 SkASSERT(!fFinalized);
Brian Salomon336ce7b2017-09-08 08:23:58 -0400682 SkASSERT(1 == fProxyCnt);
Brian Salomon34169692017-08-28 15:32:01 -0400683 fFinalized = true;
Brian Salomon336ce7b2017-09-08 08:23:58 -0400684 fProxy0->addPendingRead();
685 fProxy0->unref();
Brian Salomon34169692017-08-28 15:32:01 -0400686 return RequiresDstTexture::kNo;
687 }
688
Brian Salomon485b8c62018-01-12 15:11:06 -0500689 FixedFunctionFlags fixedFunctionFlags() const override {
690 return this->aaType() == GrAAType::kMSAA ? FixedFunctionFlags::kUsesHWAA
691 : FixedFunctionFlags::kNone;
692 }
Brian Salomon34169692017-08-28 15:32:01 -0400693
694 DEFINE_OP_CLASS_ID
695
696private:
Robert Phillips7c525e62018-06-12 10:11:12 -0400697 friend class ::GrOpMemoryPool;
Brian Salomon762d5e72017-12-01 10:25:08 -0500698
699 // This is used in a heursitic for choosing a code path. We don't care what happens with
700 // really large rects, infs, nans, etc.
701#if defined(__clang__) && (__clang_major__ * 1000 + __clang_minor__) >= 3007
702__attribute__((no_sanitize("float-cast-overflow")))
703#endif
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500704 size_t RectSizeAsSizeT(const SkRect& rect) {;
Brian Salomon762d5e72017-12-01 10:25:08 -0500705 return static_cast<size_t>(SkTMax(rect.width(), 1.f) * SkTMax(rect.height(), 1.f));
706 }
707
Brian Salomon336ce7b2017-09-08 08:23:58 -0400708 static constexpr int kMaxTextures = TextureGeometryProcessor::kMaxTextures;
709
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400710 TextureOp(sk_sp<GrTextureProxy> proxy, GrSamplerState::Filter filter, GrColor color,
Brian Salomon485b8c62018-01-12 15:11:06 -0500711 const SkRect& srcRect, const SkRect& dstRect, GrAAType aaType,
Brian Salomonb80ffee2018-05-23 16:39:39 -0400712 SkCanvas::SrcRectConstraint constraint, const SkMatrix& viewMatrix,
Brian Osman3ebd3542018-07-30 14:36:53 -0400713 sk_sp<GrColorSpaceXform> textureColorSpaceXform,
714 sk_sp<GrColorSpaceXform> paintColorSpaceXform)
Brian Salomon34169692017-08-28 15:32:01 -0400715 : INHERITED(ClassID())
Brian Osman3ebd3542018-07-30 14:36:53 -0400716 , fTextureColorSpaceXform(std::move(textureColorSpaceXform))
717 , fPaintColorSpaceXform(std::move(paintColorSpaceXform))
Brian Salomon336ce7b2017-09-08 08:23:58 -0400718 , fProxy0(proxy.release())
719 , fFilter0(filter)
720 , fProxyCnt(1)
Brian Salomon485b8c62018-01-12 15:11:06 -0500721 , fAAType(static_cast<unsigned>(aaType))
Brian Osman2b23c4b2018-06-01 12:25:08 -0400722 , fFinalized(0) {
Brian Salomon485b8c62018-01-12 15:11:06 -0500723 SkASSERT(aaType != GrAAType::kMixedSamples);
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400724 fPerspective = viewMatrix.hasPerspective();
Brian Salomon594b64c2018-05-29 12:47:57 -0400725 auto quad = GrPerspQuad(dstRect, viewMatrix);
726 auto bounds = quad.bounds();
727#ifndef SK_DONT_DROP_UNNECESSARY_AA_IN_TEXTURE_OP
728 if (GrAAType::kCoverage == this->aaType() && viewMatrix.rectStaysRect()) {
729 // Disable coverage AA when rect falls on integers in device space.
730 auto is_int = [](float f) { return f == sk_float_floor(f); };
731 if (is_int(bounds.fLeft) && is_int(bounds.fTop) && is_int(bounds.fRight) &&
732 is_int(bounds.fBottom)) {
733 fAAType = static_cast<unsigned>(GrAAType::kNone);
734 // We may have had a strict constraint with nearest filter soley due to possible AA
735 // bloat. In that case it's no longer necessary.
736 if (constraint == SkCanvas::kStrict_SrcRectConstraint &&
737 filter == GrSamplerState::Filter::kNearest) {
738 constraint = SkCanvas::kFast_SrcRectConstraint;
739 }
740 }
741 }
742#endif
743 const auto& draw = fDraws.emplace_back(srcRect, 0, quad, constraint, color);
Brian Salomon34169692017-08-28 15:32:01 -0400744 this->setBounds(bounds, HasAABloat::kNo, IsZeroArea::kNo);
Brian Salomon594b64c2018-05-29 12:47:57 -0400745 fDomain = static_cast<bool>(draw.domain());
Brian Salomon762d5e72017-12-01 10:25:08 -0500746 fMaxApproxDstPixelArea = RectSizeAsSizeT(bounds);
Brian Salomon34169692017-08-28 15:32:01 -0400747 }
748
Brian Salomonb80ffee2018-05-23 16:39:39 -0400749 template <typename Pos, MultiTexture MT, Domain D, GrAA AA>
Brian Salomon17031a72018-05-22 14:14:07 -0400750 void tess(void* v, const float iw[], const float ih[], const GrGeometryProcessor* gp) {
Brian Salomonb80ffee2018-05-23 16:39:39 -0400751 using Vertex = TextureGeometryProcessor::Vertex<Pos, MT, D, AA>;
Brian Salomon92be2f72018-06-19 14:33:47 -0400752 SkASSERT(gp->debugOnly_vertexStride() == sizeof(Vertex));
Brian Salomon17031a72018-05-22 14:14:07 -0400753 auto vertices = static_cast<Vertex*>(v);
754 auto proxies = this->proxies();
Brian Salomonb80ffee2018-05-23 16:39:39 -0400755 auto filters = this->filters();
Brian Salomon17031a72018-05-22 14:14:07 -0400756 for (const auto& draw : fDraws) {
Brian Salomonb80ffee2018-05-23 16:39:39 -0400757 auto textureIdx = draw.textureIdx();
758 auto origin = proxies[textureIdx]->origin();
759 tessellate_quad<Vertex>(draw.quad(), draw.srcRect(), draw.color(), origin,
760 filters[textureIdx], vertices, iw[textureIdx], ih[textureIdx],
761 textureIdx, draw.domain());
Brian Salomon17031a72018-05-22 14:14:07 -0400762 vertices += 4;
763 }
764 }
765
Brian Salomon34169692017-08-28 15:32:01 -0400766 void onPrepareDraws(Target* target) override {
Brian Salomon336ce7b2017-09-08 08:23:58 -0400767 sk_sp<GrTextureProxy> proxiesSPs[kMaxTextures];
768 auto proxies = this->proxies();
769 auto filters = this->filters();
770 for (int i = 0; i < fProxyCnt; ++i) {
771 if (!proxies[i]->instantiate(target->resourceProvider())) {
772 return;
773 }
774 proxiesSPs[i] = sk_ref_sp(proxies[i]);
Brian Salomon34169692017-08-28 15:32:01 -0400775 }
Brian Salomon336ce7b2017-09-08 08:23:58 -0400776
Brian Salomonb80ffee2018-05-23 16:39:39 -0400777 Domain domain = fDomain ? Domain::kYes : Domain::kNo;
Brian Salomon485b8c62018-01-12 15:11:06 -0500778 bool coverageAA = GrAAType::kCoverage == this->aaType();
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400779 sk_sp<GrGeometryProcessor> gp = TextureGeometryProcessor::Make(
Brian Osman3ebd3542018-07-30 14:36:53 -0400780 proxiesSPs, fProxyCnt, std::move(fTextureColorSpaceXform),
781 std::move(fPaintColorSpaceXform), coverageAA, fPerspective,
Brian Salomonb80ffee2018-05-23 16:39:39 -0400782 domain, filters, *target->caps().shaderCaps());
Brian Salomon34169692017-08-28 15:32:01 -0400783 GrPipeline::InitArgs args;
784 args.fProxy = target->proxy();
785 args.fCaps = &target->caps();
786 args.fResourceProvider = target->resourceProvider();
Brian Salomon485b8c62018-01-12 15:11:06 -0500787 args.fFlags = 0;
Brian Salomon485b8c62018-01-12 15:11:06 -0500788 if (GrAAType::kMSAA == this->aaType()) {
789 args.fFlags |= GrPipeline::kHWAntialias_Flag;
790 }
791
Brian Salomon49348902018-06-26 09:12:38 -0400792 auto clip = target->detachAppliedClip();
793 const auto* fixedDynamicState = target->allocFixedDynamicState(clip.scissorState().rect());
794 const auto* pipeline =
795 target->allocPipeline(args, GrProcessorSet::MakeEmptySet(), std::move(clip));
Brian Salomon92be2f72018-06-19 14:33:47 -0400796 using TessFn =
797 decltype(&TextureOp::tess<SkPoint, MultiTexture::kNo, Domain::kNo, GrAA::kNo>);
798#define TESS_FN_AND_VERTEX_SIZE(Point, MT, Domain, AA) \
799 { \
800 &TextureOp::tess<Point, MT, Domain, AA>, \
801 sizeof(TextureGeometryProcessor::Vertex<Point, MT, Domain, AA>) \
802 }
803 static constexpr struct {
804 TessFn fTessFn;
805 size_t fVertexSize;
806 } kTessFnsAndVertexSizes[] = {
807 TESS_FN_AND_VERTEX_SIZE(SkPoint, MultiTexture::kNo, Domain::kNo, GrAA::kNo),
808 TESS_FN_AND_VERTEX_SIZE(SkPoint, MultiTexture::kNo, Domain::kNo, GrAA::kYes),
809 TESS_FN_AND_VERTEX_SIZE(SkPoint, MultiTexture::kNo, Domain::kYes, GrAA::kNo),
810 TESS_FN_AND_VERTEX_SIZE(SkPoint, MultiTexture::kNo, Domain::kYes, GrAA::kYes),
811 TESS_FN_AND_VERTEX_SIZE(SkPoint, MultiTexture::kYes, Domain::kNo, GrAA::kNo),
812 TESS_FN_AND_VERTEX_SIZE(SkPoint, MultiTexture::kYes, Domain::kNo, GrAA::kYes),
813 TESS_FN_AND_VERTEX_SIZE(SkPoint, MultiTexture::kYes, Domain::kYes, GrAA::kNo),
814 TESS_FN_AND_VERTEX_SIZE(SkPoint, MultiTexture::kYes, Domain::kYes, GrAA::kYes),
815 TESS_FN_AND_VERTEX_SIZE(SkPoint3, MultiTexture::kNo, Domain::kNo, GrAA::kNo),
816 TESS_FN_AND_VERTEX_SIZE(SkPoint3, MultiTexture::kNo, Domain::kNo, GrAA::kYes),
817 TESS_FN_AND_VERTEX_SIZE(SkPoint3, MultiTexture::kNo, Domain::kYes, GrAA::kNo),
818 TESS_FN_AND_VERTEX_SIZE(SkPoint3, MultiTexture::kNo, Domain::kYes, GrAA::kYes),
819 TESS_FN_AND_VERTEX_SIZE(SkPoint3, MultiTexture::kYes, Domain::kNo, GrAA::kNo),
820 TESS_FN_AND_VERTEX_SIZE(SkPoint3, MultiTexture::kYes, Domain::kNo, GrAA::kYes),
821 TESS_FN_AND_VERTEX_SIZE(SkPoint3, MultiTexture::kYes, Domain::kYes, GrAA::kNo),
822 TESS_FN_AND_VERTEX_SIZE(SkPoint3, MultiTexture::kYes, Domain::kYes, GrAA::kYes),
823 };
824#undef TESS_FN_AND_VERTEX_SIZE
825 int tessFnIdx = 0;
826 tessFnIdx |= coverageAA ? 0x1 : 0x0;
827 tessFnIdx |= fDomain ? 0x2 : 0x0;
828 tessFnIdx |= (fProxyCnt > 1) ? 0x4 : 0x0;
829 tessFnIdx |= fPerspective ? 0x8 : 0x0;
830
831 SkASSERT(kTessFnsAndVertexSizes[tessFnIdx].fVertexSize == gp->debugOnly_vertexStride());
832
Brian Salomon34169692017-08-28 15:32:01 -0400833 int vstart;
834 const GrBuffer* vbuffer;
Brian Salomon92be2f72018-06-19 14:33:47 -0400835 void* vdata = target->makeVertexSpace(kTessFnsAndVertexSizes[tessFnIdx].fVertexSize,
836 4 * fDraws.count(), &vbuffer, &vstart);
Brian Salomon336ce7b2017-09-08 08:23:58 -0400837 if (!vdata) {
Brian Salomon34169692017-08-28 15:32:01 -0400838 SkDebugf("Could not allocate vertices\n");
839 return;
840 }
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400841
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400842 float iw[kMaxTextures];
843 float ih[kMaxTextures];
844 for (int t = 0; t < fProxyCnt; ++t) {
845 const auto* texture = proxies[t]->priv().peekTexture();
846 iw[t] = 1.f / texture->width();
847 ih[t] = 1.f / texture->height();
848 }
849
Brian Salomon92be2f72018-06-19 14:33:47 -0400850 (this->*(kTessFnsAndVertexSizes[tessFnIdx].fTessFn))(vdata, iw, ih, gp.get());
Brian Salomonb80ffee2018-05-23 16:39:39 -0400851
Brian Salomon57caa662017-10-18 12:21:05 +0000852 GrPrimitiveType primitiveType =
853 fDraws.count() > 1 ? GrPrimitiveType::kTriangles : GrPrimitiveType::kTriangleStrip;
854 GrMesh mesh(primitiveType);
Brian Salomon34169692017-08-28 15:32:01 -0400855 if (fDraws.count() > 1) {
Brian Salomon57caa662017-10-18 12:21:05 +0000856 sk_sp<const GrBuffer> ibuffer = target->resourceProvider()->refQuadIndexBuffer();
Brian Salomon34169692017-08-28 15:32:01 -0400857 if (!ibuffer) {
858 SkDebugf("Could not allocate quad indices\n");
859 return;
860 }
Brian Salomon34169692017-08-28 15:32:01 -0400861 mesh.setIndexedPatterned(ibuffer.get(), 6, 4, fDraws.count(),
862 GrResourceProvider::QuadCountOfQuadBuffer());
Brian Salomon34169692017-08-28 15:32:01 -0400863 } else {
Brian Salomon34169692017-08-28 15:32:01 -0400864 mesh.setNonIndexedNonInstanced(4);
Brian Salomon34169692017-08-28 15:32:01 -0400865 }
Brian Salomon57caa662017-10-18 12:21:05 +0000866 mesh.setVertexData(vbuffer, vstart);
Brian Salomon49348902018-06-26 09:12:38 -0400867 target->draw(gp.get(), pipeline, fixedDynamicState, mesh);
Brian Salomon34169692017-08-28 15:32:01 -0400868 }
869
870 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
871 const auto* that = t->cast<TextureOp>();
Brian Salomon762d5e72017-12-01 10:25:08 -0500872 const auto& shaderCaps = *caps.shaderCaps();
Brian Osman3ebd3542018-07-30 14:36:53 -0400873 if (!GrColorSpaceXform::Equals(fTextureColorSpaceXform.get(),
874 that->fTextureColorSpaceXform.get())) {
875 return false;
876 }
877 if (!GrColorSpaceXform::Equals(fPaintColorSpaceXform.get(),
878 that->fPaintColorSpaceXform.get())) {
Brian Salomon34169692017-08-28 15:32:01 -0400879 return false;
880 }
Brian Salomon485b8c62018-01-12 15:11:06 -0500881 if (this->aaType() != that->aaType()) {
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500882 return false;
883 }
Brian Salomon336ce7b2017-09-08 08:23:58 -0400884 // Because of an issue where GrColorSpaceXform adds the same function every time it is used
885 // in a texture lookup, we only allow multiple textures when there is no transform.
Brian Osman3ebd3542018-07-30 14:36:53 -0400886 if (TextureGeometryProcessor::SupportsMultitexture(shaderCaps) &&
887 !fTextureColorSpaceXform &&
Brian Salomon762d5e72017-12-01 10:25:08 -0500888 fMaxApproxDstPixelArea <= shaderCaps.disableImageMultitexturingDstRectAreaThreshold() &&
889 that->fMaxApproxDstPixelArea <=
890 shaderCaps.disableImageMultitexturingDstRectAreaThreshold()) {
Brian Salomon336ce7b2017-09-08 08:23:58 -0400891 int map[kMaxTextures];
Brian Salomon762d5e72017-12-01 10:25:08 -0500892 int numNewProxies = this->mergeProxies(that, map, shaderCaps);
Brian Salomon336ce7b2017-09-08 08:23:58 -0400893 if (numNewProxies < 0) {
894 return false;
895 }
896 if (1 == fProxyCnt && numNewProxies) {
897 void* mem = new char[(sizeof(GrSamplerState::Filter) + sizeof(GrTextureProxy*)) *
898 kMaxTextures];
899 auto proxies = reinterpret_cast<GrTextureProxy**>(mem);
900 auto filters = reinterpret_cast<GrSamplerState::Filter*>(proxies + kMaxTextures);
901 proxies[0] = fProxy0;
902 filters[0] = fFilter0;
903 fProxyArray = proxies;
904 }
905 fProxyCnt += numNewProxies;
906 auto thisProxies = fProxyArray;
907 auto thatProxies = that->proxies();
908 auto thatFilters = that->filters();
909 auto thisFilters = reinterpret_cast<GrSamplerState::Filter*>(thisProxies +
910 kMaxTextures);
911 for (int i = 0; i < that->fProxyCnt; ++i) {
912 if (map[i] < 0) {
913 thatProxies[i]->addPendingRead();
Robert Phillipsb493eeb2017-09-13 13:10:52 -0400914
Brian Salomon336ce7b2017-09-08 08:23:58 -0400915 thisProxies[-map[i]] = thatProxies[i];
916 thisFilters[-map[i]] = thatFilters[i];
917 map[i] = -map[i];
918 }
919 }
920 int firstNewDraw = fDraws.count();
921 fDraws.push_back_n(that->fDraws.count(), that->fDraws.begin());
922 for (int i = firstNewDraw; i < fDraws.count(); ++i) {
Brian Salomonb80ffee2018-05-23 16:39:39 -0400923 fDraws[i].setTextureIdx(map[fDraws[i].textureIdx()]);
Brian Salomon336ce7b2017-09-08 08:23:58 -0400924 }
925 } else {
Brian Salomonbbf05752017-11-30 11:30:48 -0500926 // We can get here when one of the ops is already multitextured but the other cannot
927 // be because of the dst rect size.
928 if (fProxyCnt > 1 || that->fProxyCnt > 1) {
929 return false;
930 }
Brian Salomon336ce7b2017-09-08 08:23:58 -0400931 if (fProxy0->uniqueID() != that->fProxy0->uniqueID() || fFilter0 != that->fFilter0) {
932 return false;
933 }
934 fDraws.push_back_n(that->fDraws.count(), that->fDraws.begin());
935 }
Brian Salomon34169692017-08-28 15:32:01 -0400936 this->joinBounds(*that);
Brian Salomon762d5e72017-12-01 10:25:08 -0500937 fMaxApproxDstPixelArea = SkTMax(that->fMaxApproxDstPixelArea, fMaxApproxDstPixelArea);
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400938 fPerspective |= that->fPerspective;
Brian Salomonb80ffee2018-05-23 16:39:39 -0400939 fDomain |= that->fDomain;
Brian Salomon34169692017-08-28 15:32:01 -0400940 return true;
941 }
942
Brian Salomon336ce7b2017-09-08 08:23:58 -0400943 /**
944 * Determines a mapping of indices from that's proxy array to this's proxy array. A negative map
945 * value means that's proxy should be added to this's proxy array at the absolute value of
946 * the map entry. If it is determined that the ops shouldn't combine their proxies then a
947 * negative value is returned. Otherwise, return value indicates the number of proxies that have
948 * to be added to this op or, equivalently, the number of negative entries in map.
949 */
950 int mergeProxies(const TextureOp* that, int map[kMaxTextures], const GrShaderCaps& caps) const {
951 std::fill_n(map, kMaxTextures, -kMaxTextures);
952 int sharedProxyCnt = 0;
953 auto thisProxies = this->proxies();
954 auto thisFilters = this->filters();
955 auto thatProxies = that->proxies();
956 auto thatFilters = that->filters();
957 for (int i = 0; i < fProxyCnt; ++i) {
958 for (int j = 0; j < that->fProxyCnt; ++j) {
959 if (thisProxies[i]->uniqueID() == thatProxies[j]->uniqueID()) {
960 if (thisFilters[i] != thatFilters[j]) {
961 // In GL we don't currently support using the same texture with different
962 // samplers. If we added support for sampler objects and a cap bit to know
963 // it's ok to use different filter modes then we could support this.
964 // Otherwise, we could also only allow a single filter mode for each op
965 // instance.
966 return -1;
967 }
968 map[j] = i;
969 ++sharedProxyCnt;
970 break;
971 }
972 }
973 }
Brian Salomon2b6f6142017-11-13 11:49:13 -0500974 int actualMaxTextures = SkTMin(caps.maxFragmentSamplers(), kMaxTextures);
Brian Salomon336ce7b2017-09-08 08:23:58 -0400975 int newProxyCnt = that->fProxyCnt - sharedProxyCnt;
976 if (newProxyCnt + fProxyCnt > actualMaxTextures) {
977 return -1;
978 }
979 GrPixelConfig config = thisProxies[0]->config();
980 int nextSlot = fProxyCnt;
981 for (int j = 0; j < that->fProxyCnt; ++j) {
982 // We want to avoid making many shaders because of different permutations of shader
983 // based swizzle and sampler types. The approach taken here is to require the configs to
984 // be the same and to only allow already instantiated proxies that have the most
985 // common sampler type. Otherwise we don't merge.
986 if (thatProxies[j]->config() != config) {
987 return -1;
988 }
989 if (GrTexture* tex = thatProxies[j]->priv().peekTexture()) {
Brian Salomon60dd8c72018-07-30 10:24:13 -0400990 if (tex->texturePriv().textureType() != GrTextureType::k2D) {
Brian Salomon336ce7b2017-09-08 08:23:58 -0400991 return -1;
992 }
993 }
994 if (map[j] < 0) {
995 map[j] = -(nextSlot++);
996 }
997 }
998 return newProxyCnt;
999 }
1000
Brian Salomon485b8c62018-01-12 15:11:06 -05001001 GrAAType aaType() const { return static_cast<GrAAType>(fAAType); }
Brian Salomonb5ef1f92018-01-11 11:46:21 -05001002
Brian Salomon336ce7b2017-09-08 08:23:58 -04001003 GrTextureProxy* const* proxies() const { return fProxyCnt > 1 ? fProxyArray : &fProxy0; }
1004
1005 const GrSamplerState::Filter* filters() const {
1006 if (fProxyCnt > 1) {
1007 return reinterpret_cast<const GrSamplerState::Filter*>(fProxyArray + kMaxTextures);
1008 }
1009 return &fFilter0;
1010 }
1011
Brian Salomonb80ffee2018-05-23 16:39:39 -04001012 class Draw {
1013 public:
1014 Draw(const SkRect& srcRect, int textureIdx, const GrPerspQuad& quad,
1015 SkCanvas::SrcRectConstraint constraint, GrColor color)
1016 : fSrcRect(srcRect)
1017 , fHasDomain(constraint == SkCanvas::kStrict_SrcRectConstraint)
1018 , fTextureIdx(SkToUInt(textureIdx))
1019 , fQuad(quad)
1020 , fColor(color) {}
1021 const GrPerspQuad& quad() const { return fQuad; }
1022 int textureIdx() const { return SkToInt(fTextureIdx); }
1023 const SkRect& srcRect() const { return fSrcRect; }
1024 GrColor color() const { return fColor; }
1025 Domain domain() const { return Domain(fHasDomain); }
1026 void setTextureIdx(int i) { fTextureIdx = SkToUInt(i); }
1027
1028 private:
Brian Salomon34169692017-08-28 15:32:01 -04001029 SkRect fSrcRect;
Brian Salomonb80ffee2018-05-23 16:39:39 -04001030 unsigned fHasDomain : 1;
1031 unsigned fTextureIdx : 31;
Brian Salomonbe3c1d22018-05-21 12:54:39 -04001032 GrPerspQuad fQuad;
Brian Salomon34169692017-08-28 15:32:01 -04001033 GrColor fColor;
1034 };
1035 SkSTArray<1, Draw, true> fDraws;
Brian Osman3ebd3542018-07-30 14:36:53 -04001036 sk_sp<GrColorSpaceXform> fTextureColorSpaceXform;
1037 sk_sp<GrColorSpaceXform> fPaintColorSpaceXform;
Brian Salomon336ce7b2017-09-08 08:23:58 -04001038 // Initially we store a single proxy ptr and a single filter. If we grow to have more than
1039 // one proxy we instead store pointers to dynamically allocated arrays of size kMaxTextures
1040 // followed by kMaxTextures filters.
1041 union {
1042 GrTextureProxy* fProxy0;
1043 GrTextureProxy** fProxyArray;
1044 };
Brian Salomonbbf05752017-11-30 11:30:48 -05001045 size_t fMaxApproxDstPixelArea;
Brian Salomon336ce7b2017-09-08 08:23:58 -04001046 GrSamplerState::Filter fFilter0;
1047 uint8_t fProxyCnt;
Brian Salomon485b8c62018-01-12 15:11:06 -05001048 unsigned fAAType : 2;
Brian Salomonbe3c1d22018-05-21 12:54:39 -04001049 unsigned fPerspective : 1;
Brian Salomonb80ffee2018-05-23 16:39:39 -04001050 unsigned fDomain : 1;
Brian Salomon34169692017-08-28 15:32:01 -04001051 // Used to track whether fProxy is ref'ed or has a pending IO after finalize() is called.
Brian Salomonb5ef1f92018-01-11 11:46:21 -05001052 unsigned fFinalized : 1;
Brian Salomon336ce7b2017-09-08 08:23:58 -04001053
Brian Salomon34169692017-08-28 15:32:01 -04001054 typedef GrMeshDrawOp INHERITED;
1055};
1056
Brian Salomon336ce7b2017-09-08 08:23:58 -04001057constexpr int TextureGeometryProcessor::kMaxTextures;
1058constexpr int TextureOp::kMaxTextures;
1059
Brian Salomon34169692017-08-28 15:32:01 -04001060} // anonymous namespace
1061
1062namespace GrTextureOp {
1063
Robert Phillips7c525e62018-06-12 10:11:12 -04001064std::unique_ptr<GrDrawOp> Make(GrContext* context,
1065 sk_sp<GrTextureProxy> proxy,
1066 GrSamplerState::Filter filter,
1067 GrColor color,
1068 const SkRect& srcRect,
1069 const SkRect& dstRect,
1070 GrAAType aaType,
1071 SkCanvas::SrcRectConstraint constraint,
1072 const SkMatrix& viewMatrix,
Brian Osman3ebd3542018-07-30 14:36:53 -04001073 sk_sp<GrColorSpaceXform> textureColorSpaceXform,
1074 sk_sp<GrColorSpaceXform> paintColorSpaceXform) {
Robert Phillips7c525e62018-06-12 10:11:12 -04001075 return TextureOp::Make(context, std::move(proxy), filter, color, srcRect, dstRect, aaType,
Brian Osman3ebd3542018-07-30 14:36:53 -04001076 constraint, viewMatrix, std::move(textureColorSpaceXform),
1077 std::move(paintColorSpaceXform));
Brian Salomon34169692017-08-28 15:32:01 -04001078}
1079
1080} // namespace GrTextureOp
1081
1082#if GR_TEST_UTILS
1083#include "GrContext.h"
Robert Phillips1afd4cd2018-01-08 13:40:32 -05001084#include "GrContextPriv.h"
Robert Phillips0bd24dc2018-01-16 08:06:32 -05001085#include "GrProxyProvider.h"
Brian Salomon34169692017-08-28 15:32:01 -04001086
1087GR_DRAW_OP_TEST_DEFINE(TextureOp) {
1088 GrSurfaceDesc desc;
1089 desc.fConfig = kRGBA_8888_GrPixelConfig;
1090 desc.fHeight = random->nextULessThan(90) + 10;
1091 desc.fWidth = random->nextULessThan(90) + 10;
Brian Salomon2a4f9832018-03-03 22:43:43 -05001092 auto origin = random->nextBool() ? kTopLeft_GrSurfaceOrigin : kBottomLeft_GrSurfaceOrigin;
Greg Daniel09c94002018-06-08 22:11:51 +00001093 GrMipMapped mipMapped = random->nextBool() ? GrMipMapped::kYes : GrMipMapped::kNo;
1094 SkBackingFit fit = SkBackingFit::kExact;
1095 if (mipMapped == GrMipMapped::kNo) {
1096 fit = random->nextBool() ? SkBackingFit::kApprox : SkBackingFit::kExact;
1097 }
Robert Phillips0bd24dc2018-01-16 08:06:32 -05001098
1099 GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider();
Greg Daniel09c94002018-06-08 22:11:51 +00001100 sk_sp<GrTextureProxy> proxy = proxyProvider->createProxy(desc, origin, mipMapped, fit,
1101 SkBudgeted::kNo,
1102 GrInternalSurfaceFlags::kNone);
Robert Phillips0bd24dc2018-01-16 08:06:32 -05001103
Brian Salomon34169692017-08-28 15:32:01 -04001104 SkRect rect = GrTest::TestRect(random);
1105 SkRect srcRect;
1106 srcRect.fLeft = random->nextRangeScalar(0.f, proxy->width() / 2.f);
1107 srcRect.fRight = random->nextRangeScalar(0.f, proxy->width()) + proxy->width() / 2.f;
1108 srcRect.fTop = random->nextRangeScalar(0.f, proxy->height() / 2.f);
1109 srcRect.fBottom = random->nextRangeScalar(0.f, proxy->height()) + proxy->height() / 2.f;
1110 SkMatrix viewMatrix = GrTest::TestMatrixPreservesRightAngles(random);
1111 GrColor color = SkColorToPremulGrColor(random->nextU());
Brian Salomon2bbdcc42017-09-07 12:36:34 -04001112 GrSamplerState::Filter filter = (GrSamplerState::Filter)random->nextULessThan(
1113 static_cast<uint32_t>(GrSamplerState::Filter::kMipMap) + 1);
Greg Daniel09c94002018-06-08 22:11:51 +00001114 while (mipMapped == GrMipMapped::kNo && filter == GrSamplerState::Filter::kMipMap) {
1115 filter = (GrSamplerState::Filter)random->nextULessThan(
1116 static_cast<uint32_t>(GrSamplerState::Filter::kMipMap) + 1);
1117 }
Brian Osman3ebd3542018-07-30 14:36:53 -04001118 auto texXform = GrTest::TestColorXform(random);
1119 auto paintXform = GrTest::TestColorXform(random);
Brian Salomon485b8c62018-01-12 15:11:06 -05001120 GrAAType aaType = GrAAType::kNone;
1121 if (random->nextBool()) {
1122 aaType = (fsaaType == GrFSAAType::kUnifiedMSAA) ? GrAAType::kMSAA : GrAAType::kCoverage;
1123 }
Brian Salomonb80ffee2018-05-23 16:39:39 -04001124 auto constraint = random->nextBool() ? SkCanvas::kStrict_SrcRectConstraint
1125 : SkCanvas::kFast_SrcRectConstraint;
Robert Phillips7c525e62018-06-12 10:11:12 -04001126 return GrTextureOp::Make(context, std::move(proxy), filter, color, srcRect, rect, aaType,
Brian Osman3ebd3542018-07-30 14:36:53 -04001127 constraint, viewMatrix, std::move(texXform), std::move(paintXform));
Brian Salomon34169692017-08-28 15:32:01 -04001128}
1129
1130#endif