blob: 1149ed26e50c83686cddde1db11f44efcdef77f6 [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);
111 if (args.fShaderCaps->flatInterpolationSupport()) {
112 args.fVaryingHandler->addFlatPassThroughAttribute(&textureGP.fColors,
113 args.fOutputColor);
114 } else {
115 args.fVaryingHandler->addPassThroughAttribute(&textureGP.fColors,
116 args.fOutputColor);
117 }
118 args.fFragBuilder->codeAppend("highp 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",
137 kVec2f_GrSLType,
138 &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",
147 kVec2f_GrSLType,
148 &fColorSpaceXformHelper);
149 }
Brian Salomon34169692017-08-28 15:32:01 -0400150 args.fFragBuilder->codeAppend(";");
151 args.fFragBuilder->codeAppendf("%s = float4(1);", args.fOutputCoverage);
152 }
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>();
180 fPositions =
181 this->addVertexAttrib("position", kVec2f_GrVertexAttribType, kHigh_GrSLPrecision);
Brian Salomon336ce7b2017-09-08 08:23:58 -0400182 fSamplers[0].reset(std::move(proxies[0]), filters[0]);
183 this->addTextureSampler(&fSamplers[0]);
184 for (int i = 1; i < proxyCnt; ++i) {
185 // This class has one sampler built in, the rest come from memory this processor was
186 // placement-newed into and so haven't been constructed.
187 new (&fSamplers[i]) TextureSampler(std::move(proxies[i]), filters[i]);
188 this->addTextureSampler(&fSamplers[i]);
189 }
190 if (samplerCnt > 1) {
191 // Here we initialize any extra samplers by repeating the last one samplerCnt - proxyCnt
192 // times.
193 GrTextureProxy* dupeProxy = fSamplers[proxyCnt - 1].proxy();
194 for (int i = proxyCnt; i < samplerCnt; ++i) {
195 new (&fSamplers[i]) TextureSampler(sk_ref_sp(dupeProxy), filters[proxyCnt - 1]);
196 this->addTextureSampler(&fSamplers[i]);
197 }
198 SkASSERT(caps.integerSupport());
199 fTextureIdx = this->addVertexAttrib("textureIdx", kInt_GrVertexAttribType);
200 }
201
Brian Salomon34169692017-08-28 15:32:01 -0400202 fTextureCoords = this->addVertexAttrib("textureCoords", kVec2f_GrVertexAttribType,
203 kHigh_GrSLPrecision);
204 fColors = this->addVertexAttrib("color", kVec4ub_GrVertexAttribType);
Brian Salomon34169692017-08-28 15:32:01 -0400205 }
206
207 Attribute fPositions;
Brian Salomon336ce7b2017-09-08 08:23:58 -0400208 Attribute fTextureIdx;
Brian Salomon34169692017-08-28 15:32:01 -0400209 Attribute fTextureCoords;
210 Attribute fColors;
Brian Salomon34169692017-08-28 15:32:01 -0400211 sk_sp<GrColorSpaceXform> fColorSpaceXform;
Brian Salomon336ce7b2017-09-08 08:23:58 -0400212 TextureSampler fSamplers[1];
Brian Salomon34169692017-08-28 15:32:01 -0400213};
214
215/**
216 * Op that implements GrTextureOp::Make. It draws textured quads. Each quad can modulate against a
217 * the texture by color. The blend with the destination is always src-over. The edges are non-AA.
218 */
219class TextureOp final : public GrMeshDrawOp {
220public:
221 static std::unique_ptr<GrDrawOp> Make(sk_sp<GrTextureProxy> proxy,
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400222 GrSamplerState::Filter filter, GrColor color,
Brian Salomon34169692017-08-28 15:32:01 -0400223 const SkRect srcRect, const SkRect dstRect,
224 const SkMatrix& viewMatrix, sk_sp<GrColorSpaceXform> csxf,
225 bool allowSRBInputs) {
226 return std::unique_ptr<GrDrawOp>(new TextureOp(std::move(proxy), filter, color, srcRect,
227 dstRect, viewMatrix, std::move(csxf),
228 allowSRBInputs));
229 }
230
Brian Salomon336ce7b2017-09-08 08:23:58 -0400231 ~TextureOp() override {
232 if (fFinalized) {
233 auto proxies = this->proxies();
234 for (int i = 0; i < fProxyCnt; ++i) {
235 proxies[i]->completedRead();
236 }
237 if (fProxyCnt > 1) {
238 delete[] reinterpret_cast<const char*>(proxies);
239 }
240 } else {
241 SkASSERT(1 == fProxyCnt);
242 fProxy0->unref();
243 }
244 }
Brian Salomon34169692017-08-28 15:32:01 -0400245
246 const char* name() const override { return "TextureOp"; }
247
Robert Phillipsf1748f52017-09-14 14:11:24 -0400248 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillipsb493eeb2017-09-13 13:10:52 -0400249 auto proxies = this->proxies();
250 for (int i = 0; i < fProxyCnt; ++i) {
251 func(proxies[i]);
252 }
253 }
254
Brian Salomon34169692017-08-28 15:32:01 -0400255 SkString dumpInfo() const override {
256 SkString str;
Brian Salomon336ce7b2017-09-08 08:23:58 -0400257 str.appendf("AllowSRGBInputs: %d\n", fAllowSRGBInputs);
Brian Salomon34169692017-08-28 15:32:01 -0400258 str.appendf("# draws: %d\n", fDraws.count());
Brian Salomon336ce7b2017-09-08 08:23:58 -0400259 auto proxies = this->proxies();
260 for (int i = 0; i < fProxyCnt; ++i) {
261 str.appendf("Proxy ID %d: %d, Filter: %d\n", i, proxies[i]->uniqueID().asUInt(),
262 static_cast<int>(this->filters()[i]));
263 }
Brian Salomon34169692017-08-28 15:32:01 -0400264 for (int i = 0; i < fDraws.count(); ++i) {
265 const Draw& draw = fDraws[i];
266 str.appendf(
Brian Salomon336ce7b2017-09-08 08:23:58 -0400267 "%d: Color: 0x%08x, ProxyIdx: %d, TexRect [L: %.2f, T: %.2f, R: %.2f, B: %.2f] "
268 "Quad [(%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f)]\n",
269 i, draw.fColor, draw.fTextureIdx, draw.fSrcRect.fLeft, draw.fSrcRect.fTop,
270 draw.fSrcRect.fRight, draw.fSrcRect.fBottom, draw.fQuad.points()[0].fX,
271 draw.fQuad.points()[0].fY, draw.fQuad.points()[1].fX, draw.fQuad.points()[1].fY,
272 draw.fQuad.points()[2].fX, draw.fQuad.points()[2].fY, draw.fQuad.points()[3].fX,
Brian Salomon34169692017-08-28 15:32:01 -0400273 draw.fQuad.points()[3].fY);
274 }
275 str += INHERITED::dumpInfo();
276 return str;
277 }
278
279 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip) override {
280 SkASSERT(!fFinalized);
Brian Salomon336ce7b2017-09-08 08:23:58 -0400281 SkASSERT(1 == fProxyCnt);
Brian Salomon34169692017-08-28 15:32:01 -0400282 fFinalized = true;
Brian Salomon336ce7b2017-09-08 08:23:58 -0400283 fProxy0->addPendingRead();
284 fProxy0->unref();
Brian Salomon34169692017-08-28 15:32:01 -0400285 return RequiresDstTexture::kNo;
286 }
287
288 FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
289
290 DEFINE_OP_CLASS_ID
291
292private:
Brian Salomon336ce7b2017-09-08 08:23:58 -0400293 static constexpr int kMaxTextures = TextureGeometryProcessor::kMaxTextures;
294
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400295 TextureOp(sk_sp<GrTextureProxy> proxy, GrSamplerState::Filter filter, GrColor color,
Brian Salomon34169692017-08-28 15:32:01 -0400296 const SkRect& srcRect, const SkRect& dstRect, const SkMatrix& viewMatrix,
297 sk_sp<GrColorSpaceXform> csxf, bool allowSRGBInputs)
298 : INHERITED(ClassID())
Brian Salomon34169692017-08-28 15:32:01 -0400299 , fColorSpaceXform(std::move(csxf))
Brian Salomon336ce7b2017-09-08 08:23:58 -0400300 , fProxy0(proxy.release())
301 , fFilter0(filter)
302 , fProxyCnt(1)
Brian Salomon34169692017-08-28 15:32:01 -0400303 , fFinalized(false)
304 , fAllowSRGBInputs(allowSRGBInputs) {
305 Draw& draw = fDraws.push_back();
306 draw.fSrcRect = srcRect;
Brian Salomon336ce7b2017-09-08 08:23:58 -0400307 draw.fTextureIdx = 0;
Brian Salomon34169692017-08-28 15:32:01 -0400308 draw.fColor = color;
309 draw.fQuad.setFromMappedRect(dstRect, viewMatrix);
310 SkRect bounds;
311 bounds.setBounds(draw.fQuad.points(), 4);
312 this->setBounds(bounds, HasAABloat::kNo, IsZeroArea::kNo);
313 }
314
315 void onPrepareDraws(Target* target) override {
Brian Salomon336ce7b2017-09-08 08:23:58 -0400316 sk_sp<GrTextureProxy> proxiesSPs[kMaxTextures];
317 auto proxies = this->proxies();
318 auto filters = this->filters();
319 for (int i = 0; i < fProxyCnt; ++i) {
320 if (!proxies[i]->instantiate(target->resourceProvider())) {
321 return;
322 }
323 proxiesSPs[i] = sk_ref_sp(proxies[i]);
Brian Salomon34169692017-08-28 15:32:01 -0400324 }
Brian Salomon336ce7b2017-09-08 08:23:58 -0400325
326 sk_sp<GrGeometryProcessor> gp =
327 TextureGeometryProcessor::Make(proxiesSPs, fProxyCnt, std::move(fColorSpaceXform),
328 filters, *target->caps().shaderCaps());
Brian Salomon34169692017-08-28 15:32:01 -0400329 GrPipeline::InitArgs args;
330 args.fProxy = target->proxy();
331 args.fCaps = &target->caps();
332 args.fResourceProvider = target->resourceProvider();
333 args.fFlags = fAllowSRGBInputs ? GrPipeline::kAllowSRGBInputs_Flag : 0;
334 const GrPipeline* pipeline = target->allocPipeline(args, GrProcessorSet::MakeEmptySet(),
335 target->detachAppliedClip());
Brian Salomon34169692017-08-28 15:32:01 -0400336 int vstart;
337 const GrBuffer* vbuffer;
Brian Salomon336ce7b2017-09-08 08:23:58 -0400338 void* vdata = target->makeVertexSpace(gp->getVertexStride(), 4 * fDraws.count(), &vbuffer,
339 &vstart);
340 if (!vdata) {
Brian Salomon34169692017-08-28 15:32:01 -0400341 SkDebugf("Could not allocate vertices\n");
342 return;
343 }
344 sk_sp<const GrBuffer> ibuffer;
345 if (fDraws.count() > 1) {
346 ibuffer.reset(target->resourceProvider()->refQuadIndexBuffer());
347 if (!ibuffer) {
348 SkDebugf("Could not allocate quad indices\n");
349 return;
350 }
Brian Salomon336ce7b2017-09-08 08:23:58 -0400351 if (1 == fProxyCnt) {
352 SkASSERT(gp->getVertexStride() == sizeof(TextureGeometryProcessor::Vertex));
353 for (int i = 0; i < fDraws.count(); ++i) {
354 auto vertices = static_cast<TextureGeometryProcessor::Vertex*>(vdata);
355 GrTexture* texture = proxies[0]->priv().peekTexture();
356 float iw = 1.f / texture->width();
357 float ih = 1.f / texture->height();
358 float tl = iw * fDraws[i].fSrcRect.fLeft;
359 float tr = iw * fDraws[i].fSrcRect.fRight;
360 float tt = ih * fDraws[i].fSrcRect.fTop;
361 float tb = ih * fDraws[i].fSrcRect.fBottom;
362 if (proxies[0]->origin() == kBottomLeft_GrSurfaceOrigin) {
363 tt = 1.f - tt;
364 tb = 1.f - tb;
365 }
366 vertices[0 + 4 * i].fPosition = fDraws[i].fQuad.points()[0];
367 vertices[0 + 4 * i].fTextureCoords = {tl, tt};
368 vertices[0 + 4 * i].fColor = fDraws[i].fColor;
369 vertices[1 + 4 * i].fPosition = fDraws[i].fQuad.points()[1];
370 vertices[1 + 4 * i].fTextureCoords = {tl, tb};
371 vertices[1 + 4 * i].fColor = fDraws[i].fColor;
372 vertices[2 + 4 * i].fPosition = fDraws[i].fQuad.points()[2];
373 vertices[2 + 4 * i].fTextureCoords = {tr, tb};
374 vertices[2 + 4 * i].fColor = fDraws[i].fColor;
375 vertices[3 + 4 * i].fPosition = fDraws[i].fQuad.points()[3];
376 vertices[3 + 4 * i].fTextureCoords = {tr, tt};
377 vertices[3 + 4 * i].fColor = fDraws[i].fColor;
Brian Salomon34169692017-08-28 15:32:01 -0400378 }
Brian Salomon336ce7b2017-09-08 08:23:58 -0400379 } else {
380 SkASSERT(gp->getVertexStride() ==
381 sizeof(TextureGeometryProcessor::MultiTextureVertex));
382 GrTexture* textures[kMaxTextures];
383 float iw[kMaxTextures];
384 float ih[kMaxTextures];
385 for (int t = 0; t < fProxyCnt; ++t) {
386 textures[t] = proxies[t]->priv().peekTexture();
387 iw[t] = 1.f / textures[t]->width();
388 ih[t] = 1.f / textures[t]->height();
389 }
390 for (int i = 0; i < fDraws.count(); ++i) {
391 int t = fDraws[i].fTextureIdx;
392 auto vertices =
393 static_cast<TextureGeometryProcessor::MultiTextureVertex*>(vdata);
394 float tl = iw[t] * fDraws[i].fSrcRect.fLeft;
395 float tr = iw[t] * fDraws[i].fSrcRect.fRight;
396 float tt = ih[t] * fDraws[i].fSrcRect.fTop;
397 float tb = ih[t] * fDraws[i].fSrcRect.fBottom;
398 if (proxies[t]->origin() == kBottomLeft_GrSurfaceOrigin) {
399 tt = 1.f - tt;
400 tb = 1.f - tb;
401 }
402 vertices[0 + 4 * i].fPosition = fDraws[i].fQuad.points()[0];
403 vertices[0 + 4 * i].fTextureIdx = t;
404 vertices[0 + 4 * i].fTextureCoords = {tl, tt};
405 vertices[0 + 4 * i].fColor = fDraws[i].fColor;
406 vertices[1 + 4 * i].fPosition = fDraws[i].fQuad.points()[1];
407 vertices[1 + 4 * i].fTextureIdx = t;
408 vertices[1 + 4 * i].fTextureCoords = {tl, tb};
409 vertices[1 + 4 * i].fColor = fDraws[i].fColor;
410 vertices[2 + 4 * i].fPosition = fDraws[i].fQuad.points()[2];
411 vertices[2 + 4 * i].fTextureIdx = t;
412 vertices[2 + 4 * i].fTextureCoords = {tr, tb};
413 vertices[2 + 4 * i].fColor = fDraws[i].fColor;
414 vertices[3 + 4 * i].fPosition = fDraws[i].fQuad.points()[3];
415 vertices[3 + 4 * i].fTextureIdx = t;
416 vertices[3 + 4 * i].fTextureCoords = {tr, tt};
417 vertices[3 + 4 * i].fColor = fDraws[i].fColor;
418 }
Brian Salomon34169692017-08-28 15:32:01 -0400419 }
420 GrMesh mesh(GrPrimitiveType::kTriangles);
421 mesh.setIndexedPatterned(ibuffer.get(), 6, 4, fDraws.count(),
422 GrResourceProvider::QuadCountOfQuadBuffer());
423 mesh.setVertexData(vbuffer, vstart);
424 target->draw(gp.get(), pipeline, mesh);
425 } else {
Brian Salomon336ce7b2017-09-08 08:23:58 -0400426 // If there is only one draw then there can only be one proxy.
427 SkASSERT(1 == fProxyCnt);
428 SkASSERT(gp->getVertexStride() == sizeof(TextureGeometryProcessor::Vertex));
429 auto vertices = static_cast<TextureGeometryProcessor::Vertex*>(vdata);
430 GrTexture* texture = proxies[0]->priv().peekTexture();
431 float iw = 1.f / texture->width();
432 float ih = 1.f / texture->height();
Brian Salomon34169692017-08-28 15:32:01 -0400433 float tl = iw * fDraws[0].fSrcRect.fLeft;
434 float tr = iw * fDraws[0].fSrcRect.fRight;
435 float tt = ih * fDraws[0].fSrcRect.fTop;
436 float tb = ih * fDraws[0].fSrcRect.fBottom;
Brian Salomon336ce7b2017-09-08 08:23:58 -0400437 if (proxies[0]->origin() == kBottomLeft_GrSurfaceOrigin) {
Brian Salomon34169692017-08-28 15:32:01 -0400438 tt = 1.f - tt;
439 tb = 1.f - tb;
440 }
441 vertices[0].fPosition = fDraws[0].fQuad.points()[0];
442 vertices[0].fTextureCoords = {tl, tt};
443 vertices[0].fColor = fDraws[0].fColor;
444 vertices[1].fPosition = fDraws[0].fQuad.points()[3];
445 vertices[1].fTextureCoords = {tr, tt};
446 vertices[1].fColor = fDraws[0].fColor;
447 vertices[2].fPosition = fDraws[0].fQuad.points()[1];
448 vertices[2].fTextureCoords = {tl, tb};
449 vertices[2].fColor = fDraws[0].fColor;
450 vertices[3].fPosition = fDraws[0].fQuad.points()[2];
451 vertices[3].fTextureCoords = {tr, tb};
452 vertices[3].fColor = fDraws[0].fColor;
453 GrMesh mesh(GrPrimitiveType::kTriangleStrip);
454 mesh.setNonIndexedNonInstanced(4);
455 mesh.setVertexData(vbuffer, vstart);
456 target->draw(gp.get(), pipeline, mesh);
457 }
458 }
459
460 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
461 const auto* that = t->cast<TextureOp>();
Brian Salomon336ce7b2017-09-08 08:23:58 -0400462 if (!GrColorSpaceXform::Equals(fColorSpaceXform.get(), that->fColorSpaceXform.get())) {
Brian Salomon34169692017-08-28 15:32:01 -0400463 return false;
464 }
Brian Salomon336ce7b2017-09-08 08:23:58 -0400465 // Because of an issue where GrColorSpaceXform adds the same function every time it is used
466 // in a texture lookup, we only allow multiple textures when there is no transform.
467 if (TextureGeometryProcessor::SupportsMultitexture(*caps.shaderCaps()) &&
468 !fColorSpaceXform) {
469 int map[kMaxTextures];
470 int numNewProxies = this->mergeProxies(that, map, *caps.shaderCaps());
471 if (numNewProxies < 0) {
472 return false;
473 }
474 if (1 == fProxyCnt && numNewProxies) {
475 void* mem = new char[(sizeof(GrSamplerState::Filter) + sizeof(GrTextureProxy*)) *
476 kMaxTextures];
477 auto proxies = reinterpret_cast<GrTextureProxy**>(mem);
478 auto filters = reinterpret_cast<GrSamplerState::Filter*>(proxies + kMaxTextures);
479 proxies[0] = fProxy0;
480 filters[0] = fFilter0;
481 fProxyArray = proxies;
482 }
483 fProxyCnt += numNewProxies;
484 auto thisProxies = fProxyArray;
485 auto thatProxies = that->proxies();
486 auto thatFilters = that->filters();
487 auto thisFilters = reinterpret_cast<GrSamplerState::Filter*>(thisProxies +
488 kMaxTextures);
489 for (int i = 0; i < that->fProxyCnt; ++i) {
490 if (map[i] < 0) {
491 thatProxies[i]->addPendingRead();
Robert Phillipsb493eeb2017-09-13 13:10:52 -0400492
Brian Salomon336ce7b2017-09-08 08:23:58 -0400493 thisProxies[-map[i]] = thatProxies[i];
494 thisFilters[-map[i]] = thatFilters[i];
495 map[i] = -map[i];
496 }
497 }
498 int firstNewDraw = fDraws.count();
499 fDraws.push_back_n(that->fDraws.count(), that->fDraws.begin());
500 for (int i = firstNewDraw; i < fDraws.count(); ++i) {
501 fDraws[i].fTextureIdx = map[fDraws[i].fTextureIdx];
502 }
503 } else {
504 if (fProxy0->uniqueID() != that->fProxy0->uniqueID() || fFilter0 != that->fFilter0) {
505 return false;
506 }
507 fDraws.push_back_n(that->fDraws.count(), that->fDraws.begin());
508 }
Brian Salomon34169692017-08-28 15:32:01 -0400509 this->joinBounds(*that);
510 return true;
511 }
512
Brian Salomon336ce7b2017-09-08 08:23:58 -0400513 /**
514 * Determines a mapping of indices from that's proxy array to this's proxy array. A negative map
515 * value means that's proxy should be added to this's proxy array at the absolute value of
516 * the map entry. If it is determined that the ops shouldn't combine their proxies then a
517 * negative value is returned. Otherwise, return value indicates the number of proxies that have
518 * to be added to this op or, equivalently, the number of negative entries in map.
519 */
520 int mergeProxies(const TextureOp* that, int map[kMaxTextures], const GrShaderCaps& caps) const {
521 std::fill_n(map, kMaxTextures, -kMaxTextures);
522 int sharedProxyCnt = 0;
523 auto thisProxies = this->proxies();
524 auto thisFilters = this->filters();
525 auto thatProxies = that->proxies();
526 auto thatFilters = that->filters();
527 for (int i = 0; i < fProxyCnt; ++i) {
528 for (int j = 0; j < that->fProxyCnt; ++j) {
529 if (thisProxies[i]->uniqueID() == thatProxies[j]->uniqueID()) {
530 if (thisFilters[i] != thatFilters[j]) {
531 // In GL we don't currently support using the same texture with different
532 // samplers. If we added support for sampler objects and a cap bit to know
533 // it's ok to use different filter modes then we could support this.
534 // Otherwise, we could also only allow a single filter mode for each op
535 // instance.
536 return -1;
537 }
538 map[j] = i;
539 ++sharedProxyCnt;
540 break;
541 }
542 }
543 }
544 int actualMaxTextures = SkTMin(caps.maxFragmentImageStorages(), kMaxTextures);
545 int newProxyCnt = that->fProxyCnt - sharedProxyCnt;
546 if (newProxyCnt + fProxyCnt > actualMaxTextures) {
547 return -1;
548 }
549 GrPixelConfig config = thisProxies[0]->config();
550 int nextSlot = fProxyCnt;
551 for (int j = 0; j < that->fProxyCnt; ++j) {
552 // We want to avoid making many shaders because of different permutations of shader
553 // based swizzle and sampler types. The approach taken here is to require the configs to
554 // be the same and to only allow already instantiated proxies that have the most
555 // common sampler type. Otherwise we don't merge.
556 if (thatProxies[j]->config() != config) {
557 return -1;
558 }
559 if (GrTexture* tex = thatProxies[j]->priv().peekTexture()) {
560 if (tex->texturePriv().samplerType() != kTexture2DSampler_GrSLType) {
561 return -1;
562 }
563 }
564 if (map[j] < 0) {
565 map[j] = -(nextSlot++);
566 }
567 }
568 return newProxyCnt;
569 }
570
571 GrTextureProxy* const* proxies() const { return fProxyCnt > 1 ? fProxyArray : &fProxy0; }
572
573 const GrSamplerState::Filter* filters() const {
574 if (fProxyCnt > 1) {
575 return reinterpret_cast<const GrSamplerState::Filter*>(fProxyArray + kMaxTextures);
576 }
577 return &fFilter0;
578 }
579
Brian Salomon34169692017-08-28 15:32:01 -0400580 struct Draw {
581 SkRect fSrcRect;
Brian Salomon336ce7b2017-09-08 08:23:58 -0400582 int fTextureIdx;
Brian Salomon34169692017-08-28 15:32:01 -0400583 GrQuad fQuad;
584 GrColor fColor;
585 };
586 SkSTArray<1, Draw, true> fDraws;
Brian Salomon34169692017-08-28 15:32:01 -0400587 sk_sp<GrColorSpaceXform> fColorSpaceXform;
Brian Salomon336ce7b2017-09-08 08:23:58 -0400588 // Initially we store a single proxy ptr and a single filter. If we grow to have more than
589 // one proxy we instead store pointers to dynamically allocated arrays of size kMaxTextures
590 // followed by kMaxTextures filters.
591 union {
592 GrTextureProxy* fProxy0;
593 GrTextureProxy** fProxyArray;
594 };
595 // The next four members should pack.
596 GrSamplerState::Filter fFilter0;
597 uint8_t fProxyCnt;
Brian Salomon34169692017-08-28 15:32:01 -0400598 // Used to track whether fProxy is ref'ed or has a pending IO after finalize() is called.
Brian Salomon336ce7b2017-09-08 08:23:58 -0400599 uint8_t fFinalized;
600 uint8_t fAllowSRGBInputs;
601
Brian Salomon34169692017-08-28 15:32:01 -0400602 typedef GrMeshDrawOp INHERITED;
603};
604
Brian Salomon336ce7b2017-09-08 08:23:58 -0400605constexpr int TextureGeometryProcessor::kMaxTextures;
606constexpr int TextureOp::kMaxTextures;
607
Brian Salomon34169692017-08-28 15:32:01 -0400608} // anonymous namespace
609
610namespace GrTextureOp {
611
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400612std::unique_ptr<GrDrawOp> Make(sk_sp<GrTextureProxy> proxy, GrSamplerState::Filter filter,
Brian Salomon34169692017-08-28 15:32:01 -0400613 GrColor color, const SkRect& srcRect, const SkRect& dstRect,
614 const SkMatrix& viewMatrix, sk_sp<GrColorSpaceXform> csxf,
615 bool allowSRGBInputs) {
616 SkASSERT(!viewMatrix.hasPerspective());
617 return TextureOp::Make(std::move(proxy), filter, color, srcRect, dstRect, viewMatrix,
618 std::move(csxf), allowSRGBInputs);
619}
620
621} // namespace GrTextureOp
622
623#if GR_TEST_UTILS
624#include "GrContext.h"
625
626GR_DRAW_OP_TEST_DEFINE(TextureOp) {
627 GrSurfaceDesc desc;
628 desc.fConfig = kRGBA_8888_GrPixelConfig;
629 desc.fHeight = random->nextULessThan(90) + 10;
630 desc.fWidth = random->nextULessThan(90) + 10;
631 desc.fOrigin = random->nextBool() ? kTopLeft_GrSurfaceOrigin : kBottomLeft_GrSurfaceOrigin;
632 SkBackingFit fit = random->nextBool() ? SkBackingFit::kApprox : SkBackingFit::kExact;
633 auto proxy =
634 GrSurfaceProxy::MakeDeferred(context->resourceProvider(), desc, fit, SkBudgeted::kNo);
635 SkRect rect = GrTest::TestRect(random);
636 SkRect srcRect;
637 srcRect.fLeft = random->nextRangeScalar(0.f, proxy->width() / 2.f);
638 srcRect.fRight = random->nextRangeScalar(0.f, proxy->width()) + proxy->width() / 2.f;
639 srcRect.fTop = random->nextRangeScalar(0.f, proxy->height() / 2.f);
640 srcRect.fBottom = random->nextRangeScalar(0.f, proxy->height()) + proxy->height() / 2.f;
641 SkMatrix viewMatrix = GrTest::TestMatrixPreservesRightAngles(random);
642 GrColor color = SkColorToPremulGrColor(random->nextU());
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400643 GrSamplerState::Filter filter = (GrSamplerState::Filter)random->nextULessThan(
644 static_cast<uint32_t>(GrSamplerState::Filter::kMipMap) + 1);
Brian Salomon34169692017-08-28 15:32:01 -0400645 auto csxf = GrTest::TestColorXform(random);
646 bool allowSRGBInputs = random->nextBool();
647 return GrTextureOp::Make(std::move(proxy), filter, color, srcRect, rect, viewMatrix,
648 std::move(csxf), allowSRGBInputs);
649}
650
651#endif