blob: 7ef7757939a78dc98ac332fef32921bd5cd98104 [file] [log] [blame]
Brian Salomon34169692017-08-28 15:32:01 -04001/*
2 * Copyright 2017 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "GrTextureOp.h"
9#include "GrAppliedClip.h"
Brian Salomon336ce7b2017-09-08 08:23:58 -040010#include "GrCaps.h"
Brian Salomon34169692017-08-28 15:32:01 -040011#include "GrDrawOpTest.h"
12#include "GrGeometryProcessor.h"
13#include "GrMeshDrawOp.h"
14#include "GrOpFlushState.h"
15#include "GrQuad.h"
16#include "GrResourceProvider.h"
17#include "GrShaderCaps.h"
18#include "GrTexture.h"
Brian Salomon336ce7b2017-09-08 08:23:58 -040019#include "GrTexturePriv.h"
Brian Salomon34169692017-08-28 15:32:01 -040020#include "GrTextureProxy.h"
21#include "SkGr.h"
Brian Salomon336ce7b2017-09-08 08:23:58 -040022#include "SkMathPriv.h"
Brian Salomon34169692017-08-28 15:32:01 -040023#include "glsl/GrGLSLColorSpaceXformHelper.h"
24#include "glsl/GrGLSLGeometryProcessor.h"
25#include "glsl/GrGLSLVarying.h"
26
27namespace {
28
29/**
30 * Geometry Processor that draws a texture modulated by a vertex color (though, this is meant to be
31 * the same value across all vertices of a quad and uses flat interpolation when available). This is
32 * used by TextureOp below.
33 */
34class TextureGeometryProcessor : public GrGeometryProcessor {
35public:
36 struct Vertex {
37 SkPoint fPosition;
38 SkPoint fTextureCoords;
39 GrColor fColor;
40 };
Brian Salomon336ce7b2017-09-08 08:23:58 -040041 struct MultiTextureVertex {
42 SkPoint fPosition;
43 int fTextureIdx;
44 SkPoint fTextureCoords;
45 GrColor fColor;
46 };
47
48 // Maximum number of textures supported by this op. Must also be checked against the caps
49 // limit. These numbers were based on some limited experiments on a HP Z840 and Pixel XL 2016
50 // and could probably use more tuning.
51#ifdef SK_BUILD_FOR_ANDROID
52 static constexpr int kMaxTextures = 4;
53#else
54 static constexpr int kMaxTextures = 8;
55#endif
56
57 static int SupportsMultitexture(const GrShaderCaps& caps) { return caps.integerSupport(); }
58
59 static sk_sp<GrGeometryProcessor> Make(sk_sp<GrTextureProxy> proxies[], int proxyCnt,
Brian Salomon34169692017-08-28 15:32:01 -040060 sk_sp<GrColorSpaceXform> csxf,
Brian Salomon336ce7b2017-09-08 08:23:58 -040061 const GrSamplerState::Filter filters[],
62 const GrShaderCaps& caps) {
63 // We use placement new to avoid always allocating space for kMaxTextures TextureSampler
64 // instances.
65 int samplerCnt = NumSamplersToUse(proxyCnt, caps);
66 size_t size = sizeof(TextureGeometryProcessor) + sizeof(TextureSampler) * (samplerCnt - 1);
67 void* mem = GrGeometryProcessor::operator new(size);
68 return sk_sp<TextureGeometryProcessor>(new (mem) TextureGeometryProcessor(
69 proxies, proxyCnt, samplerCnt, std::move(csxf), filters, caps));
70 }
71
72 ~TextureGeometryProcessor() override {
73 int cnt = this->numTextureSamplers();
74 for (int i = 1; i < cnt; ++i) {
75 fSamplers[i].~TextureSampler();
76 }
Brian Salomon34169692017-08-28 15:32:01 -040077 }
78
79 const char* name() const override { return "TextureGeometryProcessor"; }
80
81 void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const override {
82 b->add32(GrColorSpaceXform::XformKey(fColorSpaceXform.get()));
83 }
84
85 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps& caps) const override {
86 class GLSLProcessor : public GrGLSLGeometryProcessor {
87 public:
88 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& proc,
89 FPCoordTransformIter&& transformIter) override {
90 const auto& textureGP = proc.cast<TextureGeometryProcessor>();
91 this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
92 if (fColorSpaceXformHelper.isValid()) {
93 fColorSpaceXformHelper.setData(pdman, textureGP.fColorSpaceXform.get());
94 }
95 }
96
97 private:
98 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
99 const auto& textureGP = args.fGP.cast<TextureGeometryProcessor>();
100 fColorSpaceXformHelper.emitCode(
101 args.fUniformHandler, textureGP.fColorSpaceXform.get());
102 args.fVaryingHandler->setNoPerspective();
103 args.fVaryingHandler->emitAttributes(textureGP);
104 this->writeOutputPosition(args.fVertBuilder, gpArgs, textureGP.fPositions.fName);
105 this->emitTransforms(args.fVertBuilder,
106 args.fVaryingHandler,
107 args.fUniformHandler,
108 gpArgs->fPositionVar,
109 textureGP.fTextureCoords.fName,
110 args.fFPCoordTransformHandler);
Brian Salomon41274562017-09-15 09:40:03 -0700111 if (args.fShaderCaps->preferFlatInterpolation()) {
Brian Salomon34169692017-08-28 15:32:01 -0400112 args.fVaryingHandler->addFlatPassThroughAttribute(&textureGP.fColors,
113 args.fOutputColor);
114 } else {
115 args.fVaryingHandler->addPassThroughAttribute(&textureGP.fColors,
116 args.fOutputColor);
117 }
Ethan Nicholas8aa45692017-09-20 11:24:15 -0400118 args.fFragBuilder->codeAppend("float2 texCoord;");
Brian Salomon977b54c2017-08-31 09:06:14 -0400119 args.fVaryingHandler->addPassThroughAttribute(&textureGP.fTextureCoords, "texCoord",
120 kHigh_GrSLPrecision);
Brian Salomon336ce7b2017-09-08 08:23:58 -0400121 if (textureGP.numTextureSamplers() > 1) {
122 SkASSERT(args.fShaderCaps->integerSupport());
123 args.fFragBuilder->codeAppend("int texIdx;");
124 if (args.fShaderCaps->flatInterpolationSupport()) {
125 args.fVaryingHandler->addFlatPassThroughAttribute(&textureGP.fTextureIdx,
126 "texIdx");
127 } else {
128 args.fVaryingHandler->addPassThroughAttribute(&textureGP.fTextureIdx,
129 "texIdx");
130 }
131 args.fFragBuilder->codeAppend("switch (texIdx) {");
132 for (int i = 0; i < textureGP.numTextureSamplers(); ++i) {
133 args.fFragBuilder->codeAppendf("case %d: %s = ", i, args.fOutputColor);
134 args.fFragBuilder->appendTextureLookupAndModulate(args.fOutputColor,
135 args.fTexSamplers[i],
136 "texCoord",
Ethan Nicholas8aa45692017-09-20 11:24:15 -0400137 kFloat2_GrSLType,
Brian Salomon336ce7b2017-09-08 08:23:58 -0400138 &fColorSpaceXformHelper);
139 args.fFragBuilder->codeAppend("; break;");
140 }
141 args.fFragBuilder->codeAppend("}");
142 } else {
143 args.fFragBuilder->codeAppendf("%s = ", args.fOutputColor);
144 args.fFragBuilder->appendTextureLookupAndModulate(args.fOutputColor,
145 args.fTexSamplers[0],
146 "texCoord",
Ethan Nicholas8aa45692017-09-20 11:24:15 -0400147 kFloat2_GrSLType,
Brian Salomon336ce7b2017-09-08 08:23:58 -0400148 &fColorSpaceXformHelper);
149 }
Brian Salomon34169692017-08-28 15:32:01 -0400150 args.fFragBuilder->codeAppend(";");
Ethan Nicholas8aa45692017-09-20 11:24:15 -0400151 args.fFragBuilder->codeAppendf("%s = float4(1);", args.fOutputCoverage);
Brian Salomon34169692017-08-28 15:32:01 -0400152 }
153 GrGLSLColorSpaceXformHelper fColorSpaceXformHelper;
154 };
155 return new GLSLProcessor;
156 }
157
158private:
Brian Salomon336ce7b2017-09-08 08:23:58 -0400159 // This exists to reduce the number of shaders generated. It does some rounding of sampler
160 // counts.
161 static int NumSamplersToUse(int numRealProxies, const GrShaderCaps& caps) {
162 SkASSERT(numRealProxies > 0 && numRealProxies <= kMaxTextures &&
163 numRealProxies <= caps.maxFragmentSamplers());
164 if (1 == numRealProxies) {
165 return 1;
166 }
167 if (numRealProxies <= 4) {
168 return 4;
169 }
170 // Round to the next power of 2 and then clamp to kMaxTextures and the max allowed by caps.
171 return SkTMin(SkNextPow2(numRealProxies), SkTMin(kMaxTextures, caps.maxFragmentSamplers()));
172 }
173
174 TextureGeometryProcessor(sk_sp<GrTextureProxy> proxies[], int proxyCnt, int samplerCnt,
175 sk_sp<GrColorSpaceXform> csxf, const GrSamplerState::Filter filters[],
176 const GrShaderCaps& caps)
177 : fColorSpaceXform(std::move(csxf)) {
178 SkASSERT(proxyCnt > 0 && samplerCnt >= proxyCnt);
Brian Salomon34169692017-08-28 15:32:01 -0400179 this->initClassID<TextureGeometryProcessor>();
Ethan Nicholasfa7ee242017-09-25 09:52:04 -0400180 fPositions = this->addVertexAttrib("position", kFloat2_GrVertexAttribType);
Brian Salomon336ce7b2017-09-08 08:23:58 -0400181 fSamplers[0].reset(std::move(proxies[0]), filters[0]);
182 this->addTextureSampler(&fSamplers[0]);
183 for (int i = 1; i < proxyCnt; ++i) {
184 // This class has one sampler built in, the rest come from memory this processor was
185 // placement-newed into and so haven't been constructed.
186 new (&fSamplers[i]) TextureSampler(std::move(proxies[i]), filters[i]);
187 this->addTextureSampler(&fSamplers[i]);
188 }
189 if (samplerCnt > 1) {
190 // Here we initialize any extra samplers by repeating the last one samplerCnt - proxyCnt
191 // times.
192 GrTextureProxy* dupeProxy = fSamplers[proxyCnt - 1].proxy();
193 for (int i = proxyCnt; i < samplerCnt; ++i) {
194 new (&fSamplers[i]) TextureSampler(sk_ref_sp(dupeProxy), filters[proxyCnt - 1]);
195 this->addTextureSampler(&fSamplers[i]);
196 }
197 SkASSERT(caps.integerSupport());
198 fTextureIdx = this->addVertexAttrib("textureIdx", kInt_GrVertexAttribType);
199 }
200
Ethan Nicholasfa7ee242017-09-25 09:52:04 -0400201 fTextureCoords = this->addVertexAttrib("textureCoords", kFloat2_GrVertexAttribType);
202 fColors = this->addVertexAttrib("color", kUByte4_norm_GrVertexAttribType);
Brian Salomon34169692017-08-28 15:32:01 -0400203 }
204
205 Attribute fPositions;
Brian Salomon336ce7b2017-09-08 08:23:58 -0400206 Attribute fTextureIdx;
Brian Salomon34169692017-08-28 15:32:01 -0400207 Attribute fTextureCoords;
208 Attribute fColors;
Brian Salomon34169692017-08-28 15:32:01 -0400209 sk_sp<GrColorSpaceXform> fColorSpaceXform;
Brian Salomon336ce7b2017-09-08 08:23:58 -0400210 TextureSampler fSamplers[1];
Brian Salomon34169692017-08-28 15:32:01 -0400211};
212
213/**
214 * Op that implements GrTextureOp::Make. It draws textured quads. Each quad can modulate against a
215 * the texture by color. The blend with the destination is always src-over. The edges are non-AA.
216 */
217class TextureOp final : public GrMeshDrawOp {
218public:
219 static std::unique_ptr<GrDrawOp> Make(sk_sp<GrTextureProxy> proxy,
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400220 GrSamplerState::Filter filter, GrColor color,
Brian Salomon34169692017-08-28 15:32:01 -0400221 const SkRect srcRect, const SkRect dstRect,
222 const SkMatrix& viewMatrix, sk_sp<GrColorSpaceXform> csxf,
223 bool allowSRBInputs) {
224 return std::unique_ptr<GrDrawOp>(new TextureOp(std::move(proxy), filter, color, srcRect,
225 dstRect, viewMatrix, std::move(csxf),
226 allowSRBInputs));
227 }
228
Brian Salomon336ce7b2017-09-08 08:23:58 -0400229 ~TextureOp() override {
230 if (fFinalized) {
231 auto proxies = this->proxies();
232 for (int i = 0; i < fProxyCnt; ++i) {
233 proxies[i]->completedRead();
234 }
235 if (fProxyCnt > 1) {
236 delete[] reinterpret_cast<const char*>(proxies);
237 }
238 } else {
239 SkASSERT(1 == fProxyCnt);
240 fProxy0->unref();
241 }
242 }
Brian Salomon34169692017-08-28 15:32:01 -0400243
244 const char* name() const override { return "TextureOp"; }
245
Robert Phillipsf1748f52017-09-14 14:11:24 -0400246 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillipsb493eeb2017-09-13 13:10:52 -0400247 auto proxies = this->proxies();
248 for (int i = 0; i < fProxyCnt; ++i) {
249 func(proxies[i]);
250 }
251 }
252
Brian Salomon34169692017-08-28 15:32:01 -0400253 SkString dumpInfo() const override {
254 SkString str;
Brian Salomon336ce7b2017-09-08 08:23:58 -0400255 str.appendf("AllowSRGBInputs: %d\n", fAllowSRGBInputs);
Brian Salomon34169692017-08-28 15:32:01 -0400256 str.appendf("# draws: %d\n", fDraws.count());
Brian Salomon336ce7b2017-09-08 08:23:58 -0400257 auto proxies = this->proxies();
258 for (int i = 0; i < fProxyCnt; ++i) {
259 str.appendf("Proxy ID %d: %d, Filter: %d\n", i, proxies[i]->uniqueID().asUInt(),
260 static_cast<int>(this->filters()[i]));
261 }
Brian Salomon34169692017-08-28 15:32:01 -0400262 for (int i = 0; i < fDraws.count(); ++i) {
263 const Draw& draw = fDraws[i];
264 str.appendf(
Brian Salomon336ce7b2017-09-08 08:23:58 -0400265 "%d: Color: 0x%08x, ProxyIdx: %d, TexRect [L: %.2f, T: %.2f, R: %.2f, B: %.2f] "
266 "Quad [(%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f)]\n",
267 i, draw.fColor, draw.fTextureIdx, draw.fSrcRect.fLeft, draw.fSrcRect.fTop,
268 draw.fSrcRect.fRight, draw.fSrcRect.fBottom, draw.fQuad.points()[0].fX,
269 draw.fQuad.points()[0].fY, draw.fQuad.points()[1].fX, draw.fQuad.points()[1].fY,
270 draw.fQuad.points()[2].fX, draw.fQuad.points()[2].fY, draw.fQuad.points()[3].fX,
Brian Salomon34169692017-08-28 15:32:01 -0400271 draw.fQuad.points()[3].fY);
272 }
273 str += INHERITED::dumpInfo();
274 return str;
275 }
276
Brian Osman9a725dd2017-09-20 09:53:22 -0400277 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip,
278 GrPixelConfigIsClamped dstIsClamped) override {
Brian Salomon34169692017-08-28 15:32:01 -0400279 SkASSERT(!fFinalized);
Brian Salomon336ce7b2017-09-08 08:23:58 -0400280 SkASSERT(1 == fProxyCnt);
Brian Salomon34169692017-08-28 15:32:01 -0400281 fFinalized = true;
Brian Salomon336ce7b2017-09-08 08:23:58 -0400282 fProxy0->addPendingRead();
283 fProxy0->unref();
Brian Salomon34169692017-08-28 15:32:01 -0400284 return RequiresDstTexture::kNo;
285 }
286
287 FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
288
289 DEFINE_OP_CLASS_ID
290
291private:
Brian Salomon336ce7b2017-09-08 08:23:58 -0400292 static constexpr int kMaxTextures = TextureGeometryProcessor::kMaxTextures;
293
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400294 TextureOp(sk_sp<GrTextureProxy> proxy, GrSamplerState::Filter filter, GrColor color,
Brian Salomon34169692017-08-28 15:32:01 -0400295 const SkRect& srcRect, const SkRect& dstRect, const SkMatrix& viewMatrix,
296 sk_sp<GrColorSpaceXform> csxf, bool allowSRGBInputs)
297 : INHERITED(ClassID())
Brian Salomon34169692017-08-28 15:32:01 -0400298 , fColorSpaceXform(std::move(csxf))
Brian Salomon336ce7b2017-09-08 08:23:58 -0400299 , fProxy0(proxy.release())
300 , fFilter0(filter)
301 , fProxyCnt(1)
Brian Salomon34169692017-08-28 15:32:01 -0400302 , fFinalized(false)
303 , fAllowSRGBInputs(allowSRGBInputs) {
304 Draw& draw = fDraws.push_back();
305 draw.fSrcRect = srcRect;
Brian Salomon336ce7b2017-09-08 08:23:58 -0400306 draw.fTextureIdx = 0;
Brian Salomon34169692017-08-28 15:32:01 -0400307 draw.fColor = color;
308 draw.fQuad.setFromMappedRect(dstRect, viewMatrix);
309 SkRect bounds;
310 bounds.setBounds(draw.fQuad.points(), 4);
311 this->setBounds(bounds, HasAABloat::kNo, IsZeroArea::kNo);
312 }
313
314 void onPrepareDraws(Target* target) override {
Brian Salomon336ce7b2017-09-08 08:23:58 -0400315 sk_sp<GrTextureProxy> proxiesSPs[kMaxTextures];
316 auto proxies = this->proxies();
317 auto filters = this->filters();
318 for (int i = 0; i < fProxyCnt; ++i) {
319 if (!proxies[i]->instantiate(target->resourceProvider())) {
320 return;
321 }
322 proxiesSPs[i] = sk_ref_sp(proxies[i]);
Brian Salomon34169692017-08-28 15:32:01 -0400323 }
Brian Salomon336ce7b2017-09-08 08:23:58 -0400324
325 sk_sp<GrGeometryProcessor> gp =
326 TextureGeometryProcessor::Make(proxiesSPs, fProxyCnt, std::move(fColorSpaceXform),
327 filters, *target->caps().shaderCaps());
Brian Salomon34169692017-08-28 15:32:01 -0400328 GrPipeline::InitArgs args;
329 args.fProxy = target->proxy();
330 args.fCaps = &target->caps();
331 args.fResourceProvider = target->resourceProvider();
332 args.fFlags = fAllowSRGBInputs ? GrPipeline::kAllowSRGBInputs_Flag : 0;
333 const GrPipeline* pipeline = target->allocPipeline(args, GrProcessorSet::MakeEmptySet(),
334 target->detachAppliedClip());
Brian Salomon34169692017-08-28 15:32:01 -0400335 int vstart;
336 const GrBuffer* vbuffer;
Brian Salomon336ce7b2017-09-08 08:23:58 -0400337 void* vdata = target->makeVertexSpace(gp->getVertexStride(), 4 * fDraws.count(), &vbuffer,
338 &vstart);
339 if (!vdata) {
Brian Salomon34169692017-08-28 15:32:01 -0400340 SkDebugf("Could not allocate vertices\n");
341 return;
342 }
343 sk_sp<const GrBuffer> ibuffer;
344 if (fDraws.count() > 1) {
345 ibuffer.reset(target->resourceProvider()->refQuadIndexBuffer());
346 if (!ibuffer) {
347 SkDebugf("Could not allocate quad indices\n");
348 return;
349 }
Brian Salomon336ce7b2017-09-08 08:23:58 -0400350 if (1 == fProxyCnt) {
351 SkASSERT(gp->getVertexStride() == sizeof(TextureGeometryProcessor::Vertex));
352 for (int i = 0; i < fDraws.count(); ++i) {
353 auto vertices = static_cast<TextureGeometryProcessor::Vertex*>(vdata);
354 GrTexture* texture = proxies[0]->priv().peekTexture();
355 float iw = 1.f / texture->width();
356 float ih = 1.f / texture->height();
357 float tl = iw * fDraws[i].fSrcRect.fLeft;
358 float tr = iw * fDraws[i].fSrcRect.fRight;
359 float tt = ih * fDraws[i].fSrcRect.fTop;
360 float tb = ih * fDraws[i].fSrcRect.fBottom;
361 if (proxies[0]->origin() == kBottomLeft_GrSurfaceOrigin) {
362 tt = 1.f - tt;
363 tb = 1.f - tb;
364 }
365 vertices[0 + 4 * i].fPosition = fDraws[i].fQuad.points()[0];
366 vertices[0 + 4 * i].fTextureCoords = {tl, tt};
367 vertices[0 + 4 * i].fColor = fDraws[i].fColor;
368 vertices[1 + 4 * i].fPosition = fDraws[i].fQuad.points()[1];
369 vertices[1 + 4 * i].fTextureCoords = {tl, tb};
370 vertices[1 + 4 * i].fColor = fDraws[i].fColor;
371 vertices[2 + 4 * i].fPosition = fDraws[i].fQuad.points()[2];
372 vertices[2 + 4 * i].fTextureCoords = {tr, tb};
373 vertices[2 + 4 * i].fColor = fDraws[i].fColor;
374 vertices[3 + 4 * i].fPosition = fDraws[i].fQuad.points()[3];
375 vertices[3 + 4 * i].fTextureCoords = {tr, tt};
376 vertices[3 + 4 * i].fColor = fDraws[i].fColor;
Brian Salomon34169692017-08-28 15:32:01 -0400377 }
Brian Salomon336ce7b2017-09-08 08:23:58 -0400378 } else {
379 SkASSERT(gp->getVertexStride() ==
380 sizeof(TextureGeometryProcessor::MultiTextureVertex));
381 GrTexture* textures[kMaxTextures];
382 float iw[kMaxTextures];
383 float ih[kMaxTextures];
384 for (int t = 0; t < fProxyCnt; ++t) {
385 textures[t] = proxies[t]->priv().peekTexture();
386 iw[t] = 1.f / textures[t]->width();
387 ih[t] = 1.f / textures[t]->height();
388 }
389 for (int i = 0; i < fDraws.count(); ++i) {
390 int t = fDraws[i].fTextureIdx;
391 auto vertices =
392 static_cast<TextureGeometryProcessor::MultiTextureVertex*>(vdata);
393 float tl = iw[t] * fDraws[i].fSrcRect.fLeft;
394 float tr = iw[t] * fDraws[i].fSrcRect.fRight;
395 float tt = ih[t] * fDraws[i].fSrcRect.fTop;
396 float tb = ih[t] * fDraws[i].fSrcRect.fBottom;
397 if (proxies[t]->origin() == kBottomLeft_GrSurfaceOrigin) {
398 tt = 1.f - tt;
399 tb = 1.f - tb;
400 }
401 vertices[0 + 4 * i].fPosition = fDraws[i].fQuad.points()[0];
402 vertices[0 + 4 * i].fTextureIdx = t;
403 vertices[0 + 4 * i].fTextureCoords = {tl, tt};
404 vertices[0 + 4 * i].fColor = fDraws[i].fColor;
405 vertices[1 + 4 * i].fPosition = fDraws[i].fQuad.points()[1];
406 vertices[1 + 4 * i].fTextureIdx = t;
407 vertices[1 + 4 * i].fTextureCoords = {tl, tb};
408 vertices[1 + 4 * i].fColor = fDraws[i].fColor;
409 vertices[2 + 4 * i].fPosition = fDraws[i].fQuad.points()[2];
410 vertices[2 + 4 * i].fTextureIdx = t;
411 vertices[2 + 4 * i].fTextureCoords = {tr, tb};
412 vertices[2 + 4 * i].fColor = fDraws[i].fColor;
413 vertices[3 + 4 * i].fPosition = fDraws[i].fQuad.points()[3];
414 vertices[3 + 4 * i].fTextureIdx = t;
415 vertices[3 + 4 * i].fTextureCoords = {tr, tt};
416 vertices[3 + 4 * i].fColor = fDraws[i].fColor;
417 }
Brian Salomon34169692017-08-28 15:32:01 -0400418 }
419 GrMesh mesh(GrPrimitiveType::kTriangles);
420 mesh.setIndexedPatterned(ibuffer.get(), 6, 4, fDraws.count(),
421 GrResourceProvider::QuadCountOfQuadBuffer());
422 mesh.setVertexData(vbuffer, vstart);
423 target->draw(gp.get(), pipeline, mesh);
424 } else {
Brian Salomon336ce7b2017-09-08 08:23:58 -0400425 // If there is only one draw then there can only be one proxy.
426 SkASSERT(1 == fProxyCnt);
427 SkASSERT(gp->getVertexStride() == sizeof(TextureGeometryProcessor::Vertex));
428 auto vertices = static_cast<TextureGeometryProcessor::Vertex*>(vdata);
429 GrTexture* texture = proxies[0]->priv().peekTexture();
430 float iw = 1.f / texture->width();
431 float ih = 1.f / texture->height();
Brian Salomon34169692017-08-28 15:32:01 -0400432 float tl = iw * fDraws[0].fSrcRect.fLeft;
433 float tr = iw * fDraws[0].fSrcRect.fRight;
434 float tt = ih * fDraws[0].fSrcRect.fTop;
435 float tb = ih * fDraws[0].fSrcRect.fBottom;
Brian Salomon336ce7b2017-09-08 08:23:58 -0400436 if (proxies[0]->origin() == kBottomLeft_GrSurfaceOrigin) {
Brian Salomon34169692017-08-28 15:32:01 -0400437 tt = 1.f - tt;
438 tb = 1.f - tb;
439 }
440 vertices[0].fPosition = fDraws[0].fQuad.points()[0];
441 vertices[0].fTextureCoords = {tl, tt};
442 vertices[0].fColor = fDraws[0].fColor;
443 vertices[1].fPosition = fDraws[0].fQuad.points()[3];
444 vertices[1].fTextureCoords = {tr, tt};
445 vertices[1].fColor = fDraws[0].fColor;
446 vertices[2].fPosition = fDraws[0].fQuad.points()[1];
447 vertices[2].fTextureCoords = {tl, tb};
448 vertices[2].fColor = fDraws[0].fColor;
449 vertices[3].fPosition = fDraws[0].fQuad.points()[2];
450 vertices[3].fTextureCoords = {tr, tb};
451 vertices[3].fColor = fDraws[0].fColor;
452 GrMesh mesh(GrPrimitiveType::kTriangleStrip);
453 mesh.setNonIndexedNonInstanced(4);
454 mesh.setVertexData(vbuffer, vstart);
455 target->draw(gp.get(), pipeline, mesh);
456 }
457 }
458
459 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
460 const auto* that = t->cast<TextureOp>();
Brian Salomon336ce7b2017-09-08 08:23:58 -0400461 if (!GrColorSpaceXform::Equals(fColorSpaceXform.get(), that->fColorSpaceXform.get())) {
Brian Salomon34169692017-08-28 15:32:01 -0400462 return false;
463 }
Brian Salomon336ce7b2017-09-08 08:23:58 -0400464 // Because of an issue where GrColorSpaceXform adds the same function every time it is used
465 // in a texture lookup, we only allow multiple textures when there is no transform.
466 if (TextureGeometryProcessor::SupportsMultitexture(*caps.shaderCaps()) &&
467 !fColorSpaceXform) {
468 int map[kMaxTextures];
469 int numNewProxies = this->mergeProxies(that, map, *caps.shaderCaps());
470 if (numNewProxies < 0) {
471 return false;
472 }
473 if (1 == fProxyCnt && numNewProxies) {
474 void* mem = new char[(sizeof(GrSamplerState::Filter) + sizeof(GrTextureProxy*)) *
475 kMaxTextures];
476 auto proxies = reinterpret_cast<GrTextureProxy**>(mem);
477 auto filters = reinterpret_cast<GrSamplerState::Filter*>(proxies + kMaxTextures);
478 proxies[0] = fProxy0;
479 filters[0] = fFilter0;
480 fProxyArray = proxies;
481 }
482 fProxyCnt += numNewProxies;
483 auto thisProxies = fProxyArray;
484 auto thatProxies = that->proxies();
485 auto thatFilters = that->filters();
486 auto thisFilters = reinterpret_cast<GrSamplerState::Filter*>(thisProxies +
487 kMaxTextures);
488 for (int i = 0; i < that->fProxyCnt; ++i) {
489 if (map[i] < 0) {
490 thatProxies[i]->addPendingRead();
Robert Phillipsb493eeb2017-09-13 13:10:52 -0400491
Brian Salomon336ce7b2017-09-08 08:23:58 -0400492 thisProxies[-map[i]] = thatProxies[i];
493 thisFilters[-map[i]] = thatFilters[i];
494 map[i] = -map[i];
495 }
496 }
497 int firstNewDraw = fDraws.count();
498 fDraws.push_back_n(that->fDraws.count(), that->fDraws.begin());
499 for (int i = firstNewDraw; i < fDraws.count(); ++i) {
500 fDraws[i].fTextureIdx = map[fDraws[i].fTextureIdx];
501 }
502 } else {
503 if (fProxy0->uniqueID() != that->fProxy0->uniqueID() || fFilter0 != that->fFilter0) {
504 return false;
505 }
506 fDraws.push_back_n(that->fDraws.count(), that->fDraws.begin());
507 }
Brian Salomon34169692017-08-28 15:32:01 -0400508 this->joinBounds(*that);
509 return true;
510 }
511
Brian Salomon336ce7b2017-09-08 08:23:58 -0400512 /**
513 * Determines a mapping of indices from that's proxy array to this's proxy array. A negative map
514 * value means that's proxy should be added to this's proxy array at the absolute value of
515 * the map entry. If it is determined that the ops shouldn't combine their proxies then a
516 * negative value is returned. Otherwise, return value indicates the number of proxies that have
517 * to be added to this op or, equivalently, the number of negative entries in map.
518 */
519 int mergeProxies(const TextureOp* that, int map[kMaxTextures], const GrShaderCaps& caps) const {
520 std::fill_n(map, kMaxTextures, -kMaxTextures);
521 int sharedProxyCnt = 0;
522 auto thisProxies = this->proxies();
523 auto thisFilters = this->filters();
524 auto thatProxies = that->proxies();
525 auto thatFilters = that->filters();
526 for (int i = 0; i < fProxyCnt; ++i) {
527 for (int j = 0; j < that->fProxyCnt; ++j) {
528 if (thisProxies[i]->uniqueID() == thatProxies[j]->uniqueID()) {
529 if (thisFilters[i] != thatFilters[j]) {
530 // In GL we don't currently support using the same texture with different
531 // samplers. If we added support for sampler objects and a cap bit to know
532 // it's ok to use different filter modes then we could support this.
533 // Otherwise, we could also only allow a single filter mode for each op
534 // instance.
535 return -1;
536 }
537 map[j] = i;
538 ++sharedProxyCnt;
539 break;
540 }
541 }
542 }
543 int actualMaxTextures = SkTMin(caps.maxFragmentImageStorages(), kMaxTextures);
544 int newProxyCnt = that->fProxyCnt - sharedProxyCnt;
545 if (newProxyCnt + fProxyCnt > actualMaxTextures) {
546 return -1;
547 }
548 GrPixelConfig config = thisProxies[0]->config();
549 int nextSlot = fProxyCnt;
550 for (int j = 0; j < that->fProxyCnt; ++j) {
551 // We want to avoid making many shaders because of different permutations of shader
552 // based swizzle and sampler types. The approach taken here is to require the configs to
553 // be the same and to only allow already instantiated proxies that have the most
554 // common sampler type. Otherwise we don't merge.
555 if (thatProxies[j]->config() != config) {
556 return -1;
557 }
558 if (GrTexture* tex = thatProxies[j]->priv().peekTexture()) {
559 if (tex->texturePriv().samplerType() != kTexture2DSampler_GrSLType) {
560 return -1;
561 }
562 }
563 if (map[j] < 0) {
564 map[j] = -(nextSlot++);
565 }
566 }
567 return newProxyCnt;
568 }
569
570 GrTextureProxy* const* proxies() const { return fProxyCnt > 1 ? fProxyArray : &fProxy0; }
571
572 const GrSamplerState::Filter* filters() const {
573 if (fProxyCnt > 1) {
574 return reinterpret_cast<const GrSamplerState::Filter*>(fProxyArray + kMaxTextures);
575 }
576 return &fFilter0;
577 }
578
Brian Salomon34169692017-08-28 15:32:01 -0400579 struct Draw {
580 SkRect fSrcRect;
Brian Salomon336ce7b2017-09-08 08:23:58 -0400581 int fTextureIdx;
Brian Salomon34169692017-08-28 15:32:01 -0400582 GrQuad fQuad;
583 GrColor fColor;
584 };
585 SkSTArray<1, Draw, true> fDraws;
Brian Salomon34169692017-08-28 15:32:01 -0400586 sk_sp<GrColorSpaceXform> fColorSpaceXform;
Brian Salomon336ce7b2017-09-08 08:23:58 -0400587 // Initially we store a single proxy ptr and a single filter. If we grow to have more than
588 // one proxy we instead store pointers to dynamically allocated arrays of size kMaxTextures
589 // followed by kMaxTextures filters.
590 union {
591 GrTextureProxy* fProxy0;
592 GrTextureProxy** fProxyArray;
593 };
594 // The next four members should pack.
595 GrSamplerState::Filter fFilter0;
596 uint8_t fProxyCnt;
Brian Salomon34169692017-08-28 15:32:01 -0400597 // Used to track whether fProxy is ref'ed or has a pending IO after finalize() is called.
Brian Salomon336ce7b2017-09-08 08:23:58 -0400598 uint8_t fFinalized;
599 uint8_t fAllowSRGBInputs;
600
Brian Salomon34169692017-08-28 15:32:01 -0400601 typedef GrMeshDrawOp INHERITED;
602};
603
Brian Salomon336ce7b2017-09-08 08:23:58 -0400604constexpr int TextureGeometryProcessor::kMaxTextures;
605constexpr int TextureOp::kMaxTextures;
606
Brian Salomon34169692017-08-28 15:32:01 -0400607} // anonymous namespace
608
609namespace GrTextureOp {
610
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400611std::unique_ptr<GrDrawOp> Make(sk_sp<GrTextureProxy> proxy, GrSamplerState::Filter filter,
Brian Salomon34169692017-08-28 15:32:01 -0400612 GrColor color, const SkRect& srcRect, const SkRect& dstRect,
613 const SkMatrix& viewMatrix, sk_sp<GrColorSpaceXform> csxf,
614 bool allowSRGBInputs) {
615 SkASSERT(!viewMatrix.hasPerspective());
616 return TextureOp::Make(std::move(proxy), filter, color, srcRect, dstRect, viewMatrix,
617 std::move(csxf), allowSRGBInputs);
618}
619
620} // namespace GrTextureOp
621
622#if GR_TEST_UTILS
623#include "GrContext.h"
624
625GR_DRAW_OP_TEST_DEFINE(TextureOp) {
626 GrSurfaceDesc desc;
627 desc.fConfig = kRGBA_8888_GrPixelConfig;
628 desc.fHeight = random->nextULessThan(90) + 10;
629 desc.fWidth = random->nextULessThan(90) + 10;
630 desc.fOrigin = random->nextBool() ? kTopLeft_GrSurfaceOrigin : kBottomLeft_GrSurfaceOrigin;
631 SkBackingFit fit = random->nextBool() ? SkBackingFit::kApprox : SkBackingFit::kExact;
632 auto proxy =
633 GrSurfaceProxy::MakeDeferred(context->resourceProvider(), desc, fit, SkBudgeted::kNo);
634 SkRect rect = GrTest::TestRect(random);
635 SkRect srcRect;
636 srcRect.fLeft = random->nextRangeScalar(0.f, proxy->width() / 2.f);
637 srcRect.fRight = random->nextRangeScalar(0.f, proxy->width()) + proxy->width() / 2.f;
638 srcRect.fTop = random->nextRangeScalar(0.f, proxy->height() / 2.f);
639 srcRect.fBottom = random->nextRangeScalar(0.f, proxy->height()) + proxy->height() / 2.f;
640 SkMatrix viewMatrix = GrTest::TestMatrixPreservesRightAngles(random);
641 GrColor color = SkColorToPremulGrColor(random->nextU());
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400642 GrSamplerState::Filter filter = (GrSamplerState::Filter)random->nextULessThan(
643 static_cast<uint32_t>(GrSamplerState::Filter::kMipMap) + 1);
Brian Salomon34169692017-08-28 15:32:01 -0400644 auto csxf = GrTest::TestColorXform(random);
645 bool allowSRGBInputs = random->nextBool();
646 return GrTextureOp::Make(std::move(proxy), filter, color, srcRect, rect, viewMatrix,
647 std::move(csxf), allowSRGBInputs);
648}
649
650#endif