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