1 /*
2 * Copyright 2021 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/AtlasRenderTask.h"
9
10 #include "src/core/SkBlendModePriv.h"
11 #include "src/core/SkIPoint16.h"
12 #include "src/gpu/GrGpu.h"
13 #include "src/gpu/GrOpFlushState.h"
14 #include "src/gpu/GrOpsTypes.h"
15 #include "src/gpu/geometry/GrQuad.h"
16 #include "src/gpu/ops/FillRectOp.h"
17 #include "src/gpu/ops/PathStencilCoverOp.h"
18
19 namespace skgpu::v1 {
20
AtlasRenderTask(GrRecordingContext * rContext,sk_sp<GrArenas> arenas,std::unique_ptr<GrDynamicAtlas> dynamicAtlas)21 AtlasRenderTask::AtlasRenderTask(GrRecordingContext* rContext,
22 sk_sp<GrArenas> arenas,
23 std::unique_ptr<GrDynamicAtlas> dynamicAtlas)
24 : OpsTask(rContext->priv().drawingManager(),
25 dynamicAtlas->writeView(*rContext->priv().caps()),
26 rContext->priv().auditTrail(),
27 std::move(arenas))
28 , fDynamicAtlas(std::move(dynamicAtlas)) {
29 }
30
addPath(const SkMatrix & viewMatrix,const SkPath & path,SkIPoint pathDevTopLeft,int widthInAtlas,int heightInAtlas,bool transposedInAtlas,SkIPoint16 * locationInAtlas)31 bool AtlasRenderTask::addPath(const SkMatrix& viewMatrix, const SkPath& path,
32 SkIPoint pathDevTopLeft, int widthInAtlas, int heightInAtlas,
33 bool transposedInAtlas, SkIPoint16* locationInAtlas) {
34 SkASSERT(!this->isClosed());
35 SkASSERT(this->isEmpty());
36 SkASSERT(!fDynamicAtlas->isInstantiated()); // Paths can't be added after instantiate().
37
38 if (!fDynamicAtlas->addRect(widthInAtlas, heightInAtlas, locationInAtlas)) {
39 return false;
40 }
41
42 SkMatrix pathToAtlasMatrix = viewMatrix;
43 if (transposedInAtlas) {
44 std::swap(pathToAtlasMatrix[0], pathToAtlasMatrix[3]);
45 std::swap(pathToAtlasMatrix[1], pathToAtlasMatrix[4]);
46 float tx=pathToAtlasMatrix.getTranslateX(), ty=pathToAtlasMatrix.getTranslateY();
47 pathToAtlasMatrix.setTranslateX(ty - pathDevTopLeft.y() + locationInAtlas->x());
48 pathToAtlasMatrix.setTranslateY(tx - pathDevTopLeft.x() + locationInAtlas->y());
49 } else {
50 pathToAtlasMatrix.postTranslate(locationInAtlas->x() - pathDevTopLeft.x(),
51 locationInAtlas->y() - pathDevTopLeft.y());
52 }
53
54 if (GrFillRuleForSkPath(path) == GrFillRule::kNonzero) {
55 fWindingPathList.add(&fPathDrawAllocator, pathToAtlasMatrix, path);
56 } else {
57 fEvenOddPathList.add(&fPathDrawAllocator, pathToAtlasMatrix, path);
58 }
59 return true;
60 }
61
onMakeClosed(GrRecordingContext * rContext,SkIRect * targetUpdateBounds)62 GrRenderTask::ExpectedOutcome AtlasRenderTask::onMakeClosed(GrRecordingContext* rContext,
63 SkIRect* targetUpdateBounds) {
64 // We don't add our ops until now, at which point we know the atlas is done being built.
65 SkASSERT(this->isEmpty());
66 SkASSERT(!fDynamicAtlas->isInstantiated()); // Instantiation happens after makeClosed().
67
68 const GrCaps& caps = *rContext->priv().caps();
69
70 // Set our dimensions now. OpsTask will need them when we add our ops.
71 this->target(0)->priv().setLazyDimensions(fDynamicAtlas->drawBounds());
72 this->target(0)->asRenderTargetProxy()->setNeedsStencil();
73 SkRect drawRect = target(0)->getBoundsRect();
74
75 // Clear the atlas.
76 if (caps.performColorClearsAsDraws() || caps.performStencilClearsAsDraws()) {
77 this->setColorLoadOp(GrLoadOp::kDiscard);
78 this->setInitialStencilContent(StencilContent::kDontCare);
79
80 constexpr static GrUserStencilSettings kClearStencil(
81 GrUserStencilSettings::StaticInit<
82 0x0000,
83 GrUserStencilTest::kAlways,
84 0xffff,
85 GrUserStencilOp::kReplace,
86 GrUserStencilOp::kReplace,
87 0xffff>());
88
89 this->stencilAtlasRect(rContext, drawRect, SK_PMColor4fTRANSPARENT, &kClearStencil);
90 } else {
91 this->setColorLoadOp(GrLoadOp::kClear);
92 this->setInitialStencilContent(StencilContent::kUserBitsCleared);
93 }
94
95 // Add ops to stencil the atlas paths.
96 for (const auto* pathList : {&fWindingPathList, &fEvenOddPathList}) {
97 if (pathList->pathCount() > 0) {
98 auto op = GrOp::Make<PathStencilCoverOp>(
99 rContext,
100 pathList->pathDrawList(),
101 pathList->totalCombinedPathVerbCnt(),
102 pathList->pathCount(),
103 GrPaint(),
104 GrAAType::kMSAA,
105 FillPathFlags::kStencilOnly,
106 drawRect);
107 this->addAtlasDrawOp(std::move(op), caps);
108 }
109 }
110
111 // Finally, draw a fullscreen rect to cover our stencilled paths.
112 const GrUserStencilSettings* stencil;
113 if (caps.discardStencilValuesAfterRenderPass()) {
114 constexpr static GrUserStencilSettings kTestStencil(
115 GrUserStencilSettings::StaticInit<
116 0x0000,
117 GrUserStencilTest::kNotEqual,
118 0xffff,
119 GrUserStencilOp::kKeep,
120 GrUserStencilOp::kKeep,
121 0xffff>());
122
123 // This is the final op in the task. Since Ganesh is planning to discard the stencil values
124 // anyway, there is no need to reset the stencil values back to 0.
125 stencil = &kTestStencil;
126 } else {
127 constexpr static GrUserStencilSettings kTestAndResetStencil(
128 GrUserStencilSettings::StaticInit<
129 0x0000,
130 GrUserStencilTest::kNotEqual,
131 0xffff,
132 GrUserStencilOp::kZero,
133 GrUserStencilOp::kKeep,
134 0xffff>());
135
136 // Outset the cover rect to make extra sure we clear every stencil value touched by the
137 // atlas.
138 drawRect.outset(1, 1);
139 stencil = &kTestAndResetStencil;
140 }
141 this->stencilAtlasRect(rContext, drawRect, SK_PMColor4fWHITE, stencil);
142
143 this->OpsTask::onMakeClosed(rContext, targetUpdateBounds);
144
145 // Don't mark msaa dirty. Since this op defers being closed, the drawing manager's dirty
146 // tracking doesn't work anyway. We will just resolve msaa manually during onExecute.
147 return ExpectedOutcome::kTargetUnchanged;
148 }
149
stencilAtlasRect(GrRecordingContext * rContext,const SkRect & rect,const SkPMColor4f & color,const GrUserStencilSettings * stencil)150 void AtlasRenderTask::stencilAtlasRect(GrRecordingContext* rContext, const SkRect& rect,
151 const SkPMColor4f& color,
152 const GrUserStencilSettings* stencil) {
153 GrPaint paint;
154 paint.setColor4f(color);
155 paint.setXPFactory(SkBlendMode_AsXPFactory(SkBlendMode::kSrc));
156 GrQuad quad(rect);
157 DrawQuad drawQuad{quad, quad, GrQuadAAFlags::kAll};
158 auto op = FillRectOp::Make(rContext, std::move(paint), GrAAType::kMSAA, &drawQuad, stencil);
159 this->addAtlasDrawOp(std::move(op), *rContext->priv().caps());
160 }
161
addAtlasDrawOp(GrOp::Owner op,const GrCaps & caps)162 void AtlasRenderTask::addAtlasDrawOp(GrOp::Owner op, const GrCaps& caps) {
163 SkASSERT(!this->isClosed());
164
165 auto drawOp = static_cast<GrDrawOp*>(op.get());
166 SkDEBUGCODE(drawOp->fAddDrawOpCalled = true;)
167
168 auto processorAnalysis = drawOp->finalize(caps, nullptr,
169 GrColorTypeClampType(fDynamicAtlas->colorType()));
170 SkASSERT(!processorAnalysis.requiresDstTexture());
171 SkASSERT(!processorAnalysis.usesNonCoherentHWBlending());
172
173 drawOp->setClippedBounds(drawOp->bounds());
174 this->recordOp(std::move(op), true/*usesMSAA*/, processorAnalysis, nullptr, nullptr, caps);
175 }
176
onExecute(GrOpFlushState * flushState)177 bool AtlasRenderTask::onExecute(GrOpFlushState* flushState) {
178 if (!this->OpsTask::onExecute(flushState)) {
179 return false;
180 }
181 if (this->target(0)->requiresManualMSAAResolve()) {
182 // Since atlases don't get closed until they are done being built, the drawingManager
183 // doesn't detect that they need an MSAA resolve. Do it here manually.
184 auto nativeRect = GrNativeRect::MakeIRectRelativeTo(
185 GrDynamicAtlas::kTextureOrigin,
186 this->target(0)->backingStoreDimensions().height(),
187 SkIRect::MakeSize(fDynamicAtlas->drawBounds()));
188 flushState->gpu()->resolveRenderTarget(this->target(0)->peekRenderTarget(), nativeRect);
189 }
190 return true;
191 }
192
193 } // namespace skgpu::v1
194