blob: f1fec5b36165aca4226df64bca1cabf10a2bc028 [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 }
344 }
Brian Salomon34169692017-08-28 15:32:01 -0400345 if (fDraws.count() > 1) {
Brian Salomon336ce7b2017-09-08 08:23:58 -0400346 if (1 == fProxyCnt) {
347 SkASSERT(gp->getVertexStride() == sizeof(TextureGeometryProcessor::Vertex));
348 for (int i = 0; i < fDraws.count(); ++i) {
349 auto vertices = static_cast<TextureGeometryProcessor::Vertex*>(vdata);
350 GrTexture* texture = proxies[0]->priv().peekTexture();
351 float iw = 1.f / texture->width();
352 float ih = 1.f / texture->height();
353 float tl = iw * fDraws[i].fSrcRect.fLeft;
354 float tr = iw * fDraws[i].fSrcRect.fRight;
355 float tt = ih * fDraws[i].fSrcRect.fTop;
356 float tb = ih * fDraws[i].fSrcRect.fBottom;
357 if (proxies[0]->origin() == kBottomLeft_GrSurfaceOrigin) {
358 tt = 1.f - tt;
359 tb = 1.f - tb;
360 }
361 vertices[0 + 4 * i].fPosition = fDraws[i].fQuad.points()[0];
362 vertices[0 + 4 * i].fTextureCoords = {tl, tt};
363 vertices[0 + 4 * i].fColor = fDraws[i].fColor;
364 vertices[1 + 4 * i].fPosition = fDraws[i].fQuad.points()[1];
365 vertices[1 + 4 * i].fTextureCoords = {tl, tb};
366 vertices[1 + 4 * i].fColor = fDraws[i].fColor;
367 vertices[2 + 4 * i].fPosition = fDraws[i].fQuad.points()[2];
368 vertices[2 + 4 * i].fTextureCoords = {tr, tb};
369 vertices[2 + 4 * i].fColor = fDraws[i].fColor;
370 vertices[3 + 4 * i].fPosition = fDraws[i].fQuad.points()[3];
371 vertices[3 + 4 * i].fTextureCoords = {tr, tt};
372 vertices[3 + 4 * i].fColor = fDraws[i].fColor;
Brian Salomon34169692017-08-28 15:32:01 -0400373 }
Brian Salomon336ce7b2017-09-08 08:23:58 -0400374 } else {
375 SkASSERT(gp->getVertexStride() ==
376 sizeof(TextureGeometryProcessor::MultiTextureVertex));
377 GrTexture* textures[kMaxTextures];
378 float iw[kMaxTextures];
379 float ih[kMaxTextures];
380 for (int t = 0; t < fProxyCnt; ++t) {
381 textures[t] = proxies[t]->priv().peekTexture();
382 iw[t] = 1.f / textures[t]->width();
383 ih[t] = 1.f / textures[t]->height();
384 }
385 for (int i = 0; i < fDraws.count(); ++i) {
386 int t = fDraws[i].fTextureIdx;
387 auto vertices =
388 static_cast<TextureGeometryProcessor::MultiTextureVertex*>(vdata);
389 float tl = iw[t] * fDraws[i].fSrcRect.fLeft;
390 float tr = iw[t] * fDraws[i].fSrcRect.fRight;
391 float tt = ih[t] * fDraws[i].fSrcRect.fTop;
392 float tb = ih[t] * fDraws[i].fSrcRect.fBottom;
393 if (proxies[t]->origin() == kBottomLeft_GrSurfaceOrigin) {
394 tt = 1.f - tt;
395 tb = 1.f - tb;
396 }
397 vertices[0 + 4 * i].fPosition = fDraws[i].fQuad.points()[0];
398 vertices[0 + 4 * i].fTextureIdx = t;
399 vertices[0 + 4 * i].fTextureCoords = {tl, tt};
400 vertices[0 + 4 * i].fColor = fDraws[i].fColor;
401 vertices[1 + 4 * i].fPosition = fDraws[i].fQuad.points()[1];
402 vertices[1 + 4 * i].fTextureIdx = t;
403 vertices[1 + 4 * i].fTextureCoords = {tl, tb};
404 vertices[1 + 4 * i].fColor = fDraws[i].fColor;
405 vertices[2 + 4 * i].fPosition = fDraws[i].fQuad.points()[2];
406 vertices[2 + 4 * i].fTextureIdx = t;
407 vertices[2 + 4 * i].fTextureCoords = {tr, tb};
408 vertices[2 + 4 * i].fColor = fDraws[i].fColor;
409 vertices[3 + 4 * i].fPosition = fDraws[i].fQuad.points()[3];
410 vertices[3 + 4 * i].fTextureIdx = t;
411 vertices[3 + 4 * i].fTextureCoords = {tr, tt};
412 vertices[3 + 4 * i].fColor = fDraws[i].fColor;
413 }
Brian Salomon34169692017-08-28 15:32:01 -0400414 }
415 GrMesh mesh(GrPrimitiveType::kTriangles);
416 mesh.setIndexedPatterned(ibuffer.get(), 6, 4, fDraws.count(),
417 GrResourceProvider::QuadCountOfQuadBuffer());
418 mesh.setVertexData(vbuffer, vstart);
419 target->draw(gp.get(), pipeline, mesh);
420 } else {
Brian Salomon336ce7b2017-09-08 08:23:58 -0400421 // If there is only one draw then there can only be one proxy.
422 SkASSERT(1 == fProxyCnt);
423 SkASSERT(gp->getVertexStride() == sizeof(TextureGeometryProcessor::Vertex));
424 auto vertices = static_cast<TextureGeometryProcessor::Vertex*>(vdata);
425 GrTexture* texture = proxies[0]->priv().peekTexture();
426 float iw = 1.f / texture->width();
427 float ih = 1.f / texture->height();
Brian Salomon34169692017-08-28 15:32:01 -0400428 float tl = iw * fDraws[0].fSrcRect.fLeft;
429 float tr = iw * fDraws[0].fSrcRect.fRight;
430 float tt = ih * fDraws[0].fSrcRect.fTop;
431 float tb = ih * fDraws[0].fSrcRect.fBottom;
Brian Salomon336ce7b2017-09-08 08:23:58 -0400432 if (proxies[0]->origin() == kBottomLeft_GrSurfaceOrigin) {
Brian Salomon34169692017-08-28 15:32:01 -0400433 tt = 1.f - tt;
434 tb = 1.f - tb;
435 }
436 vertices[0].fPosition = fDraws[0].fQuad.points()[0];
437 vertices[0].fTextureCoords = {tl, tt};
438 vertices[0].fColor = fDraws[0].fColor;
439 vertices[1].fPosition = fDraws[0].fQuad.points()[3];
440 vertices[1].fTextureCoords = {tr, tt};
441 vertices[1].fColor = fDraws[0].fColor;
442 vertices[2].fPosition = fDraws[0].fQuad.points()[1];
443 vertices[2].fTextureCoords = {tl, tb};
444 vertices[2].fColor = fDraws[0].fColor;
445 vertices[3].fPosition = fDraws[0].fQuad.points()[2];
446 vertices[3].fTextureCoords = {tr, tb};
447 vertices[3].fColor = fDraws[0].fColor;
448 GrMesh mesh(GrPrimitiveType::kTriangleStrip);
449 mesh.setNonIndexedNonInstanced(4);
450 mesh.setVertexData(vbuffer, vstart);
451 target->draw(gp.get(), pipeline, mesh);
452 }
453 }
454
455 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
456 const auto* that = t->cast<TextureOp>();
Brian Salomon336ce7b2017-09-08 08:23:58 -0400457 if (!GrColorSpaceXform::Equals(fColorSpaceXform.get(), that->fColorSpaceXform.get())) {
Brian Salomon34169692017-08-28 15:32:01 -0400458 return false;
459 }
Brian Salomon336ce7b2017-09-08 08:23:58 -0400460 // Because of an issue where GrColorSpaceXform adds the same function every time it is used
461 // in a texture lookup, we only allow multiple textures when there is no transform.
462 if (TextureGeometryProcessor::SupportsMultitexture(*caps.shaderCaps()) &&
463 !fColorSpaceXform) {
464 int map[kMaxTextures];
465 int numNewProxies = this->mergeProxies(that, map, *caps.shaderCaps());
466 if (numNewProxies < 0) {
467 return false;
468 }
469 if (1 == fProxyCnt && numNewProxies) {
470 void* mem = new char[(sizeof(GrSamplerState::Filter) + sizeof(GrTextureProxy*)) *
471 kMaxTextures];
472 auto proxies = reinterpret_cast<GrTextureProxy**>(mem);
473 auto filters = reinterpret_cast<GrSamplerState::Filter*>(proxies + kMaxTextures);
474 proxies[0] = fProxy0;
475 filters[0] = fFilter0;
476 fProxyArray = proxies;
477 }
478 fProxyCnt += numNewProxies;
479 auto thisProxies = fProxyArray;
480 auto thatProxies = that->proxies();
481 auto thatFilters = that->filters();
482 auto thisFilters = reinterpret_cast<GrSamplerState::Filter*>(thisProxies +
483 kMaxTextures);
484 for (int i = 0; i < that->fProxyCnt; ++i) {
485 if (map[i] < 0) {
486 thatProxies[i]->addPendingRead();
487 thisProxies[-map[i]] = thatProxies[i];
488 thisFilters[-map[i]] = thatFilters[i];
489 map[i] = -map[i];
490 }
491 }
492 int firstNewDraw = fDraws.count();
493 fDraws.push_back_n(that->fDraws.count(), that->fDraws.begin());
494 for (int i = firstNewDraw; i < fDraws.count(); ++i) {
495 fDraws[i].fTextureIdx = map[fDraws[i].fTextureIdx];
496 }
497 } else {
498 if (fProxy0->uniqueID() != that->fProxy0->uniqueID() || fFilter0 != that->fFilter0) {
499 return false;
500 }
501 fDraws.push_back_n(that->fDraws.count(), that->fDraws.begin());
502 }
Brian Salomon34169692017-08-28 15:32:01 -0400503 this->joinBounds(*that);
504 return true;
505 }
506
Brian Salomon336ce7b2017-09-08 08:23:58 -0400507 /**
508 * Determines a mapping of indices from that's proxy array to this's proxy array. A negative map
509 * value means that's proxy should be added to this's proxy array at the absolute value of
510 * the map entry. If it is determined that the ops shouldn't combine their proxies then a
511 * negative value is returned. Otherwise, return value indicates the number of proxies that have
512 * to be added to this op or, equivalently, the number of negative entries in map.
513 */
514 int mergeProxies(const TextureOp* that, int map[kMaxTextures], const GrShaderCaps& caps) const {
515 std::fill_n(map, kMaxTextures, -kMaxTextures);
516 int sharedProxyCnt = 0;
517 auto thisProxies = this->proxies();
518 auto thisFilters = this->filters();
519 auto thatProxies = that->proxies();
520 auto thatFilters = that->filters();
521 for (int i = 0; i < fProxyCnt; ++i) {
522 for (int j = 0; j < that->fProxyCnt; ++j) {
523 if (thisProxies[i]->uniqueID() == thatProxies[j]->uniqueID()) {
524 if (thisFilters[i] != thatFilters[j]) {
525 // In GL we don't currently support using the same texture with different
526 // samplers. If we added support for sampler objects and a cap bit to know
527 // it's ok to use different filter modes then we could support this.
528 // Otherwise, we could also only allow a single filter mode for each op
529 // instance.
530 return -1;
531 }
532 map[j] = i;
533 ++sharedProxyCnt;
534 break;
535 }
536 }
537 }
538 int actualMaxTextures = SkTMin(caps.maxFragmentImageStorages(), kMaxTextures);
539 int newProxyCnt = that->fProxyCnt - sharedProxyCnt;
540 if (newProxyCnt + fProxyCnt > actualMaxTextures) {
541 return -1;
542 }
543 GrPixelConfig config = thisProxies[0]->config();
544 int nextSlot = fProxyCnt;
545 for (int j = 0; j < that->fProxyCnt; ++j) {
546 // We want to avoid making many shaders because of different permutations of shader
547 // based swizzle and sampler types. The approach taken here is to require the configs to
548 // be the same and to only allow already instantiated proxies that have the most
549 // common sampler type. Otherwise we don't merge.
550 if (thatProxies[j]->config() != config) {
551 return -1;
552 }
553 if (GrTexture* tex = thatProxies[j]->priv().peekTexture()) {
554 if (tex->texturePriv().samplerType() != kTexture2DSampler_GrSLType) {
555 return -1;
556 }
557 }
558 if (map[j] < 0) {
559 map[j] = -(nextSlot++);
560 }
561 }
562 return newProxyCnt;
563 }
564
565 GrTextureProxy* const* proxies() const { return fProxyCnt > 1 ? fProxyArray : &fProxy0; }
566
567 const GrSamplerState::Filter* filters() const {
568 if (fProxyCnt > 1) {
569 return reinterpret_cast<const GrSamplerState::Filter*>(fProxyArray + kMaxTextures);
570 }
571 return &fFilter0;
572 }
573
Brian Salomon34169692017-08-28 15:32:01 -0400574 struct Draw {
575 SkRect fSrcRect;
Brian Salomon336ce7b2017-09-08 08:23:58 -0400576 int fTextureIdx;
Brian Salomon34169692017-08-28 15:32:01 -0400577 GrQuad fQuad;
578 GrColor fColor;
579 };
580 SkSTArray<1, Draw, true> fDraws;
Brian Salomon34169692017-08-28 15:32:01 -0400581 sk_sp<GrColorSpaceXform> fColorSpaceXform;
Brian Salomon336ce7b2017-09-08 08:23:58 -0400582 // Initially we store a single proxy ptr and a single filter. If we grow to have more than
583 // one proxy we instead store pointers to dynamically allocated arrays of size kMaxTextures
584 // followed by kMaxTextures filters.
585 union {
586 GrTextureProxy* fProxy0;
587 GrTextureProxy** fProxyArray;
588 };
589 // The next four members should pack.
590 GrSamplerState::Filter fFilter0;
591 uint8_t fProxyCnt;
Brian Salomon34169692017-08-28 15:32:01 -0400592 // Used to track whether fProxy is ref'ed or has a pending IO after finalize() is called.
Brian Salomon336ce7b2017-09-08 08:23:58 -0400593 uint8_t fFinalized;
594 uint8_t fAllowSRGBInputs;
595
Brian Salomon34169692017-08-28 15:32:01 -0400596 typedef GrMeshDrawOp INHERITED;
597};
598
Brian Salomon336ce7b2017-09-08 08:23:58 -0400599constexpr int TextureGeometryProcessor::kMaxTextures;
600constexpr int TextureOp::kMaxTextures;
601
Brian Salomon34169692017-08-28 15:32:01 -0400602} // anonymous namespace
603
604namespace GrTextureOp {
605
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400606std::unique_ptr<GrDrawOp> Make(sk_sp<GrTextureProxy> proxy, GrSamplerState::Filter filter,
Brian Salomon34169692017-08-28 15:32:01 -0400607 GrColor color, const SkRect& srcRect, const SkRect& dstRect,
608 const SkMatrix& viewMatrix, sk_sp<GrColorSpaceXform> csxf,
609 bool allowSRGBInputs) {
610 SkASSERT(!viewMatrix.hasPerspective());
611 return TextureOp::Make(std::move(proxy), filter, color, srcRect, dstRect, viewMatrix,
612 std::move(csxf), allowSRGBInputs);
613}
614
615} // namespace GrTextureOp
616
617#if GR_TEST_UTILS
618#include "GrContext.h"
619
620GR_DRAW_OP_TEST_DEFINE(TextureOp) {
621 GrSurfaceDesc desc;
622 desc.fConfig = kRGBA_8888_GrPixelConfig;
623 desc.fHeight = random->nextULessThan(90) + 10;
624 desc.fWidth = random->nextULessThan(90) + 10;
625 desc.fOrigin = random->nextBool() ? kTopLeft_GrSurfaceOrigin : kBottomLeft_GrSurfaceOrigin;
626 SkBackingFit fit = random->nextBool() ? SkBackingFit::kApprox : SkBackingFit::kExact;
627 auto proxy =
628 GrSurfaceProxy::MakeDeferred(context->resourceProvider(), desc, fit, SkBudgeted::kNo);
629 SkRect rect = GrTest::TestRect(random);
630 SkRect srcRect;
631 srcRect.fLeft = random->nextRangeScalar(0.f, proxy->width() / 2.f);
632 srcRect.fRight = random->nextRangeScalar(0.f, proxy->width()) + proxy->width() / 2.f;
633 srcRect.fTop = random->nextRangeScalar(0.f, proxy->height() / 2.f);
634 srcRect.fBottom = random->nextRangeScalar(0.f, proxy->height()) + proxy->height() / 2.f;
635 SkMatrix viewMatrix = GrTest::TestMatrixPreservesRightAngles(random);
636 GrColor color = SkColorToPremulGrColor(random->nextU());
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400637 GrSamplerState::Filter filter = (GrSamplerState::Filter)random->nextULessThan(
638 static_cast<uint32_t>(GrSamplerState::Filter::kMipMap) + 1);
Brian Salomon34169692017-08-28 15:32:01 -0400639 auto csxf = GrTest::TestColorXform(random);
640 bool allowSRGBInputs = random->nextBool();
641 return GrTextureOp::Make(std::move(proxy), filter, color, srcRect, rect, viewMatrix,
642 std::move(csxf), allowSRGBInputs);
643}
644
645#endif