blob: 724d26b9810cfc84992b5bf9bf7c8325ae374481 [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>();
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
Brian Osman9a725dd2017-09-20 09:53:22 -0400279 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip,
280 GrPixelConfigIsClamped dstIsClamped) override {
Brian Salomon34169692017-08-28 15:32:01 -0400281 SkASSERT(!fFinalized);
Brian Salomon336ce7b2017-09-08 08:23:58 -0400282 SkASSERT(1 == fProxyCnt);
Brian Salomon34169692017-08-28 15:32:01 -0400283 fFinalized = true;
Brian Salomon336ce7b2017-09-08 08:23:58 -0400284 fProxy0->addPendingRead();
285 fProxy0->unref();
Brian Salomon34169692017-08-28 15:32:01 -0400286 return RequiresDstTexture::kNo;
287 }
288
289 FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
290
291 DEFINE_OP_CLASS_ID
292
293private:
Brian Salomon336ce7b2017-09-08 08:23:58 -0400294 static constexpr int kMaxTextures = TextureGeometryProcessor::kMaxTextures;
295
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400296 TextureOp(sk_sp<GrTextureProxy> proxy, GrSamplerState::Filter filter, GrColor color,
Brian Salomon34169692017-08-28 15:32:01 -0400297 const SkRect& srcRect, const SkRect& dstRect, const SkMatrix& viewMatrix,
298 sk_sp<GrColorSpaceXform> csxf, bool allowSRGBInputs)
299 : INHERITED(ClassID())
Brian Salomon34169692017-08-28 15:32:01 -0400300 , fColorSpaceXform(std::move(csxf))
Brian Salomon336ce7b2017-09-08 08:23:58 -0400301 , fProxy0(proxy.release())
302 , fFilter0(filter)
303 , fProxyCnt(1)
Brian Salomon34169692017-08-28 15:32:01 -0400304 , fFinalized(false)
305 , fAllowSRGBInputs(allowSRGBInputs) {
306 Draw& draw = fDraws.push_back();
307 draw.fSrcRect = srcRect;
Brian Salomon336ce7b2017-09-08 08:23:58 -0400308 draw.fTextureIdx = 0;
Brian Salomon34169692017-08-28 15:32:01 -0400309 draw.fColor = color;
310 draw.fQuad.setFromMappedRect(dstRect, viewMatrix);
311 SkRect bounds;
312 bounds.setBounds(draw.fQuad.points(), 4);
313 this->setBounds(bounds, HasAABloat::kNo, IsZeroArea::kNo);
314 }
315
316 void onPrepareDraws(Target* target) override {
Brian Salomon336ce7b2017-09-08 08:23:58 -0400317 sk_sp<GrTextureProxy> proxiesSPs[kMaxTextures];
318 auto proxies = this->proxies();
319 auto filters = this->filters();
320 for (int i = 0; i < fProxyCnt; ++i) {
321 if (!proxies[i]->instantiate(target->resourceProvider())) {
322 return;
323 }
324 proxiesSPs[i] = sk_ref_sp(proxies[i]);
Brian Salomon34169692017-08-28 15:32:01 -0400325 }
Brian Salomon336ce7b2017-09-08 08:23:58 -0400326
327 sk_sp<GrGeometryProcessor> gp =
328 TextureGeometryProcessor::Make(proxiesSPs, fProxyCnt, std::move(fColorSpaceXform),
329 filters, *target->caps().shaderCaps());
Brian Salomon34169692017-08-28 15:32:01 -0400330 GrPipeline::InitArgs args;
331 args.fProxy = target->proxy();
332 args.fCaps = &target->caps();
333 args.fResourceProvider = target->resourceProvider();
334 args.fFlags = fAllowSRGBInputs ? GrPipeline::kAllowSRGBInputs_Flag : 0;
335 const GrPipeline* pipeline = target->allocPipeline(args, GrProcessorSet::MakeEmptySet(),
336 target->detachAppliedClip());
Brian Salomon34169692017-08-28 15:32:01 -0400337 int vstart;
338 const GrBuffer* vbuffer;
Brian Salomon336ce7b2017-09-08 08:23:58 -0400339 void* vdata = target->makeVertexSpace(gp->getVertexStride(), 4 * fDraws.count(), &vbuffer,
340 &vstart);
341 if (!vdata) {
Brian Salomon34169692017-08-28 15:32:01 -0400342 SkDebugf("Could not allocate vertices\n");
343 return;
344 }
345 sk_sp<const GrBuffer> ibuffer;
346 if (fDraws.count() > 1) {
347 ibuffer.reset(target->resourceProvider()->refQuadIndexBuffer());
348 if (!ibuffer) {
349 SkDebugf("Could not allocate quad indices\n");
350 return;
351 }
Brian Salomon336ce7b2017-09-08 08:23:58 -0400352 if (1 == fProxyCnt) {
353 SkASSERT(gp->getVertexStride() == sizeof(TextureGeometryProcessor::Vertex));
354 for (int i = 0; i < fDraws.count(); ++i) {
355 auto vertices = static_cast<TextureGeometryProcessor::Vertex*>(vdata);
356 GrTexture* texture = proxies[0]->priv().peekTexture();
357 float iw = 1.f / texture->width();
358 float ih = 1.f / texture->height();
359 float tl = iw * fDraws[i].fSrcRect.fLeft;
360 float tr = iw * fDraws[i].fSrcRect.fRight;
361 float tt = ih * fDraws[i].fSrcRect.fTop;
362 float tb = ih * fDraws[i].fSrcRect.fBottom;
363 if (proxies[0]->origin() == kBottomLeft_GrSurfaceOrigin) {
364 tt = 1.f - tt;
365 tb = 1.f - tb;
366 }
367 vertices[0 + 4 * i].fPosition = fDraws[i].fQuad.points()[0];
368 vertices[0 + 4 * i].fTextureCoords = {tl, tt};
369 vertices[0 + 4 * i].fColor = fDraws[i].fColor;
370 vertices[1 + 4 * i].fPosition = fDraws[i].fQuad.points()[1];
371 vertices[1 + 4 * i].fTextureCoords = {tl, tb};
372 vertices[1 + 4 * i].fColor = fDraws[i].fColor;
373 vertices[2 + 4 * i].fPosition = fDraws[i].fQuad.points()[2];
374 vertices[2 + 4 * i].fTextureCoords = {tr, tb};
375 vertices[2 + 4 * i].fColor = fDraws[i].fColor;
376 vertices[3 + 4 * i].fPosition = fDraws[i].fQuad.points()[3];
377 vertices[3 + 4 * i].fTextureCoords = {tr, tt};
378 vertices[3 + 4 * i].fColor = fDraws[i].fColor;
Brian Salomon34169692017-08-28 15:32:01 -0400379 }
Brian Salomon336ce7b2017-09-08 08:23:58 -0400380 } else {
381 SkASSERT(gp->getVertexStride() ==
382 sizeof(TextureGeometryProcessor::MultiTextureVertex));
383 GrTexture* textures[kMaxTextures];
384 float iw[kMaxTextures];
385 float ih[kMaxTextures];
386 for (int t = 0; t < fProxyCnt; ++t) {
387 textures[t] = proxies[t]->priv().peekTexture();
388 iw[t] = 1.f / textures[t]->width();
389 ih[t] = 1.f / textures[t]->height();
390 }
391 for (int i = 0; i < fDraws.count(); ++i) {
392 int t = fDraws[i].fTextureIdx;
393 auto vertices =
394 static_cast<TextureGeometryProcessor::MultiTextureVertex*>(vdata);
395 float tl = iw[t] * fDraws[i].fSrcRect.fLeft;
396 float tr = iw[t] * fDraws[i].fSrcRect.fRight;
397 float tt = ih[t] * fDraws[i].fSrcRect.fTop;
398 float tb = ih[t] * fDraws[i].fSrcRect.fBottom;
399 if (proxies[t]->origin() == kBottomLeft_GrSurfaceOrigin) {
400 tt = 1.f - tt;
401 tb = 1.f - tb;
402 }
403 vertices[0 + 4 * i].fPosition = fDraws[i].fQuad.points()[0];
404 vertices[0 + 4 * i].fTextureIdx = t;
405 vertices[0 + 4 * i].fTextureCoords = {tl, tt};
406 vertices[0 + 4 * i].fColor = fDraws[i].fColor;
407 vertices[1 + 4 * i].fPosition = fDraws[i].fQuad.points()[1];
408 vertices[1 + 4 * i].fTextureIdx = t;
409 vertices[1 + 4 * i].fTextureCoords = {tl, tb};
410 vertices[1 + 4 * i].fColor = fDraws[i].fColor;
411 vertices[2 + 4 * i].fPosition = fDraws[i].fQuad.points()[2];
412 vertices[2 + 4 * i].fTextureIdx = t;
413 vertices[2 + 4 * i].fTextureCoords = {tr, tb};
414 vertices[2 + 4 * i].fColor = fDraws[i].fColor;
415 vertices[3 + 4 * i].fPosition = fDraws[i].fQuad.points()[3];
416 vertices[3 + 4 * i].fTextureIdx = t;
417 vertices[3 + 4 * i].fTextureCoords = {tr, tt};
418 vertices[3 + 4 * i].fColor = fDraws[i].fColor;
419 }
Brian Salomon34169692017-08-28 15:32:01 -0400420 }
421 GrMesh mesh(GrPrimitiveType::kTriangles);
422 mesh.setIndexedPatterned(ibuffer.get(), 6, 4, fDraws.count(),
423 GrResourceProvider::QuadCountOfQuadBuffer());
424 mesh.setVertexData(vbuffer, vstart);
425 target->draw(gp.get(), pipeline, mesh);
426 } else {
Brian Salomon336ce7b2017-09-08 08:23:58 -0400427 // If there is only one draw then there can only be one proxy.
428 SkASSERT(1 == fProxyCnt);
429 SkASSERT(gp->getVertexStride() == sizeof(TextureGeometryProcessor::Vertex));
430 auto vertices = static_cast<TextureGeometryProcessor::Vertex*>(vdata);
431 GrTexture* texture = proxies[0]->priv().peekTexture();
432 float iw = 1.f / texture->width();
433 float ih = 1.f / texture->height();
Brian Salomon34169692017-08-28 15:32:01 -0400434 float tl = iw * fDraws[0].fSrcRect.fLeft;
435 float tr = iw * fDraws[0].fSrcRect.fRight;
436 float tt = ih * fDraws[0].fSrcRect.fTop;
437 float tb = ih * fDraws[0].fSrcRect.fBottom;
Brian Salomon336ce7b2017-09-08 08:23:58 -0400438 if (proxies[0]->origin() == kBottomLeft_GrSurfaceOrigin) {
Brian Salomon34169692017-08-28 15:32:01 -0400439 tt = 1.f - tt;
440 tb = 1.f - tb;
441 }
442 vertices[0].fPosition = fDraws[0].fQuad.points()[0];
443 vertices[0].fTextureCoords = {tl, tt};
444 vertices[0].fColor = fDraws[0].fColor;
445 vertices[1].fPosition = fDraws[0].fQuad.points()[3];
446 vertices[1].fTextureCoords = {tr, tt};
447 vertices[1].fColor = fDraws[0].fColor;
448 vertices[2].fPosition = fDraws[0].fQuad.points()[1];
449 vertices[2].fTextureCoords = {tl, tb};
450 vertices[2].fColor = fDraws[0].fColor;
451 vertices[3].fPosition = fDraws[0].fQuad.points()[2];
452 vertices[3].fTextureCoords = {tr, tb};
453 vertices[3].fColor = fDraws[0].fColor;
454 GrMesh mesh(GrPrimitiveType::kTriangleStrip);
455 mesh.setNonIndexedNonInstanced(4);
456 mesh.setVertexData(vbuffer, vstart);
457 target->draw(gp.get(), pipeline, mesh);
458 }
459 }
460
461 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
462 const auto* that = t->cast<TextureOp>();
Brian Salomon336ce7b2017-09-08 08:23:58 -0400463 if (!GrColorSpaceXform::Equals(fColorSpaceXform.get(), that->fColorSpaceXform.get())) {
Brian Salomon34169692017-08-28 15:32:01 -0400464 return false;
465 }
Brian Salomon336ce7b2017-09-08 08:23:58 -0400466 // Because of an issue where GrColorSpaceXform adds the same function every time it is used
467 // in a texture lookup, we only allow multiple textures when there is no transform.
468 if (TextureGeometryProcessor::SupportsMultitexture(*caps.shaderCaps()) &&
469 !fColorSpaceXform) {
470 int map[kMaxTextures];
471 int numNewProxies = this->mergeProxies(that, map, *caps.shaderCaps());
472 if (numNewProxies < 0) {
473 return false;
474 }
475 if (1 == fProxyCnt && numNewProxies) {
476 void* mem = new char[(sizeof(GrSamplerState::Filter) + sizeof(GrTextureProxy*)) *
477 kMaxTextures];
478 auto proxies = reinterpret_cast<GrTextureProxy**>(mem);
479 auto filters = reinterpret_cast<GrSamplerState::Filter*>(proxies + kMaxTextures);
480 proxies[0] = fProxy0;
481 filters[0] = fFilter0;
482 fProxyArray = proxies;
483 }
484 fProxyCnt += numNewProxies;
485 auto thisProxies = fProxyArray;
486 auto thatProxies = that->proxies();
487 auto thatFilters = that->filters();
488 auto thisFilters = reinterpret_cast<GrSamplerState::Filter*>(thisProxies +
489 kMaxTextures);
490 for (int i = 0; i < that->fProxyCnt; ++i) {
491 if (map[i] < 0) {
492 thatProxies[i]->addPendingRead();
Robert Phillipsb493eeb2017-09-13 13:10:52 -0400493
Brian Salomon336ce7b2017-09-08 08:23:58 -0400494 thisProxies[-map[i]] = thatProxies[i];
495 thisFilters[-map[i]] = thatFilters[i];
496 map[i] = -map[i];
497 }
498 }
499 int firstNewDraw = fDraws.count();
500 fDraws.push_back_n(that->fDraws.count(), that->fDraws.begin());
501 for (int i = firstNewDraw; i < fDraws.count(); ++i) {
502 fDraws[i].fTextureIdx = map[fDraws[i].fTextureIdx];
503 }
504 } else {
505 if (fProxy0->uniqueID() != that->fProxy0->uniqueID() || fFilter0 != that->fFilter0) {
506 return false;
507 }
508 fDraws.push_back_n(that->fDraws.count(), that->fDraws.begin());
509 }
Brian Salomon34169692017-08-28 15:32:01 -0400510 this->joinBounds(*that);
511 return true;
512 }
513
Brian Salomon336ce7b2017-09-08 08:23:58 -0400514 /**
515 * Determines a mapping of indices from that's proxy array to this's proxy array. A negative map
516 * value means that's proxy should be added to this's proxy array at the absolute value of
517 * the map entry. If it is determined that the ops shouldn't combine their proxies then a
518 * negative value is returned. Otherwise, return value indicates the number of proxies that have
519 * to be added to this op or, equivalently, the number of negative entries in map.
520 */
521 int mergeProxies(const TextureOp* that, int map[kMaxTextures], const GrShaderCaps& caps) const {
522 std::fill_n(map, kMaxTextures, -kMaxTextures);
523 int sharedProxyCnt = 0;
524 auto thisProxies = this->proxies();
525 auto thisFilters = this->filters();
526 auto thatProxies = that->proxies();
527 auto thatFilters = that->filters();
528 for (int i = 0; i < fProxyCnt; ++i) {
529 for (int j = 0; j < that->fProxyCnt; ++j) {
530 if (thisProxies[i]->uniqueID() == thatProxies[j]->uniqueID()) {
531 if (thisFilters[i] != thatFilters[j]) {
532 // In GL we don't currently support using the same texture with different
533 // samplers. If we added support for sampler objects and a cap bit to know
534 // it's ok to use different filter modes then we could support this.
535 // Otherwise, we could also only allow a single filter mode for each op
536 // instance.
537 return -1;
538 }
539 map[j] = i;
540 ++sharedProxyCnt;
541 break;
542 }
543 }
544 }
545 int actualMaxTextures = SkTMin(caps.maxFragmentImageStorages(), kMaxTextures);
546 int newProxyCnt = that->fProxyCnt - sharedProxyCnt;
547 if (newProxyCnt + fProxyCnt > actualMaxTextures) {
548 return -1;
549 }
550 GrPixelConfig config = thisProxies[0]->config();
551 int nextSlot = fProxyCnt;
552 for (int j = 0; j < that->fProxyCnt; ++j) {
553 // We want to avoid making many shaders because of different permutations of shader
554 // based swizzle and sampler types. The approach taken here is to require the configs to
555 // be the same and to only allow already instantiated proxies that have the most
556 // common sampler type. Otherwise we don't merge.
557 if (thatProxies[j]->config() != config) {
558 return -1;
559 }
560 if (GrTexture* tex = thatProxies[j]->priv().peekTexture()) {
561 if (tex->texturePriv().samplerType() != kTexture2DSampler_GrSLType) {
562 return -1;
563 }
564 }
565 if (map[j] < 0) {
566 map[j] = -(nextSlot++);
567 }
568 }
569 return newProxyCnt;
570 }
571
572 GrTextureProxy* const* proxies() const { return fProxyCnt > 1 ? fProxyArray : &fProxy0; }
573
574 const GrSamplerState::Filter* filters() const {
575 if (fProxyCnt > 1) {
576 return reinterpret_cast<const GrSamplerState::Filter*>(fProxyArray + kMaxTextures);
577 }
578 return &fFilter0;
579 }
580
Brian Salomon34169692017-08-28 15:32:01 -0400581 struct Draw {
582 SkRect fSrcRect;
Brian Salomon336ce7b2017-09-08 08:23:58 -0400583 int fTextureIdx;
Brian Salomon34169692017-08-28 15:32:01 -0400584 GrQuad fQuad;
585 GrColor fColor;
586 };
587 SkSTArray<1, Draw, true> fDraws;
Brian Salomon34169692017-08-28 15:32:01 -0400588 sk_sp<GrColorSpaceXform> fColorSpaceXform;
Brian Salomon336ce7b2017-09-08 08:23:58 -0400589 // Initially we store a single proxy ptr and a single filter. If we grow to have more than
590 // one proxy we instead store pointers to dynamically allocated arrays of size kMaxTextures
591 // followed by kMaxTextures filters.
592 union {
593 GrTextureProxy* fProxy0;
594 GrTextureProxy** fProxyArray;
595 };
596 // The next four members should pack.
597 GrSamplerState::Filter fFilter0;
598 uint8_t fProxyCnt;
Brian Salomon34169692017-08-28 15:32:01 -0400599 // Used to track whether fProxy is ref'ed or has a pending IO after finalize() is called.
Brian Salomon336ce7b2017-09-08 08:23:58 -0400600 uint8_t fFinalized;
601 uint8_t fAllowSRGBInputs;
602
Brian Salomon34169692017-08-28 15:32:01 -0400603 typedef GrMeshDrawOp INHERITED;
604};
605
Brian Salomon336ce7b2017-09-08 08:23:58 -0400606constexpr int TextureGeometryProcessor::kMaxTextures;
607constexpr int TextureOp::kMaxTextures;
608
Brian Salomon34169692017-08-28 15:32:01 -0400609} // anonymous namespace
610
611namespace GrTextureOp {
612
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400613std::unique_ptr<GrDrawOp> Make(sk_sp<GrTextureProxy> proxy, GrSamplerState::Filter filter,
Brian Salomon34169692017-08-28 15:32:01 -0400614 GrColor color, const SkRect& srcRect, const SkRect& dstRect,
615 const SkMatrix& viewMatrix, sk_sp<GrColorSpaceXform> csxf,
616 bool allowSRGBInputs) {
617 SkASSERT(!viewMatrix.hasPerspective());
618 return TextureOp::Make(std::move(proxy), filter, color, srcRect, dstRect, viewMatrix,
619 std::move(csxf), allowSRGBInputs);
620}
621
622} // namespace GrTextureOp
623
624#if GR_TEST_UTILS
625#include "GrContext.h"
626
627GR_DRAW_OP_TEST_DEFINE(TextureOp) {
628 GrSurfaceDesc desc;
629 desc.fConfig = kRGBA_8888_GrPixelConfig;
630 desc.fHeight = random->nextULessThan(90) + 10;
631 desc.fWidth = random->nextULessThan(90) + 10;
632 desc.fOrigin = random->nextBool() ? kTopLeft_GrSurfaceOrigin : kBottomLeft_GrSurfaceOrigin;
633 SkBackingFit fit = random->nextBool() ? SkBackingFit::kApprox : SkBackingFit::kExact;
634 auto proxy =
635 GrSurfaceProxy::MakeDeferred(context->resourceProvider(), desc, fit, SkBudgeted::kNo);
636 SkRect rect = GrTest::TestRect(random);
637 SkRect srcRect;
638 srcRect.fLeft = random->nextRangeScalar(0.f, proxy->width() / 2.f);
639 srcRect.fRight = random->nextRangeScalar(0.f, proxy->width()) + proxy->width() / 2.f;
640 srcRect.fTop = random->nextRangeScalar(0.f, proxy->height() / 2.f);
641 srcRect.fBottom = random->nextRangeScalar(0.f, proxy->height()) + proxy->height() / 2.f;
642 SkMatrix viewMatrix = GrTest::TestMatrixPreservesRightAngles(random);
643 GrColor color = SkColorToPremulGrColor(random->nextU());
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400644 GrSamplerState::Filter filter = (GrSamplerState::Filter)random->nextULessThan(
645 static_cast<uint32_t>(GrSamplerState::Filter::kMipMap) + 1);
Brian Salomon34169692017-08-28 15:32:01 -0400646 auto csxf = GrTest::TestColorXform(random);
647 bool allowSRGBInputs = random->nextBool();
648 return GrTextureOp::Make(std::move(proxy), filter, color, srcRect, rect, viewMatrix,
649 std::move(csxf), allowSRGBInputs);
650}
651
652#endif