Flutter Engine
The Flutter Engine
BoundsManager.h
Go to the documentation of this file.
1/*
2 * Copyright 2021 Google LLC
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#ifndef skgpu_graphite_geom_BoundsManager_DEFINED
9#define skgpu_graphite_geom_BoundsManager_DEFINED
10
11#include "include/core/SkSize.h"
13
15#include "src/base/SkVx.h"
18
19#include <cstdint>
20
21namespace skgpu::graphite {
22
23/**
24 * BoundsManager is an acceleration structure for device-space related pixel bounds queries.
25 * The BoundsManager tracks a single ordinal value per bounds: the CompressedPaintersOrder of a draw
26 * The CompressedPaintersOrder enforces a specific submission order of draws to the GPU but can
27 * re-arrange draws out of their original painter's order if the GREATER depth test and the draw's Z
28 * value resolve out-of-order rendering.
29 *
30 * It supports querying the most recent draw intersecting a bounding rect (represented as a
31 * CompressedPaintersOrder value), and recording a (bounds, CompressedPaintersOrder) pair.
32 */
34public:
35 virtual ~BoundsManager() {}
36
38
39 virtual void recordDraw(const Rect& bounds, CompressedPaintersOrder order) = 0;
40
41 virtual void reset() = 0;
42};
43
44// TODO: Select one most-effective BoundsManager implementation, make it the only option, and remove
45// virtual-ness. For now, this seems useful for correctness testing by comparing against trivial
46// implementations and for identifying how much "smarts" are actually worthwhile.
47
48// A BoundsManager that produces exact painter's order and assumes nothing is occluded.
49class NaiveBoundsManager final : public BoundsManager {
50public:
51 ~NaiveBoundsManager() override {}
52
54 return fLatestDraw;
55 }
56
57
58 void recordDraw(const Rect& bounds, CompressedPaintersOrder order) override {
59 if (fLatestDraw < order) {
60 fLatestDraw = order;
61 }
62 }
63
64 void reset() override {
65 fLatestDraw = CompressedPaintersOrder::First();
66 }
67
68private:
70};
71
72// A BoundsManager that tracks every draw and can exactly determine all queries
73// using a brute force search.
75public:
77
79 SkASSERT(fRects.count() == fOrders.count());
80
81 Rect::ComplementRect boundsComplement(bounds);
83 auto orderIter = fOrders.items().begin();
84 for (const Rect& r : fRects.items()) {
85 if (r.intersects(boundsComplement) && max < *orderIter) {
86 max = *orderIter;
87 }
88 ++orderIter;
89 }
90 return max;
91 }
92
93 void recordDraw(const Rect& bounds, CompressedPaintersOrder order) override {
94 fRects.push_back(bounds);
95 fOrders.push_back(order);
96 }
97
98 void reset() override {
99 fRects.reset();
100 fOrders.reset();
101 }
102
103 int count() const { return fRects.count(); }
104
106 auto orderIter = fOrders.items().begin();
107 for (const Rect& r : fRects.items()) {
108 manager->recordDraw(r, *orderIter);
109 ++orderIter;
110 }
111 }
112
113private:
114 // fRects and fOrders are parallel, but kept separate to avoid wasting padding since Rect is
115 // an over-aligned type.
118};
119
120// A BoundsManager that tracks highest CompressedPaintersOrder over a uniform spatial grid.
122public:
123 // 'gridSize' is the number of cells in the X and Y directions, splitting the pixels from [0,0]
124 // to 'deviceSize' into uniformly-sized cells.
125 static std::unique_ptr<GridBoundsManager> Make(const SkISize& deviceSize,
126 const SkISize& gridSize) {
127 SkASSERT(deviceSize.width() > 0 && deviceSize.height() > 0);
128 SkASSERT(gridSize.width() >= 1 && gridSize.height() >= 1);
129
130 return std::unique_ptr<GridBoundsManager>(new GridBoundsManager(deviceSize, gridSize));
131 }
132
133 static std::unique_ptr<GridBoundsManager> Make(const SkISize& deviceSize, int gridSize) {
134 return Make(deviceSize, {gridSize, gridSize});
135 }
136
137 static std::unique_ptr<GridBoundsManager> MakeRes(SkISize deviceSize,
138 int gridCellSize,
139 int maxGridSize=0) {
140 SkASSERT(deviceSize.width() > 0 && deviceSize.height() > 0);
141 SkASSERT(gridCellSize >= 1);
142
143 int gridWidth = SkScalarCeilToInt(deviceSize.width() / (float) gridCellSize);
144 if (maxGridSize > 0 && gridWidth > maxGridSize) {
145 // We'd have too many sizes so clamp the grid resolution, leave the device size alone
146 // since the grid cell size can't be preserved anyways.
147 gridWidth = maxGridSize;
148 } else {
149 // Pad out the device size to keep cell size the same
150 deviceSize.fWidth = gridWidth * gridCellSize;
151 }
152
153 int gridHeight = SkScalarCeilToInt(deviceSize.height() / (float) gridCellSize);
154 if (maxGridSize > 0 && gridHeight > maxGridSize) {
155 gridHeight = maxGridSize;
156 } else {
157 deviceSize.fHeight = gridHeight * gridCellSize;
158 }
159 return Make(deviceSize, {gridWidth, gridHeight});
160 }
161
162 ~GridBoundsManager() override {}
163
164
166 SkASSERT(!bounds.isEmptyNegativeOrNaN());
167
168 auto ltrb = this->getGridCoords(bounds);
169 const CompressedPaintersOrder* p = fNodes.data() + ltrb[1] * fGridWidth + ltrb[0];
170 int h = ltrb[3] - ltrb[1];
171 int w = ltrb[2] - ltrb[0];
172
174 for (int y = 0; y <= h; ++y ) {
175 for (int x = 0; x <= w; ++x) {
176 CompressedPaintersOrder v = *(p + x);
177 if (v > max) {
178 max = v;
179 }
180 }
181 p = p + fGridWidth;
182 }
183
184 return max;
185 }
186
187 void recordDraw(const Rect& bounds, CompressedPaintersOrder order) override {
188 SkASSERT(!bounds.isEmptyNegativeOrNaN());
189
190 auto ltrb = this->getGridCoords(bounds);
191 CompressedPaintersOrder* p = fNodes.data() + ltrb[1] * fGridWidth + ltrb[0];
192 int h = ltrb[3] - ltrb[1];
193 int w = ltrb[2] - ltrb[0];
194
195 for (int y = 0; y <= h; ++y) {
196 for (int x = 0; x <= w; ++x) {
197 CompressedPaintersOrder v = *(p + x);
198 if (order > v) {
199 *(p + x) = order;
200 }
201 }
202 p = p + fGridWidth;
203 }
204 }
205
206 void reset() override {
207 memset(fNodes.data(), 0, sizeof(CompressedPaintersOrder) * fGridWidth * fGridHeight);
208 }
209
210private:
211 GridBoundsManager(const SkISize& deviceSize, const SkISize& gridSize)
212 : fScaleX(gridSize.width() / (float) deviceSize.width())
213 , fScaleY(gridSize.height() / (float) deviceSize.height())
214 , fGridWidth(gridSize.width())
215 , fGridHeight(gridSize.height())
216 , fNodes((size_t) fGridWidth * fGridHeight) {
217 // Reset is needed to zero-out the uninitialized fNodes values.
218 this->reset();
219 }
220
221 skvx::int4 getGridCoords(const Rect& bounds) const {
222 // Normalize bounds by 1/wh of device bounds, then scale up to number of cells per side.
223 // fScaleXY includes both 1/wh and the grid dimension scaling, then clamp to [0, gridDim-1].
224 return pin(skvx::cast<int32_t>(bounds.ltrb() * skvx::float2(fScaleX, fScaleY).xyxy()),
225 skvx::int4(0),
226 skvx::int2(fGridWidth, fGridHeight).xyxy() - 1);
227 }
228
229 const float fScaleX;
230 const float fScaleY;
231
232 const int fGridWidth;
233 const int fGridHeight;
234
236};
237
238// A BoundsManager that first relies on BruteForceBoundsManager for N draw calls, and then switches
239// to the GridBoundsManager if it exceeds its limit. For low N, the brute force approach is
240// surprisingly efficient, has the highest accuracy, and very low memory overhead. Once the draw
241// call count is large enough, the grid's lower performance complexity outweigh its memory cost and
242// reduced accuracy.
244public:
245 HybridBoundsManager(const SkISize& deviceSize,
246 int gridCellSize,
247 int maxBruteForceN,
248 int maxGridSize=0)
249 : fDeviceSize(deviceSize)
250 , fGridCellSize(gridCellSize)
251 , fMaxBruteForceN(maxBruteForceN)
252 , fMaxGridSize(maxGridSize)
253 , fCurrentManager(&fBruteForceManager) {
254 SkASSERT(deviceSize.width() >= 1 && deviceSize.height() >= 1 &&
255 gridCellSize >= 1 && maxBruteForceN >= 1);
256 }
257
259 return fCurrentManager->getMostRecentDraw(bounds);
260 }
261
262 void recordDraw(const Rect& bounds, CompressedPaintersOrder order) override {
263 this->updateCurrentManagerIfNeeded();
264 fCurrentManager->recordDraw(bounds, order);
265 }
266
267 void reset() override {
268 const bool usedGrid = fCurrentManager == fGridManager.get();
269 if (usedGrid) {
270 // Reset the grid manager so it's ready to use next frame, but don't delete it.
271 fGridManager->reset();
272 // Assume brute force manager was reset when we swapped to the grid originally
273 fCurrentManager = &fBruteForceManager;
274 } else {
275 if (fGridManager) {
276 // Clean up the grid manager that was created over a frame ago without being used.
277 // This could lead to re-allocating the grid every-other frame, but it's a simple
278 // way to ensure we don't hold onto the grid in perpetuity if it's not needed.
279 fGridManager = nullptr;
280 }
281 fBruteForceManager.reset();
282 SkASSERT(fCurrentManager == &fBruteForceManager);
283 }
284 }
285
286private:
287 const SkISize fDeviceSize;
288 const int fGridCellSize;
289 const int fMaxBruteForceN;
290 const int fMaxGridSize;
291
292 BoundsManager* fCurrentManager;
293
294 BruteForceBoundsManager fBruteForceManager;
295
296 // The grid manager starts out null and is created the first time we exceed fMaxBruteForceN.
297 // However, even if we reset back to the brute force manager, we keep the grid around under the
298 // assumption that the owning Device will have similar frame-to-frame draw counts and will need
299 // to upgrade to the grid manager again.
300 std::unique_ptr<GridBoundsManager> fGridManager;
301
302 void updateCurrentManagerIfNeeded() {
303 if (fCurrentManager == fGridManager.get() ||
304 fBruteForceManager.count() < fMaxBruteForceN) {
305 // Already using the grid or the about-to-be-recorded draw will not cause us to exceed
306 // the brute force limit, so no need to change the current manager implementation.
307 return;
308 }
309 // Else we need to switch from the brute force manager to the grid manager
310 if (!fGridManager) {
311 fGridManager = GridBoundsManager::MakeRes(fDeviceSize, fGridCellSize, fMaxGridSize);
312 }
313 fCurrentManager = fGridManager.get();
314
315 // Fill out the grid manager with the recorded draws in the brute force manager
316 fBruteForceManager.replayDraws(fCurrentManager);
317 fBruteForceManager.reset();
318 }
319};
320
321} // namespace skgpu::graphite
322
323#endif // skgpu_graphite_geom_BoundsManager_DEFINED
#define SkASSERT(cond)
Definition: SkAssert.h:116
#define SkScalarCeilToInt(x)
Definition: SkScalar.h:36
virtual void recordDraw(const Rect &bounds, CompressedPaintersOrder order)=0
virtual CompressedPaintersOrder getMostRecentDraw(const Rect &bounds) const =0
CompressedPaintersOrder getMostRecentDraw(const Rect &bounds) const override
Definition: BoundsManager.h:78
void recordDraw(const Rect &bounds, CompressedPaintersOrder order) override
Definition: BoundsManager.h:93
void replayDraws(BoundsManager *manager) const
CompressedPaintersOrder getMostRecentDraw(const Rect &bounds) const override
static std::unique_ptr< GridBoundsManager > Make(const SkISize &deviceSize, int gridSize)
static std::unique_ptr< GridBoundsManager > Make(const SkISize &deviceSize, const SkISize &gridSize)
void recordDraw(const Rect &bounds, CompressedPaintersOrder order) override
static std::unique_ptr< GridBoundsManager > MakeRes(SkISize deviceSize, int gridCellSize, int maxGridSize=0)
void recordDraw(const Rect &bounds, CompressedPaintersOrder order) override
CompressedPaintersOrder getMostRecentDraw(const Rect &bounds) const override
HybridBoundsManager(const SkISize &deviceSize, int gridCellSize, int maxBruteForceN, int maxGridSize=0)
void recordDraw(const Rect &bounds, CompressedPaintersOrder order) override
Definition: BoundsManager.h:58
CompressedPaintersOrder getMostRecentDraw(const Rect &bounds) const override
Definition: BoundsManager.h:53
static float max(float r, float g, float b)
Definition: hsl.cpp:49
double y
double x
Optional< SkRect > bounds
Definition: SkRecords.h:189
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font manager
Definition: switches.h:218
TRect< Scalar > Rect
Definition: rect.h:769
SINT Vec< N, T > pin(const Vec< N, T > &x, const Vec< N, T > &lo, const Vec< N, T > &hi)
Definition: SkVx.h:655
SkScalar w
SkScalar h
int32_t height
int32_t width
Definition: SkSize.h:16
int32_t fHeight
Definition: SkSize.h:18
int32_t fWidth
Definition: SkSize.h:17
constexpr int32_t width() const
Definition: SkSize.h:36
constexpr int32_t height() const
Definition: SkSize.h:37
Definition: SkVx.h:83