• 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 #include "src/gpu/graphite/BufferManager.h"
9 
10 #include "include/gpu/graphite/Recording.h"
11 #include "src/gpu/graphite/Buffer.h"
12 #include "src/gpu/graphite/Caps.h"
13 #include "src/gpu/graphite/ContextPriv.h"
14 #include "src/gpu/graphite/CopyTask.h"
15 #include "src/gpu/graphite/Log.h"
16 #include "src/gpu/graphite/QueueManager.h"
17 #include "src/gpu/graphite/RecordingPriv.h"
18 #include "src/gpu/graphite/ResourceProvider.h"
19 #include "src/gpu/graphite/SharedContext.h"
20 
21 namespace skgpu::graphite {
22 
23 namespace {
24 
25 // TODO: Tune these values on real world data
26 static constexpr size_t kVertexBufferSize = 16 << 10; // 16 KB
27 static constexpr size_t kIndexBufferSize =   2 << 10; //  2 KB
28 static constexpr size_t kUniformBufferSize = 2 << 10; //  2 KB
29 static constexpr size_t kStorageBufferSize = 2 << 10; //  2 KB
30 
31 // TODO: Is it better to keep this above the max data size so we typically have one transfer buffer
32 // allocation? Or have it line up with kVertexBufferSize so if we end up needing to use transfer
33 // buffers for dynamic vertex data we can just reuse the first one?
34 static constexpr size_t kStaticTransferBufferSize = 2 << 10; // 2 KB
35 
36 // The limit for all data created by the StaticBufferManager. This data remains alive for
37 // the entire SharedContext so we want to keep it small and give a concrete upper bound to
38 // clients for our steady-state memory usage.
39 // FIXME The current usage is 4732 bytes across static vertex and index buffers, but that includes
40 // multiple copies of tessellation data, and an unoptimized AnalyticRRect mesh. Once those issues
41 // are addressed, we can tighten this and decide on the transfer buffer sizing as well.
42 [[maybe_unused]] static constexpr size_t kMaxStaticDataSize = 6 << 10;
43 
sufficient_block_size(size_t requiredBytes,size_t blockSize)44 size_t sufficient_block_size(size_t requiredBytes, size_t blockSize) {
45     // Always request a buffer at least 'requiredBytes', but keep them in multiples of
46     // 'blockSize' for improved reuse.
47     static constexpr size_t kMaxSize   = std::numeric_limits<size_t>::max();
48     size_t maxBlocks = kMaxSize / blockSize;
49     size_t blocks = (requiredBytes / blockSize) + 1;
50     size_t bufferSize = blocks > maxBlocks ? kMaxSize : (blocks * blockSize);
51     SkASSERT(requiredBytes < bufferSize);
52     return bufferSize;
53 }
54 
can_fit(size_t requestedSize,Buffer * buffer,size_t currentOffset,size_t alignment)55 bool can_fit(size_t requestedSize,
56              Buffer* buffer,
57              size_t currentOffset,
58              size_t alignment) {
59     size_t startOffset = SkAlignTo(currentOffset, alignment);
60     return requestedSize <= (buffer->size() - startOffset);
61 }
62 
starting_alignment(BufferType type,bool useTransferBuffers,const Caps * caps)63 size_t starting_alignment(BufferType type, bool useTransferBuffers, const Caps* caps) {
64     // Both vertex and index data is aligned to 4 bytes by default
65     size_t alignment = 4;
66     if (type == BufferType::kUniform) {
67         alignment = caps->requiredUniformBufferAlignment();
68     } else if (type == BufferType::kStorage || type == BufferType::kVertexStorage ||
69                type == BufferType::kIndexStorage || type == BufferType::kIndirect) {
70         alignment = caps->requiredStorageBufferAlignment();
71     }
72     if (useTransferBuffers) {
73         alignment = std::max(alignment, caps->requiredTransferBufferAlignment());
74     }
75     return alignment;
76 }
77 
78 } // anonymous namespace
79 
80 // ------------------------------------------------------------------------------------------------
81 // DrawBufferManager
82 
DrawBufferManager(ResourceProvider * resourceProvider,const Caps * caps)83 DrawBufferManager::DrawBufferManager(ResourceProvider* resourceProvider, const Caps* caps)
84         : fResourceProvider(resourceProvider)
85         , fCaps(caps)
86         , fCurrentBuffers{{
87                 { BufferType::kVertex,        kVertexBufferSize,  caps },
88                 { BufferType::kIndex,         kIndexBufferSize,   caps },
89                 { BufferType::kUniform,       kUniformBufferSize, caps },
90                 { BufferType::kStorage,       kStorageBufferSize, caps },  // mapped storage
91                 { BufferType::kStorage,       kStorageBufferSize, caps },  // GPU-only storage
92                 { BufferType::kVertexStorage, kVertexBufferSize,  caps },
93                 { BufferType::kIndexStorage,  kIndexBufferSize,   caps },
94                 { BufferType::kIndirect,      kStorageBufferSize, caps } }} {}
95 
~DrawBufferManager()96 DrawBufferManager::~DrawBufferManager() {}
97 
98 // For simplicity, if transfer buffers are being used, we align the data to the max alignment of
99 // either the final buffer type or cpu->gpu transfer alignment so that the buffers are laid out
100 // the same in memory.
BufferInfo(BufferType type,size_t blockSize,const Caps * caps)101 DrawBufferManager::BufferInfo::BufferInfo(BufferType type, size_t blockSize, const Caps* caps)
102         : fType(type)
103         , fStartAlignment(starting_alignment(type, !caps->drawBufferCanBeMapped(), caps))
104         , fBlockSize(SkAlignTo(blockSize, fStartAlignment)) {}
105 
getVertexWriter(size_t requiredBytes)106 std::tuple<VertexWriter, BindBufferInfo> DrawBufferManager::getVertexWriter(size_t requiredBytes) {
107     if (!requiredBytes) {
108         return {};
109     }
110 
111     auto& info = fCurrentBuffers[kVertexBufferIndex];
112     auto [ptr, bindInfo] = this->prepareMappedBindBuffer(&info, requiredBytes);
113     if (!ptr) {
114         return {};
115     }
116 
117     return {VertexWriter(ptr, requiredBytes), bindInfo};
118 }
119 
returnVertexBytes(size_t unusedBytes)120 void DrawBufferManager::returnVertexBytes(size_t unusedBytes) {
121     SkASSERT(fCurrentBuffers[kVertexBufferIndex].fOffset >= unusedBytes);
122     fCurrentBuffers[kVertexBufferIndex].fOffset -= unusedBytes;
123 }
124 
getIndexWriter(size_t requiredBytes)125 std::tuple<IndexWriter, BindBufferInfo> DrawBufferManager::getIndexWriter(size_t requiredBytes) {
126     if (!requiredBytes) {
127         return {};
128     }
129 
130     auto& info = fCurrentBuffers[kIndexBufferIndex];
131     auto [ptr, bindInfo] = this->prepareMappedBindBuffer(&info, requiredBytes);
132     if (!ptr) {
133         return {};
134     }
135 
136     return {IndexWriter(ptr, requiredBytes), bindInfo};
137 }
138 
getUniformWriter(size_t requiredBytes)139 std::tuple<UniformWriter, BindBufferInfo> DrawBufferManager::getUniformWriter(
140         size_t requiredBytes) {
141     if (!requiredBytes) {
142         return {};
143     }
144 
145     auto& info = fCurrentBuffers[kUniformBufferIndex];
146     auto [ptr, bindInfo] = this->prepareMappedBindBuffer(&info, requiredBytes);
147     if (!ptr) {
148         return {};
149     }
150 
151     return {UniformWriter(ptr, requiredBytes), bindInfo};
152 }
153 
getSsboWriter(size_t requiredBytes)154 std::tuple<UniformWriter, BindBufferInfo> DrawBufferManager::getSsboWriter(size_t requiredBytes) {
155     if (!requiredBytes) {
156         return {};
157     }
158 
159     auto& info = fCurrentBuffers[kStorageBufferIndex];
160     auto [ptr, bindInfo] = this->prepareMappedBindBuffer(&info, requiredBytes);
161     if (!ptr) {
162         return {};
163     }
164     return {UniformWriter(ptr, requiredBytes), bindInfo};
165 }
166 
getMappedStorage(size_t requiredBytes)167 std::tuple<void*, BindBufferInfo> DrawBufferManager::getMappedStorage(size_t requiredBytes) {
168     if (!requiredBytes) {
169         return {};
170     }
171 
172     auto& info = fCurrentBuffers[kStorageBufferIndex];
173     return this->prepareMappedBindBuffer(&info, requiredBytes);
174 }
175 
getStorage(size_t requiredBytes)176 BindBufferInfo DrawBufferManager::getStorage(size_t requiredBytes) {
177     if (!requiredBytes) {
178         return {};
179     }
180 
181     auto& info = fCurrentBuffers[kGpuOnlyStorageBufferIndex];
182     return this->prepareBindBuffer(&info, requiredBytes);
183 }
184 
getVertexStorage(size_t requiredBytes)185 BindBufferInfo DrawBufferManager::getVertexStorage(size_t requiredBytes) {
186     if (!requiredBytes) {
187         return {};
188     }
189 
190     auto& info = fCurrentBuffers[kVertexStorageBufferIndex];
191     return this->prepareBindBuffer(&info, requiredBytes);
192 }
193 
getIndexStorage(size_t requiredBytes)194 BindBufferInfo DrawBufferManager::getIndexStorage(size_t requiredBytes) {
195     if (!requiredBytes) {
196         return {};
197     }
198 
199     auto& info = fCurrentBuffers[kIndexStorageBufferIndex];
200     return this->prepareBindBuffer(&info, requiredBytes);
201 }
202 
getIndirectStorage(size_t requiredBytes)203 BindBufferInfo DrawBufferManager::getIndirectStorage(size_t requiredBytes) {
204     if (!requiredBytes) {
205         return {};
206     }
207 
208     auto& info = fCurrentBuffers[kIndirectStorageBufferIndex];
209     return this->prepareBindBuffer(&info, requiredBytes);
210 }
211 
transferToRecording(Recording * recording)212 void DrawBufferManager::transferToRecording(Recording* recording) {
213     bool useTransferBuffer = !fCaps->drawBufferCanBeMapped();
214     for (auto& [buffer, transferBuffer] : fUsedBuffers) {
215         if (useTransferBuffer) {
216             if (transferBuffer) {
217                 SkASSERT(buffer);
218                 // A transfer buffer should always be mapped at this stage
219                 transferBuffer->unmap();
220                 recording->priv().addTask(CopyBufferToBufferTask::Make(std::move(transferBuffer),
221                                                                        std::move(buffer)));
222             }
223         } else {
224             if (buffer->isMapped()) {
225                 buffer->unmap();
226             }
227            recording->priv().addResourceRef(std::move(buffer));
228         }
229     }
230     fUsedBuffers.clear();
231 
232     // The current draw buffers have not been added to fUsedBuffers,
233     // so we need to handle them as well.
234     for (auto &info : fCurrentBuffers) {
235         if (!info.fBuffer) {
236             continue;
237         }
238         if (useTransferBuffer) {
239             if (info.fTransferBuffer) {
240                 // A transfer buffer should always be mapped at this stage
241                 info.fTransferBuffer->unmap();
242                 SkASSERT(info.fBuffer);
243                 recording->priv().addTask(CopyBufferToBufferTask::Make(
244                         std::move(info.fTransferBuffer), info.fBuffer));
245             }
246         } else {
247             if (info.fBuffer->isMapped()) {
248                 info.fBuffer->unmap();
249             }
250             recording->priv().addResourceRef(std::move(info.fBuffer));
251         }
252         info.fOffset = 0;
253     }
254 }
255 
prepareMappedBindBuffer(BufferInfo * info,size_t requiredBytes)256 std::pair<void*, BindBufferInfo> DrawBufferManager::prepareMappedBindBuffer(BufferInfo* info,
257                                                                             size_t requiredBytes) {
258     auto bindInfo = this->prepareBindBuffer(info, requiredBytes, /*mappable=*/true);
259     if (!bindInfo) {
260         return {nullptr, {}};
261     }
262 
263     void* ptr = SkTAddOffset<void>(info->getMappableBuffer()->map(),
264                                    static_cast<ptrdiff_t>(bindInfo.fOffset));
265     return {ptr, bindInfo};
266 }
267 
prepareBindBuffer(BufferInfo * info,size_t requiredBytes,bool mappable)268 BindBufferInfo DrawBufferManager::prepareBindBuffer(BufferInfo* info,
269                                                     size_t requiredBytes,
270                                                     bool mappable) {
271     SkASSERT(info);
272     SkASSERT(requiredBytes);
273 
274     bool useTransferBuffer = mappable && !fCaps->drawBufferCanBeMapped();
275 
276     if (info->fBuffer &&
277         !can_fit(requiredBytes, info->fBuffer.get(), info->fOffset, info->fStartAlignment)) {
278         SkASSERT(!info->fTransferBuffer || info->fBuffer->size() == info->fTransferBuffer->size());
279         fUsedBuffers.emplace_back(std::move(info->fBuffer), std::move(info->fTransferBuffer));
280     }
281 
282     if (!info->fBuffer) {
283         size_t bufferSize = sufficient_block_size(requiredBytes, info->fBlockSize);
284         info->fBuffer = fResourceProvider->findOrCreateBuffer(
285                 bufferSize,
286                 info->fType,
287                 useTransferBuffer ? PrioritizeGpuReads::kYes : PrioritizeGpuReads::kNo);
288         info->fOffset = 0;
289         if (!info->fBuffer) {
290             return {};
291         }
292     }
293 
294     if (useTransferBuffer && !info->fTransferBuffer) {
295         info->fTransferBuffer = fResourceProvider->findOrCreateBuffer(
296                 info->fBuffer->size(),
297                 BufferType::kXferCpuToGpu,
298                 PrioritizeGpuReads::kNo);
299         SkASSERT(info->fBuffer->size() == info->fTransferBuffer->size());
300         SkASSERT(info->fOffset == 0);
301         if (!info->fTransferBuffer) {
302             return {};
303         }
304     }
305 
306     info->fOffset = SkAlignTo(info->fOffset, info->fStartAlignment);
307     BindBufferInfo bindInfo{info->fBuffer.get(), info->fOffset};
308     info->fOffset += requiredBytes;
309 
310     return bindInfo;
311 }
312 
313 // ------------------------------------------------------------------------------------------------
314 // StaticBufferManager
315 
StaticBufferManager(ResourceProvider * resourceProvider,const Caps * caps)316 StaticBufferManager::StaticBufferManager(ResourceProvider* resourceProvider,
317                                          const Caps* caps)
318         : fResourceProvider(resourceProvider)
319         , fVertexBufferInfo(BufferType::kVertex, caps)
320         , fIndexBufferInfo(BufferType::kIndex, caps)
321         , fCurrentTransferBuffer(nullptr)
322         , fCurrentOffset(0) {}
323 StaticBufferManager::~StaticBufferManager() = default;
324 
BufferInfo(BufferType type,const Caps * caps)325 StaticBufferManager::BufferInfo::BufferInfo(BufferType type, const Caps* caps)
326         : fBufferType(type)
327         , fAlignment(starting_alignment(type, /*useTransferBuffers=*/true, caps))
328         , fTotalRequiredBytes(0) {}
329 
getVertexWriter(size_t size,BindBufferInfo * binding)330 VertexWriter StaticBufferManager::getVertexWriter(size_t size, BindBufferInfo* binding) {
331     void* data = this->prepareStaticData(&fVertexBufferInfo, size, binding);
332     return VertexWriter{data, size};
333 }
334 
getIndexWriter(size_t size,BindBufferInfo * binding)335 VertexWriter StaticBufferManager::getIndexWriter(size_t size, BindBufferInfo* binding) {
336     void* data = this->prepareStaticData(&fIndexBufferInfo, size, binding);
337     return VertexWriter{data, size};
338 }
339 
prepareStaticData(BufferInfo * info,size_t size,BindBufferInfo * target)340 void* StaticBufferManager::prepareStaticData(BufferInfo* info,
341                                              size_t size,
342                                              BindBufferInfo* target) {
343     // Zero-out the target binding in the event of any failure in actually transfering data later.
344     SkASSERT(target);
345     *target = {nullptr, 0};
346     if (!size) {
347         return nullptr;
348     }
349 
350     // Both the transfer buffer and static buffers are aligned to the max required alignment for
351     // the pair of buffer types involved (transfer cpu->gpu and either index or vertex). Copies
352     // must also copy an aligned amount of bytes.
353     size = SkAlignTo(size, info->fAlignment);
354     if (fCurrentTransferBuffer &&
355         !can_fit(size, fCurrentTransferBuffer.get(), fCurrentOffset, info->fAlignment)) {
356         fCurrentTransferBuffer->unmap();
357         fUsedBuffers.push_back(std::move(fCurrentTransferBuffer));
358     }
359     if (!fCurrentTransferBuffer) {
360         size_t bufferSize = sufficient_block_size(size, kStaticTransferBufferSize);
361         fCurrentTransferBuffer = fResourceProvider->findOrCreateBuffer(
362                 bufferSize,
363                 BufferType::kXferCpuToGpu,
364                 PrioritizeGpuReads::kNo);
365         fCurrentOffset = 0;
366     }
367 
368     fCurrentOffset = SkAlignTo(fCurrentOffset, info->fAlignment);
369     info->fData.push_back({BindBufferInfo{fCurrentTransferBuffer.get(), fCurrentOffset},
370                            target, size});
371     void* ptr = SkTAddOffset<void>(fCurrentTransferBuffer->map(),
372                                    static_cast<ptrdiff_t>(fCurrentOffset));
373     fCurrentOffset += size;
374     info->fTotalRequiredBytes += size;
375     return ptr;
376 }
377 
createAndUpdateBindings(ResourceProvider * resourceProvider,Context * context,QueueManager * queueManager,GlobalCache * globalCache) const378 bool StaticBufferManager::BufferInfo::createAndUpdateBindings(
379         ResourceProvider* resourceProvider,
380         Context* context,
381         QueueManager* queueManager,
382         GlobalCache* globalCache) const {
383     if (!fTotalRequiredBytes) {
384         return true; // No buffer needed
385     }
386 
387     sk_sp<Buffer> staticBuffer = resourceProvider->findOrCreateBuffer(
388             fTotalRequiredBytes,
389             fBufferType,
390             PrioritizeGpuReads::kYes);
391     if (!staticBuffer) {
392         SKGPU_LOG_E("Failed to create static buffer for type %d of size %zu bytes.\n",
393                     (int) fBufferType, fTotalRequiredBytes);
394         return false;
395     }
396 
397     size_t offset = 0;
398     for (const CopyRange& data : fData) {
399         // Each copy range's size should be aligned to the max of the required buffer alignment and
400         // the transfer alignment, so we can just increment the offset into the static buffer.
401         SkASSERT(offset % fAlignment == 0);
402         data.fTarget->fBuffer = staticBuffer.get();
403         data.fTarget->fOffset = offset;
404 
405         auto copyTask = CopyBufferToBufferTask::Make(
406                 sk_ref_sp(data.fSource.fBuffer), data.fSource.fOffset,
407                 sk_ref_sp(data.fTarget->fBuffer), data.fTarget->fOffset,
408                 data.fSize);
409         if (!queueManager->addTask(copyTask.get(), context)) {
410             SKGPU_LOG_E("Failed to copy data to static buffer.\n");
411             return false;
412         }
413 
414         offset += data.fSize;
415     }
416 
417     SkASSERT(offset == fTotalRequiredBytes);
418     globalCache->addStaticResource(std::move(staticBuffer));
419     return true;
420 }
421 
finalize(Context * context,QueueManager * queueManager,GlobalCache * globalCache)422 StaticBufferManager::FinishResult StaticBufferManager::finalize(Context* context,
423                                                                 QueueManager* queueManager,
424                                                                 GlobalCache* globalCache) {
425     // Used buffers were already unmapped, but we're also done with the current transfer buffer
426     if (fCurrentTransferBuffer) {
427         fCurrentTransferBuffer->unmap();
428     }
429 
430     const size_t totalRequiredBytes = fVertexBufferInfo.fTotalRequiredBytes +
431                                       fIndexBufferInfo.fTotalRequiredBytes;
432     SkASSERT(totalRequiredBytes <= kMaxStaticDataSize);
433     if (!totalRequiredBytes) {
434         return FinishResult::kNoWork;
435     }
436 
437     if (!fVertexBufferInfo.createAndUpdateBindings(fResourceProvider, context,
438                                                    queueManager, globalCache)) {
439         return FinishResult::kFailure;
440     }
441     if (!fIndexBufferInfo.createAndUpdateBindings(fResourceProvider, context,
442                                                   queueManager, globalCache)) {
443         return FinishResult::kFailure;
444     }
445 
446     // Reset the static buffer manager since the Recording's copy tasks now manage ownership of
447     // the transfer buffers and the GlobalCache owns the final static buffers.
448     fCurrentTransferBuffer = nullptr;
449     fCurrentOffset = 0;
450     fUsedBuffers.clear();
451     fVertexBufferInfo.reset();
452     fIndexBufferInfo.reset();
453 
454     return FinishResult::kSuccess;
455 }
456 
457 } // namespace skgpu::graphite
458