xref: /aosp_15_r20/external/skia/src/gpu/graphite/BufferManager.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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 #include "src/gpu/graphite/BufferManager.h"
9 
10 #include "include/gpu/graphite/Recording.h"
11 #include "src/gpu/graphite/Caps.h"
12 #include "src/gpu/graphite/ContextPriv.h"
13 #include "src/gpu/graphite/Log.h"
14 #include "src/gpu/graphite/QueueManager.h"
15 #include "src/gpu/graphite/RecordingPriv.h"
16 #include "src/gpu/graphite/ResourceProvider.h"
17 #include "src/gpu/graphite/SharedContext.h"
18 #include "src/gpu/graphite/UploadBufferManager.h"
19 #include "src/gpu/graphite/task/ClearBuffersTask.h"
20 #include "src/gpu/graphite/task/CopyTask.h"
21 #include "src/gpu/graphite/task/TaskList.h"
22 
23 #include <limits>
24 
25 namespace skgpu::graphite {
26 
27 namespace {
28 
29 // TODO: Tune these values on real world data
30 static constexpr uint32_t kVertexBufferMinSize = 16 << 10; // 16 KB
31 static constexpr uint32_t kVertexBufferMaxSize =  1 << 20; //  1 MB
32 static constexpr uint32_t kIndexBufferSize   = 2 << 10; // 2 KB
33 static constexpr uint32_t kUniformBufferSize = 2 << 10; // 2 KB
34 static constexpr uint32_t kStorageBufferMinSize = 2 << 10; // 2 KB
35 static constexpr uint32_t kStorageBufferMaxSize = 1 << 20; // 1 MB
36 
37 // Make sure the buffer size constants are all powers of two, so we can align to them efficiently
38 // when dynamically sizing buffers.
39 static_assert(SkIsPow2(kVertexBufferMinSize));
40 static_assert(SkIsPow2(kVertexBufferMaxSize));
41 static_assert(SkIsPow2(kIndexBufferSize));
42 static_assert(SkIsPow2(kUniformBufferSize));
43 static_assert(SkIsPow2(kStorageBufferMinSize));
44 static_assert(SkIsPow2(kStorageBufferMaxSize));
45 
46 // The limit for all data created by the StaticBufferManager. This data remains alive for
47 // the entire SharedContext so we want to keep it small and give a concrete upper bound to
48 // clients for our steady-state memory usage.
49 // FIXME The current usage is 4732 bytes across static vertex and index buffers, but that includes
50 // multiple copies of tessellation data, and an unoptimized AnalyticRRect mesh. Once those issues
51 // are addressed, we can tighten this and decide on the transfer buffer sizing as well.
52 [[maybe_unused]] static constexpr uint32_t kMaxStaticDataSize = 6 << 10;
53 
validate_count_and_stride(size_t count,size_t stride)54 uint32_t validate_count_and_stride(size_t count, size_t stride) {
55     // size_t may just be uint32_t, so this ensures we have enough bits to do
56     // compute the required byte product.
57     uint64_t count64 = SkTo<uint64_t>(count);
58     uint64_t stride64 = SkTo<uint64_t>(stride);
59     uint64_t bytes64 = count64*stride64;
60     if (count64 > std::numeric_limits<uint32_t>::max() ||
61         stride64 > std::numeric_limits<uint32_t>::max() ||
62         bytes64 > std::numeric_limits<uint32_t>::max()) {
63         // Return 0 to skip further allocation attempts.
64         return 0;
65     }
66     // Since count64 and stride64 fit into 32-bits, their product did not overflow, and the product
67     // fits into 32-bits so this cast is safe.
68     return SkTo<uint32_t>(bytes64);
69 }
70 
validate_size(size_t requiredBytes)71 uint32_t validate_size(size_t requiredBytes) {
72     return validate_count_and_stride(1, requiredBytes);
73 }
74 
sufficient_block_size(uint32_t requiredBytes,uint32_t blockSize)75 uint32_t sufficient_block_size(uint32_t requiredBytes, uint32_t blockSize) {
76     // Always request a buffer at least 'requiredBytes', but keep them in multiples of
77     // 'blockSize' for improved reuse.
78     static constexpr uint32_t kMaxSize   = std::numeric_limits<uint32_t>::max();
79     uint32_t maxBlocks = kMaxSize / blockSize;
80     uint32_t blocks = (requiredBytes / blockSize) + 1;
81     uint32_t bufferSize = blocks > maxBlocks ? kMaxSize : (blocks * blockSize);
82     SkASSERT(requiredBytes < bufferSize);
83     return bufferSize;
84 }
85 
can_fit(uint32_t requestedSize,uint32_t allocatedSize,uint32_t currentOffset,uint32_t alignment)86 bool can_fit(uint32_t requestedSize,
87              uint32_t allocatedSize,
88              uint32_t currentOffset,
89              uint32_t alignment) {
90     uint32_t startOffset = SkAlignTo(currentOffset, alignment);
91     return requestedSize <= (allocatedSize - startOffset);
92 }
93 
starting_alignment(BufferType type,bool useTransferBuffers,const Caps * caps)94 uint32_t starting_alignment(BufferType type, bool useTransferBuffers, const Caps* caps) {
95     // Both vertex and index data is aligned to 4 bytes by default
96     uint32_t alignment = 4;
97     if (type == BufferType::kUniform) {
98         alignment = SkTo<uint32_t>(caps->requiredUniformBufferAlignment());
99     } else if (type == BufferType::kStorage || type == BufferType::kVertexStorage ||
100                type == BufferType::kIndexStorage || type == BufferType::kIndirect) {
101         alignment = SkTo<uint32_t>(caps->requiredStorageBufferAlignment());
102     }
103     if (useTransferBuffers) {
104         alignment = std::max(alignment, SkTo<uint32_t>(caps->requiredTransferBufferAlignment()));
105     }
106     return alignment;
107 }
108 
109 } // anonymous namespace
110 
111 // ------------------------------------------------------------------------------------------------
112 // ScratchBuffer
113 
ScratchBuffer(uint32_t size,uint32_t alignment,sk_sp<Buffer> buffer,DrawBufferManager * owner)114 ScratchBuffer::ScratchBuffer(uint32_t size, uint32_t alignment,
115                              sk_sp<Buffer> buffer, DrawBufferManager* owner)
116         : fSize(size)
117         , fAlignment(alignment)
118         , fBuffer(std::move(buffer))
119         , fOwner(owner) {
120     SkASSERT(fSize > 0);
121     SkASSERT(fBuffer);
122     SkASSERT(fOwner);
123     SkASSERT(fSize <= fBuffer->size());
124 }
125 
~ScratchBuffer()126 ScratchBuffer::~ScratchBuffer() { this->returnToPool(); }
127 
suballocate(size_t requiredBytes)128 BindBufferInfo ScratchBuffer::suballocate(size_t requiredBytes) {
129     const uint32_t requiredBytes32 = validate_size(requiredBytes);
130     if (!this->isValid() || !requiredBytes32) {
131         return {};
132     }
133     if (!can_fit(requiredBytes32, fSize, fOffset, fAlignment)) {
134         return {};
135     }
136     const uint32_t offset = SkAlignTo(fOffset, fAlignment);
137     fOffset = offset + requiredBytes32;
138     return {fBuffer.get(), offset, requiredBytes32};
139 }
140 
returnToPool()141 void ScratchBuffer::returnToPool() {
142     if (fOwner && fBuffer) {
143         // TODO: Generalize the pool to other buffer types.
144         fOwner->fReusableScratchStorageBuffers.push_back(std::move(fBuffer));
145         SkASSERT(!fBuffer);
146     }
147 }
148 
149 // ------------------------------------------------------------------------------------------------
150 // DrawBufferManager
151 
DrawBufferManager(ResourceProvider * resourceProvider,const Caps * caps,UploadBufferManager * uploadManager)152 DrawBufferManager::DrawBufferManager(ResourceProvider* resourceProvider,
153                                      const Caps* caps,
154                                      UploadBufferManager* uploadManager)
155         : fResourceProvider(resourceProvider)
156         , fCaps(caps)
157         , fUploadManager(uploadManager)
158         , fCurrentBuffers{{
159             { BufferType::kVertex,        kVertexBufferMinSize,  kVertexBufferMaxSize,  caps },
160             { BufferType::kIndex,         kIndexBufferSize,      kIndexBufferSize,      caps },
161             { BufferType::kUniform,       kUniformBufferSize,    kUniformBufferSize,    caps },
162 
163             // mapped storage
164             { BufferType::kStorage,       kStorageBufferMinSize, kStorageBufferMaxSize, caps },
165             // GPU-only storage
166             { BufferType::kStorage,       kStorageBufferMinSize, kStorageBufferMinSize, caps },
167 
168             { BufferType::kVertexStorage, kVertexBufferMinSize,  kVertexBufferMinSize,  caps },
169             { BufferType::kIndexStorage,  kIndexBufferSize,      kIndexBufferSize,      caps },
170             { BufferType::kIndirect,      kStorageBufferMinSize, kStorageBufferMinSize, caps } }} {}
171 
~DrawBufferManager()172 DrawBufferManager::~DrawBufferManager() {}
173 
174 // For simplicity, if transfer buffers are being used, we align the data to the max alignment of
175 // either the final buffer type or cpu->gpu transfer alignment so that the buffers are laid out
176 // the same in memory.
BufferInfo(BufferType type,uint32_t minBlockSize,uint32_t maxBlockSize,const Caps * caps)177 DrawBufferManager::BufferInfo::BufferInfo(BufferType type,
178                                           uint32_t minBlockSize,
179                                           uint32_t maxBlockSize,
180                                           const Caps* caps)
181         : fType(type)
182         , fStartAlignment(starting_alignment(type, !caps->drawBufferCanBeMapped(), caps))
183         , fMinBlockSize(minBlockSize)
184         , fMaxBlockSize(maxBlockSize)
185         , fCurBlockSize(SkAlignTo(minBlockSize, fStartAlignment)) {}
186 
getVertexWriter(size_t count,size_t stride)187 std::pair<VertexWriter, BindBufferInfo> DrawBufferManager::getVertexWriter(size_t count,
188                                                                            size_t stride) {
189     uint32_t requiredBytes = validate_count_and_stride(count, stride);
190     if (!requiredBytes) {
191         return {};
192     }
193 
194     auto& info = fCurrentBuffers[kVertexBufferIndex];
195     auto [ptr, bindInfo] = this->prepareMappedBindBuffer(&info, "VertexBuffer", requiredBytes);
196     return {VertexWriter(ptr, requiredBytes), bindInfo};
197 }
198 
returnVertexBytes(size_t unusedBytes)199 void DrawBufferManager::returnVertexBytes(size_t unusedBytes) {
200     if (fMappingFailed) {
201         // The caller can be unaware that the written data went to no-where and will still call
202         // this function.
203         return;
204     }
205     SkASSERT(fCurrentBuffers[kVertexBufferIndex].fOffset >= unusedBytes);
206     fCurrentBuffers[kVertexBufferIndex].fOffset -= unusedBytes;
207 }
208 
getIndexWriter(size_t count,size_t stride)209 std::pair<IndexWriter, BindBufferInfo> DrawBufferManager::getIndexWriter(size_t count,
210                                                                          size_t stride) {
211     uint32_t requiredBytes = validate_count_and_stride(count, stride);
212     if (!requiredBytes) {
213         return {};
214     }
215 
216     auto& info = fCurrentBuffers[kIndexBufferIndex];
217     auto [ptr, bindInfo] = this->prepareMappedBindBuffer(&info, "IndexBuffer", requiredBytes);
218     return {IndexWriter(ptr, requiredBytes), bindInfo};
219 }
220 
getUniformWriter(size_t count,size_t stride)221 std::pair<UniformWriter, BindBufferInfo> DrawBufferManager::getUniformWriter(size_t count,
222                                                                              size_t stride) {
223     uint32_t requiredBytes = validate_count_and_stride(count, stride);
224     if (!requiredBytes) {
225         return {};
226     }
227 
228     auto& info = fCurrentBuffers[kUniformBufferIndex];
229     auto [ptr, bindInfo] = this->prepareMappedBindBuffer(&info, "UniformBuffer", requiredBytes);
230     return {UniformWriter(ptr, requiredBytes), bindInfo};
231 }
232 
getSsboWriter(size_t count,size_t stride,size_t alignment)233 std::pair<UniformWriter, BindBufferInfo> DrawBufferManager::getSsboWriter(size_t count,
234                                                                           size_t stride,
235                                                                           size_t alignment) {
236     uint32_t requiredBytes = validate_count_and_stride(count, stride);
237     if (!requiredBytes) {
238         return {};
239     }
240 
241     auto& info = fCurrentBuffers[kStorageBufferIndex];
242     auto [ptr, bindInfo] =
243             this->prepareMappedBindBuffer(&info, "StorageBuffer", requiredBytes, alignment);
244     return {UniformWriter(ptr, requiredBytes), bindInfo};
245 }
246 
getSsboWriter(size_t count,size_t stride)247 std::pair<UniformWriter, BindBufferInfo> DrawBufferManager::getSsboWriter(size_t count,
248                                                                           size_t stride) {
249     // By setting alignment=0, use the default buffer alignment requirement for storage buffers.
250     return this->getSsboWriter(count, stride, /*alignment=*/0);
251 }
252 
getAlignedSsboWriter(size_t count,size_t stride)253 std::pair<UniformWriter, BindBufferInfo> DrawBufferManager::getAlignedSsboWriter(size_t count,
254                                                                                  size_t stride) {
255     // Align to the provided element stride.
256     return this->getSsboWriter(count, stride, stride);
257 }
258 
getUniformPointer(size_t requiredBytes)259 std::pair<void* /*mappedPtr*/, BindBufferInfo> DrawBufferManager::getUniformPointer(
260             size_t requiredBytes) {
261     uint32_t requiredBytes32 = validate_size(requiredBytes);
262     if (!requiredBytes32) {
263         return {};
264     }
265 
266     auto& info = fCurrentBuffers[kUniformBufferIndex];
267     return this->prepareMappedBindBuffer(&info, "UniformBuffer", requiredBytes32);
268 }
269 
getStoragePointer(size_t requiredBytes)270 std::pair<void* /*mappedPtr*/, BindBufferInfo> DrawBufferManager::getStoragePointer(
271         size_t requiredBytes) {
272     uint32_t requiredBytes32 = validate_size(requiredBytes);
273     if (!requiredBytes32) {
274         return {};
275     }
276 
277     auto& info = fCurrentBuffers[kStorageBufferIndex];
278     return this->prepareMappedBindBuffer(&info, "StorageBuffer", requiredBytes32);
279 }
280 
getStorage(size_t requiredBytes,ClearBuffer cleared)281 BindBufferInfo DrawBufferManager::getStorage(size_t requiredBytes, ClearBuffer cleared) {
282     uint32_t requiredBytes32 = validate_size(requiredBytes);
283     if (!requiredBytes32) {
284         return {};
285     }
286 
287     auto& info = fCurrentBuffers[kGpuOnlyStorageBufferIndex];
288     return this->prepareBindBuffer(&info,
289                                    "StorageBuffer",
290                                    requiredBytes32,
291                                    /*requiredAlignment=*/0,
292                                    /*supportCpuUpload=*/false,
293                                    cleared);
294 }
295 
getVertexStorage(size_t requiredBytes)296 BindBufferInfo DrawBufferManager::getVertexStorage(size_t requiredBytes) {
297     uint32_t requiredBytes32 = validate_size(requiredBytes);
298     if (!requiredBytes32) {
299         return {};
300     }
301 
302     auto& info = fCurrentBuffers[kVertexStorageBufferIndex];
303     return this->prepareBindBuffer(&info, "VertexStorageBuffer", requiredBytes32);
304 }
305 
getIndexStorage(size_t requiredBytes)306 BindBufferInfo DrawBufferManager::getIndexStorage(size_t requiredBytes) {
307     uint32_t requiredBytes32 = validate_size(requiredBytes);
308     if (!requiredBytes32) {
309         return {};
310     }
311 
312     auto& info = fCurrentBuffers[kIndexStorageBufferIndex];
313     return this->prepareBindBuffer(&info, "IndexStorageBuffer", requiredBytes32);
314 }
315 
getIndirectStorage(size_t requiredBytes,ClearBuffer cleared)316 BindBufferInfo DrawBufferManager::getIndirectStorage(size_t requiredBytes, ClearBuffer cleared) {
317     uint32_t requiredBytes32 = validate_size(requiredBytes);
318     if (!requiredBytes32) {
319         return {};
320     }
321 
322     auto& info = fCurrentBuffers[kIndirectStorageBufferIndex];
323     return this->prepareBindBuffer(&info,
324                                    "IndirectStorageBuffer",
325                                    requiredBytes32,
326                                    /*requiredAlignment=*/0,
327                                    /*supportCpuUpload=*/false,
328                                    cleared);
329 }
330 
getScratchStorage(size_t requiredBytes)331 ScratchBuffer DrawBufferManager::getScratchStorage(size_t requiredBytes) {
332     uint32_t requiredBytes32 = validate_size(requiredBytes);
333     if (!requiredBytes32 || fMappingFailed) {
334         return {};
335     }
336 
337     // TODO: Generalize the pool to other buffer types.
338     auto& info = fCurrentBuffers[kStorageBufferIndex];
339     uint32_t bufferSize = sufficient_block_size(requiredBytes32, info.fCurBlockSize);
340     sk_sp<Buffer> buffer = this->findReusableSbo(bufferSize);
341     if (!buffer) {
342         buffer = fResourceProvider->findOrCreateBuffer(
343                 bufferSize, BufferType::kStorage, AccessPattern::kGpuOnly, "ScratchStorageBuffer");
344 
345         if (!buffer) {
346             this->onFailedBuffer();
347             return {};
348         }
349     }
350     return {requiredBytes32, info.fStartAlignment, std::move(buffer), this};
351 }
352 
onFailedBuffer()353 void DrawBufferManager::onFailedBuffer() {
354     fMappingFailed = true;
355 
356     // Clean up and unmap everything now
357     fClearList.clear();
358     fReusableScratchStorageBuffers.clear();
359 
360     for (auto& [buffer, _] : fUsedBuffers) {
361         if (buffer->isMapped()) {
362             buffer->unmap();
363         }
364     }
365     fUsedBuffers.clear();
366 
367     for (auto& info : fCurrentBuffers) {
368         if (info.fBuffer && info.fBuffer->isMapped()) {
369             info.fBuffer->unmap();
370         }
371         info.fBuffer = nullptr;
372         info.fTransferBuffer = {};
373         info.fOffset = 0;
374     }
375 }
376 
transferToRecording(Recording * recording)377 bool DrawBufferManager::transferToRecording(Recording* recording) {
378     if (fMappingFailed) {
379         // All state should have been reset by onFailedBuffer() except for this error flag.
380         SkASSERT(fUsedBuffers.empty() &&
381                  fClearList.empty() &&
382                  fReusableScratchStorageBuffers.empty());
383         fMappingFailed = false;
384         return false;
385     }
386 
387     if (!fClearList.empty()) {
388         recording->priv().taskList()->add(ClearBuffersTask::Make(std::move(fClearList)));
389     }
390 
391     // Transfer the buffers in the reuse pool to the recording.
392     // TODO: Allow reuse across different Recordings?
393     for (auto& buffer : fReusableScratchStorageBuffers) {
394         recording->priv().addResourceRef(std::move(buffer));
395     }
396     fReusableScratchStorageBuffers.clear();
397 
398     for (auto& [buffer, transferBuffer] : fUsedBuffers) {
399         if (transferBuffer) {
400             SkASSERT(buffer);
401             SkASSERT(!fCaps->drawBufferCanBeMapped());
402             // Since the transfer buffer is managed by the UploadManager, we don't manually unmap
403             // it here or need to pass a ref into CopyBufferToBufferTask.
404             size_t copySize = buffer->size();
405             recording->priv().taskList()->add(
406                     CopyBufferToBufferTask::Make(transferBuffer.fBuffer,
407                                                  transferBuffer.fOffset,
408                                                  std::move(buffer),
409                                                  /*dstOffset=*/0,
410                                                  copySize));
411         } else {
412             if (buffer->isMapped()) {
413                 buffer->unmap();
414             }
415             recording->priv().addResourceRef(std::move(buffer));
416         }
417     }
418     fUsedBuffers.clear();
419 
420     // The current draw buffers have not been added to fUsedBuffers,
421     // so we need to handle them as well.
422     for (auto& info : fCurrentBuffers) {
423         if (!info.fBuffer) {
424             continue;
425         }
426         if (info.fTransferBuffer) {
427             // A transfer buffer should always be mapped at this stage
428             SkASSERT(info.fBuffer);
429             SkASSERT(!fCaps->drawBufferCanBeMapped());
430             // Since the transfer buffer is managed by the UploadManager, we don't manually unmap
431             // it here or need to pass a ref into CopyBufferToBufferTask.
432             recording->priv().taskList()->add(
433                     CopyBufferToBufferTask::Make(info.fTransferBuffer.fBuffer,
434                                                  info.fTransferBuffer.fOffset,
435                                                  info.fBuffer,
436                                                  /*dstOffset=*/0,
437                                                  info.fBuffer->size()));
438         } else {
439             if (info.fBuffer->isMapped()) {
440                 info.fBuffer->unmap();
441             }
442             recording->priv().addResourceRef(std::move(info.fBuffer));
443         }
444 
445         // For each buffer type, update the block size to use for new buffers, based on the total
446         // storage used since the last flush.
447         const uint32_t reqSize = SkAlignTo(info.fUsedSize + info.fOffset, info.fMinBlockSize);
448         info.fCurBlockSize = std::clamp(reqSize, info.fMinBlockSize, info.fMaxBlockSize);
449         info.fUsedSize = 0;
450 
451         info.fTransferBuffer = {};
452         info.fOffset = 0;
453     }
454 
455     return true;
456 }
457 
prepareMappedBindBuffer(BufferInfo * info,std::string_view label,uint32_t requiredBytes,uint32_t requiredAlignment)458 std::pair<void*, BindBufferInfo> DrawBufferManager::prepareMappedBindBuffer(
459         BufferInfo* info,
460         std::string_view label,
461         uint32_t requiredBytes,
462         uint32_t requiredAlignment) {
463     BindBufferInfo bindInfo = this->prepareBindBuffer(info,
464                                                       std::move(label),
465                                                       requiredBytes,
466                                                       requiredAlignment,
467                                                       /*supportCpuUpload=*/true);
468     if (!bindInfo) {
469         // prepareBindBuffer() already called onFailedBuffer()
470         SkASSERT(fMappingFailed);
471         return {nullptr, {}};
472     }
473 
474     // If there's a transfer buffer, its mapped pointer should already have been validated
475     SkASSERT(!info->fTransferBuffer || info->fTransferMapPtr);
476     void* mapPtr = info->fTransferBuffer ? info->fTransferMapPtr : info->fBuffer->map();
477     if (!mapPtr) {
478         // Mapping a direct draw buffer failed
479         this->onFailedBuffer();
480         return {nullptr, {}};
481     }
482 
483     mapPtr = SkTAddOffset<void>(mapPtr, static_cast<ptrdiff_t>(bindInfo.fOffset));
484     return {mapPtr, bindInfo};
485 }
486 
prepareBindBuffer(BufferInfo * info,std::string_view label,uint32_t requiredBytes,uint32_t requiredAlignment,bool supportCpuUpload,ClearBuffer cleared)487 BindBufferInfo DrawBufferManager::prepareBindBuffer(BufferInfo* info,
488                                                     std::string_view label,
489                                                     uint32_t requiredBytes,
490                                                     uint32_t requiredAlignment,
491                                                     bool supportCpuUpload,
492                                                     ClearBuffer cleared) {
493     SkASSERT(info);
494     SkASSERT(requiredBytes);
495 
496     if (fMappingFailed) {
497         return {};
498     }
499 
500     // A transfer buffer is not necessary if the caller does not intend to upload CPU data to it.
501     bool useTransferBuffer = supportCpuUpload && !fCaps->drawBufferCanBeMapped();
502 
503     if (requiredAlignment == 0) {
504         // If explicitly required alignment is not provided, use the default buffer alignment.
505         requiredAlignment = info->fStartAlignment;
506 
507     } else {
508         // If an explicitly required alignment is provided, use that instead of the default buffer
509         // alignment. This is useful when the offset is used as an index into a storage buffer
510         // rather than an offset for an actual binding.
511         // We can't simply use SkAlignTo here, because that can only align to powers of two.
512         const uint32_t misalignment = info->fOffset % requiredAlignment;
513         if (misalignment > 0) {
514             info->fOffset += requiredAlignment - misalignment;
515         }
516 
517         // Don't align the offset any further.
518         requiredAlignment = 1;
519     }
520 
521     const bool overflowedBuffer =
522             info->fBuffer && (info->fOffset >= SkTo<uint32_t>(info->fBuffer->size()) ||
523                               !can_fit(requiredBytes,
524                                        SkTo<uint32_t>(info->fBuffer->size()),
525                                        info->fOffset,
526                                        requiredAlignment));
527     if (overflowedBuffer) {
528         fUsedBuffers.emplace_back(std::move(info->fBuffer), info->fTransferBuffer);
529         info->fTransferBuffer = {};
530         info->fUsedSize += info->fOffset;
531     }
532 
533     if (!info->fBuffer) {
534         // Create the first buffer with the full fCurBlockSize, but create subsequent buffers with a
535         // smaller size if fCurBlockSize has increased from the minimum. This way if we use just a
536         // little more than fCurBlockSize total storage this frame, we won't necessarily double our
537         // total storage allocation.
538         const uint32_t blockSize = overflowedBuffer
539                                            ? std::max(info->fCurBlockSize / 4, info->fMinBlockSize)
540                                            : info->fCurBlockSize;
541         const uint32_t bufferSize = sufficient_block_size(requiredBytes, blockSize);
542 
543         // This buffer can be GPU-only if
544         //     a) the caller does not intend to ever upload CPU data to the buffer; or
545         //     b) CPU data will get uploaded to fBuffer only via a transfer buffer
546         AccessPattern accessPattern = (useTransferBuffer || !supportCpuUpload)
547                                               ? AccessPattern::kGpuOnly
548                                               : AccessPattern::kHostVisible;
549 
550         info->fBuffer = fResourceProvider->findOrCreateBuffer(bufferSize,
551                                                               info->fType,
552                                                               accessPattern,
553                                                               std::move(label));
554         info->fOffset = 0;
555         if (!info->fBuffer) {
556             this->onFailedBuffer();
557             return {};
558         }
559     }
560 
561     if (useTransferBuffer && !info->fTransferBuffer) {
562         std::tie(info->fTransferMapPtr, info->fTransferBuffer) =
563                 fUploadManager->makeBindInfo(info->fBuffer->size(),
564                                              fCaps->requiredTransferBufferAlignment(),
565                                              "TransferForDataBuffer");
566 
567         if (!info->fTransferBuffer) {
568             this->onFailedBuffer();
569             return {};
570         }
571         SkASSERT(info->fTransferMapPtr);
572     }
573 
574     info->fOffset = SkAlignTo(info->fOffset, requiredAlignment);
575     BindBufferInfo bindInfo{info->fBuffer.get(), info->fOffset, requiredBytes};
576     info->fOffset += requiredBytes;
577 
578     if (cleared == ClearBuffer::kYes) {
579         fClearList.push_back(bindInfo);
580     }
581 
582     SkASSERT(info->fOffset <= info->fBuffer->size());
583     return bindInfo;
584 }
585 
findReusableSbo(size_t bufferSize)586 sk_sp<Buffer> DrawBufferManager::findReusableSbo(size_t bufferSize) {
587     SkASSERT(bufferSize);
588     SkASSERT(!fMappingFailed);
589 
590     for (int i = 0; i < fReusableScratchStorageBuffers.size(); ++i) {
591         sk_sp<Buffer>* buffer = &fReusableScratchStorageBuffers[i];
592         if ((*buffer)->size() >= bufferSize) {
593             auto found = std::move(*buffer);
594             // Fill the hole left by the move (if necessary) and shrink the pool.
595             if (i < fReusableScratchStorageBuffers.size() - 1) {
596                 *buffer = std::move(fReusableScratchStorageBuffers.back());
597             }
598             fReusableScratchStorageBuffers.pop_back();
599             return found;
600         }
601     }
602     return nullptr;
603 }
604 
605 // ------------------------------------------------------------------------------------------------
606 // StaticBufferManager
607 
StaticBufferManager(ResourceProvider * resourceProvider,const Caps * caps)608 StaticBufferManager::StaticBufferManager(ResourceProvider* resourceProvider,
609                                          const Caps* caps)
610         : fResourceProvider(resourceProvider)
611         , fUploadManager(resourceProvider, caps)
612         , fRequiredTransferAlignment(SkTo<uint32_t>(caps->requiredTransferBufferAlignment()))
613         , fVertexBufferInfo(BufferType::kVertex, caps)
614         , fIndexBufferInfo(BufferType::kIndex, caps) {}
615 StaticBufferManager::~StaticBufferManager() = default;
616 
BufferInfo(BufferType type,const Caps * caps)617 StaticBufferManager::BufferInfo::BufferInfo(BufferType type, const Caps* caps)
618         : fBufferType(type)
619         , fAlignment(starting_alignment(type, /*useTransferBuffers=*/true, caps))
620         , fTotalRequiredBytes(0) {}
621 
getVertexWriter(size_t size,BindBufferInfo * binding)622 VertexWriter StaticBufferManager::getVertexWriter(size_t size, BindBufferInfo* binding) {
623     void* data = this->prepareStaticData(&fVertexBufferInfo, size, binding);
624     return VertexWriter{data, size};
625 }
626 
getIndexWriter(size_t size,BindBufferInfo * binding)627 VertexWriter StaticBufferManager::getIndexWriter(size_t size, BindBufferInfo* binding) {
628     void* data = this->prepareStaticData(&fIndexBufferInfo, size, binding);
629     return VertexWriter{data, size};
630 }
631 
prepareStaticData(BufferInfo * info,size_t size,BindBufferInfo * target)632 void* StaticBufferManager::prepareStaticData(BufferInfo* info,
633                                              size_t size,
634                                              BindBufferInfo* target) {
635     // Zero-out the target binding in the event of any failure in actually transfering data later.
636     SkASSERT(target);
637     *target = {nullptr, 0};
638     uint32_t size32 = validate_size(size);
639     if (!size32 || fMappingFailed) {
640         return nullptr;
641     }
642 
643     // Both the transfer buffer and static buffers are aligned to the max required alignment for
644     // the pair of buffer types involved (transfer cpu->gpu and either index or vertex). Copies
645     // must also copy an aligned amount of bytes.
646     size32 = SkAlignTo(size32, info->fAlignment);
647 
648     auto [transferMapPtr, transferBindInfo] =
649             fUploadManager.makeBindInfo(size32,
650                                         fRequiredTransferAlignment,
651                                         "TransferForStaticBuffer");
652     if (!transferMapPtr) {
653         SKGPU_LOG_E("Failed to create or map transfer buffer that initializes static GPU data.");
654         fMappingFailed = true;
655         return nullptr;
656     }
657 
658     info->fData.push_back({transferBindInfo, target});
659     info->fTotalRequiredBytes += size32;
660     return transferMapPtr;
661 }
662 
createAndUpdateBindings(ResourceProvider * resourceProvider,Context * context,QueueManager * queueManager,GlobalCache * globalCache,std::string_view label) const663 bool StaticBufferManager::BufferInfo::createAndUpdateBindings(
664         ResourceProvider* resourceProvider,
665         Context* context,
666         QueueManager* queueManager,
667         GlobalCache* globalCache,
668         std::string_view label) const {
669     if (!fTotalRequiredBytes) {
670         return true; // No buffer needed
671     }
672 
673     sk_sp<Buffer> staticBuffer = resourceProvider->findOrCreateBuffer(
674             fTotalRequiredBytes, fBufferType, AccessPattern::kGpuOnly, std::move(label));
675     if (!staticBuffer) {
676         SKGPU_LOG_E("Failed to create static buffer for type %d of size %u bytes.\n",
677                     (int) fBufferType, fTotalRequiredBytes);
678         return false;
679     }
680 
681     uint32_t offset = 0;
682     for (const CopyRange& data : fData) {
683         // Each copy range's size should be aligned to the max of the required buffer alignment and
684         // the transfer alignment, so we can just increment the offset into the static buffer.
685         SkASSERT(offset % fAlignment == 0);
686         uint32_t size = data.fSource.fSize;
687         data.fTarget->fBuffer = staticBuffer.get();
688         data.fTarget->fOffset = offset;
689         data.fTarget->fSize = size;
690 
691         auto copyTask = CopyBufferToBufferTask::Make(
692                 data.fSource.fBuffer, data.fSource.fOffset,
693                 sk_ref_sp(data.fTarget->fBuffer), data.fTarget->fOffset,
694                 size);
695         // For static buffers, we want them all to be optimized as GPU only buffers. If we are in
696         // a protected context, this means the buffers must be non-protected since they will be
697         // read in the vertex shader which doesn't allow protected memory access. Thus all the
698         // uploads to these buffers must be done as non-protected commands.
699         if (!queueManager->addTask(copyTask.get(), context, Protected::kNo)) {
700             SKGPU_LOG_E("Failed to copy data to static buffer.\n");
701             return false;
702         }
703 
704         offset += size;
705     }
706 
707     SkASSERT(offset == fTotalRequiredBytes);
708     globalCache->addStaticResource(std::move(staticBuffer));
709     return true;
710 }
711 
finalize(Context * context,QueueManager * queueManager,GlobalCache * globalCache)712 StaticBufferManager::FinishResult StaticBufferManager::finalize(Context* context,
713                                                                 QueueManager* queueManager,
714                                                                 GlobalCache* globalCache) {
715     if (fMappingFailed) {
716         return FinishResult::kFailure;
717     }
718 
719     const size_t totalRequiredBytes = fVertexBufferInfo.fTotalRequiredBytes +
720                                       fIndexBufferInfo.fTotalRequiredBytes;
721     SkASSERT(totalRequiredBytes <= kMaxStaticDataSize);
722     if (!totalRequiredBytes) {
723         return FinishResult::kNoWork;
724     }
725 
726     if (!fVertexBufferInfo.createAndUpdateBindings(fResourceProvider,
727                                                    context,
728                                                    queueManager,
729                                                    globalCache,
730                                                    "StaticVertexBuffer")) {
731         return FinishResult::kFailure;
732     }
733     if (!fIndexBufferInfo.createAndUpdateBindings(fResourceProvider,
734                                                   context,
735                                                   queueManager,
736                                                   globalCache,
737                                                   "StaticIndexBuffer")) {
738         return FinishResult::kFailure;
739     }
740     queueManager->addUploadBufferManagerRefs(&fUploadManager);
741 
742     // Reset the static buffer manager since the Recording's copy tasks now manage ownership of
743     // the transfer buffers and the GlobalCache owns the final static buffers.
744     fVertexBufferInfo.reset();
745     fIndexBufferInfo.reset();
746 
747     return FinishResult::kSuccess;
748 }
749 
750 } // namespace skgpu::graphite
751