blob: 77f194cd754b34c870eaeb432211ccc300c5eedf [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
Brian Salomonaf874832018-08-03 11:53:40 -0400107 static sk_sp<GrGeometryProcessor> Make(GrTextureType textureType, GrPixelConfig textureConfig,
108 int textureCnt,
Brian Osman3ebd3542018-07-30 14:36:53 -0400109 sk_sp<GrColorSpaceXform> textureColorSpaceXform,
110 sk_sp<GrColorSpaceXform> paintColorSpaceXform,
Brian Salomonaf874832018-08-03 11:53:40 -0400111 bool coverageAA, bool perspective, Domain domain,
Brian Salomonb80ffee2018-05-23 16:39:39 -0400112 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.
Brian Salomonaf874832018-08-03 11:53:40 -0400116 int samplerCnt = NumSamplersToUse(textureCnt, caps);
Brian Salomon336ce7b2017-09-08 08:23:58 -0400117 size_t size = sizeof(TextureGeometryProcessor) + sizeof(TextureSampler) * (samplerCnt - 1);
118 void* mem = GrGeometryProcessor::operator new(size);
Brian Salomonaf874832018-08-03 11:53:40 -0400119 return sk_sp<TextureGeometryProcessor>(new (mem) TextureGeometryProcessor(
120 textureType, textureConfig, textureCnt, samplerCnt,
121 std::move(textureColorSpaceXform), std::move(paintColorSpaceXform), coverageAA,
122 perspective, domain, filters, caps));
Brian Salomon336ce7b2017-09-08 08:23:58 -0400123 }
124
125 ~TextureGeometryProcessor() override {
126 int cnt = this->numTextureSamplers();
127 for (int i = 1; i < cnt; ++i) {
128 fSamplers[i].~TextureSampler();
129 }
Brian Salomon34169692017-08-28 15:32:01 -0400130 }
131
132 const char* name() const override { return "TextureGeometryProcessor"; }
133
134 void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const override {
Brian Osman3ebd3542018-07-30 14:36:53 -0400135 b->add32(GrColorSpaceXform::XformKey(fTextureColorSpaceXform.get()));
136 b->add32(GrColorSpaceXform::XformKey(fPaintColorSpaceXform.get()));
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400137 uint32_t x = this->usesCoverageEdgeAA() ? 0 : 1;
Brian Salomon70132d02018-05-29 15:33:06 -0400138 x |= kFloat3_GrVertexAttribType == fPositions.type() ? 0 : 2;
Brian Salomonb80ffee2018-05-23 16:39:39 -0400139 x |= fDomain.isInitialized() ? 4 : 0;
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400140 b->add32(x);
Brian Salomon34169692017-08-28 15:32:01 -0400141 }
142
143 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps& caps) const override {
144 class GLSLProcessor : public GrGLSLGeometryProcessor {
145 public:
146 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& proc,
147 FPCoordTransformIter&& transformIter) override {
148 const auto& textureGP = proc.cast<TextureGeometryProcessor>();
149 this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
Brian Osman3ebd3542018-07-30 14:36:53 -0400150 fTextureColorSpaceXformHelper.setData(
151 pdman, textureGP.fTextureColorSpaceXform.get());
152 fPaintColorSpaceXformHelper.setData(pdman, textureGP.fPaintColorSpaceXform.get());
Brian Salomon34169692017-08-28 15:32:01 -0400153 }
154
155 private:
156 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
Chris Dalton7b046312018-02-02 11:06:30 -0700157 using Interpolation = GrGLSLVaryingHandler::Interpolation;
Brian Salomon34169692017-08-28 15:32:01 -0400158 const auto& textureGP = args.fGP.cast<TextureGeometryProcessor>();
Brian Osman3ebd3542018-07-30 14:36:53 -0400159 fTextureColorSpaceXformHelper.emitCode(
160 args.fUniformHandler, textureGP.fTextureColorSpaceXform.get());
161 fPaintColorSpaceXformHelper.emitCode(
162 args.fUniformHandler, textureGP.fPaintColorSpaceXform.get(),
163 kVertex_GrShaderFlag);
Brian Salomon70132d02018-05-29 15:33:06 -0400164 if (kFloat2_GrVertexAttribType == textureGP.fPositions.type()) {
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400165 args.fVaryingHandler->setNoPerspective();
166 }
Brian Salomon34169692017-08-28 15:32:01 -0400167 args.fVaryingHandler->emitAttributes(textureGP);
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400168 gpArgs->fPositionVar = textureGP.fPositions.asShaderVar();
169
Brian Salomon34169692017-08-28 15:32:01 -0400170 this->emitTransforms(args.fVertBuilder,
171 args.fVaryingHandler,
172 args.fUniformHandler,
Brian Salomon04460cc2017-12-06 14:47:42 -0500173 textureGP.fTextureCoords.asShaderVar(),
Brian Salomon34169692017-08-28 15:32:01 -0400174 args.fFPCoordTransformHandler);
Brian Osman3ebd3542018-07-30 14:36:53 -0400175 if (fPaintColorSpaceXformHelper.isNoop()) {
176 args.fVaryingHandler->addPassThroughAttribute(
177 textureGP.fColors, args.fOutputColor, Interpolation::kCanBeFlat);
178 } else {
179 GrGLSLVarying varying(kHalf4_GrSLType);
180 args.fVaryingHandler->addVarying("color", &varying);
181 args.fVertBuilder->codeAppend("half4 color = ");
182 args.fVertBuilder->appendColorGamutXform(textureGP.fColors.name(),
183 &fPaintColorSpaceXformHelper);
184 args.fVertBuilder->codeAppend(";");
185 args.fVertBuilder->codeAppendf("%s = half4(color.rgb * color.a, color.a);",
186 varying.vsOut());
187 args.fFragBuilder->codeAppendf("%s = %s;", args.fOutputColor, varying.fsIn());
188 }
Ethan Nicholas8aa45692017-09-20 11:24:15 -0400189 args.fFragBuilder->codeAppend("float2 texCoord;");
Brian Salomon92be2f72018-06-19 14:33:47 -0400190 args.fVaryingHandler->addPassThroughAttribute(textureGP.fTextureCoords, "texCoord");
Brian Salomonb80ffee2018-05-23 16:39:39 -0400191 if (textureGP.fDomain.isInitialized()) {
192 args.fFragBuilder->codeAppend("float4 domain;");
193 args.fVaryingHandler->addPassThroughAttribute(
Brian Salomon92be2f72018-06-19 14:33:47 -0400194 textureGP.fDomain, "domain",
Brian Salomonb80ffee2018-05-23 16:39:39 -0400195 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
196 args.fFragBuilder->codeAppend(
197 "texCoord = clamp(texCoord, domain.xy, domain.zw);");
198 }
Brian Salomon336ce7b2017-09-08 08:23:58 -0400199 if (textureGP.numTextureSamplers() > 1) {
Chris Dalton7b046312018-02-02 11:06:30 -0700200 // If this changes to float, reconsider Interpolation::kMustBeFlat.
Brian Salomon70132d02018-05-29 15:33:06 -0400201 SkASSERT(kInt_GrVertexAttribType == textureGP.fTextureIdx.type());
Brian Salomon336ce7b2017-09-08 08:23:58 -0400202 SkASSERT(args.fShaderCaps->integerSupport());
203 args.fFragBuilder->codeAppend("int texIdx;");
Brian Salomon92be2f72018-06-19 14:33:47 -0400204 args.fVaryingHandler->addPassThroughAttribute(textureGP.fTextureIdx, "texIdx",
Chris Dalton7b046312018-02-02 11:06:30 -0700205 Interpolation::kMustBeFlat);
Brian Salomon336ce7b2017-09-08 08:23:58 -0400206 args.fFragBuilder->codeAppend("switch (texIdx) {");
207 for (int i = 0; i < textureGP.numTextureSamplers(); ++i) {
208 args.fFragBuilder->codeAppendf("case %d: %s = ", i, args.fOutputColor);
Brian Osman3ebd3542018-07-30 14:36:53 -0400209 args.fFragBuilder->appendTextureLookupAndModulate(
210 args.fOutputColor, args.fTexSamplers[i], "texCoord",
211 kFloat2_GrSLType, &fTextureColorSpaceXformHelper);
Brian Salomon336ce7b2017-09-08 08:23:58 -0400212 args.fFragBuilder->codeAppend("; break;");
213 }
214 args.fFragBuilder->codeAppend("}");
215 } else {
216 args.fFragBuilder->codeAppendf("%s = ", args.fOutputColor);
Brian Osman3ebd3542018-07-30 14:36:53 -0400217 args.fFragBuilder->appendTextureLookupAndModulate(
218 args.fOutputColor, args.fTexSamplers[0], "texCoord",
219 kFloat2_GrSLType, &fTextureColorSpaceXformHelper);
Brian Salomon336ce7b2017-09-08 08:23:58 -0400220 }
Brian Salomon34169692017-08-28 15:32:01 -0400221 args.fFragBuilder->codeAppend(";");
Brian Salomon485b8c62018-01-12 15:11:06 -0500222 if (textureGP.usesCoverageEdgeAA()) {
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400223 bool mulByFragCoordW = false;
Brian Salomon9b4bd592018-07-10 09:23:40 -0400224 GrGLSLVarying aaDistVarying(kFloat4_GrSLType,
225 GrGLSLVarying::Scope::kVertToFrag);
226 if (kFloat3_GrVertexAttribType == textureGP.fPositions.type()) {
227 args.fVaryingHandler->addVarying("aaDists", &aaDistVarying);
228 // The distance from edge equation e to homogenous point p=sk_Position
229 // is e.x*p.x/p.wx + e.y*p.y/p.w + e.z. However, we want screen space
230 // interpolation of this distance. We can do this by multiplying the
231 // varying in the VS by p.w and then multiplying by sk_FragCoord.w in
232 // the FS. So we output e.x*p.x + e.y*p.y + e.z * p.w
233 args.fVertBuilder->codeAppendf(
234 R"(%s = float4(dot(aaEdge0, %s), dot(aaEdge1, %s),
235 dot(aaEdge2, %s), dot(aaEdge3, %s));)",
236 aaDistVarying.vsOut(), textureGP.fPositions.name(),
237 textureGP.fPositions.name(), textureGP.fPositions.name(),
238 textureGP.fPositions.name());
239 mulByFragCoordW = true;
Brian Salomondba65f92018-01-22 08:43:38 -0500240 } else {
Brian Salomon9b4bd592018-07-10 09:23:40 -0400241 args.fVaryingHandler->addVarying("aaDists", &aaDistVarying);
242 args.fVertBuilder->codeAppendf(
243 R"(%s = float4(dot(aaEdge0.xy, %s.xy) + aaEdge0.z,
244 dot(aaEdge1.xy, %s.xy) + aaEdge1.z,
245 dot(aaEdge2.xy, %s.xy) + aaEdge2.z,
246 dot(aaEdge3.xy, %s.xy) + aaEdge3.z);)",
247 aaDistVarying.vsOut(), textureGP.fPositions.name(),
248 textureGP.fPositions.name(), textureGP.fPositions.name(),
249 textureGP.fPositions.name());
Brian Salomondba65f92018-01-22 08:43:38 -0500250 }
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500251 args.fFragBuilder->codeAppendf(
252 "float mindist = min(min(%s.x, %s.y), min(%s.z, %s.w));",
Brian Salomon9b4bd592018-07-10 09:23:40 -0400253 aaDistVarying.fsIn(), aaDistVarying.fsIn(), aaDistVarying.fsIn(),
254 aaDistVarying.fsIn());
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400255 if (mulByFragCoordW) {
256 args.fFragBuilder->codeAppend("mindist *= sk_FragCoord.w;");
257 }
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400258 args.fFragBuilder->codeAppendf("%s = float4(saturate(mindist));",
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500259 args.fOutputCoverage);
260 } else {
261 args.fFragBuilder->codeAppendf("%s = float4(1);", args.fOutputCoverage);
262 }
Brian Salomon34169692017-08-28 15:32:01 -0400263 }
Brian Osman3ebd3542018-07-30 14:36:53 -0400264 GrGLSLColorSpaceXformHelper fTextureColorSpaceXformHelper;
265 GrGLSLColorSpaceXformHelper fPaintColorSpaceXformHelper;
Brian Salomon34169692017-08-28 15:32:01 -0400266 };
267 return new GLSLProcessor;
268 }
269
Brian Salomon485b8c62018-01-12 15:11:06 -0500270 bool usesCoverageEdgeAA() const { return SkToBool(fAAEdges[0].isInitialized()); }
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500271
Brian Salomon34169692017-08-28 15:32:01 -0400272private:
Brian Salomon336ce7b2017-09-08 08:23:58 -0400273 // This exists to reduce the number of shaders generated. It does some rounding of sampler
274 // counts.
275 static int NumSamplersToUse(int numRealProxies, const GrShaderCaps& caps) {
276 SkASSERT(numRealProxies > 0 && numRealProxies <= kMaxTextures &&
277 numRealProxies <= caps.maxFragmentSamplers());
278 if (1 == numRealProxies) {
279 return 1;
280 }
281 if (numRealProxies <= 4) {
282 return 4;
283 }
284 // Round to the next power of 2 and then clamp to kMaxTextures and the max allowed by caps.
285 return SkTMin(SkNextPow2(numRealProxies), SkTMin(kMaxTextures, caps.maxFragmentSamplers()));
286 }
287
Brian Salomonaf874832018-08-03 11:53:40 -0400288 TextureGeometryProcessor(GrTextureType textureType, GrPixelConfig textureConfig, int textureCnt,
289 int samplerCnt, sk_sp<GrColorSpaceXform> textureColorSpaceXform,
290 sk_sp<GrColorSpaceXform> paintColorSpaceXform, bool coverageAA,
291 bool perspective, Domain domain,
Brian Osman3ebd3542018-07-30 14:36:53 -0400292 const GrSamplerState::Filter filters[], const GrShaderCaps& caps)
293 : INHERITED(kTextureGeometryProcessor_ClassID)
294 , fTextureColorSpaceXform(std::move(textureColorSpaceXform))
295 , fPaintColorSpaceXform(std::move(paintColorSpaceXform)) {
Brian Salomonaf874832018-08-03 11:53:40 -0400296 SkASSERT(textureCnt > 0 && samplerCnt >= textureCnt);
297 fSamplers[0].reset(textureType, textureConfig, filters[0]);
298 for (int i = 1; i < textureCnt; ++i) {
Brian Salomon336ce7b2017-09-08 08:23:58 -0400299 // This class has one sampler built in, the rest come from memory this processor was
300 // placement-newed into and so haven't been constructed.
Brian Salomonaf874832018-08-03 11:53:40 -0400301 new (&fSamplers[i]) TextureSampler(textureType, textureConfig, filters[i]);
Brian Salomon336ce7b2017-09-08 08:23:58 -0400302 }
Brian Salomon30e1a5e2018-05-18 12:32:32 -0400303
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400304 if (perspective) {
Brian Salomon92be2f72018-06-19 14:33:47 -0400305 fPositions = {"position", kFloat3_GrVertexAttribType};
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400306 } else {
Brian Salomon92be2f72018-06-19 14:33:47 -0400307 fPositions = {"position", kFloat2_GrVertexAttribType};
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400308 }
Brian Salomon92be2f72018-06-19 14:33:47 -0400309 fColors = {"color", kUByte4_norm_GrVertexAttribType};
310 fTextureCoords = {"textureCoords", kFloat2_GrVertexAttribType};
311 int vertexAttributeCnt = 3;
Brian Salomon30e1a5e2018-05-18 12:32:32 -0400312
Brian Salomon336ce7b2017-09-08 08:23:58 -0400313 if (samplerCnt > 1) {
Brian Salomonaf874832018-08-03 11:53:40 -0400314 // Here we initialize any extra samplers. We repeat the first filter because our caller
315 // will specify the first texture for all the extra samplers. In GL the filter is
316 // implemented as a texture parameter and the last sampler will win.
317 for (int i = textureCnt; i < samplerCnt; ++i) {
318 new (&fSamplers[i]) TextureSampler(textureType, textureConfig, filters[0]);
Brian Salomon336ce7b2017-09-08 08:23:58 -0400319 }
320 SkASSERT(caps.integerSupport());
Brian Salomon92be2f72018-06-19 14:33:47 -0400321 fTextureIdx = {"textureIdx", kInt_GrVertexAttribType};
322 ++vertexAttributeCnt;
Brian Salomon336ce7b2017-09-08 08:23:58 -0400323 }
Brian Salomonb80ffee2018-05-23 16:39:39 -0400324 if (domain == Domain::kYes) {
Brian Salomon92be2f72018-06-19 14:33:47 -0400325 fDomain = {"domain", kFloat4_GrVertexAttribType};
326 ++vertexAttributeCnt;
Brian Salomonb80ffee2018-05-23 16:39:39 -0400327 }
Brian Salomon485b8c62018-01-12 15:11:06 -0500328 if (coverageAA) {
Brian Salomon92be2f72018-06-19 14:33:47 -0400329 fAAEdges[0] = {"aaEdge0", kFloat3_GrVertexAttribType};
330 fAAEdges[1] = {"aaEdge1", kFloat3_GrVertexAttribType};
331 fAAEdges[2] = {"aaEdge2", kFloat3_GrVertexAttribType};
332 fAAEdges[3] = {"aaEdge3", kFloat3_GrVertexAttribType};
333 vertexAttributeCnt += 4;
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500334 }
Brian Salomon92be2f72018-06-19 14:33:47 -0400335 this->setVertexAttributeCnt(vertexAttributeCnt);
Brian Salomonf7dcd762018-07-30 14:48:15 -0400336 this->setTextureSamplerCnt(samplerCnt);
Brian Salomon92be2f72018-06-19 14:33:47 -0400337 }
338
339 const Attribute& onVertexAttribute(int i) const override {
340 return IthInitializedAttribute(i, fPositions, fColors, fTextureCoords, fTextureIdx, fDomain,
341 fAAEdges[0], fAAEdges[1], fAAEdges[2], fAAEdges[3]);
Brian Salomon34169692017-08-28 15:32:01 -0400342 }
343
Brian Salomonf7dcd762018-07-30 14:48:15 -0400344 const TextureSampler& onTextureSampler(int i) const override { return fSamplers[i]; }
345
Brian Salomon34169692017-08-28 15:32:01 -0400346 Attribute fPositions;
Brian Salomon34169692017-08-28 15:32:01 -0400347 Attribute fColors;
Brian Salomon30e1a5e2018-05-18 12:32:32 -0400348 Attribute fTextureCoords;
349 Attribute fTextureIdx;
Brian Salomonb80ffee2018-05-23 16:39:39 -0400350 Attribute fDomain;
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500351 Attribute fAAEdges[4];
Brian Osman3ebd3542018-07-30 14:36:53 -0400352 sk_sp<GrColorSpaceXform> fTextureColorSpaceXform;
353 sk_sp<GrColorSpaceXform> fPaintColorSpaceXform;
Brian Salomon336ce7b2017-09-08 08:23:58 -0400354 TextureSampler fSamplers[1];
Ethan Nicholasabff9562017-10-09 10:54:08 -0400355
356 typedef GrGeometryProcessor INHERITED;
Brian Salomon34169692017-08-28 15:32:01 -0400357};
358
Brian Salomon6872e942018-05-18 10:29:54 -0400359// This computes the four edge equations for a quad, then outsets them and computes a new quad
360// as the intersection points of the outset edges. 'x' and 'y' contain the original points as input
361// and the outset points as output. 'a', 'b', and 'c' are the edge equation coefficients on output.
362static void compute_quad_edges_and_outset_vertices(Sk4f* x, Sk4f* y, Sk4f* a, Sk4f* b, Sk4f* c) {
363 static constexpr auto fma = SkNx_fma<4, float>;
364 // These rotate the points/edge values either clockwise or counterclockwise assuming tri strip
365 // order.
366 auto nextCW = [](const Sk4f& v) { return SkNx_shuffle<2, 0, 3, 1>(v); };
367 auto nextCCW = [](const Sk4f& v) { return SkNx_shuffle<1, 3, 0, 2>(v); };
368
369 auto xnext = nextCCW(*x);
370 auto ynext = nextCCW(*y);
371 *a = ynext - *y;
372 *b = *x - xnext;
373 *c = fma(xnext, *y, -ynext * *x);
374 Sk4f invNormLengths = (*a * *a + *b * *b).rsqrt();
375 // Make sure the edge equations have their normals facing into the quad in device space.
376 auto test = fma(*a, nextCW(*x), fma(*b, nextCW(*y), *c));
377 if ((test < Sk4f(0)).anyTrue()) {
378 invNormLengths = -invNormLengths;
379 }
380 *a *= invNormLengths;
381 *b *= invNormLengths;
382 *c *= invNormLengths;
383
384 // Here is the outset. This makes our edge equations compute coverage without requiring a
385 // half pixel offset and is also used to compute the bloated quad that will cover all
386 // pixels.
387 *c += Sk4f(0.5f);
388
389 // Reverse the process to compute the points of the bloated quad from the edge equations.
390 // This time the inputs don't have 1s as their third coord and we want to homogenize rather
391 // than normalize.
392 auto anext = nextCW(*a);
393 auto bnext = nextCW(*b);
394 auto cnext = nextCW(*c);
395 *x = fma(bnext, *c, -*b * cnext);
396 *y = fma(*a, cnext, -anext * *c);
397 auto ic = (fma(anext, *b, -bnext * *a)).invert();
398 *x *= ic;
399 *y *= ic;
400}
401
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500402namespace {
403// This is a class soley so it can be partially specialized (functions cannot be).
Brian Salomon86c40012018-05-22 10:48:49 -0400404template <typename V, GrAA AA = V::kAA, typename Position = typename V::Position>
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400405class VertexAAHandler;
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500406
Brian Salomon86c40012018-05-22 10:48:49 -0400407template<typename V> class VertexAAHandler<V, GrAA::kNo, SkPoint> {
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500408public:
Brian Salomon86c40012018-05-22 10:48:49 -0400409 static void AssignPositionsAndTexCoords(V* vertices, const GrPerspQuad& quad,
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500410 const SkRect& texRect) {
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400411 SkASSERT((quad.w4f() == Sk4f(1.f)).allTrue());
Brian Salomon86c40012018-05-22 10:48:49 -0400412 SkPointPriv::SetRectTriStrip(&vertices[0].fTextureCoords, texRect, sizeof(V));
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400413 for (int i = 0; i < 4; ++i) {
414 vertices[i].fPosition = {quad.x(i), quad.y(i)};
415 }
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500416 }
417};
418
Brian Salomon86c40012018-05-22 10:48:49 -0400419template<typename V> class VertexAAHandler<V, GrAA::kNo, SkPoint3> {
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500420public:
Brian Salomon86c40012018-05-22 10:48:49 -0400421 static void AssignPositionsAndTexCoords(V* vertices, const GrPerspQuad& quad,
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500422 const SkRect& texRect) {
Brian Salomon86c40012018-05-22 10:48:49 -0400423 SkPointPriv::SetRectTriStrip(&vertices[0].fTextureCoords, texRect, sizeof(V));
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400424 for (int i = 0; i < 4; ++i) {
425 vertices[i].fPosition = quad.point(i);
426 }
427 }
428};
429
Brian Salomon86c40012018-05-22 10:48:49 -0400430template<typename V> class VertexAAHandler<V, GrAA::kYes, SkPoint> {
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400431public:
Brian Salomon86c40012018-05-22 10:48:49 -0400432 static void AssignPositionsAndTexCoords(V* vertices, const GrPerspQuad& quad,
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400433 const SkRect& texRect) {
434 SkASSERT((quad.w4f() == Sk4f(1.f)).allTrue());
Brian Salomon6872e942018-05-18 10:29:54 -0400435 auto x = quad.x4f();
436 auto y = quad.y4f();
437 Sk4f a, b, c;
438 compute_quad_edges_and_outset_vertices(&x, &y, &a, &b, &c);
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500439
440 for (int i = 0; i < 4; ++i) {
Brian Salomon6872e942018-05-18 10:29:54 -0400441 vertices[i].fPosition = {x[i], y[i]};
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500442 for (int j = 0; j < 4; ++j) {
Brian Salomon6872e942018-05-18 10:29:54 -0400443 vertices[i].fEdges[j] = {a[j], b[j], c[j]};
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500444 }
445 }
446
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500447 AssignTexCoords(vertices, quad, texRect);
448 }
449
450private:
Brian Salomon86c40012018-05-22 10:48:49 -0400451 static void AssignTexCoords(V* vertices, const GrPerspQuad& quad, const SkRect& tex) {
Brian Salomona33b67c2018-05-17 10:42:14 -0400452 SkMatrix q = SkMatrix::MakeAll(quad.x(0), quad.x(1), quad.x(2),
453 quad.y(0), quad.y(1), quad.y(2),
454 1.f, 1.f, 1.f);
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500455 SkMatrix qinv;
456 if (!q.invert(&qinv)) {
457 return;
458 }
459 SkMatrix t = SkMatrix::MakeAll(tex.fLeft, tex.fLeft, tex.fRight,
460 tex.fTop, tex.fBottom, tex.fTop,
461 1.f, 1.f, 1.f);
462 SkMatrix map;
463 map.setConcat(t, qinv);
Brian Salomon86c40012018-05-22 10:48:49 -0400464 SkMatrixPriv::MapPointsWithStride(map, &vertices[0].fTextureCoords, sizeof(V),
465 &vertices[0].fPosition, sizeof(V), 4);
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500466 }
467};
468
Brian Salomon86c40012018-05-22 10:48:49 -0400469template<typename V> class VertexAAHandler<V, GrAA::kYes, SkPoint3> {
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400470public:
Brian Salomon86c40012018-05-22 10:48:49 -0400471 static void AssignPositionsAndTexCoords(V* vertices, const GrPerspQuad& quad,
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400472 const SkRect& texRect) {
473 auto x = quad.x4f();
474 auto y = quad.y4f();
475 auto iw = quad.iw4f();
476 x *= iw;
477 y *= iw;
478
479 // Get an equation for w from device space coords.
480 SkMatrix P;
481 P.setAll(x[0], y[0], 1, x[1], y[1], 1, x[2], y[2], 1);
482 SkAssertResult(P.invert(&P));
483 SkPoint3 weq{quad.w(0), quad.w(1), quad.w(2)};
484 P.mapHomogeneousPoints(&weq, &weq, 1);
485
486 Sk4f a, b, c;
487 compute_quad_edges_and_outset_vertices(&x, &y, &a, &b, &c);
488
489 // Compute new w values for the output vertices;
490 auto w = Sk4f(weq.fX) * x + Sk4f(weq.fY) * y + Sk4f(weq.fZ);
491 x *= w;
492 y *= w;
493
494 for (int i = 0; i < 4; ++i) {
495 vertices[i].fPosition = {x[i], y[i], w[i]};
496 for (int j = 0; j < 4; ++j) {
497 vertices[i].fEdges[j] = {a[j], b[j], c[j]};
498 }
499 }
500
501 AssignTexCoords(vertices, quad, texRect);
502 }
503
504private:
Brian Salomon86c40012018-05-22 10:48:49 -0400505 static void AssignTexCoords(V* vertices, const GrPerspQuad& quad, const SkRect& tex) {
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400506 SkMatrix q = SkMatrix::MakeAll(quad.x(0), quad.x(1), quad.x(2),
507 quad.y(0), quad.y(1), quad.y(2),
508 quad.w(0), quad.w(1), quad.w(2));
509 SkMatrix qinv;
510 if (!q.invert(&qinv)) {
511 return;
512 }
513 SkMatrix t = SkMatrix::MakeAll(tex.fLeft, tex.fLeft, tex.fRight,
514 tex.fTop, tex.fBottom, tex.fTop,
515 1.f, 1.f, 1.f);
516 SkMatrix map;
517 map.setConcat(t, qinv);
518 SkPoint3 tempTexCoords[4];
519 SkMatrixPriv::MapHomogeneousPointsWithStride(map, tempTexCoords, sizeof(SkPoint3),
Brian Salomon86c40012018-05-22 10:48:49 -0400520 &vertices[0].fPosition, sizeof(V), 4);
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400521 for (int i = 0; i < 4; ++i) {
522 auto invW = 1.f / tempTexCoords[i].fZ;
523 vertices[i].fTextureCoords.fX = tempTexCoords[i].fX * invW;
524 vertices[i].fTextureCoords.fY = tempTexCoords[i].fY * invW;
525 }
526 }
527};
528
Brian Salomon17031a72018-05-22 14:14:07 -0400529template <typename V, MultiTexture MT = V::kMultiTexture> struct TexIdAssigner;
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500530
Brian Salomon17031a72018-05-22 14:14:07 -0400531template <typename V> struct TexIdAssigner<V, MultiTexture::kYes> {
Brian Salomon86c40012018-05-22 10:48:49 -0400532 static void Assign(V* vertices, int textureIdx) {
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400533 for (int i = 0; i < 4; ++i) {
534 vertices[i].fTextureIdx = textureIdx;
535 }
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500536 }
537};
538
Brian Salomon17031a72018-05-22 14:14:07 -0400539template <typename V> struct TexIdAssigner<V, MultiTexture::kNo> {
Brian Salomon86c40012018-05-22 10:48:49 -0400540 static void Assign(V* vertices, int textureIdx) {}
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500541};
Brian Salomonb80ffee2018-05-23 16:39:39 -0400542
543template <typename V, Domain D = V::kDomain> struct DomainAssigner;
544
545template <typename V> struct DomainAssigner<V, Domain::kYes> {
546 static void Assign(V* vertices, Domain domain, GrSamplerState::Filter filter,
547 const SkRect& srcRect, GrSurfaceOrigin origin, float iw, float ih) {
548 static constexpr SkRect kLargeRect = {-2, -2, 2, 2};
549 SkRect domainRect;
550 if (domain == Domain::kYes) {
551 auto ltrb = Sk4f::Load(&srcRect);
552 if (filter == GrSamplerState::Filter::kBilerp) {
553 auto rblt = SkNx_shuffle<2, 3, 0, 1>(ltrb);
554 auto whwh = (rblt - ltrb).abs();
555 auto c = (rblt + ltrb) * 0.5f;
556 static const Sk4f kOffsets = {0.5f, 0.5f, -0.5f, -0.5f};
557 ltrb = (whwh < 1.f).thenElse(c, ltrb + kOffsets);
558 }
559 ltrb *= Sk4f(iw, ih, iw, ih);
560 if (origin == kBottomLeft_GrSurfaceOrigin) {
561 static const Sk4f kMul = {1.f, -1.f, 1.f, -1.f};
562 static const Sk4f kAdd = {0.f, 1.f, 0.f, 1.f};
563 ltrb = SkNx_shuffle<0, 3, 2, 1>(kMul * ltrb + kAdd);
564 }
565 ltrb.store(&domainRect);
566 } else {
567 domainRect = kLargeRect;
568 }
569 for (int i = 0; i < 4; ++i) {
570 vertices[i].fTextureDomain = domainRect;
571 }
572 }
573};
574
575template <typename V> struct DomainAssigner<V, Domain::kNo> {
576 static void Assign(V*, Domain domain, GrSamplerState::Filter, const SkRect&, GrSurfaceOrigin,
577 float iw, float ih) {
578 SkASSERT(domain == Domain::kNo);
579 }
580};
581
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500582} // anonymous namespace
583
Brian Salomon86c40012018-05-22 10:48:49 -0400584template <typename V>
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400585static void tessellate_quad(const GrPerspQuad& devQuad, const SkRect& srcRect, GrColor color,
Brian Salomonb80ffee2018-05-23 16:39:39 -0400586 GrSurfaceOrigin origin, GrSamplerState::Filter filter, V* vertices,
587 SkScalar iw, SkScalar ih, int textureIdx, Domain domain) {
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500588 SkRect texRect = {
589 iw * srcRect.fLeft,
590 ih * srcRect.fTop,
591 iw * srcRect.fRight,
592 ih * srcRect.fBottom
593 };
594 if (origin == kBottomLeft_GrSurfaceOrigin) {
595 texRect.fTop = 1.f - texRect.fTop;
596 texRect.fBottom = 1.f - texRect.fBottom;
597 }
Brian Salomon86c40012018-05-22 10:48:49 -0400598 VertexAAHandler<V>::AssignPositionsAndTexCoords(vertices, devQuad, texRect);
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500599 vertices[0].fColor = color;
600 vertices[1].fColor = color;
601 vertices[2].fColor = color;
602 vertices[3].fColor = color;
Brian Salomon86c40012018-05-22 10:48:49 -0400603 TexIdAssigner<V>::Assign(vertices, textureIdx);
Brian Salomonb80ffee2018-05-23 16:39:39 -0400604 DomainAssigner<V>::Assign(vertices, domain, filter, srcRect, origin, iw, ih);
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500605}
Brian Salomon17031a72018-05-22 14:14:07 -0400606
Brian Salomon34169692017-08-28 15:32:01 -0400607/**
608 * Op that implements GrTextureOp::Make. It draws textured quads. Each quad can modulate against a
609 * the texture by color. The blend with the destination is always src-over. The edges are non-AA.
610 */
611class TextureOp final : public GrMeshDrawOp {
612public:
Robert Phillips7c525e62018-06-12 10:11:12 -0400613 static std::unique_ptr<GrDrawOp> Make(GrContext* context,
614 sk_sp<GrTextureProxy> proxy,
615 GrSamplerState::Filter filter,
616 GrColor color,
617 const SkRect& srcRect,
618 const SkRect& dstRect,
619 GrAAType aaType,
620 SkCanvas::SrcRectConstraint constraint,
Brian Osman2b23c4b2018-06-01 12:25:08 -0400621 const SkMatrix& viewMatrix,
Brian Osman3ebd3542018-07-30 14:36:53 -0400622 sk_sp<GrColorSpaceXform> textureColorSpaceXform,
623 sk_sp<GrColorSpaceXform> paintColorSpaceXform) {
Robert Phillipsc994a932018-06-19 13:09:54 -0400624 GrOpMemoryPool* pool = context->contextPriv().opMemoryPool();
625
626 return pool->allocate<TextureOp>(std::move(proxy), filter, color,
627 srcRect, dstRect, aaType, constraint,
Brian Osman3ebd3542018-07-30 14:36:53 -0400628 viewMatrix, std::move(textureColorSpaceXform),
629 std::move(paintColorSpaceXform));
Brian Salomon34169692017-08-28 15:32:01 -0400630 }
631
Brian Salomon336ce7b2017-09-08 08:23:58 -0400632 ~TextureOp() override {
633 if (fFinalized) {
634 auto proxies = this->proxies();
635 for (int i = 0; i < fProxyCnt; ++i) {
636 proxies[i]->completedRead();
637 }
638 if (fProxyCnt > 1) {
639 delete[] reinterpret_cast<const char*>(proxies);
640 }
641 } else {
642 SkASSERT(1 == fProxyCnt);
643 fProxy0->unref();
644 }
645 }
Brian Salomon34169692017-08-28 15:32:01 -0400646
647 const char* name() const override { return "TextureOp"; }
648
Robert Phillipsf1748f52017-09-14 14:11:24 -0400649 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillipsb493eeb2017-09-13 13:10:52 -0400650 auto proxies = this->proxies();
651 for (int i = 0; i < fProxyCnt; ++i) {
652 func(proxies[i]);
653 }
654 }
655
Brian Salomon34169692017-08-28 15:32:01 -0400656 SkString dumpInfo() const override {
657 SkString str;
Brian Salomon34169692017-08-28 15:32:01 -0400658 str.appendf("# draws: %d\n", fDraws.count());
Brian Salomon336ce7b2017-09-08 08:23:58 -0400659 auto proxies = this->proxies();
660 for (int i = 0; i < fProxyCnt; ++i) {
661 str.appendf("Proxy ID %d: %d, Filter: %d\n", i, proxies[i]->uniqueID().asUInt(),
662 static_cast<int>(this->filters()[i]));
663 }
Brian Salomon34169692017-08-28 15:32:01 -0400664 for (int i = 0; i < fDraws.count(); ++i) {
665 const Draw& draw = fDraws[i];
666 str.appendf(
Brian Salomon336ce7b2017-09-08 08:23:58 -0400667 "%d: Color: 0x%08x, ProxyIdx: %d, TexRect [L: %.2f, T: %.2f, R: %.2f, B: %.2f] "
668 "Quad [(%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f)]\n",
Brian Salomonb80ffee2018-05-23 16:39:39 -0400669 i, draw.color(), draw.textureIdx(), draw.srcRect().fLeft, draw.srcRect().fTop,
670 draw.srcRect().fRight, draw.srcRect().fBottom, draw.quad().point(0).fX,
671 draw.quad().point(0).fY, draw.quad().point(1).fX, draw.quad().point(1).fY,
672 draw.quad().point(2).fX, draw.quad().point(2).fY, draw.quad().point(3).fX,
673 draw.quad().point(3).fY);
Brian Salomon34169692017-08-28 15:32:01 -0400674 }
675 str += INHERITED::dumpInfo();
676 return str;
677 }
678
Brian Osman532b3f92018-07-11 10:02:07 -0400679 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip) override {
Brian Salomon34169692017-08-28 15:32:01 -0400680 SkASSERT(!fFinalized);
Brian Salomon336ce7b2017-09-08 08:23:58 -0400681 SkASSERT(1 == fProxyCnt);
Brian Salomon34169692017-08-28 15:32:01 -0400682 fFinalized = true;
Brian Salomon336ce7b2017-09-08 08:23:58 -0400683 fProxy0->addPendingRead();
684 fProxy0->unref();
Brian Salomon34169692017-08-28 15:32:01 -0400685 return RequiresDstTexture::kNo;
686 }
687
Brian Salomon485b8c62018-01-12 15:11:06 -0500688 FixedFunctionFlags fixedFunctionFlags() const override {
689 return this->aaType() == GrAAType::kMSAA ? FixedFunctionFlags::kUsesHWAA
690 : FixedFunctionFlags::kNone;
691 }
Brian Salomon34169692017-08-28 15:32:01 -0400692
693 DEFINE_OP_CLASS_ID
694
695private:
Robert Phillips7c525e62018-06-12 10:11:12 -0400696 friend class ::GrOpMemoryPool;
Brian Salomon762d5e72017-12-01 10:25:08 -0500697
698 // This is used in a heursitic for choosing a code path. We don't care what happens with
699 // really large rects, infs, nans, etc.
700#if defined(__clang__) && (__clang_major__ * 1000 + __clang_minor__) >= 3007
701__attribute__((no_sanitize("float-cast-overflow")))
702#endif
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500703 size_t RectSizeAsSizeT(const SkRect& rect) {;
Brian Salomon762d5e72017-12-01 10:25:08 -0500704 return static_cast<size_t>(SkTMax(rect.width(), 1.f) * SkTMax(rect.height(), 1.f));
705 }
706
Brian Salomon336ce7b2017-09-08 08:23:58 -0400707 static constexpr int kMaxTextures = TextureGeometryProcessor::kMaxTextures;
708
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400709 TextureOp(sk_sp<GrTextureProxy> proxy, GrSamplerState::Filter filter, GrColor color,
Brian Salomon485b8c62018-01-12 15:11:06 -0500710 const SkRect& srcRect, const SkRect& dstRect, GrAAType aaType,
Brian Salomonb80ffee2018-05-23 16:39:39 -0400711 SkCanvas::SrcRectConstraint constraint, const SkMatrix& viewMatrix,
Brian Osman3ebd3542018-07-30 14:36:53 -0400712 sk_sp<GrColorSpaceXform> textureColorSpaceXform,
713 sk_sp<GrColorSpaceXform> paintColorSpaceXform)
Brian Salomon34169692017-08-28 15:32:01 -0400714 : INHERITED(ClassID())
Brian Osman3ebd3542018-07-30 14:36:53 -0400715 , fTextureColorSpaceXform(std::move(textureColorSpaceXform))
716 , fPaintColorSpaceXform(std::move(paintColorSpaceXform))
Brian Salomon336ce7b2017-09-08 08:23:58 -0400717 , fProxy0(proxy.release())
718 , fFilter0(filter)
719 , fProxyCnt(1)
Brian Salomon485b8c62018-01-12 15:11:06 -0500720 , fAAType(static_cast<unsigned>(aaType))
Brian Osman2b23c4b2018-06-01 12:25:08 -0400721 , fFinalized(0) {
Brian Salomon485b8c62018-01-12 15:11:06 -0500722 SkASSERT(aaType != GrAAType::kMixedSamples);
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400723 fPerspective = viewMatrix.hasPerspective();
Brian Salomon594b64c2018-05-29 12:47:57 -0400724 auto quad = GrPerspQuad(dstRect, viewMatrix);
725 auto bounds = quad.bounds();
726#ifndef SK_DONT_DROP_UNNECESSARY_AA_IN_TEXTURE_OP
727 if (GrAAType::kCoverage == this->aaType() && viewMatrix.rectStaysRect()) {
728 // Disable coverage AA when rect falls on integers in device space.
729 auto is_int = [](float f) { return f == sk_float_floor(f); };
730 if (is_int(bounds.fLeft) && is_int(bounds.fTop) && is_int(bounds.fRight) &&
731 is_int(bounds.fBottom)) {
732 fAAType = static_cast<unsigned>(GrAAType::kNone);
733 // We may have had a strict constraint with nearest filter soley due to possible AA
734 // bloat. In that case it's no longer necessary.
735 if (constraint == SkCanvas::kStrict_SrcRectConstraint &&
736 filter == GrSamplerState::Filter::kNearest) {
737 constraint = SkCanvas::kFast_SrcRectConstraint;
738 }
739 }
740 }
741#endif
742 const auto& draw = fDraws.emplace_back(srcRect, 0, quad, constraint, color);
Brian Salomon34169692017-08-28 15:32:01 -0400743 this->setBounds(bounds, HasAABloat::kNo, IsZeroArea::kNo);
Brian Salomon594b64c2018-05-29 12:47:57 -0400744 fDomain = static_cast<bool>(draw.domain());
Brian Salomon762d5e72017-12-01 10:25:08 -0500745 fMaxApproxDstPixelArea = RectSizeAsSizeT(bounds);
Brian Salomon34169692017-08-28 15:32:01 -0400746 }
747
Brian Salomonb80ffee2018-05-23 16:39:39 -0400748 template <typename Pos, MultiTexture MT, Domain D, GrAA AA>
Brian Salomon17031a72018-05-22 14:14:07 -0400749 void tess(void* v, const float iw[], const float ih[], const GrGeometryProcessor* gp) {
Brian Salomonb80ffee2018-05-23 16:39:39 -0400750 using Vertex = TextureGeometryProcessor::Vertex<Pos, MT, D, AA>;
Brian Salomon92be2f72018-06-19 14:33:47 -0400751 SkASSERT(gp->debugOnly_vertexStride() == sizeof(Vertex));
Brian Salomon17031a72018-05-22 14:14:07 -0400752 auto vertices = static_cast<Vertex*>(v);
753 auto proxies = this->proxies();
Brian Salomonb80ffee2018-05-23 16:39:39 -0400754 auto filters = this->filters();
Brian Salomon17031a72018-05-22 14:14:07 -0400755 for (const auto& draw : fDraws) {
Brian Salomonb80ffee2018-05-23 16:39:39 -0400756 auto textureIdx = draw.textureIdx();
757 auto origin = proxies[textureIdx]->origin();
758 tessellate_quad<Vertex>(draw.quad(), draw.srcRect(), draw.color(), origin,
759 filters[textureIdx], vertices, iw[textureIdx], ih[textureIdx],
760 textureIdx, draw.domain());
Brian Salomon17031a72018-05-22 14:14:07 -0400761 vertices += 4;
762 }
763 }
764
Brian Salomon34169692017-08-28 15:32:01 -0400765 void onPrepareDraws(Target* target) override {
Brian Salomon336ce7b2017-09-08 08:23:58 -0400766 auto proxies = this->proxies();
767 auto filters = this->filters();
768 for (int i = 0; i < fProxyCnt; ++i) {
769 if (!proxies[i]->instantiate(target->resourceProvider())) {
770 return;
771 }
Brian Salomon34169692017-08-28 15:32:01 -0400772 }
Brian Salomon336ce7b2017-09-08 08:23:58 -0400773
Brian Salomonb80ffee2018-05-23 16:39:39 -0400774 Domain domain = fDomain ? Domain::kYes : Domain::kNo;
Brian Salomon485b8c62018-01-12 15:11:06 -0500775 bool coverageAA = GrAAType::kCoverage == this->aaType();
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400776 sk_sp<GrGeometryProcessor> gp = TextureGeometryProcessor::Make(
Brian Salomonaf874832018-08-03 11:53:40 -0400777 proxies[0]->textureType(), proxies[0]->config(), fProxyCnt,
778 std::move(fTextureColorSpaceXform), std::move(fPaintColorSpaceXform), coverageAA,
779 fPerspective, domain, filters, *target->caps().shaderCaps());
Brian Salomon34169692017-08-28 15:32:01 -0400780 GrPipeline::InitArgs args;
781 args.fProxy = target->proxy();
782 args.fCaps = &target->caps();
783 args.fResourceProvider = target->resourceProvider();
Brian Salomon485b8c62018-01-12 15:11:06 -0500784 args.fFlags = 0;
Brian Salomon485b8c62018-01-12 15:11:06 -0500785 if (GrAAType::kMSAA == this->aaType()) {
786 args.fFlags |= GrPipeline::kHWAntialias_Flag;
787 }
788
Brian Salomon49348902018-06-26 09:12:38 -0400789 auto clip = target->detachAppliedClip();
Brian Salomonaf874832018-08-03 11:53:40 -0400790 auto* fixedDynamicState = target->allocFixedDynamicState(clip.scissorState().rect(),
791 gp->numTextureSamplers());
792 for (int i = 0; i < fProxyCnt; ++i) {
793 SkASSERT(proxies[i]->textureType() == proxies[0]->textureType());
794 SkASSERT(proxies[i]->config() == proxies[0]->config());
795 fixedDynamicState->fPrimitiveProcessorTextures[i] = proxies[i];
796 }
797 // Rebind texture proxy 0 to the extra samplers.
798 for (int i = fProxyCnt; i < gp->numTextureSamplers(); ++i) {
799 fixedDynamicState->fPrimitiveProcessorTextures[i] = proxies[0];
800 }
Brian Salomon49348902018-06-26 09:12:38 -0400801 const auto* pipeline =
802 target->allocPipeline(args, GrProcessorSet::MakeEmptySet(), std::move(clip));
Brian Salomon92be2f72018-06-19 14:33:47 -0400803 using TessFn =
804 decltype(&TextureOp::tess<SkPoint, MultiTexture::kNo, Domain::kNo, GrAA::kNo>);
805#define TESS_FN_AND_VERTEX_SIZE(Point, MT, Domain, AA) \
806 { \
807 &TextureOp::tess<Point, MT, Domain, AA>, \
808 sizeof(TextureGeometryProcessor::Vertex<Point, MT, Domain, AA>) \
809 }
810 static constexpr struct {
811 TessFn fTessFn;
812 size_t fVertexSize;
813 } kTessFnsAndVertexSizes[] = {
814 TESS_FN_AND_VERTEX_SIZE(SkPoint, MultiTexture::kNo, Domain::kNo, GrAA::kNo),
815 TESS_FN_AND_VERTEX_SIZE(SkPoint, MultiTexture::kNo, Domain::kNo, GrAA::kYes),
816 TESS_FN_AND_VERTEX_SIZE(SkPoint, MultiTexture::kNo, Domain::kYes, GrAA::kNo),
817 TESS_FN_AND_VERTEX_SIZE(SkPoint, MultiTexture::kNo, Domain::kYes, GrAA::kYes),
818 TESS_FN_AND_VERTEX_SIZE(SkPoint, MultiTexture::kYes, Domain::kNo, GrAA::kNo),
819 TESS_FN_AND_VERTEX_SIZE(SkPoint, MultiTexture::kYes, Domain::kNo, GrAA::kYes),
820 TESS_FN_AND_VERTEX_SIZE(SkPoint, MultiTexture::kYes, Domain::kYes, GrAA::kNo),
821 TESS_FN_AND_VERTEX_SIZE(SkPoint, MultiTexture::kYes, Domain::kYes, GrAA::kYes),
822 TESS_FN_AND_VERTEX_SIZE(SkPoint3, MultiTexture::kNo, Domain::kNo, GrAA::kNo),
823 TESS_FN_AND_VERTEX_SIZE(SkPoint3, MultiTexture::kNo, Domain::kNo, GrAA::kYes),
824 TESS_FN_AND_VERTEX_SIZE(SkPoint3, MultiTexture::kNo, Domain::kYes, GrAA::kNo),
825 TESS_FN_AND_VERTEX_SIZE(SkPoint3, MultiTexture::kNo, Domain::kYes, GrAA::kYes),
826 TESS_FN_AND_VERTEX_SIZE(SkPoint3, MultiTexture::kYes, Domain::kNo, GrAA::kNo),
827 TESS_FN_AND_VERTEX_SIZE(SkPoint3, MultiTexture::kYes, Domain::kNo, GrAA::kYes),
828 TESS_FN_AND_VERTEX_SIZE(SkPoint3, MultiTexture::kYes, Domain::kYes, GrAA::kNo),
829 TESS_FN_AND_VERTEX_SIZE(SkPoint3, MultiTexture::kYes, Domain::kYes, GrAA::kYes),
830 };
831#undef TESS_FN_AND_VERTEX_SIZE
832 int tessFnIdx = 0;
833 tessFnIdx |= coverageAA ? 0x1 : 0x0;
834 tessFnIdx |= fDomain ? 0x2 : 0x0;
835 tessFnIdx |= (fProxyCnt > 1) ? 0x4 : 0x0;
836 tessFnIdx |= fPerspective ? 0x8 : 0x0;
837
838 SkASSERT(kTessFnsAndVertexSizes[tessFnIdx].fVertexSize == gp->debugOnly_vertexStride());
839
Brian Salomon34169692017-08-28 15:32:01 -0400840 int vstart;
841 const GrBuffer* vbuffer;
Brian Salomon92be2f72018-06-19 14:33:47 -0400842 void* vdata = target->makeVertexSpace(kTessFnsAndVertexSizes[tessFnIdx].fVertexSize,
843 4 * fDraws.count(), &vbuffer, &vstart);
Brian Salomon336ce7b2017-09-08 08:23:58 -0400844 if (!vdata) {
Brian Salomon34169692017-08-28 15:32:01 -0400845 SkDebugf("Could not allocate vertices\n");
846 return;
847 }
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400848
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400849 float iw[kMaxTextures];
850 float ih[kMaxTextures];
851 for (int t = 0; t < fProxyCnt; ++t) {
Brian Salomonfd98c2c2018-07-31 17:25:29 -0400852 const auto* texture = proxies[t]->peekTexture();
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400853 iw[t] = 1.f / texture->width();
854 ih[t] = 1.f / texture->height();
855 }
856
Brian Salomon92be2f72018-06-19 14:33:47 -0400857 (this->*(kTessFnsAndVertexSizes[tessFnIdx].fTessFn))(vdata, iw, ih, gp.get());
Brian Salomonb80ffee2018-05-23 16:39:39 -0400858
Brian Salomon57caa662017-10-18 12:21:05 +0000859 GrPrimitiveType primitiveType =
860 fDraws.count() > 1 ? GrPrimitiveType::kTriangles : GrPrimitiveType::kTriangleStrip;
861 GrMesh mesh(primitiveType);
Brian Salomon34169692017-08-28 15:32:01 -0400862 if (fDraws.count() > 1) {
Brian Salomon57caa662017-10-18 12:21:05 +0000863 sk_sp<const GrBuffer> ibuffer = target->resourceProvider()->refQuadIndexBuffer();
Brian Salomon34169692017-08-28 15:32:01 -0400864 if (!ibuffer) {
865 SkDebugf("Could not allocate quad indices\n");
866 return;
867 }
Brian Salomon34169692017-08-28 15:32:01 -0400868 mesh.setIndexedPatterned(ibuffer.get(), 6, 4, fDraws.count(),
869 GrResourceProvider::QuadCountOfQuadBuffer());
Brian Salomon34169692017-08-28 15:32:01 -0400870 } else {
Brian Salomon34169692017-08-28 15:32:01 -0400871 mesh.setNonIndexedNonInstanced(4);
Brian Salomon34169692017-08-28 15:32:01 -0400872 }
Brian Salomon57caa662017-10-18 12:21:05 +0000873 mesh.setVertexData(vbuffer, vstart);
Brian Salomon607be372018-08-03 11:54:34 -0400874 target->draw(std::move(gp), pipeline, fixedDynamicState, mesh);
Brian Salomon34169692017-08-28 15:32:01 -0400875 }
876
877 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
878 const auto* that = t->cast<TextureOp>();
Brian Salomon762d5e72017-12-01 10:25:08 -0500879 const auto& shaderCaps = *caps.shaderCaps();
Brian Osman3ebd3542018-07-30 14:36:53 -0400880 if (!GrColorSpaceXform::Equals(fTextureColorSpaceXform.get(),
881 that->fTextureColorSpaceXform.get())) {
882 return false;
883 }
884 if (!GrColorSpaceXform::Equals(fPaintColorSpaceXform.get(),
885 that->fPaintColorSpaceXform.get())) {
Brian Salomon34169692017-08-28 15:32:01 -0400886 return false;
887 }
Brian Salomon485b8c62018-01-12 15:11:06 -0500888 if (this->aaType() != that->aaType()) {
Brian Salomonb5ef1f92018-01-11 11:46:21 -0500889 return false;
890 }
Brian Salomon336ce7b2017-09-08 08:23:58 -0400891 // Because of an issue where GrColorSpaceXform adds the same function every time it is used
892 // in a texture lookup, we only allow multiple textures when there is no transform.
Brian Osman3ebd3542018-07-30 14:36:53 -0400893 if (TextureGeometryProcessor::SupportsMultitexture(shaderCaps) &&
894 !fTextureColorSpaceXform &&
Brian Salomon762d5e72017-12-01 10:25:08 -0500895 fMaxApproxDstPixelArea <= shaderCaps.disableImageMultitexturingDstRectAreaThreshold() &&
896 that->fMaxApproxDstPixelArea <=
897 shaderCaps.disableImageMultitexturingDstRectAreaThreshold()) {
Brian Salomon336ce7b2017-09-08 08:23:58 -0400898 int map[kMaxTextures];
Brian Salomon762d5e72017-12-01 10:25:08 -0500899 int numNewProxies = this->mergeProxies(that, map, shaderCaps);
Brian Salomon336ce7b2017-09-08 08:23:58 -0400900 if (numNewProxies < 0) {
901 return false;
902 }
903 if (1 == fProxyCnt && numNewProxies) {
904 void* mem = new char[(sizeof(GrSamplerState::Filter) + sizeof(GrTextureProxy*)) *
905 kMaxTextures];
906 auto proxies = reinterpret_cast<GrTextureProxy**>(mem);
907 auto filters = reinterpret_cast<GrSamplerState::Filter*>(proxies + kMaxTextures);
908 proxies[0] = fProxy0;
909 filters[0] = fFilter0;
910 fProxyArray = proxies;
911 }
912 fProxyCnt += numNewProxies;
913 auto thisProxies = fProxyArray;
914 auto thatProxies = that->proxies();
915 auto thatFilters = that->filters();
916 auto thisFilters = reinterpret_cast<GrSamplerState::Filter*>(thisProxies +
917 kMaxTextures);
918 for (int i = 0; i < that->fProxyCnt; ++i) {
919 if (map[i] < 0) {
920 thatProxies[i]->addPendingRead();
Robert Phillipsb493eeb2017-09-13 13:10:52 -0400921
Brian Salomon336ce7b2017-09-08 08:23:58 -0400922 thisProxies[-map[i]] = thatProxies[i];
923 thisFilters[-map[i]] = thatFilters[i];
924 map[i] = -map[i];
925 }
926 }
927 int firstNewDraw = fDraws.count();
928 fDraws.push_back_n(that->fDraws.count(), that->fDraws.begin());
929 for (int i = firstNewDraw; i < fDraws.count(); ++i) {
Brian Salomonb80ffee2018-05-23 16:39:39 -0400930 fDraws[i].setTextureIdx(map[fDraws[i].textureIdx()]);
Brian Salomon336ce7b2017-09-08 08:23:58 -0400931 }
932 } else {
Brian Salomonbbf05752017-11-30 11:30:48 -0500933 // We can get here when one of the ops is already multitextured but the other cannot
934 // be because of the dst rect size.
935 if (fProxyCnt > 1 || that->fProxyCnt > 1) {
936 return false;
937 }
Brian Salomon336ce7b2017-09-08 08:23:58 -0400938 if (fProxy0->uniqueID() != that->fProxy0->uniqueID() || fFilter0 != that->fFilter0) {
939 return false;
940 }
941 fDraws.push_back_n(that->fDraws.count(), that->fDraws.begin());
942 }
Brian Salomon34169692017-08-28 15:32:01 -0400943 this->joinBounds(*that);
Brian Salomon762d5e72017-12-01 10:25:08 -0500944 fMaxApproxDstPixelArea = SkTMax(that->fMaxApproxDstPixelArea, fMaxApproxDstPixelArea);
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400945 fPerspective |= that->fPerspective;
Brian Salomonb80ffee2018-05-23 16:39:39 -0400946 fDomain |= that->fDomain;
Brian Salomon34169692017-08-28 15:32:01 -0400947 return true;
948 }
949
Brian Salomon336ce7b2017-09-08 08:23:58 -0400950 /**
951 * Determines a mapping of indices from that's proxy array to this's proxy array. A negative map
952 * value means that's proxy should be added to this's proxy array at the absolute value of
953 * the map entry. If it is determined that the ops shouldn't combine their proxies then a
954 * negative value is returned. Otherwise, return value indicates the number of proxies that have
955 * to be added to this op or, equivalently, the number of negative entries in map.
956 */
957 int mergeProxies(const TextureOp* that, int map[kMaxTextures], const GrShaderCaps& caps) const {
958 std::fill_n(map, kMaxTextures, -kMaxTextures);
959 int sharedProxyCnt = 0;
960 auto thisProxies = this->proxies();
961 auto thisFilters = this->filters();
962 auto thatProxies = that->proxies();
963 auto thatFilters = that->filters();
964 for (int i = 0; i < fProxyCnt; ++i) {
965 for (int j = 0; j < that->fProxyCnt; ++j) {
966 if (thisProxies[i]->uniqueID() == thatProxies[j]->uniqueID()) {
967 if (thisFilters[i] != thatFilters[j]) {
968 // In GL we don't currently support using the same texture with different
969 // samplers. If we added support for sampler objects and a cap bit to know
970 // it's ok to use different filter modes then we could support this.
971 // Otherwise, we could also only allow a single filter mode for each op
972 // instance.
973 return -1;
974 }
975 map[j] = i;
976 ++sharedProxyCnt;
977 break;
978 }
979 }
980 }
Brian Salomon2b6f6142017-11-13 11:49:13 -0500981 int actualMaxTextures = SkTMin(caps.maxFragmentSamplers(), kMaxTextures);
Brian Salomon336ce7b2017-09-08 08:23:58 -0400982 int newProxyCnt = that->fProxyCnt - sharedProxyCnt;
983 if (newProxyCnt + fProxyCnt > actualMaxTextures) {
984 return -1;
985 }
986 GrPixelConfig config = thisProxies[0]->config();
987 int nextSlot = fProxyCnt;
988 for (int j = 0; j < that->fProxyCnt; ++j) {
989 // We want to avoid making many shaders because of different permutations of shader
990 // based swizzle and sampler types. The approach taken here is to require the configs to
991 // be the same and to only allow already instantiated proxies that have the most
992 // common sampler type. Otherwise we don't merge.
993 if (thatProxies[j]->config() != config) {
994 return -1;
995 }
Brian Salomonfd98c2c2018-07-31 17:25:29 -0400996 if (GrTexture* tex = thatProxies[j]->peekTexture()) {
Brian Salomon60dd8c72018-07-30 10:24:13 -0400997 if (tex->texturePriv().textureType() != GrTextureType::k2D) {
Brian Salomon336ce7b2017-09-08 08:23:58 -0400998 return -1;
999 }
1000 }
1001 if (map[j] < 0) {
1002 map[j] = -(nextSlot++);
1003 }
1004 }
1005 return newProxyCnt;
1006 }
1007
Brian Salomon485b8c62018-01-12 15:11:06 -05001008 GrAAType aaType() const { return static_cast<GrAAType>(fAAType); }
Brian Salomonb5ef1f92018-01-11 11:46:21 -05001009
Brian Salomon336ce7b2017-09-08 08:23:58 -04001010 GrTextureProxy* const* proxies() const { return fProxyCnt > 1 ? fProxyArray : &fProxy0; }
1011
1012 const GrSamplerState::Filter* filters() const {
1013 if (fProxyCnt > 1) {
1014 return reinterpret_cast<const GrSamplerState::Filter*>(fProxyArray + kMaxTextures);
1015 }
1016 return &fFilter0;
1017 }
1018
Brian Salomonb80ffee2018-05-23 16:39:39 -04001019 class Draw {
1020 public:
1021 Draw(const SkRect& srcRect, int textureIdx, const GrPerspQuad& quad,
1022 SkCanvas::SrcRectConstraint constraint, GrColor color)
1023 : fSrcRect(srcRect)
1024 , fHasDomain(constraint == SkCanvas::kStrict_SrcRectConstraint)
1025 , fTextureIdx(SkToUInt(textureIdx))
1026 , fQuad(quad)
1027 , fColor(color) {}
1028 const GrPerspQuad& quad() const { return fQuad; }
1029 int textureIdx() const { return SkToInt(fTextureIdx); }
1030 const SkRect& srcRect() const { return fSrcRect; }
1031 GrColor color() const { return fColor; }
1032 Domain domain() const { return Domain(fHasDomain); }
1033 void setTextureIdx(int i) { fTextureIdx = SkToUInt(i); }
1034
1035 private:
Brian Salomon34169692017-08-28 15:32:01 -04001036 SkRect fSrcRect;
Brian Salomonb80ffee2018-05-23 16:39:39 -04001037 unsigned fHasDomain : 1;
1038 unsigned fTextureIdx : 31;
Brian Salomonbe3c1d22018-05-21 12:54:39 -04001039 GrPerspQuad fQuad;
Brian Salomon34169692017-08-28 15:32:01 -04001040 GrColor fColor;
1041 };
1042 SkSTArray<1, Draw, true> fDraws;
Brian Osman3ebd3542018-07-30 14:36:53 -04001043 sk_sp<GrColorSpaceXform> fTextureColorSpaceXform;
1044 sk_sp<GrColorSpaceXform> fPaintColorSpaceXform;
Brian Salomon336ce7b2017-09-08 08:23:58 -04001045 // Initially we store a single proxy ptr and a single filter. If we grow to have more than
1046 // one proxy we instead store pointers to dynamically allocated arrays of size kMaxTextures
1047 // followed by kMaxTextures filters.
1048 union {
1049 GrTextureProxy* fProxy0;
1050 GrTextureProxy** fProxyArray;
1051 };
Brian Salomonbbf05752017-11-30 11:30:48 -05001052 size_t fMaxApproxDstPixelArea;
Brian Salomon336ce7b2017-09-08 08:23:58 -04001053 GrSamplerState::Filter fFilter0;
1054 uint8_t fProxyCnt;
Brian Salomon485b8c62018-01-12 15:11:06 -05001055 unsigned fAAType : 2;
Brian Salomonbe3c1d22018-05-21 12:54:39 -04001056 unsigned fPerspective : 1;
Brian Salomonb80ffee2018-05-23 16:39:39 -04001057 unsigned fDomain : 1;
Brian Salomon34169692017-08-28 15:32:01 -04001058 // Used to track whether fProxy is ref'ed or has a pending IO after finalize() is called.
Brian Salomonb5ef1f92018-01-11 11:46:21 -05001059 unsigned fFinalized : 1;
Brian Salomon336ce7b2017-09-08 08:23:58 -04001060
Brian Salomon34169692017-08-28 15:32:01 -04001061 typedef GrMeshDrawOp INHERITED;
1062};
1063
Brian Salomon336ce7b2017-09-08 08:23:58 -04001064constexpr int TextureGeometryProcessor::kMaxTextures;
1065constexpr int TextureOp::kMaxTextures;
1066
Brian Salomon34169692017-08-28 15:32:01 -04001067} // anonymous namespace
1068
1069namespace GrTextureOp {
1070
Robert Phillips7c525e62018-06-12 10:11:12 -04001071std::unique_ptr<GrDrawOp> Make(GrContext* context,
1072 sk_sp<GrTextureProxy> proxy,
1073 GrSamplerState::Filter filter,
1074 GrColor color,
1075 const SkRect& srcRect,
1076 const SkRect& dstRect,
1077 GrAAType aaType,
1078 SkCanvas::SrcRectConstraint constraint,
1079 const SkMatrix& viewMatrix,
Brian Osman3ebd3542018-07-30 14:36:53 -04001080 sk_sp<GrColorSpaceXform> textureColorSpaceXform,
1081 sk_sp<GrColorSpaceXform> paintColorSpaceXform) {
Robert Phillips7c525e62018-06-12 10:11:12 -04001082 return TextureOp::Make(context, std::move(proxy), filter, color, srcRect, dstRect, aaType,
Brian Osman3ebd3542018-07-30 14:36:53 -04001083 constraint, viewMatrix, std::move(textureColorSpaceXform),
1084 std::move(paintColorSpaceXform));
Brian Salomon34169692017-08-28 15:32:01 -04001085}
1086
1087} // namespace GrTextureOp
1088
1089#if GR_TEST_UTILS
1090#include "GrContext.h"
Robert Phillips1afd4cd2018-01-08 13:40:32 -05001091#include "GrContextPriv.h"
Robert Phillips0bd24dc2018-01-16 08:06:32 -05001092#include "GrProxyProvider.h"
Brian Salomon34169692017-08-28 15:32:01 -04001093
1094GR_DRAW_OP_TEST_DEFINE(TextureOp) {
1095 GrSurfaceDesc desc;
1096 desc.fConfig = kRGBA_8888_GrPixelConfig;
1097 desc.fHeight = random->nextULessThan(90) + 10;
1098 desc.fWidth = random->nextULessThan(90) + 10;
Brian Salomon2a4f9832018-03-03 22:43:43 -05001099 auto origin = random->nextBool() ? kTopLeft_GrSurfaceOrigin : kBottomLeft_GrSurfaceOrigin;
Greg Daniel09c94002018-06-08 22:11:51 +00001100 GrMipMapped mipMapped = random->nextBool() ? GrMipMapped::kYes : GrMipMapped::kNo;
1101 SkBackingFit fit = SkBackingFit::kExact;
1102 if (mipMapped == GrMipMapped::kNo) {
1103 fit = random->nextBool() ? SkBackingFit::kApprox : SkBackingFit::kExact;
1104 }
Robert Phillips0bd24dc2018-01-16 08:06:32 -05001105
1106 GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider();
Greg Daniel09c94002018-06-08 22:11:51 +00001107 sk_sp<GrTextureProxy> proxy = proxyProvider->createProxy(desc, origin, mipMapped, fit,
1108 SkBudgeted::kNo,
1109 GrInternalSurfaceFlags::kNone);
Robert Phillips0bd24dc2018-01-16 08:06:32 -05001110
Brian Salomon34169692017-08-28 15:32:01 -04001111 SkRect rect = GrTest::TestRect(random);
1112 SkRect srcRect;
1113 srcRect.fLeft = random->nextRangeScalar(0.f, proxy->width() / 2.f);
1114 srcRect.fRight = random->nextRangeScalar(0.f, proxy->width()) + proxy->width() / 2.f;
1115 srcRect.fTop = random->nextRangeScalar(0.f, proxy->height() / 2.f);
1116 srcRect.fBottom = random->nextRangeScalar(0.f, proxy->height()) + proxy->height() / 2.f;
1117 SkMatrix viewMatrix = GrTest::TestMatrixPreservesRightAngles(random);
1118 GrColor color = SkColorToPremulGrColor(random->nextU());
Brian Salomon2bbdcc42017-09-07 12:36:34 -04001119 GrSamplerState::Filter filter = (GrSamplerState::Filter)random->nextULessThan(
1120 static_cast<uint32_t>(GrSamplerState::Filter::kMipMap) + 1);
Greg Daniel09c94002018-06-08 22:11:51 +00001121 while (mipMapped == GrMipMapped::kNo && filter == GrSamplerState::Filter::kMipMap) {
1122 filter = (GrSamplerState::Filter)random->nextULessThan(
1123 static_cast<uint32_t>(GrSamplerState::Filter::kMipMap) + 1);
1124 }
Brian Osman3ebd3542018-07-30 14:36:53 -04001125 auto texXform = GrTest::TestColorXform(random);
1126 auto paintXform = GrTest::TestColorXform(random);
Brian Salomon485b8c62018-01-12 15:11:06 -05001127 GrAAType aaType = GrAAType::kNone;
1128 if (random->nextBool()) {
1129 aaType = (fsaaType == GrFSAAType::kUnifiedMSAA) ? GrAAType::kMSAA : GrAAType::kCoverage;
1130 }
Brian Salomonb80ffee2018-05-23 16:39:39 -04001131 auto constraint = random->nextBool() ? SkCanvas::kStrict_SrcRectConstraint
1132 : SkCanvas::kFast_SrcRectConstraint;
Robert Phillips7c525e62018-06-12 10:11:12 -04001133 return GrTextureOp::Make(context, std::move(proxy), filter, color, srcRect, rect, aaType,
Brian Osman3ebd3542018-07-30 14:36:53 -04001134 constraint, viewMatrix, std::move(texXform), std::move(paintXform));
Brian Salomon34169692017-08-28 15:32:01 -04001135}
1136
1137#endif