1 /*
2 * Copyright 2010 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 #include "src/gpu/ganesh/GrBufferAllocPool.h"
8
9 #include "include/gpu/ganesh/GrDirectContext.h"
10 #include "include/private/base/SkMacros.h"
11 #include "src/base/SkSafeMath.h"
12 #include "src/core/SkTraceEvent.h"
13 #include "src/gpu/ganesh/GrCaps.h"
14 #include "src/gpu/ganesh/GrCpuBuffer.h"
15 #include "src/gpu/ganesh/GrDirectContextPriv.h"
16 #include "src/gpu/ganesh/GrGpu.h"
17 #include "src/gpu/ganesh/GrGpuBuffer.h"
18 #include "src/gpu/ganesh/GrResourceProvider.h"
19
20 #include <algorithm>
21 #include <cstdint>
22 #include <cstring>
23 #include <memory>
24
Make(int maxBuffersToCache)25 sk_sp<GrBufferAllocPool::CpuBufferCache> GrBufferAllocPool::CpuBufferCache::Make(
26 int maxBuffersToCache) {
27 return sk_sp<CpuBufferCache>(new CpuBufferCache(maxBuffersToCache));
28 }
29
CpuBufferCache(int maxBuffersToCache)30 GrBufferAllocPool::CpuBufferCache::CpuBufferCache(int maxBuffersToCache)
31 : fMaxBuffersToCache(maxBuffersToCache) {
32 if (fMaxBuffersToCache) {
33 fBuffers = std::make_unique<Buffer[]>(fMaxBuffersToCache);
34 }
35 }
36
makeBuffer(size_t size,bool mustBeInitialized)37 sk_sp<GrCpuBuffer> GrBufferAllocPool::CpuBufferCache::makeBuffer(size_t size,
38 bool mustBeInitialized) {
39 SkASSERT(size > 0);
40 Buffer* result = nullptr;
41 if (size == kDefaultBufferSize) {
42 int i = 0;
43 for (; i < fMaxBuffersToCache && fBuffers[i].fBuffer; ++i) {
44 SkASSERT(fBuffers[i].fBuffer->size() == kDefaultBufferSize);
45 if (fBuffers[i].fBuffer->unique()) {
46 result = &fBuffers[i];
47 }
48 }
49 if (!result && i < fMaxBuffersToCache) {
50 fBuffers[i].fBuffer = GrCpuBuffer::Make(size);
51 result = &fBuffers[i];
52 }
53 }
54 Buffer tempResult;
55 if (!result) {
56 tempResult.fBuffer = GrCpuBuffer::Make(size);
57 result = &tempResult;
58 }
59 if (mustBeInitialized && !result->fCleared) {
60 result->fCleared = true;
61 memset(result->fBuffer->data(), 0, result->fBuffer->size());
62 }
63 return result->fBuffer;
64 }
65
releaseAll()66 void GrBufferAllocPool::CpuBufferCache::releaseAll() {
67 for (int i = 0; i < fMaxBuffersToCache && fBuffers[i].fBuffer; ++i) {
68 fBuffers[i].fBuffer.reset();
69 fBuffers[i].fCleared = false;
70 }
71 }
72
73 //////////////////////////////////////////////////////////////////////////////
74
75 #ifdef SK_DEBUG
76 #define VALIDATE validate
77 #else
VALIDATE(bool=false)78 static void VALIDATE(bool = false) {}
79 #endif
80
81 #define UNMAP_BUFFER(block) \
82 do { \
83 TRACE_EVENT_INSTANT1("skia.gpu", "GrBufferAllocPool Unmapping Buffer", \
84 TRACE_EVENT_SCOPE_THREAD, "percent_unwritten", \
85 (float)((block).fBytesFree) / (block).fBuffer->size()); \
86 SkASSERT(!block.fBuffer->isCpuBuffer()); \
87 static_cast<GrGpuBuffer*>(block.fBuffer.get())->unmap(); \
88 } while (false)
89
GrBufferAllocPool(GrGpu * gpu,GrGpuBufferType bufferType,sk_sp<CpuBufferCache> cpuBufferCache)90 GrBufferAllocPool::GrBufferAllocPool(GrGpu* gpu, GrGpuBufferType bufferType,
91 sk_sp<CpuBufferCache> cpuBufferCache)
92 : fBlocks(8)
93 , fCpuBufferCache(std::move(cpuBufferCache))
94 , fGpu(gpu)
95 , fBufferType(bufferType) {}
96
deleteBlocks()97 void GrBufferAllocPool::deleteBlocks() {
98 if (!fBlocks.empty()) {
99 GrBuffer* buffer = fBlocks.back().fBuffer.get();
100 if (!buffer->isCpuBuffer() && static_cast<GrGpuBuffer*>(buffer)->isMapped()) {
101 UNMAP_BUFFER(fBlocks.back());
102 }
103 }
104 while (!fBlocks.empty()) {
105 this->destroyBlock();
106 }
107 SkASSERT(!fBufferPtr);
108 }
109
~GrBufferAllocPool()110 GrBufferAllocPool::~GrBufferAllocPool() {
111 VALIDATE();
112 this->deleteBlocks();
113 }
114
reset()115 void GrBufferAllocPool::reset() {
116 VALIDATE();
117 fBytesInUse = 0;
118 this->deleteBlocks();
119 this->resetCpuData(0);
120 VALIDATE();
121 }
122
unmap()123 void GrBufferAllocPool::unmap() {
124 VALIDATE();
125
126 if (fBufferPtr) {
127 BufferBlock& block = fBlocks.back();
128 GrBuffer* buffer = block.fBuffer.get();
129 if (!buffer->isCpuBuffer()) {
130 if (static_cast<GrGpuBuffer*>(buffer)->isMapped()) {
131 UNMAP_BUFFER(block);
132 } else {
133 size_t flushSize = block.fBuffer->size() - block.fBytesFree;
134 this->flushCpuData(fBlocks.back(), flushSize);
135 }
136 }
137 fBufferPtr = nullptr;
138 }
139 VALIDATE();
140 }
141
142 #ifdef SK_DEBUG
validate(bool unusedBlockAllowed) const143 void GrBufferAllocPool::validate(bool unusedBlockAllowed) const {
144 bool wasDestroyed = false;
145 if (fBufferPtr) {
146 SkASSERT(!fBlocks.empty());
147 const GrBuffer* buffer = fBlocks.back().fBuffer.get();
148 if (!buffer->isCpuBuffer() && !static_cast<const GrGpuBuffer*>(buffer)->isMapped()) {
149 SkASSERT(fCpuStagingBuffer && fCpuStagingBuffer->data() == fBufferPtr);
150 }
151 } else if (!fBlocks.empty()) {
152 const GrBuffer* buffer = fBlocks.back().fBuffer.get();
153 SkASSERT(buffer->isCpuBuffer() || !static_cast<const GrGpuBuffer*>(buffer)->isMapped());
154 }
155 size_t bytesInUse = 0;
156 for (int i = 0; i < fBlocks.size() - 1; ++i) {
157 const GrBuffer* buffer = fBlocks[i].fBuffer.get();
158 SkASSERT(buffer->isCpuBuffer() || !static_cast<const GrGpuBuffer*>(buffer)->isMapped());
159 }
160 for (int i = 0; !wasDestroyed && i < fBlocks.size(); ++i) {
161 GrBuffer* buffer = fBlocks[i].fBuffer.get();
162 if (!buffer->isCpuBuffer() && static_cast<GrGpuBuffer*>(buffer)->wasDestroyed()) {
163 wasDestroyed = true;
164 } else {
165 size_t bytes = fBlocks[i].fBuffer->size() - fBlocks[i].fBytesFree;
166 bytesInUse += bytes;
167 SkASSERT(bytes || unusedBlockAllowed);
168 }
169 }
170
171 if (!wasDestroyed) {
172 SkASSERT(bytesInUse == fBytesInUse);
173 if (unusedBlockAllowed) {
174 SkASSERT((fBytesInUse && !fBlocks.empty()) ||
175 (!fBytesInUse && (fBlocks.size() < 2)));
176 } else {
177 SkASSERT((0 == fBytesInUse) == fBlocks.empty());
178 }
179 }
180 }
181 #endif
182
align_up_pad(size_t x,size_t alignment)183 static inline size_t align_up_pad(size_t x, size_t alignment) {
184 return (alignment - x % alignment) % alignment;
185 }
186
align_down(size_t x,uint32_t alignment)187 static inline size_t align_down(size_t x, uint32_t alignment) {
188 return (x / alignment) * alignment;
189 }
190
makeSpace(size_t size,size_t alignment,sk_sp<const GrBuffer> * buffer,size_t * offset)191 void* GrBufferAllocPool::makeSpace(size_t size,
192 size_t alignment,
193 sk_sp<const GrBuffer>* buffer,
194 size_t* offset) {
195 VALIDATE();
196
197 SkASSERT(buffer);
198 SkASSERT(offset);
199
200 if (fBufferPtr) {
201 BufferBlock& back = fBlocks.back();
202 size_t usedBytes = back.fBuffer->size() - back.fBytesFree;
203 size_t pad = align_up_pad(usedBytes, alignment);
204 SkSafeMath safeMath;
205 size_t alignedSize = safeMath.add(pad, size);
206 if (!safeMath.ok()) {
207 return nullptr;
208 }
209 if (alignedSize <= back.fBytesFree) {
210 memset((void*)(reinterpret_cast<intptr_t>(fBufferPtr) + usedBytes), 0, pad);
211 usedBytes += pad;
212 *offset = usedBytes;
213 *buffer = back.fBuffer;
214 back.fBytesFree -= alignedSize;
215 fBytesInUse += alignedSize;
216 VALIDATE();
217 return (void*)(reinterpret_cast<intptr_t>(fBufferPtr) + usedBytes);
218 }
219 }
220
221 // We could honor the space request using by a partial update of the current
222 // VB (if there is room). But we don't currently use draw calls to GL that
223 // allow the driver to know that previously issued draws won't read from
224 // the part of the buffer we update. Also, when this was written the GL
225 // buffer implementation was cheating on the actual buffer size by shrinking
226 // the buffer in updateData() if the amount of data passed was less than
227 // the full buffer size. This is old code and both concerns may be obsolete.
228
229 if (!this->createBlock(size)) {
230 return nullptr;
231 }
232 SkASSERT(fBufferPtr);
233
234 *offset = 0;
235 BufferBlock& back = fBlocks.back();
236 *buffer = back.fBuffer;
237 back.fBytesFree -= size;
238 fBytesInUse += size;
239 VALIDATE();
240 return fBufferPtr;
241 }
242
makeSpaceAtLeast(size_t minSize,size_t fallbackSize,size_t alignment,sk_sp<const GrBuffer> * buffer,size_t * offset,size_t * actualSize)243 void* GrBufferAllocPool::makeSpaceAtLeast(size_t minSize,
244 size_t fallbackSize,
245 size_t alignment,
246 sk_sp<const GrBuffer>* buffer,
247 size_t* offset,
248 size_t* actualSize) {
249 VALIDATE();
250
251 SkASSERT(buffer);
252 SkASSERT(offset);
253 SkASSERT(actualSize);
254
255 size_t usedBytes = (fBlocks.empty()) ? 0 : fBlocks.back().fBuffer->size() -
256 fBlocks.back().fBytesFree;
257 size_t pad = align_up_pad(usedBytes, alignment);
258 if (!fBufferPtr || fBlocks.empty() || (minSize + pad) > fBlocks.back().fBytesFree) {
259 // We either don't have a block yet or the current block doesn't have enough free space.
260 // Create a new one.
261 if (!this->createBlock(fallbackSize)) {
262 return nullptr;
263 }
264 usedBytes = 0;
265 pad = 0;
266 }
267 SkASSERT(fBufferPtr);
268
269 // Consume padding first, to make subsequent alignment math easier
270 memset(static_cast<char*>(fBufferPtr) + usedBytes, 0, pad);
271 usedBytes += pad;
272 fBlocks.back().fBytesFree -= pad;
273 fBytesInUse += pad;
274
275 // Give caller all remaining space in this block (but aligned correctly)
276 size_t size = align_down(fBlocks.back().fBytesFree, alignment);
277 *offset = usedBytes;
278 *buffer = fBlocks.back().fBuffer;
279 *actualSize = size;
280 fBlocks.back().fBytesFree -= size;
281 fBytesInUse += size;
282 VALIDATE();
283 return static_cast<char*>(fBufferPtr) + usedBytes;
284 }
285
putBack(size_t bytes)286 void GrBufferAllocPool::putBack(size_t bytes) {
287 VALIDATE();
288 if (!bytes) {
289 return;
290 }
291 SkASSERT(!fBlocks.empty());
292 BufferBlock& block = fBlocks.back();
293 // Caller shouldn't try to put back more than they've taken and all those bytes should fit into
294 // one block. All the uses of this call are sequential with a single makeSpaceAtLeast call. So
295 // we should not have a case where someone is putting back bytes that are greater than the
296 // current block.
297 // It is possible the caller returns all their allocated bytes thus the <= and not just <.
298 SkASSERT(bytes <= (block.fBuffer->size() - block.fBytesFree));
299 block.fBytesFree += bytes;
300 fBytesInUse -= bytes;
301
302 // We don't allow blocks without any used bytes. So if we end up in that case after putting
303 // back the bytes then destroy the block. This scenario shouldn't occur often, but even if we
304 // end up allocating a new block immediately after destroying this one, the GPU and CPU buffers
305 // will usually be cached so the new block shouldn't be too expensive to make.
306 // TODO: This was true in older versions and uses of this class but is it still needed to
307 // have this restriction?
308 if (block.fBytesFree == block.fBuffer->size()) {
309 GrBuffer* buffer = block.fBuffer.get();
310 if (!buffer->isCpuBuffer() && static_cast<GrGpuBuffer*>(buffer)->isMapped()) {
311 UNMAP_BUFFER(block);
312 }
313 this->destroyBlock();
314 }
315
316 VALIDATE();
317 }
318
createBlock(size_t requestSize)319 bool GrBufferAllocPool::createBlock(size_t requestSize) {
320 size_t size = std::max(requestSize, kDefaultBufferSize);
321
322 VALIDATE();
323
324 BufferBlock& block = fBlocks.push_back();
325
326 block.fBuffer = this->getBuffer(size);
327 if (!block.fBuffer) {
328 fBlocks.pop_back();
329 return false;
330 }
331
332 block.fBytesFree = block.fBuffer->size();
333 if (fBufferPtr) {
334 SkASSERT(fBlocks.size() > 1);
335 BufferBlock& prev = fBlocks.fromBack(1);
336 GrBuffer* buffer = prev.fBuffer.get();
337 if (!buffer->isCpuBuffer()) {
338 if (static_cast<GrGpuBuffer*>(buffer)->isMapped()) {
339 UNMAP_BUFFER(prev);
340 } else {
341 this->flushCpuData(prev, prev.fBuffer->size() - prev.fBytesFree);
342 }
343 }
344 fBufferPtr = nullptr;
345 }
346
347 SkASSERT(!fBufferPtr);
348
349 // If the buffer is CPU-backed we "map" it because it is free to do so and saves a copy.
350 // Otherwise when buffer mapping is supported we map if the buffer size is greater than the
351 // threshold.
352 if (block.fBuffer->isCpuBuffer()) {
353 fBufferPtr = static_cast<GrCpuBuffer*>(block.fBuffer.get())->data();
354 SkASSERT(fBufferPtr);
355 } else {
356 if (GrCaps::kNone_MapFlags != fGpu->caps()->mapBufferFlags() &&
357 size > fGpu->caps()->bufferMapThreshold()) {
358 fBufferPtr = static_cast<GrGpuBuffer*>(block.fBuffer.get())->map();
359 }
360 }
361 if (!fBufferPtr) {
362 this->resetCpuData(block.fBytesFree);
363 fBufferPtr = fCpuStagingBuffer->data();
364 }
365
366 VALIDATE(true);
367
368 return true;
369 }
370
destroyBlock()371 void GrBufferAllocPool::destroyBlock() {
372 SkASSERT(!fBlocks.empty());
373 SkASSERT(fBlocks.back().fBuffer->isCpuBuffer() ||
374 !static_cast<GrGpuBuffer*>(fBlocks.back().fBuffer.get())->isMapped());
375 fBlocks.pop_back();
376 fBufferPtr = nullptr;
377 }
378
resetCpuData(size_t newSize)379 void GrBufferAllocPool::resetCpuData(size_t newSize) {
380 SkASSERT(newSize >= kDefaultBufferSize || !newSize);
381 if (!newSize) {
382 fCpuStagingBuffer.reset();
383 return;
384 }
385 if (fCpuStagingBuffer && newSize <= fCpuStagingBuffer->size()) {
386 return;
387 }
388 bool mustInitialize = fGpu->caps()->mustClearUploadedBufferData();
389 fCpuStagingBuffer = fCpuBufferCache ? fCpuBufferCache->makeBuffer(newSize, mustInitialize)
390 : GrCpuBuffer::Make(newSize);
391 }
392
flushCpuData(const BufferBlock & block,size_t flushSize)393 void GrBufferAllocPool::flushCpuData(const BufferBlock& block, size_t flushSize) {
394 SkASSERT(block.fBuffer.get());
395 SkASSERT(!block.fBuffer.get()->isCpuBuffer());
396 GrGpuBuffer* buffer = static_cast<GrGpuBuffer*>(block.fBuffer.get());
397 SkASSERT(!buffer->isMapped());
398 SkASSERT(fCpuStagingBuffer && fCpuStagingBuffer->data() == fBufferPtr);
399 SkASSERT(flushSize <= buffer->size());
400 VALIDATE(true);
401
402 if (GrCaps::kNone_MapFlags != fGpu->caps()->mapBufferFlags() &&
403 flushSize > fGpu->caps()->bufferMapThreshold()) {
404 void* data = buffer->map();
405 if (data) {
406 memcpy(data, fBufferPtr, flushSize);
407 UNMAP_BUFFER(block);
408 return;
409 }
410 }
411 buffer->updateData(fBufferPtr, /*offset=*/0, flushSize, /*preserve=*/false);
412 VALIDATE(true);
413 }
414
getBuffer(size_t size)415 sk_sp<GrBuffer> GrBufferAllocPool::getBuffer(size_t size) {
416 const GrCaps& caps = *fGpu->caps();
417 auto resourceProvider = fGpu->getContext()->priv().resourceProvider();
418 if (caps.preferClientSideDynamicBuffers() ||
419 (fBufferType == GrGpuBufferType::kDrawIndirect && caps.useClientSideIndirectBuffers())) {
420 // Create a CPU buffer.
421 bool mustInitialize = caps.mustClearUploadedBufferData();
422 return fCpuBufferCache ? fCpuBufferCache->makeBuffer(size, mustInitialize)
423 : GrCpuBuffer::Make(size);
424 }
425 return resourceProvider->createBuffer(size,
426 fBufferType,
427 kDynamic_GrAccessPattern,
428 GrResourceProvider::ZeroInit::kNo);
429 }
430
431 ////////////////////////////////////////////////////////////////////////////////
432
GrVertexBufferAllocPool(GrGpu * gpu,sk_sp<CpuBufferCache> cpuBufferCache)433 GrVertexBufferAllocPool::GrVertexBufferAllocPool(GrGpu* gpu, sk_sp<CpuBufferCache> cpuBufferCache)
434 : GrBufferAllocPool(gpu, GrGpuBufferType::kVertex, std::move(cpuBufferCache)) {}
435
makeSpace(size_t vertexSize,int vertexCount,sk_sp<const GrBuffer> * buffer,int * startVertex)436 void* GrVertexBufferAllocPool::makeSpace(size_t vertexSize,
437 int vertexCount,
438 sk_sp<const GrBuffer>* buffer,
439 int* startVertex) {
440 SkASSERT(vertexCount >= 0);
441 SkASSERT(buffer);
442 SkASSERT(startVertex);
443
444 size_t offset SK_INIT_TO_AVOID_WARNING;
445 void* ptr = INHERITED::makeSpace(SkSafeMath::Mul(vertexSize, vertexCount),
446 vertexSize,
447 buffer,
448 &offset);
449
450 SkASSERT(0 == offset % vertexSize);
451 *startVertex = static_cast<int>(offset / vertexSize);
452 return ptr;
453 }
454
makeSpaceAtLeast(size_t vertexSize,int minVertexCount,int fallbackVertexCount,sk_sp<const GrBuffer> * buffer,int * startVertex,int * actualVertexCount)455 void* GrVertexBufferAllocPool::makeSpaceAtLeast(size_t vertexSize, int minVertexCount,
456 int fallbackVertexCount,
457 sk_sp<const GrBuffer>* buffer, int* startVertex,
458 int* actualVertexCount) {
459 SkASSERT(minVertexCount >= 0);
460 SkASSERT(fallbackVertexCount >= minVertexCount);
461 SkASSERT(buffer);
462 SkASSERT(startVertex);
463 SkASSERT(actualVertexCount);
464
465 size_t offset SK_INIT_TO_AVOID_WARNING;
466 size_t actualSize SK_INIT_TO_AVOID_WARNING;
467 void* ptr = INHERITED::makeSpaceAtLeast(SkSafeMath::Mul(vertexSize, minVertexCount),
468 SkSafeMath::Mul(vertexSize, fallbackVertexCount),
469 vertexSize,
470 buffer,
471 &offset,
472 &actualSize);
473
474 SkASSERT(0 == offset % vertexSize);
475 *startVertex = static_cast<int>(offset / vertexSize);
476
477 SkASSERT(0 == actualSize % vertexSize);
478 SkASSERT(actualSize >= vertexSize * minVertexCount);
479 *actualVertexCount = static_cast<int>(actualSize / vertexSize);
480
481 return ptr;
482 }
483
484 ////////////////////////////////////////////////////////////////////////////////
485
GrIndexBufferAllocPool(GrGpu * gpu,sk_sp<CpuBufferCache> cpuBufferCache)486 GrIndexBufferAllocPool::GrIndexBufferAllocPool(GrGpu* gpu, sk_sp<CpuBufferCache> cpuBufferCache)
487 : GrBufferAllocPool(gpu, GrGpuBufferType::kIndex, std::move(cpuBufferCache)) {}
488
makeSpace(int indexCount,sk_sp<const GrBuffer> * buffer,int * startIndex)489 void* GrIndexBufferAllocPool::makeSpace(int indexCount, sk_sp<const GrBuffer>* buffer,
490 int* startIndex) {
491 SkASSERT(indexCount >= 0);
492 SkASSERT(buffer);
493 SkASSERT(startIndex);
494
495 size_t offset SK_INIT_TO_AVOID_WARNING;
496 void* ptr = INHERITED::makeSpace(SkSafeMath::Mul(indexCount, sizeof(uint16_t)),
497 sizeof(uint16_t),
498 buffer,
499 &offset);
500
501 SkASSERT(0 == offset % sizeof(uint16_t));
502 *startIndex = static_cast<int>(offset / sizeof(uint16_t));
503 return ptr;
504 }
505
makeSpaceAtLeast(int minIndexCount,int fallbackIndexCount,sk_sp<const GrBuffer> * buffer,int * startIndex,int * actualIndexCount)506 void* GrIndexBufferAllocPool::makeSpaceAtLeast(int minIndexCount, int fallbackIndexCount,
507 sk_sp<const GrBuffer>* buffer, int* startIndex,
508 int* actualIndexCount) {
509 SkASSERT(minIndexCount >= 0);
510 SkASSERT(fallbackIndexCount >= minIndexCount);
511 SkASSERT(buffer);
512 SkASSERT(startIndex);
513 SkASSERT(actualIndexCount);
514
515 size_t offset SK_INIT_TO_AVOID_WARNING;
516 size_t actualSize SK_INIT_TO_AVOID_WARNING;
517 void* ptr = INHERITED::makeSpaceAtLeast(SkSafeMath::Mul(minIndexCount, sizeof(uint16_t)),
518 SkSafeMath::Mul(fallbackIndexCount, sizeof(uint16_t)),
519 sizeof(uint16_t),
520 buffer,
521 &offset,
522 &actualSize);
523
524 SkASSERT(0 == offset % sizeof(uint16_t));
525 *startIndex = static_cast<int>(offset / sizeof(uint16_t));
526
527 SkASSERT(0 == actualSize % sizeof(uint16_t));
528 SkASSERT(actualSize >= minIndexCount * sizeof(uint16_t));
529 *actualIndexCount = static_cast<int>(actualSize / sizeof(uint16_t));
530 return ptr;
531 }
532