• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
11 #include "include/core/SkRefCnt.h"
12 #include "include/private/base/SkTArray.h"
13 #include "src/gpu/BufferWriter.h"
14 #include "src/gpu/graphite/Buffer.h"
15 #include "src/gpu/graphite/DrawTypes.h"
16 #include "src/gpu/graphite/ResourceTypes.h"
17 #include "src/gpu/graphite/UploadBufferManager.h"
18 
19 #include <array>
20 #include <tuple>
21 
22 namespace skgpu::graphite {
23 
24 class Caps;
25 class Context;
26 class DrawBufferManager;
27 class GlobalCache;
28 class QueueManager;
29 class Recording;
30 class ResourceProvider;
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  */
38 class ScratchBuffer final {
39 public:
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.
46     ~ScratchBuffer();
47 
48     // Disallow copy
49     ScratchBuffer(const ScratchBuffer&) = delete;
50     ScratchBuffer& operator=(const ScratchBuffer&) = delete;
51 
52     // Allow move
53     ScratchBuffer(ScratchBuffer&&) = default;
54     ScratchBuffer& operator=(ScratchBuffer&&) = default;
55 
56     // Returns false if the underlying buffer has been returned to the reuse pool.
isValid()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.
size()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 
80 private:
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 */
99 class DrawBufferManager {
100 public:
101     DrawBufferManager(ResourceProvider*, const Caps*, UploadBufferManager*);
102     ~DrawBufferManager();
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.
hasMappingFailed()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 
alignUniformBlockSize(size_t dataSize)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.
152     void transferToRecording(Recording*);
153 
154 private:
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.
202     skia_private::TArray<ClearBufferInfo> fClearList;
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  */
228 class StaticBufferManager {
229 public:
230     StaticBufferManager(ResourceProvider*, const Caps*);
231     ~StaticBufferManager();
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.
249     FinishResult finalize(Context*, QueueManager*, GlobalCache*);
250 
251 private:
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;
resetBufferInfo265         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
295