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 uint32_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(uint32_t size, uint32_t alignment, sk_sp<Buffer>, DrawBufferManager*); 84 85 uint32_t fSize; 86 uint32_t fAlignment; 87 sk_sp<Buffer> fBuffer; 88 uint32_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 // These writers automatically calculate the required bytes based on count and stride. If a 109 // valid writer is returned, the byte count will fit in a uint32_t. 110 std::pair<VertexWriter, BindBufferInfo> getVertexWriter(size_t count, size_t stride); 111 std::pair<IndexWriter, BindBufferInfo> getIndexWriter(size_t count, size_t stride); 112 std::pair<UniformWriter, BindBufferInfo> getUniformWriter(size_t count, size_t stride); 113 114 // Return an SSBO writer that is aligned for binding, per the requirements in fCurrentBuffers. 115 std::pair<UniformWriter, BindBufferInfo> getSsboWriter(size_t count, size_t stride); 116 // Return an SSBO writer that is aligned for indexing from the shader, per the provided stride. 117 std::pair<UniformWriter, BindBufferInfo> getAlignedSsboWriter(size_t count, size_t stride); 118 119 // The remaining writers and buffer allocator functions assume that byte counts are safely 120 // calculated by the caller (e.g. Vello or ). 121 122 // Return a pointer to a mapped storage buffer suballocation without a specific data writer. 123 std::pair<void* /* mappedPtr */, BindBufferInfo> getUniformPointer(size_t requiredBytes); 124 std::pair<void* /* mappedPtr */, BindBufferInfo> getStoragePointer(size_t requiredBytes); 125 126 // Utilities that return an unmapped buffer suballocation for a particular usage. These buffers 127 // are intended to be only accessed by the GPU and are not intended for CPU data uploads. 128 BindBufferInfo getStorage(size_t requiredBytes, ClearBuffer cleared = ClearBuffer::kNo); 129 BindBufferInfo getVertexStorage(size_t requiredBytes); 130 BindBufferInfo getIndexStorage(size_t requiredBytes); 131 BindBufferInfo getIndirectStorage(size_t requiredBytes, ClearBuffer cleared = ClearBuffer::kNo); 132 133 // Returns an entire storage buffer object that is large enough to fit `requiredBytes`. The 134 // returned ScratchBuffer can be used to sub-allocate one or more storage buffer bindings that 135 // reference the same buffer object. 136 // 137 // When the ScratchBuffer goes out of scope, the buffer object gets added to an internal pool 138 // and is available for immediate reuse. getScratchStorage() returns buffers from this pool if 139 // possible. A ScratchBuffer can be explicitly returned to the pool by calling `returnToPool()`. 140 // 141 // Returning a ScratchBuffer back to the buffer too early can result in validation failures 142 // and/or data races. It is the callers responsibility to manage reuse within a Recording and 143 // guarantee synchronized access to buffer bindings. 144 // 145 // This type of usage is currently limited to GPU-only storage buffers. 146 // 147 // TODO(b/330743233): Generalize the underlying pool to other buffer types, including mapped 148 // ones. 149 ScratchBuffer getScratchStorage(size_t requiredBytes); 150 151 // Returns the last 'unusedBytes' from the last call to getVertexWriter(). Assumes that 152 // 'unusedBytes' is less than the 'count*stride' to the original allocation. 153 void returnVertexBytes(size_t unusedBytes); 154 155 // Finalizes all buffers and transfers ownership of them to a Recording. Returns true on success 156 // and false if a mapping had previously failed. 157 // 158 // Regardless of success or failure, the DrawBufferManager is reset to a valid initial state 159 // for recording buffer data for the next Recording. 160 [[nodiscard]] bool transferToRecording(Recording*); 161 162 private: 163 friend class ScratchBuffer; 164 165 struct BufferInfo { 166 BufferInfo(BufferType type, uint32_t minBlockSize, uint32_t maxBlockSize, const Caps* caps); 167 168 const BufferType fType; 169 const uint32_t fStartAlignment; 170 const uint32_t fMinBlockSize; 171 const uint32_t fMaxBlockSize; 172 sk_sp<Buffer> fBuffer; 173 // The fTransferBuffer can be null, if draw buffer cannot be mapped, 174 // see Caps::drawBufferCanBeMapped() for detail. 175 BindBufferInfo fTransferBuffer{}; 176 void* fTransferMapPtr = nullptr; 177 uint32_t fOffset = 0; 178 179 // Block size to use when creating new buffers; between fMinBlockSize and fMaxBlockSize. 180 uint32_t fCurBlockSize = 0; 181 // How many bytes have been used for for this buffer type since the last Recording snap. 182 uint32_t fUsedSize = 0; 183 }; 184 std::pair<void* /*mappedPtr*/, BindBufferInfo> prepareMappedBindBuffer( 185 BufferInfo* info, 186 std::string_view label, 187 uint32_t requiredBytes, 188 uint32_t requiredAlignment = 0); 189 BindBufferInfo prepareBindBuffer(BufferInfo* info, 190 std::string_view label, 191 uint32_t requiredBytes, 192 uint32_t requiredAlignment = 0, 193 bool supportCpuUpload = false, 194 ClearBuffer cleared = ClearBuffer::kNo); 195 196 // Helper method for public getSsboWriter methods. 197 std::pair<UniformWriter, BindBufferInfo> getSsboWriter(size_t count, 198 size_t stride, 199 size_t alignment); 200 201 sk_sp<Buffer> findReusableSbo(size_t bufferSize); 202 203 // Marks manager in a failed state, unmaps any previously collected buffers. 204 void onFailedBuffer(); 205 206 ResourceProvider* const fResourceProvider; 207 const Caps* const fCaps; 208 UploadBufferManager* fUploadManager; 209 210 static constexpr size_t kVertexBufferIndex = 0; 211 static constexpr size_t kIndexBufferIndex = 1; 212 static constexpr size_t kUniformBufferIndex = 2; 213 static constexpr size_t kStorageBufferIndex = 3; 214 static constexpr size_t kGpuOnlyStorageBufferIndex = 4; 215 static constexpr size_t kVertexStorageBufferIndex = 5; 216 static constexpr size_t kIndexStorageBufferIndex = 6; 217 static constexpr size_t kIndirectStorageBufferIndex = 7; 218 std::array<BufferInfo, 8> fCurrentBuffers; 219 220 // Vector of buffer and transfer buffer pairs. 221 skia_private::TArray<std::pair<sk_sp<Buffer>, BindBufferInfo>> fUsedBuffers; 222 223 // List of buffer regions that were requested to be cleared at the time of allocation. 224 skia_private::TArray<BindBufferInfo> fClearList; 225 226 // TODO(b/330744081): These should probably be maintained in a sorted data structure that 227 // supports fast insertion and lookup doesn't waste buffers (e.g. by vending out large buffers 228 // for small buffer sizes). 229 // TODO(b/330743233): We may want this pool to contain buffers with mixed usages (such as 230 // VERTEX|INDEX|UNIFORM|STORAGE) to reduce buffer usage on platforms like Dawn where 231 // host-written data always go through a copy via transfer buffer. 232 skia_private::TArray<sk_sp<Buffer>> fReusableScratchStorageBuffers; 233 234 // If mapping failed on Buffers created/managed by this DrawBufferManager or by the mapped 235 // transfer buffers from the UploadManager, remember so that the next Recording will fail. 236 bool fMappingFailed = false; 237 }; 238 239 /** 240 * The StaticBufferManager is the one-time-only analog to DrawBufferManager and provides "static" 241 * Buffers to RenderSteps and other Context-lifetime-tied objects, where the Buffers' contents will 242 * not change and can benefit from prioritizing GPU reads. The assumed use case is that they remain 243 * read-only on the GPU as well, so a single static buffer can be shared by all Recorders. 244 * 245 * Unlike DrawBufferManager's getXWriter() functions that return both a Writer and a BindBufferInfo, 246 * StaticBufferManager returns only a Writer and accepts a BindBufferInfo* as an argument. This will 247 * be re-written with the final binding info for the GPU-private data once that can be determined 248 * after *all* static buffers have been requested. 249 */ 250 class StaticBufferManager { 251 public: 252 StaticBufferManager(ResourceProvider*, const Caps*); 253 ~StaticBufferManager(); 254 255 // The passed in BindBufferInfos are updated when finalize() is later called, to point to the 256 // packed, GPU-private buffer at the appropriate offset. The data written to the returned Writer 257 // is copied to the private buffer at that offset. 'binding' must live until finalize() returns. 258 VertexWriter getVertexWriter(size_t size, BindBufferInfo* binding); 259 // TODO: Update the tessellation index buffer generation functions to use an IndexWriter so this 260 // can return an IndexWriter vs. a VertexWriter that happens to just write uint16s... 261 VertexWriter getIndexWriter(size_t size, BindBufferInfo* binding); 262 263 enum class FinishResult : int { 264 kFailure, // Unable to create or copy static buffers 265 kSuccess, // Successfully created static buffers and added GPU tasks to the queue 266 kNoWork // No static buffers required, no GPU tasks add to the queue 267 }; 268 269 // Finalizes all buffers and records a copy task to compact and privatize static data. The 270 // final static buffers will become owned by the Context's GlobalCache. 271 FinishResult finalize(Context*, QueueManager*, GlobalCache*); 272 273 private: 274 struct CopyRange { 275 BindBufferInfo fSource; // The CPU-to-GPU buffer and offset for the source of the copy 276 BindBufferInfo* fTarget; // The late-assigned destination of the copy 277 }; 278 struct BufferInfo { 279 BufferInfo(BufferType type, const Caps* caps); 280 281 bool createAndUpdateBindings(ResourceProvider*, 282 Context*, 283 QueueManager*, 284 GlobalCache*, 285 std::string_view label) const; resetBufferInfo286 void reset() { 287 fData.clear(); 288 fTotalRequiredBytes = 0; 289 } 290 291 const BufferType fBufferType; 292 const uint32_t fAlignment; 293 294 skia_private::TArray<CopyRange> fData; 295 uint32_t fTotalRequiredBytes; 296 }; 297 298 void* prepareStaticData(BufferInfo* info, size_t requiredBytes, BindBufferInfo* target); 299 300 ResourceProvider* const fResourceProvider; 301 UploadBufferManager fUploadManager; 302 const uint32_t fRequiredTransferAlignment; 303 304 // The source data that's copied into a final GPU-private buffer 305 BufferInfo fVertexBufferInfo; 306 BufferInfo fIndexBufferInfo; 307 308 // If mapping failed on Buffers created/managed by this StaticBufferManager or by the mapped 309 // transfer buffers from the UploadManager, remember so that finalize() will fail. 310 bool fMappingFailed = false; 311 }; 312 313 } // namespace skgpu::graphite 314 315 #endif // skgpu_graphite_BufferManager_DEFINED 316