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