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