Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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.
236 VertexWriter getVertexWriter(size_t size, BindBufferInfo* binding);
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...
239 VertexWriter getIndexWriter(size_t size, BindBufferInfo* binding);
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
BindBufferInfo getIndexStorage(size_t requiredBytes)
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)
VertexWriter getIndexWriter(size_t size, BindBufferInfo *binding)
uint32_t * target