blob: 686852b6981bb2265162bd0dc9bbb2b7993389c7 [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,
40 GrSamplerParams::FilterMode filter) {
41 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;");
85 args.fVaryingHandler->addPassThroughAttribute(&textureGP.fTextureCoords,
86 "texCoord");
87 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,
103 GrSamplerParams::FilterMode filter)
104 : 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,
128 GrSamplerParams::FilterMode filter, GrColor color,
129 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;
143 str.appendf("Filter: %d AllowSRGBInputs: %d\n", fFilter, fAllowSRGBInputs);
144 str.appendf("# draws: %d\n", fDraws.count());
145 for (int i = 0; i < fDraws.count(); ++i) {
146 const Draw& draw = fDraws[i];
147 str.appendf(
148 "%d: Color: 0x%08x, TexRect [L: %.2f, T: %.2f, R: %.2f, B: %.2f] Quad [(%.2f, "
149 "%.2f), (%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f)]\n",
150 i, draw.fColor, draw.fSrcRect.fLeft, draw.fSrcRect.fTop, draw.fSrcRect.fRight,
151 draw.fSrcRect.fBottom, draw.fQuad.points()[0].fX, draw.fQuad.points()[0].fY,
152 draw.fQuad.points()[1].fX, draw.fQuad.points()[1].fY, draw.fQuad.points()[2].fX,
153 draw.fQuad.points()[2].fY, draw.fQuad.points()[3].fX,
154 draw.fQuad.points()[3].fY);
155 }
156 str += INHERITED::dumpInfo();
157 return str;
158 }
159
160 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip) override {
161 SkASSERT(!fFinalized);
162 fFinalized = true;
163 fProxy->addPendingRead();
164 fProxy->unref();
165 return RequiresDstTexture::kNo;
166 }
167
168 FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
169
170 DEFINE_OP_CLASS_ID
171
172private:
173 TextureOp(sk_sp<GrTextureProxy> proxy, GrSamplerParams::FilterMode filter, GrColor color,
174 const SkRect& srcRect, const SkRect& dstRect, const SkMatrix& viewMatrix,
175 sk_sp<GrColorSpaceXform> csxf, bool allowSRGBInputs)
176 : INHERITED(ClassID())
177 , fProxy(proxy.release())
178 , fFilter(filter)
179 , fColorSpaceXform(std::move(csxf))
180 , fFinalized(false)
181 , fAllowSRGBInputs(allowSRGBInputs) {
182 Draw& draw = fDraws.push_back();
183 draw.fSrcRect = srcRect;
184 draw.fColor = color;
185 draw.fQuad.setFromMappedRect(dstRect, viewMatrix);
186 SkRect bounds;
187 bounds.setBounds(draw.fQuad.points(), 4);
188 this->setBounds(bounds, HasAABloat::kNo, IsZeroArea::kNo);
189 }
190
191 void onPrepareDraws(Target* target) override {
192 if (!fProxy->instantiate(target->resourceProvider())) {
193 return;
194 }
195 sk_sp<GrGeometryProcessor> gp = TextureGeometryProcessor::Make(
196 sk_ref_sp(fProxy), std::move(fColorSpaceXform), fFilter);
197 GrPipeline::InitArgs args;
198 args.fProxy = target->proxy();
199 args.fCaps = &target->caps();
200 args.fResourceProvider = target->resourceProvider();
201 args.fFlags = fAllowSRGBInputs ? GrPipeline::kAllowSRGBInputs_Flag : 0;
202 const GrPipeline* pipeline = target->allocPipeline(args, GrProcessorSet::MakeEmptySet(),
203 target->detachAppliedClip());
204
205 using Vertex = TextureGeometryProcessor::Vertex;
206 SkASSERT(gp->getVertexStride() == sizeof(Vertex));
207
208 int vstart;
209 const GrBuffer* vbuffer;
210 auto vertices = (Vertex*)target->makeVertexSpace(sizeof(Vertex), 4 * fDraws.count(),
211 &vbuffer, &vstart);
212 if (!vertices) {
213 SkDebugf("Could not allocate vertices\n");
214 return;
215 }
216 sk_sp<const GrBuffer> ibuffer;
217 if (fDraws.count() > 1) {
218 ibuffer.reset(target->resourceProvider()->refQuadIndexBuffer());
219 if (!ibuffer) {
220 SkDebugf("Could not allocate quad indices\n");
221 return;
222 }
223 }
224 GrTexture* texture = fProxy->priv().peekTexture();
225 float iw = 1.f / texture->width();
226 float ih = 1.f / texture->height();
227 if (fDraws.count() > 1) {
228 for (int i = 0; i < fDraws.count(); ++i) {
229 float tl = iw * fDraws[i].fSrcRect.fLeft;
230 float tr = iw * fDraws[i].fSrcRect.fRight;
231 float tt = ih * fDraws[i].fSrcRect.fTop;
232 float tb = ih * fDraws[i].fSrcRect.fBottom;
233 if (fProxy->origin() == kBottomLeft_GrSurfaceOrigin) {
234 tt = 1.f - tt;
235 tb = 1.f - tb;
236 }
237 vertices[0 + 4 * i].fPosition = fDraws[i].fQuad.points()[0];
238 vertices[0 + 4 * i].fTextureCoords = {tl, tt};
239 vertices[0 + 4 * i].fColor = fDraws[i].fColor;
240 vertices[1 + 4 * i].fPosition = fDraws[i].fQuad.points()[1];
241 vertices[1 + 4 * i].fTextureCoords = {tl, tb};
242 vertices[1 + 4 * i].fColor = fDraws[i].fColor;
243 vertices[2 + 4 * i].fPosition = fDraws[i].fQuad.points()[2];
244 vertices[2 + 4 * i].fTextureCoords = {tr, tb};
245 vertices[2 + 4 * i].fColor = fDraws[i].fColor;
246 vertices[3 + 4 * i].fPosition = fDraws[i].fQuad.points()[3];
247 vertices[3 + 4 * i].fTextureCoords = {tr, tt};
248 vertices[3 + 4 * i].fColor = fDraws[i].fColor;
249 }
250 GrMesh mesh(GrPrimitiveType::kTriangles);
251 mesh.setIndexedPatterned(ibuffer.get(), 6, 4, fDraws.count(),
252 GrResourceProvider::QuadCountOfQuadBuffer());
253 mesh.setVertexData(vbuffer, vstart);
254 target->draw(gp.get(), pipeline, mesh);
255 } else {
256 float tl = iw * fDraws[0].fSrcRect.fLeft;
257 float tr = iw * fDraws[0].fSrcRect.fRight;
258 float tt = ih * fDraws[0].fSrcRect.fTop;
259 float tb = ih * fDraws[0].fSrcRect.fBottom;
260 if (fProxy->origin() == kBottomLeft_GrSurfaceOrigin) {
261 tt = 1.f - tt;
262 tb = 1.f - tb;
263 }
264 vertices[0].fPosition = fDraws[0].fQuad.points()[0];
265 vertices[0].fTextureCoords = {tl, tt};
266 vertices[0].fColor = fDraws[0].fColor;
267 vertices[1].fPosition = fDraws[0].fQuad.points()[3];
268 vertices[1].fTextureCoords = {tr, tt};
269 vertices[1].fColor = fDraws[0].fColor;
270 vertices[2].fPosition = fDraws[0].fQuad.points()[1];
271 vertices[2].fTextureCoords = {tl, tb};
272 vertices[2].fColor = fDraws[0].fColor;
273 vertices[3].fPosition = fDraws[0].fQuad.points()[2];
274 vertices[3].fTextureCoords = {tr, tb};
275 vertices[3].fColor = fDraws[0].fColor;
276 GrMesh mesh(GrPrimitiveType::kTriangleStrip);
277 mesh.setNonIndexedNonInstanced(4);
278 mesh.setVertexData(vbuffer, vstart);
279 target->draw(gp.get(), pipeline, mesh);
280 }
281 }
282
283 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
284 const auto* that = t->cast<TextureOp>();
285 if (fProxy->uniqueID() != that->fProxy->uniqueID() || fFilter != that->fFilter ||
286 !GrColorSpaceXform::Equals(fColorSpaceXform.get(), that->fColorSpaceXform.get())) {
287 return false;
288 }
289 fDraws.push_back_n(that->fDraws.count(), that->fDraws.begin());
290 this->joinBounds(*that);
291 return true;
292 }
293
294 struct Draw {
295 SkRect fSrcRect;
296 GrQuad fQuad;
297 GrColor fColor;
298 };
299 SkSTArray<1, Draw, true> fDraws;
300 GrTextureProxy* fProxy;
301 GrSamplerParams::FilterMode fFilter;
302 sk_sp<GrColorSpaceXform> fColorSpaceXform;
303 // Used to track whether fProxy is ref'ed or has a pending IO after finalize() is called.
304 bool fFinalized : 1;
305 bool fAllowSRGBInputs : 1;
306 typedef GrMeshDrawOp INHERITED;
307};
308
309} // anonymous namespace
310
311namespace GrTextureOp {
312
313std::unique_ptr<GrDrawOp> Make(sk_sp<GrTextureProxy> proxy, GrSamplerParams::FilterMode filter,
314 GrColor color, const SkRect& srcRect, const SkRect& dstRect,
315 const SkMatrix& viewMatrix, sk_sp<GrColorSpaceXform> csxf,
316 bool allowSRGBInputs) {
317 SkASSERT(!viewMatrix.hasPerspective());
318 return TextureOp::Make(std::move(proxy), filter, color, srcRect, dstRect, viewMatrix,
319 std::move(csxf), allowSRGBInputs);
320}
321
322} // namespace GrTextureOp
323
324#if GR_TEST_UTILS
325#include "GrContext.h"
326
327GR_DRAW_OP_TEST_DEFINE(TextureOp) {
328 GrSurfaceDesc desc;
329 desc.fConfig = kRGBA_8888_GrPixelConfig;
330 desc.fHeight = random->nextULessThan(90) + 10;
331 desc.fWidth = random->nextULessThan(90) + 10;
332 desc.fOrigin = random->nextBool() ? kTopLeft_GrSurfaceOrigin : kBottomLeft_GrSurfaceOrigin;
333 SkBackingFit fit = random->nextBool() ? SkBackingFit::kApprox : SkBackingFit::kExact;
334 auto proxy =
335 GrSurfaceProxy::MakeDeferred(context->resourceProvider(), desc, fit, SkBudgeted::kNo);
336 SkRect rect = GrTest::TestRect(random);
337 SkRect srcRect;
338 srcRect.fLeft = random->nextRangeScalar(0.f, proxy->width() / 2.f);
339 srcRect.fRight = random->nextRangeScalar(0.f, proxy->width()) + proxy->width() / 2.f;
340 srcRect.fTop = random->nextRangeScalar(0.f, proxy->height() / 2.f);
341 srcRect.fBottom = random->nextRangeScalar(0.f, proxy->height()) + proxy->height() / 2.f;
342 SkMatrix viewMatrix = GrTest::TestMatrixPreservesRightAngles(random);
343 GrColor color = SkColorToPremulGrColor(random->nextU());
344 GrSamplerParams::FilterMode filter = (GrSamplerParams::FilterMode)random->nextULessThan(
345 GrSamplerParams::kMipMap_FilterMode + 1);
346 auto csxf = GrTest::TestColorXform(random);
347 bool allowSRGBInputs = random->nextBool();
348 return GrTextureOp::Make(std::move(proxy), filter, color, srcRect, rect, viewMatrix,
349 std::move(csxf), allowSRGBInputs);
350}
351
352#endif