Flutter Engine
The Flutter Engine
BufferManager.h
Go to the documentation of this file.
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#ifndef skgpu_graphite_BufferManager_DEFINED
9#define skgpu_graphite_BufferManager_DEFINED
10
18
19#include <array>
20#include <tuple>
21
22namespace skgpu::graphite {
23
24class Caps;
25class Context;
26class DrawBufferManager;
27class GlobalCache;
28class QueueManager;
29class Recording;
31
32/**
33 * ScratchBuffer represents a GPU buffer object that is allowed to be reused across strictly
34 * sequential tasks within a Recording. It can be used to sub-allocate multiple bindings.
35 * When a ScratchBuffer gets deallocated, the underlying GPU buffer gets returned to the
36 * originating DrawBufferManager for reuse.
37 */
38class ScratchBuffer final {
39public:
40 // The default constructor creates an invalid ScratchBuffer that cannot be used for
41 // suballocations.
42 ScratchBuffer() = default;
43
44 // The destructor returns the underlying buffer back to the reuse pool, if the ScratchBuffer is
45 // valid.
47
48 // Disallow copy
49 ScratchBuffer(const ScratchBuffer&) = delete;
51
52 // Allow move
55
56 // Returns false if the underlying buffer has been returned to the reuse pool.
57 bool isValid() const { return static_cast<bool>(fBuffer); }
58
59 // Convenience wrapper for checking the validity of a buffer.
60 explicit operator bool() { return this->isValid(); }
61
62 // Logical size of the initially requested allocation.
63 //
64 // NOTE: This number may be different from the size of the underlying GPU buffer but it is
65 // guaranteed to be less than or equal to it.
66 size_t size() const { return fSize; }
67
68 // Sub-allocate a slice within the scratch buffer object. Fails and returns a NULL pointer if
69 // the buffer doesn't have enough space remaining for `requiredBytes`.
70 // TODO(b/330743233): Currently the suballocations use the alignment for the BufferInfo that was
71 // assigned by the DrawBufferManager based on the ScratchBuffer's buffer type. One way to
72 // generalize this across different buffer usages/types is to have this function accept an
73 // additional alignment parameter. That should happen after we loosen the coupling between
74 // DrawBufferManager's BufferInfos and ScratchBuffer reuse pools.
75 BindBufferInfo suballocate(size_t requiredBytes);
76
77 // Returns the underlying buffer object back to the pool and invalidates this ScratchBuffer.
78 void returnToPool();
79
80private:
81 friend class DrawBufferManager;
82
83 ScratchBuffer(size_t size, size_t alignment, sk_sp<Buffer>, DrawBufferManager*);
84
85 size_t fSize;
86 size_t fAlignment;
87 sk_sp<Buffer> fBuffer;
88 size_t fOffset = 0;
89
90 DrawBufferManager* fOwner = nullptr;
91};
92
93/**
94 * DrawBufferManager controls writing to buffer data ranges within larger, cacheable Buffers and
95 * automatically handles either mapping or copying via transfer buffer depending on what the GPU
96 * hardware supports for the requested buffer type and use case. It is intended for repeatedly
97 * uploading dynamic data to the GPU.
98*/
100public:
103
104 // Let possible users check if the manager is already in a bad mapping state and skip any extra
105 // work that will be wasted because the next Recording snap will fail.
106 bool hasMappingFailed() const { return fMappingFailed; }
107
108 std::pair<VertexWriter, BindBufferInfo> getVertexWriter(size_t requiredBytes);
109 std::pair<IndexWriter, BindBufferInfo> getIndexWriter(size_t requiredBytes);
110 std::pair<UniformWriter, BindBufferInfo> getUniformWriter(size_t requiredBytes);
111 std::pair<UniformWriter, BindBufferInfo> getSsboWriter(size_t requiredBytes);
112
113 // Return a pointer to a mapped storage buffer suballocation without a specific data writer.
114 std::pair<void* /* mappedPtr */, BindBufferInfo> getUniformPointer(size_t requiredBytes);
115 std::pair<void* /* mappedPtr */, BindBufferInfo> getStoragePointer(size_t requiredBytes);
116
117 // Utilities that return an unmapped buffer suballocation for a particular usage. These buffers
118 // are intended to be only accessed by the GPU and are not intended for CPU data uploads.
119 BindBufferInfo getStorage(size_t requiredBytes, ClearBuffer cleared = ClearBuffer::kNo);
120 BindBufferInfo getVertexStorage(size_t requiredBytes);
121 BindBufferInfo getIndexStorage(size_t requiredBytes);
122 BindBufferInfo getIndirectStorage(size_t requiredBytes, ClearBuffer cleared = ClearBuffer::kNo);
123
124 // Returns an entire storage buffer object that is large enough to fit `requiredBytes`. The
125 // returned ScratchBuffer can be used to sub-allocate one or more storage buffer bindings that
126 // reference the same buffer object.
127 //
128 // When the ScratchBuffer goes out of scope, the buffer object gets added to an internal pool
129 // and is available for immediate reuse. getScratchStorage() returns buffers from this pool if
130 // possible. A ScratchBuffer can be explicitly returned to the pool by calling `returnToPool()`.
131 //
132 // Returning a ScratchBuffer back to the buffer too early can result in validation failures
133 // and/or data races. It is the callers responsibility to manage reuse within a Recording and
134 // guarantee synchronized access to buffer bindings.
135 //
136 // This type of usage is currently limited to GPU-only storage buffers.
137 //
138 // TODO(b/330743233): Generalize the underlying pool to other buffer types, including mapped
139 // ones.
140 ScratchBuffer getScratchStorage(size_t requiredBytes);
141
142 // Returns the last 'unusedBytes' from the last call to getVertexWriter(). Assumes that
143 // 'unusedBytes' is less than the 'requiredBytes' to the original allocation.
144 void returnVertexBytes(size_t unusedBytes);
145
146 size_t alignUniformBlockSize(size_t dataSize) {
147 return SkAlignTo(dataSize, fCurrentBuffers[kUniformBufferIndex].fStartAlignment);
148 }
149
150 // Finalizes all buffers and transfers ownership of them to a Recording. Should not call if
151 // hasMappingFailed() returns true.
153
154private:
155 friend class ScratchBuffer;
156
157 struct BufferInfo {
158 BufferInfo(BufferType type, size_t blockSize, const Caps* caps);
159
160 const BufferType fType;
161 const size_t fStartAlignment;
162 const size_t fBlockSize;
163 sk_sp<Buffer> fBuffer;
164 // The fTransferBuffer can be null, if draw buffer cannot be mapped,
165 // see Caps::drawBufferCanBeMapped() for detail.
166 BindBufferInfo fTransferBuffer{};
167 void* fTransferMapPtr = nullptr;
168 size_t fOffset = 0;
169 };
170 std::pair<void* /*mappedPtr*/, BindBufferInfo> prepareMappedBindBuffer(BufferInfo* info,
171 size_t requiredBytes,
172 std::string_view label);
173 BindBufferInfo prepareBindBuffer(BufferInfo* info,
174 size_t requiredBytes,
175 std::string_view label,
176 bool supportCpuUpload = false,
177 ClearBuffer cleared = ClearBuffer::kNo);
178
179 sk_sp<Buffer> findReusableSbo(size_t bufferSize);
180
181 // Marks manager in a failed state, unmaps any previously collected buffers.
182 void onFailedBuffer();
183
184 ResourceProvider* const fResourceProvider;
185 const Caps* const fCaps;
186 UploadBufferManager* fUploadManager;
187
188 static constexpr size_t kVertexBufferIndex = 0;
189 static constexpr size_t kIndexBufferIndex = 1;
190 static constexpr size_t kUniformBufferIndex = 2;
191 static constexpr size_t kStorageBufferIndex = 3;
192 static constexpr size_t kGpuOnlyStorageBufferIndex = 4;
193 static constexpr size_t kVertexStorageBufferIndex = 5;
194 static constexpr size_t kIndexStorageBufferIndex = 6;
195 static constexpr size_t kIndirectStorageBufferIndex = 7;
196 std::array<BufferInfo, 8> fCurrentBuffers;
197
198 // Vector of buffer and transfer buffer pairs.
199 skia_private::TArray<std::pair<sk_sp<Buffer>, BindBufferInfo>> fUsedBuffers;
200
201 // List of buffer regions that were requested to be cleared at the time of allocation.
203
204 // TODO(b/330744081): These should probably be maintained in a sorted data structure that
205 // supports fast insertion and lookup doesn't waste buffers (e.g. by vending out large buffers
206 // for small buffer sizes).
207 // TODO(b/330743233): We may want this pool to contain buffers with mixed usages (such as
208 // VERTEX|INDEX|UNIFORM|STORAGE) to reduce buffer usage on platforms like Dawn where
209 // host-written data always go through a copy via transfer buffer.
210 skia_private::TArray<sk_sp<Buffer>> fReusableScratchStorageBuffers;
211
212 // If mapping failed on Buffers created/managed by this DrawBufferManager or by the mapped
213 // transfer buffers from the UploadManager, remember so that the next Recording will fail.
214 bool fMappingFailed = false;
215};
216
217/**
218 * The StaticBufferManager is the one-time-only analog to DrawBufferManager and provides "static"
219 * Buffers to RenderSteps and other Context-lifetime-tied objects, where the Buffers' contents will
220 * not change and can benefit from prioritizing GPU reads. The assumed use case is that they remain
221 * read-only on the GPU as well, so a single static buffer can be shared by all Recorders.
222 *
223 * Unlike DrawBufferManager's getXWriter() functions that return both a Writer and a BindBufferInfo,
224 * StaticBufferManager returns only a Writer and accepts a BindBufferInfo* as an argument. This will
225 * be re-written with the final binding info for the GPU-private data once that can be determined
226 * after *all* static buffers have been requested.
227 */
229public:
232
233 // The passed in BindBufferInfos are updated when finalize() is later called, to point to the
234 // packed, GPU-private buffer at the appropriate offset. The data written to the returned Writer
235 // is copied to the private buffer at that offset. 'binding' must live until finalize() returns.
237 // TODO: Update the tessellation index buffer generation functions to use an IndexWriter so this
238 // can return an IndexWriter vs. a VertexWriter that happens to just write uint16s...
240
241 enum class FinishResult : int {
242 kFailure, // Unable to create or copy static buffers
243 kSuccess, // Successfully created static buffers and added GPU tasks to the queue
244 kNoWork // No static buffers required, no GPU tasks add to the queue
245 };
246
247 // Finalizes all buffers and records a copy task to compact and privatize static data. The
248 // final static buffers will become owned by the Context's GlobalCache.
250
251private:
252 struct CopyRange {
253 BindBufferInfo fSource; // The CPU-to-GPU buffer and offset for the source of the copy
254 BindBufferInfo* fTarget; // The late-assigned destination of the copy
255 size_t fSize; // The number of bytes to copy
256 };
257 struct BufferInfo {
258 BufferInfo(BufferType type, const Caps* caps);
259
260 bool createAndUpdateBindings(ResourceProvider*,
261 Context*,
262 QueueManager*,
263 GlobalCache*,
264 std::string_view label) const;
265 void reset() {
266 fData.clear();
267 fTotalRequiredBytes = 0;
268 }
269
270 const BufferType fBufferType;
271 const size_t fAlignment;
272
273 std::vector<CopyRange> fData;
274 size_t fTotalRequiredBytes;
275 };
276
277 void* prepareStaticData(BufferInfo* info, size_t requiredBytes, BindBufferInfo* target);
278
279 ResourceProvider* const fResourceProvider;
280 UploadBufferManager fUploadManager;
281 const size_t fRequiredTransferAlignment;
282
283 // The source data that's copied into a final GPU-private buffer
284 BufferInfo fVertexBufferInfo;
285 BufferInfo fIndexBufferInfo;
286
287 // If mapping failed on Buffers created/managed by this StaticBufferManager or by the mapped
288 // transfer buffers from the UploadManager, remember so that finalize() will fail.
289 bool fMappingFailed = false;
290};
291
292} // namespace skgpu::graphite
293
294#endif // skgpu_graphite_BufferManager_DEFINED
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition: DM.cpp:213
m reset()
static constexpr size_t SkAlignTo(size_t x, size_t alignment)
Definition: SkAlign.h:33
GLenum type
BindBufferInfo getIndexStorage(size_t requiredBytes)
DrawBufferManager(ResourceProvider *, const Caps *, UploadBufferManager *)
ScratchBuffer getScratchStorage(size_t requiredBytes)
std::pair< void *, BindBufferInfo > getUniformPointer(size_t requiredBytes)
std::pair< VertexWriter, BindBufferInfo > getVertexWriter(size_t requiredBytes)
std::pair< void *, BindBufferInfo > getStoragePointer(size_t requiredBytes)
std::pair< UniformWriter, BindBufferInfo > getUniformWriter(size_t requiredBytes)
std::pair< IndexWriter, BindBufferInfo > getIndexWriter(size_t requiredBytes)
BindBufferInfo getStorage(size_t requiredBytes, ClearBuffer cleared=ClearBuffer::kNo)
size_t alignUniformBlockSize(size_t dataSize)
std::pair< UniformWriter, BindBufferInfo > getSsboWriter(size_t requiredBytes)
void returnVertexBytes(size_t unusedBytes)
BindBufferInfo getIndirectStorage(size_t requiredBytes, ClearBuffer cleared=ClearBuffer::kNo)
BindBufferInfo getVertexStorage(size_t requiredBytes)
ScratchBuffer(ScratchBuffer &&)=default
ScratchBuffer & operator=(ScratchBuffer &&)=default
ScratchBuffer & operator=(const ScratchBuffer &)=delete
BindBufferInfo suballocate(size_t requiredBytes)
ScratchBuffer(const ScratchBuffer &)=delete
FinishResult finalize(Context *, QueueManager *, GlobalCache *)
VertexWriter getVertexWriter(size_t size, BindBufferInfo *binding)
StaticBufferManager(ResourceProvider *, const Caps *)
VertexWriter getIndexWriter(size_t size, BindBufferInfo *binding)
@ kSuccess
Definition: embedder.h:73
uint32_t * target
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 Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259