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