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