blob: 0415d9df240392ffe6446cc268e153f42fac8b15 [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
248 SkString dumpInfo() const override {
249 SkString str;
Brian Salomon336ce7b2017-09-08 08:23:58 -0400250 str.appendf("AllowSRGBInputs: %d\n", fAllowSRGBInputs);
Brian Salomon34169692017-08-28 15:32:01 -0400251 str.appendf("# draws: %d\n", fDraws.count());
Brian Salomon336ce7b2017-09-08 08:23:58 -0400252 auto proxies = this->proxies();
253 for (int i = 0; i < fProxyCnt; ++i) {
254 str.appendf("Proxy ID %d: %d, Filter: %d\n", i, proxies[i]->uniqueID().asUInt(),
255 static_cast<int>(this->filters()[i]));
256 }
Brian Salomon34169692017-08-28 15:32:01 -0400257 for (int i = 0; i < fDraws.count(); ++i) {
258 const Draw& draw = fDraws[i];
259 str.appendf(
Brian Salomon336ce7b2017-09-08 08:23:58 -0400260 "%d: Color: 0x%08x, ProxyIdx: %d, TexRect [L: %.2f, T: %.2f, R: %.2f, B: %.2f] "
261 "Quad [(%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f)]\n",
262 i, draw.fColor, draw.fTextureIdx, draw.fSrcRect.fLeft, draw.fSrcRect.fTop,
263 draw.fSrcRect.fRight, draw.fSrcRect.fBottom, draw.fQuad.points()[0].fX,
264 draw.fQuad.points()[0].fY, draw.fQuad.points()[1].fX, draw.fQuad.points()[1].fY,
265 draw.fQuad.points()[2].fX, draw.fQuad.points()[2].fY, draw.fQuad.points()[3].fX,
Brian Salomon34169692017-08-28 15:32:01 -0400266 draw.fQuad.points()[3].fY);
267 }
268 str += INHERITED::dumpInfo();
269 return str;
270 }
271
272 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip) override {
273 SkASSERT(!fFinalized);
Brian Salomon336ce7b2017-09-08 08:23:58 -0400274 SkASSERT(1 == fProxyCnt);
Brian Salomon34169692017-08-28 15:32:01 -0400275 fFinalized = true;
Brian Salomon336ce7b2017-09-08 08:23:58 -0400276 fProxy0->addPendingRead();
277 fProxy0->unref();
Brian Salomon34169692017-08-28 15:32:01 -0400278 return RequiresDstTexture::kNo;
279 }
280
281 FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
282
283 DEFINE_OP_CLASS_ID
284
285private:
Brian Salomon336ce7b2017-09-08 08:23:58 -0400286 static constexpr int kMaxTextures = TextureGeometryProcessor::kMaxTextures;
287
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400288 TextureOp(sk_sp<GrTextureProxy> proxy, GrSamplerState::Filter filter, GrColor color,
Brian Salomon34169692017-08-28 15:32:01 -0400289 const SkRect& srcRect, const SkRect& dstRect, const SkMatrix& viewMatrix,
290 sk_sp<GrColorSpaceXform> csxf, bool allowSRGBInputs)
291 : INHERITED(ClassID())
Brian Salomon34169692017-08-28 15:32:01 -0400292 , fColorSpaceXform(std::move(csxf))
Brian Salomon336ce7b2017-09-08 08:23:58 -0400293 , fProxy0(proxy.release())
294 , fFilter0(filter)
295 , fProxyCnt(1)
Brian Salomon34169692017-08-28 15:32:01 -0400296 , fFinalized(false)
297 , fAllowSRGBInputs(allowSRGBInputs) {
298 Draw& draw = fDraws.push_back();
299 draw.fSrcRect = srcRect;
Brian Salomon336ce7b2017-09-08 08:23:58 -0400300 draw.fTextureIdx = 0;
Brian Salomon34169692017-08-28 15:32:01 -0400301 draw.fColor = color;
302 draw.fQuad.setFromMappedRect(dstRect, viewMatrix);
303 SkRect bounds;
304 bounds.setBounds(draw.fQuad.points(), 4);
305 this->setBounds(bounds, HasAABloat::kNo, IsZeroArea::kNo);
306 }
307
308 void onPrepareDraws(Target* target) override {
Brian Salomon336ce7b2017-09-08 08:23:58 -0400309 sk_sp<GrTextureProxy> proxiesSPs[kMaxTextures];
310 auto proxies = this->proxies();
311 auto filters = this->filters();
312 for (int i = 0; i < fProxyCnt; ++i) {
313 if (!proxies[i]->instantiate(target->resourceProvider())) {
314 return;
315 }
316 proxiesSPs[i] = sk_ref_sp(proxies[i]);
Brian Salomon34169692017-08-28 15:32:01 -0400317 }
Brian Salomon336ce7b2017-09-08 08:23:58 -0400318
319 sk_sp<GrGeometryProcessor> gp =
320 TextureGeometryProcessor::Make(proxiesSPs, fProxyCnt, std::move(fColorSpaceXform),
321 filters, *target->caps().shaderCaps());
Brian Salomon34169692017-08-28 15:32:01 -0400322 GrPipeline::InitArgs args;
323 args.fProxy = target->proxy();
324 args.fCaps = &target->caps();
325 args.fResourceProvider = target->resourceProvider();
326 args.fFlags = fAllowSRGBInputs ? GrPipeline::kAllowSRGBInputs_Flag : 0;
327 const GrPipeline* pipeline = target->allocPipeline(args, GrProcessorSet::MakeEmptySet(),
328 target->detachAppliedClip());
Brian Salomon34169692017-08-28 15:32:01 -0400329 int vstart;
330 const GrBuffer* vbuffer;
Brian Salomon336ce7b2017-09-08 08:23:58 -0400331 void* vdata = target->makeVertexSpace(gp->getVertexStride(), 4 * fDraws.count(), &vbuffer,
332 &vstart);
333 if (!vdata) {
Brian Salomon34169692017-08-28 15:32:01 -0400334 SkDebugf("Could not allocate vertices\n");
335 return;
336 }
337 sk_sp<const GrBuffer> ibuffer;
338 if (fDraws.count() > 1) {
339 ibuffer.reset(target->resourceProvider()->refQuadIndexBuffer());
340 if (!ibuffer) {
341 SkDebugf("Could not allocate quad indices\n");
342 return;
343 }
Brian Salomon336ce7b2017-09-08 08:23:58 -0400344 if (1 == fProxyCnt) {
345 SkASSERT(gp->getVertexStride() == sizeof(TextureGeometryProcessor::Vertex));
346 for (int i = 0; i < fDraws.count(); ++i) {
347 auto vertices = static_cast<TextureGeometryProcessor::Vertex*>(vdata);
348 GrTexture* texture = proxies[0]->priv().peekTexture();
349 float iw = 1.f / texture->width();
350 float ih = 1.f / texture->height();
351 float tl = iw * fDraws[i].fSrcRect.fLeft;
352 float tr = iw * fDraws[i].fSrcRect.fRight;
353 float tt = ih * fDraws[i].fSrcRect.fTop;
354 float tb = ih * fDraws[i].fSrcRect.fBottom;
355 if (proxies[0]->origin() == kBottomLeft_GrSurfaceOrigin) {
356 tt = 1.f - tt;
357 tb = 1.f - tb;
358 }
359 vertices[0 + 4 * i].fPosition = fDraws[i].fQuad.points()[0];
360 vertices[0 + 4 * i].fTextureCoords = {tl, tt};
361 vertices[0 + 4 * i].fColor = fDraws[i].fColor;
362 vertices[1 + 4 * i].fPosition = fDraws[i].fQuad.points()[1];
363 vertices[1 + 4 * i].fTextureCoords = {tl, tb};
364 vertices[1 + 4 * i].fColor = fDraws[i].fColor;
365 vertices[2 + 4 * i].fPosition = fDraws[i].fQuad.points()[2];
366 vertices[2 + 4 * i].fTextureCoords = {tr, tb};
367 vertices[2 + 4 * i].fColor = fDraws[i].fColor;
368 vertices[3 + 4 * i].fPosition = fDraws[i].fQuad.points()[3];
369 vertices[3 + 4 * i].fTextureCoords = {tr, tt};
370 vertices[3 + 4 * i].fColor = fDraws[i].fColor;
Brian Salomon34169692017-08-28 15:32:01 -0400371 }
Brian Salomon336ce7b2017-09-08 08:23:58 -0400372 } else {
373 SkASSERT(gp->getVertexStride() ==
374 sizeof(TextureGeometryProcessor::MultiTextureVertex));
375 GrTexture* textures[kMaxTextures];
376 float iw[kMaxTextures];
377 float ih[kMaxTextures];
378 for (int t = 0; t < fProxyCnt; ++t) {
379 textures[t] = proxies[t]->priv().peekTexture();
380 iw[t] = 1.f / textures[t]->width();
381 ih[t] = 1.f / textures[t]->height();
382 }
383 for (int i = 0; i < fDraws.count(); ++i) {
384 int t = fDraws[i].fTextureIdx;
385 auto vertices =
386 static_cast<TextureGeometryProcessor::MultiTextureVertex*>(vdata);
387 float tl = iw[t] * fDraws[i].fSrcRect.fLeft;
388 float tr = iw[t] * fDraws[i].fSrcRect.fRight;
389 float tt = ih[t] * fDraws[i].fSrcRect.fTop;
390 float tb = ih[t] * fDraws[i].fSrcRect.fBottom;
391 if (proxies[t]->origin() == kBottomLeft_GrSurfaceOrigin) {
392 tt = 1.f - tt;
393 tb = 1.f - tb;
394 }
395 vertices[0 + 4 * i].fPosition = fDraws[i].fQuad.points()[0];
396 vertices[0 + 4 * i].fTextureIdx = t;
397 vertices[0 + 4 * i].fTextureCoords = {tl, tt};
398 vertices[0 + 4 * i].fColor = fDraws[i].fColor;
399 vertices[1 + 4 * i].fPosition = fDraws[i].fQuad.points()[1];
400 vertices[1 + 4 * i].fTextureIdx = t;
401 vertices[1 + 4 * i].fTextureCoords = {tl, tb};
402 vertices[1 + 4 * i].fColor = fDraws[i].fColor;
403 vertices[2 + 4 * i].fPosition = fDraws[i].fQuad.points()[2];
404 vertices[2 + 4 * i].fTextureIdx = t;
405 vertices[2 + 4 * i].fTextureCoords = {tr, tb};
406 vertices[2 + 4 * i].fColor = fDraws[i].fColor;
407 vertices[3 + 4 * i].fPosition = fDraws[i].fQuad.points()[3];
408 vertices[3 + 4 * i].fTextureIdx = t;
409 vertices[3 + 4 * i].fTextureCoords = {tr, tt};
410 vertices[3 + 4 * i].fColor = fDraws[i].fColor;
411 }
Brian Salomon34169692017-08-28 15:32:01 -0400412 }
413 GrMesh mesh(GrPrimitiveType::kTriangles);
414 mesh.setIndexedPatterned(ibuffer.get(), 6, 4, fDraws.count(),
415 GrResourceProvider::QuadCountOfQuadBuffer());
416 mesh.setVertexData(vbuffer, vstart);
417 target->draw(gp.get(), pipeline, mesh);
418 } else {
Brian Salomon336ce7b2017-09-08 08:23:58 -0400419 // If there is only one draw then there can only be one proxy.
420 SkASSERT(1 == fProxyCnt);
421 SkASSERT(gp->getVertexStride() == sizeof(TextureGeometryProcessor::Vertex));
422 auto vertices = static_cast<TextureGeometryProcessor::Vertex*>(vdata);
423 GrTexture* texture = proxies[0]->priv().peekTexture();
424 float iw = 1.f / texture->width();
425 float ih = 1.f / texture->height();
Brian Salomon34169692017-08-28 15:32:01 -0400426 float tl = iw * fDraws[0].fSrcRect.fLeft;
427 float tr = iw * fDraws[0].fSrcRect.fRight;
428 float tt = ih * fDraws[0].fSrcRect.fTop;
429 float tb = ih * fDraws[0].fSrcRect.fBottom;
Brian Salomon336ce7b2017-09-08 08:23:58 -0400430 if (proxies[0]->origin() == kBottomLeft_GrSurfaceOrigin) {
Brian Salomon34169692017-08-28 15:32:01 -0400431 tt = 1.f - tt;
432 tb = 1.f - tb;
433 }
434 vertices[0].fPosition = fDraws[0].fQuad.points()[0];
435 vertices[0].fTextureCoords = {tl, tt};
436 vertices[0].fColor = fDraws[0].fColor;
437 vertices[1].fPosition = fDraws[0].fQuad.points()[3];
438 vertices[1].fTextureCoords = {tr, tt};
439 vertices[1].fColor = fDraws[0].fColor;
440 vertices[2].fPosition = fDraws[0].fQuad.points()[1];
441 vertices[2].fTextureCoords = {tl, tb};
442 vertices[2].fColor = fDraws[0].fColor;
443 vertices[3].fPosition = fDraws[0].fQuad.points()[2];
444 vertices[3].fTextureCoords = {tr, tb};
445 vertices[3].fColor = fDraws[0].fColor;
446 GrMesh mesh(GrPrimitiveType::kTriangleStrip);
447 mesh.setNonIndexedNonInstanced(4);
448 mesh.setVertexData(vbuffer, vstart);
449 target->draw(gp.get(), pipeline, mesh);
450 }
451 }
452
453 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
454 const auto* that = t->cast<TextureOp>();
Brian Salomon336ce7b2017-09-08 08:23:58 -0400455 if (!GrColorSpaceXform::Equals(fColorSpaceXform.get(), that->fColorSpaceXform.get())) {
Brian Salomon34169692017-08-28 15:32:01 -0400456 return false;
457 }
Brian Salomon336ce7b2017-09-08 08:23:58 -0400458 // Because of an issue where GrColorSpaceXform adds the same function every time it is used
459 // in a texture lookup, we only allow multiple textures when there is no transform.
460 if (TextureGeometryProcessor::SupportsMultitexture(*caps.shaderCaps()) &&
461 !fColorSpaceXform) {
462 int map[kMaxTextures];
463 int numNewProxies = this->mergeProxies(that, map, *caps.shaderCaps());
464 if (numNewProxies < 0) {
465 return false;
466 }
467 if (1 == fProxyCnt && numNewProxies) {
468 void* mem = new char[(sizeof(GrSamplerState::Filter) + sizeof(GrTextureProxy*)) *
469 kMaxTextures];
470 auto proxies = reinterpret_cast<GrTextureProxy**>(mem);
471 auto filters = reinterpret_cast<GrSamplerState::Filter*>(proxies + kMaxTextures);
472 proxies[0] = fProxy0;
473 filters[0] = fFilter0;
474 fProxyArray = proxies;
475 }
476 fProxyCnt += numNewProxies;
477 auto thisProxies = fProxyArray;
478 auto thatProxies = that->proxies();
479 auto thatFilters = that->filters();
480 auto thisFilters = reinterpret_cast<GrSamplerState::Filter*>(thisProxies +
481 kMaxTextures);
482 for (int i = 0; i < that->fProxyCnt; ++i) {
483 if (map[i] < 0) {
484 thatProxies[i]->addPendingRead();
485 thisProxies[-map[i]] = thatProxies[i];
486 thisFilters[-map[i]] = thatFilters[i];
487 map[i] = -map[i];
488 }
489 }
490 int firstNewDraw = fDraws.count();
491 fDraws.push_back_n(that->fDraws.count(), that->fDraws.begin());
492 for (int i = firstNewDraw; i < fDraws.count(); ++i) {
493 fDraws[i].fTextureIdx = map[fDraws[i].fTextureIdx];
494 }
495 } else {
496 if (fProxy0->uniqueID() != that->fProxy0->uniqueID() || fFilter0 != that->fFilter0) {
497 return false;
498 }
499 fDraws.push_back_n(that->fDraws.count(), that->fDraws.begin());
500 }
Brian Salomon34169692017-08-28 15:32:01 -0400501 this->joinBounds(*that);
502 return true;
503 }
504
Brian Salomon336ce7b2017-09-08 08:23:58 -0400505 /**
506 * Determines a mapping of indices from that's proxy array to this's proxy array. A negative map
507 * value means that's proxy should be added to this's proxy array at the absolute value of
508 * the map entry. If it is determined that the ops shouldn't combine their proxies then a
509 * negative value is returned. Otherwise, return value indicates the number of proxies that have
510 * to be added to this op or, equivalently, the number of negative entries in map.
511 */
512 int mergeProxies(const TextureOp* that, int map[kMaxTextures], const GrShaderCaps& caps) const {
513 std::fill_n(map, kMaxTextures, -kMaxTextures);
514 int sharedProxyCnt = 0;
515 auto thisProxies = this->proxies();
516 auto thisFilters = this->filters();
517 auto thatProxies = that->proxies();
518 auto thatFilters = that->filters();
519 for (int i = 0; i < fProxyCnt; ++i) {
520 for (int j = 0; j < that->fProxyCnt; ++j) {
521 if (thisProxies[i]->uniqueID() == thatProxies[j]->uniqueID()) {
522 if (thisFilters[i] != thatFilters[j]) {
523 // In GL we don't currently support using the same texture with different
524 // samplers. If we added support for sampler objects and a cap bit to know
525 // it's ok to use different filter modes then we could support this.
526 // Otherwise, we could also only allow a single filter mode for each op
527 // instance.
528 return -1;
529 }
530 map[j] = i;
531 ++sharedProxyCnt;
532 break;
533 }
534 }
535 }
536 int actualMaxTextures = SkTMin(caps.maxFragmentImageStorages(), kMaxTextures);
537 int newProxyCnt = that->fProxyCnt - sharedProxyCnt;
538 if (newProxyCnt + fProxyCnt > actualMaxTextures) {
539 return -1;
540 }
541 GrPixelConfig config = thisProxies[0]->config();
542 int nextSlot = fProxyCnt;
543 for (int j = 0; j < that->fProxyCnt; ++j) {
544 // We want to avoid making many shaders because of different permutations of shader
545 // based swizzle and sampler types. The approach taken here is to require the configs to
546 // be the same and to only allow already instantiated proxies that have the most
547 // common sampler type. Otherwise we don't merge.
548 if (thatProxies[j]->config() != config) {
549 return -1;
550 }
551 if (GrTexture* tex = thatProxies[j]->priv().peekTexture()) {
552 if (tex->texturePriv().samplerType() != kTexture2DSampler_GrSLType) {
553 return -1;
554 }
555 }
556 if (map[j] < 0) {
557 map[j] = -(nextSlot++);
558 }
559 }
560 return newProxyCnt;
561 }
562
563 GrTextureProxy* const* proxies() const { return fProxyCnt > 1 ? fProxyArray : &fProxy0; }
564
565 const GrSamplerState::Filter* filters() const {
566 if (fProxyCnt > 1) {
567 return reinterpret_cast<const GrSamplerState::Filter*>(fProxyArray + kMaxTextures);
568 }
569 return &fFilter0;
570 }
571
Brian Salomon34169692017-08-28 15:32:01 -0400572 struct Draw {
573 SkRect fSrcRect;
Brian Salomon336ce7b2017-09-08 08:23:58 -0400574 int fTextureIdx;
Brian Salomon34169692017-08-28 15:32:01 -0400575 GrQuad fQuad;
576 GrColor fColor;
577 };
578 SkSTArray<1, Draw, true> fDraws;
Brian Salomon34169692017-08-28 15:32:01 -0400579 sk_sp<GrColorSpaceXform> fColorSpaceXform;
Brian Salomon336ce7b2017-09-08 08:23:58 -0400580 // Initially we store a single proxy ptr and a single filter. If we grow to have more than
581 // one proxy we instead store pointers to dynamically allocated arrays of size kMaxTextures
582 // followed by kMaxTextures filters.
583 union {
584 GrTextureProxy* fProxy0;
585 GrTextureProxy** fProxyArray;
586 };
587 // The next four members should pack.
588 GrSamplerState::Filter fFilter0;
589 uint8_t fProxyCnt;
Brian Salomon34169692017-08-28 15:32:01 -0400590 // Used to track whether fProxy is ref'ed or has a pending IO after finalize() is called.
Brian Salomon336ce7b2017-09-08 08:23:58 -0400591 uint8_t fFinalized;
592 uint8_t fAllowSRGBInputs;
593
Brian Salomon34169692017-08-28 15:32:01 -0400594 typedef GrMeshDrawOp INHERITED;
595};
596
Brian Salomon336ce7b2017-09-08 08:23:58 -0400597constexpr int TextureGeometryProcessor::kMaxTextures;
598constexpr int TextureOp::kMaxTextures;
599
Brian Salomon34169692017-08-28 15:32:01 -0400600} // anonymous namespace
601
602namespace GrTextureOp {
603
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400604std::unique_ptr<GrDrawOp> Make(sk_sp<GrTextureProxy> proxy, GrSamplerState::Filter filter,
Brian Salomon34169692017-08-28 15:32:01 -0400605 GrColor color, const SkRect& srcRect, const SkRect& dstRect,
606 const SkMatrix& viewMatrix, sk_sp<GrColorSpaceXform> csxf,
607 bool allowSRGBInputs) {
608 SkASSERT(!viewMatrix.hasPerspective());
609 return TextureOp::Make(std::move(proxy), filter, color, srcRect, dstRect, viewMatrix,
610 std::move(csxf), allowSRGBInputs);
611}
612
613} // namespace GrTextureOp
614
615#if GR_TEST_UTILS
616#include "GrContext.h"
617
618GR_DRAW_OP_TEST_DEFINE(TextureOp) {
619 GrSurfaceDesc desc;
620 desc.fConfig = kRGBA_8888_GrPixelConfig;
621 desc.fHeight = random->nextULessThan(90) + 10;
622 desc.fWidth = random->nextULessThan(90) + 10;
623 desc.fOrigin = random->nextBool() ? kTopLeft_GrSurfaceOrigin : kBottomLeft_GrSurfaceOrigin;
624 SkBackingFit fit = random->nextBool() ? SkBackingFit::kApprox : SkBackingFit::kExact;
625 auto proxy =
626 GrSurfaceProxy::MakeDeferred(context->resourceProvider(), desc, fit, SkBudgeted::kNo);
627 SkRect rect = GrTest::TestRect(random);
628 SkRect srcRect;
629 srcRect.fLeft = random->nextRangeScalar(0.f, proxy->width() / 2.f);
630 srcRect.fRight = random->nextRangeScalar(0.f, proxy->width()) + proxy->width() / 2.f;
631 srcRect.fTop = random->nextRangeScalar(0.f, proxy->height() / 2.f);
632 srcRect.fBottom = random->nextRangeScalar(0.f, proxy->height()) + proxy->height() / 2.f;
633 SkMatrix viewMatrix = GrTest::TestMatrixPreservesRightAngles(random);
634 GrColor color = SkColorToPremulGrColor(random->nextU());
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400635 GrSamplerState::Filter filter = (GrSamplerState::Filter)random->nextULessThan(
636 static_cast<uint32_t>(GrSamplerState::Filter::kMipMap) + 1);
Brian Salomon34169692017-08-28 15:32:01 -0400637 auto csxf = GrTest::TestColorXform(random);
638 bool allowSRGBInputs = random->nextBool();
639 return GrTextureOp::Make(std::move(proxy), filter, color, srcRect, rect, viewMatrix,
640 std::move(csxf), allowSRGBInputs);
641}
642
643#endif