1 /*
2 * Copyright 2015 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 "src/gpu/ops/DrawAtlasOp.h"
9
10 #include "include/core/SkRSXform.h"
11 #include "include/gpu/GrRecordingContext.h"
12 #include "include/utils/SkRandom.h"
13 #include "src/core/SkSafeMath.h"
14 #include "src/core/SkMatrixPriv.h"
15 #include "src/core/SkRectPriv.h"
16 #include "src/gpu/GrCaps.h"
17 #include "src/gpu/GrDefaultGeoProcFactory.h"
18 #include "src/gpu/GrOpFlushState.h"
19 #include "src/gpu/GrProgramInfo.h"
20 #include "src/gpu/GrRecordingContextPriv.h"
21 #include "src/gpu/SkGr.h"
22 #include "src/gpu/ops/GrSimpleMeshDrawOpHelper.h"
23
24 namespace {
25
26 class DrawAtlasOpImpl final : public GrMeshDrawOp {
27 private:
28 using Helper = GrSimpleMeshDrawOpHelper;
29
30 public:
31 DEFINE_OP_CLASS_ID
32
33 DrawAtlasOpImpl(GrProcessorSet*, const SkPMColor4f& color,
34 const SkMatrix& viewMatrix, GrAAType, int spriteCount, const SkRSXform* xforms,
35 const SkRect* rects, const SkColor* colors);
36
name() const37 const char* name() const override { return "DrawAtlasOp"; }
38
visitProxies(const GrVisitProxyFunc & func) const39 void visitProxies(const GrVisitProxyFunc& func) const override {
40 if (fProgramInfo) {
41 fProgramInfo->visitFPProxies(func);
42 } else {
43 fHelper.visitProxies(func);
44 }
45 }
46
47 FixedFunctionFlags fixedFunctionFlags() const override;
48
49 GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*, GrClampType) override;
50
51 private:
programInfo()52 GrProgramInfo* programInfo() override { return fProgramInfo; }
53
54 void onCreateProgramInfo(const GrCaps*,
55 SkArenaAlloc*,
56 const GrSurfaceProxyView& writeView,
57 bool usesMSAASurface,
58 GrAppliedClip&&,
59 const GrDstProxyView&,
60 GrXferBarrierFlags renderPassXferBarriers,
61 GrLoadOp colorLoadOp) override;
62
63 void onPrepareDraws(GrMeshDrawTarget*) override;
64 void onExecute(GrOpFlushState*, const SkRect& chainBounds) override;
65 #if GR_TEST_UTILS
66 SkString onDumpInfo() const override;
67 #endif
68
color() const69 const SkPMColor4f& color() const { return fColor; }
viewMatrix() const70 const SkMatrix& viewMatrix() const { return fViewMatrix; }
hasColors() const71 bool hasColors() const { return fHasColors; }
quadCount() const72 int quadCount() const { return fQuadCount; }
73
74 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps&) override;
75
76 struct Geometry {
77 SkPMColor4f fColor;
78 SkTArray<uint8_t, true> fVerts;
79 };
80
81 SkSTArray<1, Geometry, true> fGeoData;
82 Helper fHelper;
83 SkMatrix fViewMatrix;
84 SkPMColor4f fColor;
85 int fQuadCount;
86 bool fHasColors;
87
88 GrSimpleMesh* fMesh = nullptr;
89 GrProgramInfo* fProgramInfo = nullptr;
90 };
91
make_gp(SkArenaAlloc * arena,bool hasColors,const SkPMColor4f & color,const SkMatrix & viewMatrix)92 GrGeometryProcessor* make_gp(SkArenaAlloc* arena,
93 bool hasColors,
94 const SkPMColor4f& color,
95 const SkMatrix& viewMatrix) {
96 using namespace GrDefaultGeoProcFactory;
97 Color gpColor(color);
98 if (hasColors) {
99 gpColor.fType = Color::kPremulGrColorAttribute_Type;
100 }
101
102 return GrDefaultGeoProcFactory::Make(arena, gpColor, Coverage::kSolid_Type,
103 LocalCoords::kHasExplicit_Type, viewMatrix);
104 }
105
DrawAtlasOpImpl(GrProcessorSet * processorSet,const SkPMColor4f & color,const SkMatrix & viewMatrix,GrAAType aaType,int spriteCount,const SkRSXform * xforms,const SkRect * rects,const SkColor * colors)106 DrawAtlasOpImpl::DrawAtlasOpImpl(GrProcessorSet* processorSet, const SkPMColor4f& color,
107 const SkMatrix& viewMatrix, GrAAType aaType, int spriteCount,
108 const SkRSXform* xforms, const SkRect* rects,
109 const SkColor* colors)
110 : GrMeshDrawOp(ClassID()), fHelper(processorSet, aaType), fColor(color) {
111 SkASSERT(xforms);
112 SkASSERT(rects);
113
114 fViewMatrix = viewMatrix;
115 Geometry& installedGeo = fGeoData.push_back();
116 installedGeo.fColor = color;
117
118 // Figure out stride and offsets
119 // Order within the vertex is: position [color] texCoord
120 size_t texOffset = sizeof(SkPoint);
121 size_t vertexStride = 2 * sizeof(SkPoint);
122 fHasColors = SkToBool(colors);
123 if (colors) {
124 texOffset += sizeof(GrColor);
125 vertexStride += sizeof(GrColor);
126 }
127
128 // Compute buffer size and alloc buffer
129 fQuadCount = spriteCount;
130 int allocSize = static_cast<int>(4 * vertexStride * spriteCount);
131 installedGeo.fVerts.reset(allocSize);
132 uint8_t* currVertex = installedGeo.fVerts.begin();
133
134 SkRect bounds = SkRectPriv::MakeLargestInverted();
135 // TODO4F: Preserve float colors
136 int paintAlpha = GrColorUnpackA(installedGeo.fColor.toBytes_RGBA());
137 for (int spriteIndex = 0; spriteIndex < spriteCount; ++spriteIndex) {
138 // Transform rect
139 SkPoint strip[4];
140 const SkRect& currRect = rects[spriteIndex];
141 xforms[spriteIndex].toTriStrip(currRect.width(), currRect.height(), strip);
142
143 // Copy colors if necessary
144 if (colors) {
145 // convert to GrColor
146 SkColor spriteColor = colors[spriteIndex];
147 if (paintAlpha != 255) {
148 spriteColor = SkColorSetA(spriteColor,
149 SkMulDiv255Round(SkColorGetA(spriteColor), paintAlpha));
150 }
151 GrColor grColor = SkColorToPremulGrColor(spriteColor);
152
153 *(reinterpret_cast<GrColor*>(currVertex + sizeof(SkPoint))) = grColor;
154 *(reinterpret_cast<GrColor*>(currVertex + vertexStride + sizeof(SkPoint))) = grColor;
155 *(reinterpret_cast<GrColor*>(currVertex + 2 * vertexStride + sizeof(SkPoint))) =
156 grColor;
157 *(reinterpret_cast<GrColor*>(currVertex + 3 * vertexStride + sizeof(SkPoint))) =
158 grColor;
159 }
160
161 // Copy position and uv to verts
162 *(reinterpret_cast<SkPoint*>(currVertex)) = strip[0];
163 *(reinterpret_cast<SkPoint*>(currVertex + texOffset)) =
164 SkPoint::Make(currRect.fLeft, currRect.fTop);
165 SkRectPriv::GrowToInclude(&bounds, strip[0]);
166 currVertex += vertexStride;
167
168 *(reinterpret_cast<SkPoint*>(currVertex)) = strip[1];
169 *(reinterpret_cast<SkPoint*>(currVertex + texOffset)) =
170 SkPoint::Make(currRect.fLeft, currRect.fBottom);
171 SkRectPriv::GrowToInclude(&bounds, strip[1]);
172 currVertex += vertexStride;
173
174 *(reinterpret_cast<SkPoint*>(currVertex)) = strip[2];
175 *(reinterpret_cast<SkPoint*>(currVertex + texOffset)) =
176 SkPoint::Make(currRect.fRight, currRect.fTop);
177 SkRectPriv::GrowToInclude(&bounds, strip[2]);
178 currVertex += vertexStride;
179
180 *(reinterpret_cast<SkPoint*>(currVertex)) = strip[3];
181 *(reinterpret_cast<SkPoint*>(currVertex + texOffset)) =
182 SkPoint::Make(currRect.fRight, currRect.fBottom);
183 SkRectPriv::GrowToInclude(&bounds, strip[3]);
184 currVertex += vertexStride;
185 }
186
187 this->setTransformedBounds(bounds, viewMatrix, HasAABloat::kNo, IsHairline::kNo);
188 }
189
190 #if GR_TEST_UTILS
onDumpInfo() const191 SkString DrawAtlasOpImpl::onDumpInfo() const {
192 SkString string;
193 for (const auto& geo : fGeoData) {
194 string.appendf("Color: 0x%08x, Quads: %d\n", geo.fColor.toBytes_RGBA(),
195 geo.fVerts.count() / 4);
196 }
197 string += fHelper.dumpInfo();
198 return string;
199 }
200 #endif
201
onCreateProgramInfo(const GrCaps * caps,SkArenaAlloc * arena,const GrSurfaceProxyView & writeView,bool usesMSAASurface,GrAppliedClip && appliedClip,const GrDstProxyView & dstProxyView,GrXferBarrierFlags renderPassXferBarriers,GrLoadOp colorLoadOp)202 void DrawAtlasOpImpl::onCreateProgramInfo(const GrCaps* caps,
203 SkArenaAlloc* arena,
204 const GrSurfaceProxyView& writeView,
205 bool usesMSAASurface,
206 GrAppliedClip&& appliedClip,
207 const GrDstProxyView& dstProxyView,
208 GrXferBarrierFlags renderPassXferBarriers,
209 GrLoadOp colorLoadOp) {
210 // Setup geometry processor
211 GrGeometryProcessor* gp = make_gp(arena,
212 this->hasColors(),
213 this->color(),
214 this->viewMatrix());
215
216 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, usesMSAASurface,
217 std::move(appliedClip), dstProxyView, gp,
218 GrPrimitiveType::kTriangles, renderPassXferBarriers,
219 colorLoadOp);
220 }
221
onPrepareDraws(GrMeshDrawTarget * target)222 void DrawAtlasOpImpl::onPrepareDraws(GrMeshDrawTarget* target) {
223 if (!fProgramInfo) {
224 this->createProgramInfo(target);
225 }
226
227 int instanceCount = fGeoData.count();
228 size_t vertexStride = fProgramInfo->geomProc().vertexStride();
229
230 int numQuads = this->quadCount();
231 QuadHelper helper(target, vertexStride, numQuads);
232 void* verts = helper.vertices();
233 if (!verts) {
234 SkDebugf("Could not allocate vertices\n");
235 return;
236 }
237
238 uint8_t* vertPtr = reinterpret_cast<uint8_t*>(verts);
239 for (int i = 0; i < instanceCount; i++) {
240 const Geometry& args = fGeoData[i];
241
242 size_t allocSize = args.fVerts.count();
243 memcpy(vertPtr, args.fVerts.begin(), allocSize);
244 vertPtr += allocSize;
245 }
246
247 fMesh = helper.mesh();
248 }
249
onExecute(GrOpFlushState * flushState,const SkRect & chainBounds)250 void DrawAtlasOpImpl::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
251 if (!fProgramInfo || !fMesh) {
252 return;
253 }
254
255 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
256 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
257 flushState->drawMesh(*fMesh);
258 }
259
onCombineIfPossible(GrOp * t,SkArenaAlloc *,const GrCaps & caps)260 GrOp::CombineResult DrawAtlasOpImpl::onCombineIfPossible(GrOp* t,
261 SkArenaAlloc*,
262 const GrCaps& caps) {
263 auto that = t->cast<DrawAtlasOpImpl>();
264
265 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
266 return CombineResult::kCannotCombine;
267 }
268
269 // We currently use a uniform viewmatrix for this op.
270 if (!SkMatrixPriv::CheapEqual(this->viewMatrix(), that->viewMatrix())) {
271 return CombineResult::kCannotCombine;
272 }
273
274 if (this->hasColors() != that->hasColors()) {
275 return CombineResult::kCannotCombine;
276 }
277
278 if (!this->hasColors() && this->color() != that->color()) {
279 return CombineResult::kCannotCombine;
280 }
281
282 SkSafeMath safeMath;
283 int newQuadCount = safeMath.addInt(fQuadCount, that->quadCount());
284 if (!safeMath) {
285 return CombineResult::kCannotCombine;
286 }
287
288 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
289 fQuadCount = newQuadCount;
290
291 return CombineResult::kMerged;
292 }
293
fixedFunctionFlags() const294 GrDrawOp::FixedFunctionFlags DrawAtlasOpImpl::fixedFunctionFlags() const {
295 return fHelper.fixedFunctionFlags();
296 }
297
finalize(const GrCaps & caps,const GrAppliedClip * clip,GrClampType clampType)298 GrProcessorSet::Analysis DrawAtlasOpImpl::finalize(const GrCaps& caps,
299 const GrAppliedClip* clip,
300 GrClampType clampType) {
301 GrProcessorAnalysisColor gpColor;
302 if (this->hasColors()) {
303 gpColor.setToUnknown();
304 } else {
305 gpColor.setToConstant(fColor);
306 }
307 auto result = fHelper.finalizeProcessors(caps, clip, clampType,
308 GrProcessorAnalysisCoverage::kNone, &gpColor);
309 if (gpColor.isConstant(&fColor)) {
310 fHasColors = false;
311 }
312 return result;
313 }
314
315 } // anonymous namespace
316
317 namespace skgpu::v1::DrawAtlasOp {
318
Make(GrRecordingContext * context,GrPaint && paint,const SkMatrix & viewMatrix,GrAAType aaType,int spriteCount,const SkRSXform * xforms,const SkRect * rects,const SkColor * colors)319 GrOp::Owner Make(GrRecordingContext* context,
320 GrPaint&& paint,
321 const SkMatrix& viewMatrix,
322 GrAAType aaType,
323 int spriteCount,
324 const SkRSXform* xforms,
325 const SkRect* rects,
326 const SkColor* colors) {
327 return GrSimpleMeshDrawOpHelper::FactoryHelper<DrawAtlasOpImpl>(context, std::move(paint),
328 viewMatrix, aaType,
329 spriteCount, xforms,
330 rects, colors);
331 }
332
333 } // namespace skgpu::v1::DrawAtlasOp
334
335 #if GR_TEST_UTILS
336 #include "src/gpu/GrDrawOpTest.h"
337
random_xform(SkRandom * random)338 static SkRSXform random_xform(SkRandom* random) {
339 static const SkScalar kMinExtent = -100.f;
340 static const SkScalar kMaxExtent = 100.f;
341 static const SkScalar kMinScale = 0.1f;
342 static const SkScalar kMaxScale = 100.f;
343 static const SkScalar kMinRotate = -SK_ScalarPI;
344 static const SkScalar kMaxRotate = SK_ScalarPI;
345
346 SkRSXform xform = SkRSXform::MakeFromRadians(random->nextRangeScalar(kMinScale, kMaxScale),
347 random->nextRangeScalar(kMinRotate, kMaxRotate),
348 random->nextRangeScalar(kMinExtent, kMaxExtent),
349 random->nextRangeScalar(kMinExtent, kMaxExtent),
350 random->nextRangeScalar(kMinExtent, kMaxExtent),
351 random->nextRangeScalar(kMinExtent, kMaxExtent));
352 return xform;
353 }
354
random_texRect(SkRandom * random)355 static SkRect random_texRect(SkRandom* random) {
356 static const SkScalar kMinCoord = 0.0f;
357 static const SkScalar kMaxCoord = 1024.f;
358
359 SkRect texRect = SkRect::MakeLTRB(random->nextRangeScalar(kMinCoord, kMaxCoord),
360 random->nextRangeScalar(kMinCoord, kMaxCoord),
361 random->nextRangeScalar(kMinCoord, kMaxCoord),
362 random->nextRangeScalar(kMinCoord, kMaxCoord));
363 texRect.sort();
364 return texRect;
365 }
366
randomize_params(uint32_t count,SkRandom * random,SkTArray<SkRSXform> * xforms,SkTArray<SkRect> * texRects,SkTArray<GrColor> * colors,bool hasColors)367 static void randomize_params(uint32_t count, SkRandom* random, SkTArray<SkRSXform>* xforms,
368 SkTArray<SkRect>* texRects, SkTArray<GrColor>* colors,
369 bool hasColors) {
370 for (uint32_t v = 0; v < count; v++) {
371 xforms->push_back(random_xform(random));
372 texRects->push_back(random_texRect(random));
373 if (hasColors) {
374 colors->push_back(GrTest::RandomColor(random));
375 }
376 }
377 }
378
GR_DRAW_OP_TEST_DEFINE(DrawAtlasOp)379 GR_DRAW_OP_TEST_DEFINE(DrawAtlasOp) {
380 uint32_t spriteCount = random->nextRangeU(1, 100);
381
382 SkTArray<SkRSXform> xforms(spriteCount);
383 SkTArray<SkRect> texRects(spriteCount);
384 SkTArray<GrColor> colors;
385
386 bool hasColors = random->nextBool();
387
388 randomize_params(spriteCount, random, &xforms, &texRects, &colors, hasColors);
389
390 SkMatrix viewMatrix = GrTest::TestMatrix(random);
391 GrAAType aaType = GrAAType::kNone;
392 if (numSamples > 1 && random->nextBool()) {
393 aaType = GrAAType::kMSAA;
394 }
395
396 return skgpu::v1::DrawAtlasOp::Make(context, std::move(paint), viewMatrix, aaType, spriteCount,
397 xforms.begin(), texRects.begin(),
398 hasColors ? colors.begin() : nullptr);
399 }
400
401 #endif
402