blob: 3cb4dc08a56d2801d2b5a0b7ad233a3f464a2ae8 [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"
10#include "GrDrawOpTest.h"
11#include "GrGeometryProcessor.h"
12#include "GrMeshDrawOp.h"
13#include "GrOpFlushState.h"
14#include "GrQuad.h"
15#include "GrResourceProvider.h"
16#include "GrShaderCaps.h"
17#include "GrTexture.h"
18#include "GrTextureProxy.h"
19#include "SkGr.h"
20#include "glsl/GrGLSLColorSpaceXformHelper.h"
21#include "glsl/GrGLSLGeometryProcessor.h"
22#include "glsl/GrGLSLVarying.h"
23
24namespace {
25
26/**
27 * Geometry Processor that draws a texture modulated by a vertex color (though, this is meant to be
28 * the same value across all vertices of a quad and uses flat interpolation when available). This is
29 * used by TextureOp below.
30 */
31class TextureGeometryProcessor : public GrGeometryProcessor {
32public:
33 struct Vertex {
34 SkPoint fPosition;
35 SkPoint fTextureCoords;
36 GrColor fColor;
37 };
38 static sk_sp<GrGeometryProcessor> Make(sk_sp<GrTextureProxy> proxy,
39 sk_sp<GrColorSpaceXform> csxf,
Brian Salomon2bbdcc42017-09-07 12:36:34 -040040 GrSamplerState::Filter filter) {
Brian Salomon34169692017-08-28 15:32:01 -040041 return sk_sp<TextureGeometryProcessor>(
42 new TextureGeometryProcessor(std::move(proxy), std::move(csxf), filter));
43 }
44
45 const char* name() const override { return "TextureGeometryProcessor"; }
46
47 void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const override {
48 b->add32(GrColorSpaceXform::XformKey(fColorSpaceXform.get()));
49 }
50
51 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps& caps) const override {
52 class GLSLProcessor : public GrGLSLGeometryProcessor {
53 public:
54 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& proc,
55 FPCoordTransformIter&& transformIter) override {
56 const auto& textureGP = proc.cast<TextureGeometryProcessor>();
57 this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
58 if (fColorSpaceXformHelper.isValid()) {
59 fColorSpaceXformHelper.setData(pdman, textureGP.fColorSpaceXform.get());
60 }
61 }
62
63 private:
64 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
65 const auto& textureGP = args.fGP.cast<TextureGeometryProcessor>();
66 fColorSpaceXformHelper.emitCode(
67 args.fUniformHandler, textureGP.fColorSpaceXform.get());
68 args.fVaryingHandler->setNoPerspective();
69 args.fVaryingHandler->emitAttributes(textureGP);
70 this->writeOutputPosition(args.fVertBuilder, gpArgs, textureGP.fPositions.fName);
71 this->emitTransforms(args.fVertBuilder,
72 args.fVaryingHandler,
73 args.fUniformHandler,
74 gpArgs->fPositionVar,
75 textureGP.fTextureCoords.fName,
76 args.fFPCoordTransformHandler);
77 if (args.fShaderCaps->flatInterpolationSupport()) {
78 args.fVaryingHandler->addFlatPassThroughAttribute(&textureGP.fColors,
79 args.fOutputColor);
80 } else {
81 args.fVaryingHandler->addPassThroughAttribute(&textureGP.fColors,
82 args.fOutputColor);
83 }
84 args.fFragBuilder->codeAppend("highp float2 texCoord;");
Brian Salomon977b54c2017-08-31 09:06:14 -040085 args.fVaryingHandler->addPassThroughAttribute(&textureGP.fTextureCoords, "texCoord",
86 kHigh_GrSLPrecision);
Brian Salomon34169692017-08-28 15:32:01 -040087 args.fFragBuilder->codeAppendf("%s = ", args.fOutputColor);
88 args.fFragBuilder->appendTextureLookupAndModulate(args.fOutputColor,
89 args.fTexSamplers[0],
90 "texCoord",
91 kVec2f_GrSLType,
92 &fColorSpaceXformHelper);
93 args.fFragBuilder->codeAppend(";");
94 args.fFragBuilder->codeAppendf("%s = float4(1);", args.fOutputCoverage);
95 }
96 GrGLSLColorSpaceXformHelper fColorSpaceXformHelper;
97 };
98 return new GLSLProcessor;
99 }
100
101private:
102 TextureGeometryProcessor(sk_sp<GrTextureProxy> proxy, sk_sp<GrColorSpaceXform> csxf,
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400103 GrSamplerState::Filter filter)
Brian Salomon34169692017-08-28 15:32:01 -0400104 : fSampler(std::move(proxy), filter), fColorSpaceXform(std::move(csxf)) {
105 this->initClassID<TextureGeometryProcessor>();
106 fPositions =
107 this->addVertexAttrib("position", kVec2f_GrVertexAttribType, kHigh_GrSLPrecision);
108 fTextureCoords = this->addVertexAttrib("textureCoords", kVec2f_GrVertexAttribType,
109 kHigh_GrSLPrecision);
110 fColors = this->addVertexAttrib("color", kVec4ub_GrVertexAttribType);
111 this->addTextureSampler(&fSampler);
112 }
113
114 Attribute fPositions;
115 Attribute fTextureCoords;
116 Attribute fColors;
117 TextureSampler fSampler;
118 sk_sp<GrColorSpaceXform> fColorSpaceXform;
119};
120
121/**
122 * Op that implements GrTextureOp::Make. It draws textured quads. Each quad can modulate against a
123 * the texture by color. The blend with the destination is always src-over. The edges are non-AA.
124 */
125class TextureOp final : public GrMeshDrawOp {
126public:
127 static std::unique_ptr<GrDrawOp> Make(sk_sp<GrTextureProxy> proxy,
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400128 GrSamplerState::Filter filter, GrColor color,
Brian Salomon34169692017-08-28 15:32:01 -0400129 const SkRect srcRect, const SkRect dstRect,
130 const SkMatrix& viewMatrix, sk_sp<GrColorSpaceXform> csxf,
131 bool allowSRBInputs) {
132 return std::unique_ptr<GrDrawOp>(new TextureOp(std::move(proxy), filter, color, srcRect,
133 dstRect, viewMatrix, std::move(csxf),
134 allowSRBInputs));
135 }
136
137 ~TextureOp() override { fFinalized ? fProxy->completedRead() : fProxy->unref(); }
138
139 const char* name() const override { return "TextureOp"; }
140
141 SkString dumpInfo() const override {
142 SkString str;
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400143 str.appendf("Filter: %d AllowSRGBInputs: %d\n", static_cast<int>(fFilter),
144 fAllowSRGBInputs);
Brian Salomon34169692017-08-28 15:32:01 -0400145 str.appendf("# draws: %d\n", fDraws.count());
146 for (int i = 0; i < fDraws.count(); ++i) {
147 const Draw& draw = fDraws[i];
148 str.appendf(
149 "%d: Color: 0x%08x, TexRect [L: %.2f, T: %.2f, R: %.2f, B: %.2f] Quad [(%.2f, "
150 "%.2f), (%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f)]\n",
151 i, draw.fColor, draw.fSrcRect.fLeft, draw.fSrcRect.fTop, draw.fSrcRect.fRight,
152 draw.fSrcRect.fBottom, draw.fQuad.points()[0].fX, draw.fQuad.points()[0].fY,
153 draw.fQuad.points()[1].fX, draw.fQuad.points()[1].fY, draw.fQuad.points()[2].fX,
154 draw.fQuad.points()[2].fY, draw.fQuad.points()[3].fX,
155 draw.fQuad.points()[3].fY);
156 }
157 str += INHERITED::dumpInfo();
158 return str;
159 }
160
161 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip) override {
162 SkASSERT(!fFinalized);
163 fFinalized = true;
164 fProxy->addPendingRead();
165 fProxy->unref();
166 return RequiresDstTexture::kNo;
167 }
168
169 FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
170
171 DEFINE_OP_CLASS_ID
172
173private:
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400174 TextureOp(sk_sp<GrTextureProxy> proxy, GrSamplerState::Filter filter, GrColor color,
Brian Salomon34169692017-08-28 15:32:01 -0400175 const SkRect& srcRect, const SkRect& dstRect, const SkMatrix& viewMatrix,
176 sk_sp<GrColorSpaceXform> csxf, bool allowSRGBInputs)
177 : INHERITED(ClassID())
178 , fProxy(proxy.release())
179 , fFilter(filter)
180 , fColorSpaceXform(std::move(csxf))
181 , fFinalized(false)
182 , fAllowSRGBInputs(allowSRGBInputs) {
183 Draw& draw = fDraws.push_back();
184 draw.fSrcRect = srcRect;
185 draw.fColor = color;
186 draw.fQuad.setFromMappedRect(dstRect, viewMatrix);
187 SkRect bounds;
188 bounds.setBounds(draw.fQuad.points(), 4);
189 this->setBounds(bounds, HasAABloat::kNo, IsZeroArea::kNo);
190 }
191
192 void onPrepareDraws(Target* target) override {
193 if (!fProxy->instantiate(target->resourceProvider())) {
194 return;
195 }
196 sk_sp<GrGeometryProcessor> gp = TextureGeometryProcessor::Make(
197 sk_ref_sp(fProxy), std::move(fColorSpaceXform), fFilter);
198 GrPipeline::InitArgs args;
199 args.fProxy = target->proxy();
200 args.fCaps = &target->caps();
201 args.fResourceProvider = target->resourceProvider();
202 args.fFlags = fAllowSRGBInputs ? GrPipeline::kAllowSRGBInputs_Flag : 0;
203 const GrPipeline* pipeline = target->allocPipeline(args, GrProcessorSet::MakeEmptySet(),
204 target->detachAppliedClip());
205
206 using Vertex = TextureGeometryProcessor::Vertex;
207 SkASSERT(gp->getVertexStride() == sizeof(Vertex));
208
209 int vstart;
210 const GrBuffer* vbuffer;
211 auto vertices = (Vertex*)target->makeVertexSpace(sizeof(Vertex), 4 * fDraws.count(),
212 &vbuffer, &vstart);
213 if (!vertices) {
214 SkDebugf("Could not allocate vertices\n");
215 return;
216 }
217 sk_sp<const GrBuffer> ibuffer;
218 if (fDraws.count() > 1) {
219 ibuffer.reset(target->resourceProvider()->refQuadIndexBuffer());
220 if (!ibuffer) {
221 SkDebugf("Could not allocate quad indices\n");
222 return;
223 }
224 }
225 GrTexture* texture = fProxy->priv().peekTexture();
226 float iw = 1.f / texture->width();
227 float ih = 1.f / texture->height();
228 if (fDraws.count() > 1) {
229 for (int i = 0; i < fDraws.count(); ++i) {
230 float tl = iw * fDraws[i].fSrcRect.fLeft;
231 float tr = iw * fDraws[i].fSrcRect.fRight;
232 float tt = ih * fDraws[i].fSrcRect.fTop;
233 float tb = ih * fDraws[i].fSrcRect.fBottom;
234 if (fProxy->origin() == kBottomLeft_GrSurfaceOrigin) {
235 tt = 1.f - tt;
236 tb = 1.f - tb;
237 }
238 vertices[0 + 4 * i].fPosition = fDraws[i].fQuad.points()[0];
239 vertices[0 + 4 * i].fTextureCoords = {tl, tt};
240 vertices[0 + 4 * i].fColor = fDraws[i].fColor;
241 vertices[1 + 4 * i].fPosition = fDraws[i].fQuad.points()[1];
242 vertices[1 + 4 * i].fTextureCoords = {tl, tb};
243 vertices[1 + 4 * i].fColor = fDraws[i].fColor;
244 vertices[2 + 4 * i].fPosition = fDraws[i].fQuad.points()[2];
245 vertices[2 + 4 * i].fTextureCoords = {tr, tb};
246 vertices[2 + 4 * i].fColor = fDraws[i].fColor;
247 vertices[3 + 4 * i].fPosition = fDraws[i].fQuad.points()[3];
248 vertices[3 + 4 * i].fTextureCoords = {tr, tt};
249 vertices[3 + 4 * i].fColor = fDraws[i].fColor;
250 }
251 GrMesh mesh(GrPrimitiveType::kTriangles);
252 mesh.setIndexedPatterned(ibuffer.get(), 6, 4, fDraws.count(),
253 GrResourceProvider::QuadCountOfQuadBuffer());
254 mesh.setVertexData(vbuffer, vstart);
255 target->draw(gp.get(), pipeline, mesh);
256 } else {
257 float tl = iw * fDraws[0].fSrcRect.fLeft;
258 float tr = iw * fDraws[0].fSrcRect.fRight;
259 float tt = ih * fDraws[0].fSrcRect.fTop;
260 float tb = ih * fDraws[0].fSrcRect.fBottom;
261 if (fProxy->origin() == kBottomLeft_GrSurfaceOrigin) {
262 tt = 1.f - tt;
263 tb = 1.f - tb;
264 }
265 vertices[0].fPosition = fDraws[0].fQuad.points()[0];
266 vertices[0].fTextureCoords = {tl, tt};
267 vertices[0].fColor = fDraws[0].fColor;
268 vertices[1].fPosition = fDraws[0].fQuad.points()[3];
269 vertices[1].fTextureCoords = {tr, tt};
270 vertices[1].fColor = fDraws[0].fColor;
271 vertices[2].fPosition = fDraws[0].fQuad.points()[1];
272 vertices[2].fTextureCoords = {tl, tb};
273 vertices[2].fColor = fDraws[0].fColor;
274 vertices[3].fPosition = fDraws[0].fQuad.points()[2];
275 vertices[3].fTextureCoords = {tr, tb};
276 vertices[3].fColor = fDraws[0].fColor;
277 GrMesh mesh(GrPrimitiveType::kTriangleStrip);
278 mesh.setNonIndexedNonInstanced(4);
279 mesh.setVertexData(vbuffer, vstart);
280 target->draw(gp.get(), pipeline, mesh);
281 }
282 }
283
284 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
285 const auto* that = t->cast<TextureOp>();
286 if (fProxy->uniqueID() != that->fProxy->uniqueID() || fFilter != that->fFilter ||
287 !GrColorSpaceXform::Equals(fColorSpaceXform.get(), that->fColorSpaceXform.get())) {
288 return false;
289 }
290 fDraws.push_back_n(that->fDraws.count(), that->fDraws.begin());
291 this->joinBounds(*that);
292 return true;
293 }
294
295 struct Draw {
296 SkRect fSrcRect;
297 GrQuad fQuad;
298 GrColor fColor;
299 };
300 SkSTArray<1, Draw, true> fDraws;
301 GrTextureProxy* fProxy;
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400302 GrSamplerState::Filter fFilter;
Brian Salomon34169692017-08-28 15:32:01 -0400303 sk_sp<GrColorSpaceXform> fColorSpaceXform;
304 // Used to track whether fProxy is ref'ed or has a pending IO after finalize() is called.
305 bool fFinalized : 1;
306 bool fAllowSRGBInputs : 1;
307 typedef GrMeshDrawOp INHERITED;
308};
309
310} // anonymous namespace
311
312namespace GrTextureOp {
313
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400314std::unique_ptr<GrDrawOp> Make(sk_sp<GrTextureProxy> proxy, GrSamplerState::Filter filter,
Brian Salomon34169692017-08-28 15:32:01 -0400315 GrColor color, const SkRect& srcRect, const SkRect& dstRect,
316 const SkMatrix& viewMatrix, sk_sp<GrColorSpaceXform> csxf,
317 bool allowSRGBInputs) {
318 SkASSERT(!viewMatrix.hasPerspective());
319 return TextureOp::Make(std::move(proxy), filter, color, srcRect, dstRect, viewMatrix,
320 std::move(csxf), allowSRGBInputs);
321}
322
323} // namespace GrTextureOp
324
325#if GR_TEST_UTILS
326#include "GrContext.h"
327
328GR_DRAW_OP_TEST_DEFINE(TextureOp) {
329 GrSurfaceDesc desc;
330 desc.fConfig = kRGBA_8888_GrPixelConfig;
331 desc.fHeight = random->nextULessThan(90) + 10;
332 desc.fWidth = random->nextULessThan(90) + 10;
333 desc.fOrigin = random->nextBool() ? kTopLeft_GrSurfaceOrigin : kBottomLeft_GrSurfaceOrigin;
334 SkBackingFit fit = random->nextBool() ? SkBackingFit::kApprox : SkBackingFit::kExact;
335 auto proxy =
336 GrSurfaceProxy::MakeDeferred(context->resourceProvider(), desc, fit, SkBudgeted::kNo);
337 SkRect rect = GrTest::TestRect(random);
338 SkRect srcRect;
339 srcRect.fLeft = random->nextRangeScalar(0.f, proxy->width() / 2.f);
340 srcRect.fRight = random->nextRangeScalar(0.f, proxy->width()) + proxy->width() / 2.f;
341 srcRect.fTop = random->nextRangeScalar(0.f, proxy->height() / 2.f);
342 srcRect.fBottom = random->nextRangeScalar(0.f, proxy->height()) + proxy->height() / 2.f;
343 SkMatrix viewMatrix = GrTest::TestMatrixPreservesRightAngles(random);
344 GrColor color = SkColorToPremulGrColor(random->nextU());
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400345 GrSamplerState::Filter filter = (GrSamplerState::Filter)random->nextULessThan(
346 static_cast<uint32_t>(GrSamplerState::Filter::kMipMap) + 1);
Brian Salomon34169692017-08-28 15:32:01 -0400347 auto csxf = GrTest::TestColorXform(random);
348 bool allowSRGBInputs = random->nextBool();
349 return GrTextureOp::Make(std::move(proxy), filter, color, srcRect, rect, viewMatrix,
350 std::move(csxf), allowSRGBInputs);
351}
352
353#endif