Home | History | Annotate | Download | only in gpu
      1 
      2 /*
      3  * Copyright 2010 Google Inc.
      4  *
      5  * Use of this source code is governed by a BSD-style license that can be
      6  * found in the LICENSE file.
      7  */
      8 
      9 
     10 #include "GrBufferAllocPool.h"
     11 #include "GrDrawTargetCaps.h"
     12 #include "GrGpu.h"
     13 #include "GrIndexBuffer.h"
     14 #include "GrTypes.h"
     15 #include "GrVertexBuffer.h"
     16 
     17 #include "SkTraceEvent.h"
     18 
     19 #ifdef SK_DEBUG
     20     #define VALIDATE validate
     21 #else
     22     static void VALIDATE(bool = false) {}
     23 #endif
     24 
     25 // page size
     26 #define GrBufferAllocPool_MIN_BLOCK_SIZE ((size_t)1 << 12)
     27 
     28 #define UNMAP_BUFFER(block)                                                               \
     29 do {                                                                                      \
     30     TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("skia.gpu"),                           \
     31                          "GrBufferAllocPool Unmapping Buffer",                            \
     32                          TRACE_EVENT_SCOPE_THREAD,                                        \
     33                          "percent_unwritten",                                             \
     34                          (float)((block).fBytesFree) / (block).fBuffer->gpuMemorySize()); \
     35     (block).fBuffer->unmap();                                                             \
     36 } while (false)
     37 
     38 GrBufferAllocPool::GrBufferAllocPool(GrGpu* gpu,
     39                                      BufferType bufferType,
     40                                      bool frequentResetHint,
     41                                      size_t blockSize,
     42                                      int preallocBufferCnt) :
     43         fBlocks(SkTMax(8, 2*preallocBufferCnt)) {
     44 
     45     SkASSERT(gpu);
     46     fGpu = gpu;
     47     fGpu->ref();
     48     fGpuIsReffed = true;
     49 
     50     fBufferType = bufferType;
     51     fFrequentResetHint = frequentResetHint;
     52     fBufferPtr = NULL;
     53     fMinBlockSize = SkTMax(GrBufferAllocPool_MIN_BLOCK_SIZE, blockSize);
     54 
     55     fBytesInUse = 0;
     56 
     57     fPreallocBuffersInUse = 0;
     58     fPreallocBufferStartIdx = 0;
     59     for (int i = 0; i < preallocBufferCnt; ++i) {
     60         GrGeometryBuffer* buffer = this->createBuffer(fMinBlockSize);
     61         if (buffer) {
     62             *fPreallocBuffers.append() = buffer;
     63         }
     64     }
     65 }
     66 
     67 GrBufferAllocPool::~GrBufferAllocPool() {
     68     VALIDATE();
     69     if (fBlocks.count()) {
     70         GrGeometryBuffer* buffer = fBlocks.back().fBuffer;
     71         if (buffer->isMapped()) {
     72             UNMAP_BUFFER(fBlocks.back());
     73         }
     74     }
     75     while (!fBlocks.empty()) {
     76         destroyBlock();
     77     }
     78     fPreallocBuffers.unrefAll();
     79     releaseGpuRef();
     80 }
     81 
     82 void GrBufferAllocPool::releaseGpuRef() {
     83     if (fGpuIsReffed) {
     84         fGpu->unref();
     85         fGpuIsReffed = false;
     86     }
     87 }
     88 
     89 void GrBufferAllocPool::reset() {
     90     VALIDATE();
     91     fBytesInUse = 0;
     92     if (fBlocks.count()) {
     93         GrGeometryBuffer* buffer = fBlocks.back().fBuffer;
     94         if (buffer->isMapped()) {
     95             UNMAP_BUFFER(fBlocks.back());
     96         }
     97     }
     98     // fPreallocBuffersInUse will be decremented down to zero in the while loop
     99     int preallocBuffersInUse = fPreallocBuffersInUse;
    100     while (!fBlocks.empty()) {
    101         this->destroyBlock();
    102     }
    103     if (fPreallocBuffers.count()) {
    104         // must set this after above loop.
    105         fPreallocBufferStartIdx = (fPreallocBufferStartIdx +
    106                                    preallocBuffersInUse) %
    107                                   fPreallocBuffers.count();
    108     }
    109     // we may have created a large cpu mirror of a large VB. Reset the size
    110     // to match our pre-allocated VBs.
    111     fCpuData.reset(fMinBlockSize);
    112     SkASSERT(0 == fPreallocBuffersInUse);
    113     VALIDATE();
    114 }
    115 
    116 void GrBufferAllocPool::unmap() {
    117     VALIDATE();
    118 
    119     if (fBufferPtr) {
    120         BufferBlock& block = fBlocks.back();
    121         if (block.fBuffer->isMapped()) {
    122             UNMAP_BUFFER(block);
    123         } else {
    124             size_t flushSize = block.fBuffer->gpuMemorySize() - block.fBytesFree;
    125             this->flushCpuData(fBlocks.back(), flushSize);
    126         }
    127         fBufferPtr = NULL;
    128     }
    129     VALIDATE();
    130 }
    131 
    132 #ifdef SK_DEBUG
    133 void GrBufferAllocPool::validate(bool unusedBlockAllowed) const {
    134     if (fBufferPtr) {
    135         SkASSERT(!fBlocks.empty());
    136         if (fBlocks.back().fBuffer->isMapped()) {
    137             GrGeometryBuffer* buf = fBlocks.back().fBuffer;
    138             SkASSERT(buf->mapPtr() == fBufferPtr);
    139         } else {
    140             SkASSERT(fCpuData.get() == fBufferPtr);
    141         }
    142     } else {
    143         SkASSERT(fBlocks.empty() || !fBlocks.back().fBuffer->isMapped());
    144     }
    145     size_t bytesInUse = 0;
    146     for (int i = 0; i < fBlocks.count() - 1; ++i) {
    147         SkASSERT(!fBlocks[i].fBuffer->isMapped());
    148     }
    149     for (int i = 0; i < fBlocks.count(); ++i) {
    150         size_t bytes = fBlocks[i].fBuffer->gpuMemorySize() - fBlocks[i].fBytesFree;
    151         bytesInUse += bytes;
    152         SkASSERT(bytes || unusedBlockAllowed);
    153     }
    154 
    155     SkASSERT(bytesInUse == fBytesInUse);
    156     if (unusedBlockAllowed) {
    157         SkASSERT((fBytesInUse && !fBlocks.empty()) ||
    158                  (!fBytesInUse && (fBlocks.count() < 2)));
    159     } else {
    160         SkASSERT((0 == fBytesInUse) == fBlocks.empty());
    161     }
    162 }
    163 #endif
    164 
    165 void* GrBufferAllocPool::makeSpace(size_t size,
    166                                    size_t alignment,
    167                                    const GrGeometryBuffer** buffer,
    168                                    size_t* offset) {
    169     VALIDATE();
    170 
    171     SkASSERT(buffer);
    172     SkASSERT(offset);
    173 
    174     if (fBufferPtr) {
    175         BufferBlock& back = fBlocks.back();
    176         size_t usedBytes = back.fBuffer->gpuMemorySize() - back.fBytesFree;
    177         size_t pad = GrSizeAlignUpPad(usedBytes,
    178                                       alignment);
    179         if ((size + pad) <= back.fBytesFree) {
    180             usedBytes += pad;
    181             *offset = usedBytes;
    182             *buffer = back.fBuffer;
    183             back.fBytesFree -= size + pad;
    184             fBytesInUse += size + pad;
    185             VALIDATE();
    186             return (void*)(reinterpret_cast<intptr_t>(fBufferPtr) + usedBytes);
    187         }
    188     }
    189 
    190     // We could honor the space request using by a partial update of the current
    191     // VB (if there is room). But we don't currently use draw calls to GL that
    192     // allow the driver to know that previously issued draws won't read from
    193     // the part of the buffer we update. Also, the GL buffer implementation
    194     // may be cheating on the actual buffer size by shrinking the buffer on
    195     // updateData() if the amount of data passed is less than the full buffer
    196     // size.
    197 
    198     if (!createBlock(size)) {
    199         return NULL;
    200     }
    201     SkASSERT(fBufferPtr);
    202 
    203     *offset = 0;
    204     BufferBlock& back = fBlocks.back();
    205     *buffer = back.fBuffer;
    206     back.fBytesFree -= size;
    207     fBytesInUse += size;
    208     VALIDATE();
    209     return fBufferPtr;
    210 }
    211 
    212 int GrBufferAllocPool::currentBufferItems(size_t itemSize) const {
    213     VALIDATE();
    214     if (fBufferPtr) {
    215         const BufferBlock& back = fBlocks.back();
    216         size_t usedBytes = back.fBuffer->gpuMemorySize() - back.fBytesFree;
    217         size_t pad = GrSizeAlignUpPad(usedBytes, itemSize);
    218         return static_cast<int>((back.fBytesFree - pad) / itemSize);
    219     } else if (fPreallocBuffersInUse < fPreallocBuffers.count()) {
    220         return static_cast<int>(fMinBlockSize / itemSize);
    221     }
    222     return 0;
    223 }
    224 
    225 int GrBufferAllocPool::preallocatedBuffersRemaining() const {
    226     return fPreallocBuffers.count() - fPreallocBuffersInUse;
    227 }
    228 
    229 int GrBufferAllocPool::preallocatedBufferCount() const {
    230     return fPreallocBuffers.count();
    231 }
    232 
    233 void GrBufferAllocPool::putBack(size_t bytes) {
    234     VALIDATE();
    235 
    236     // if the putBack unwinds all the preallocated buffers then we will
    237     // advance the starting index. As blocks are destroyed fPreallocBuffersInUse
    238     // will be decremented. I will reach zero if all blocks using preallocated
    239     // buffers are released.
    240     int preallocBuffersInUse = fPreallocBuffersInUse;
    241 
    242     while (bytes) {
    243         // caller shouldnt try to put back more than they've taken
    244         SkASSERT(!fBlocks.empty());
    245         BufferBlock& block = fBlocks.back();
    246         size_t bytesUsed = block.fBuffer->gpuMemorySize() - block.fBytesFree;
    247         if (bytes >= bytesUsed) {
    248             bytes -= bytesUsed;
    249             fBytesInUse -= bytesUsed;
    250             // if we locked a vb to satisfy the make space and we're releasing
    251             // beyond it, then unmap it.
    252             if (block.fBuffer->isMapped()) {
    253                 UNMAP_BUFFER(block);
    254             }
    255             this->destroyBlock();
    256         } else {
    257             block.fBytesFree += bytes;
    258             fBytesInUse -= bytes;
    259             bytes = 0;
    260             break;
    261         }
    262     }
    263     if (!fPreallocBuffersInUse && fPreallocBuffers.count()) {
    264             fPreallocBufferStartIdx = (fPreallocBufferStartIdx +
    265                                        preallocBuffersInUse) %
    266                                       fPreallocBuffers.count();
    267     }
    268     VALIDATE();
    269 }
    270 
    271 bool GrBufferAllocPool::createBlock(size_t requestSize) {
    272 
    273     size_t size = SkTMax(requestSize, fMinBlockSize);
    274     SkASSERT(size >= GrBufferAllocPool_MIN_BLOCK_SIZE);
    275 
    276     VALIDATE();
    277 
    278     BufferBlock& block = fBlocks.push_back();
    279 
    280     if (size == fMinBlockSize &&
    281         fPreallocBuffersInUse < fPreallocBuffers.count()) {
    282 
    283         uint32_t nextBuffer = (fPreallocBuffersInUse +
    284                                fPreallocBufferStartIdx) %
    285                               fPreallocBuffers.count();
    286         block.fBuffer = fPreallocBuffers[nextBuffer];
    287         block.fBuffer->ref();
    288         ++fPreallocBuffersInUse;
    289     } else {
    290         block.fBuffer = this->createBuffer(size);
    291         if (NULL == block.fBuffer) {
    292             fBlocks.pop_back();
    293             return false;
    294         }
    295     }
    296 
    297     block.fBytesFree = size;
    298     if (fBufferPtr) {
    299         SkASSERT(fBlocks.count() > 1);
    300         BufferBlock& prev = fBlocks.fromBack(1);
    301         if (prev.fBuffer->isMapped()) {
    302             UNMAP_BUFFER(prev);
    303         } else {
    304             this->flushCpuData(prev, prev.fBuffer->gpuMemorySize() - prev.fBytesFree);
    305         }
    306         fBufferPtr = NULL;
    307     }
    308 
    309     SkASSERT(NULL == fBufferPtr);
    310 
    311     // If the buffer is CPU-backed we map it because it is free to do so and saves a copy.
    312     // Otherwise when buffer mapping is supported:
    313     //      a) If the frequently reset hint is set we only map when the requested size meets a
    314     //      threshold (since we don't expect it is likely that we will see more vertex data)
    315     //      b) If the hint is not set we map if the buffer size is greater than the threshold.
    316     bool attemptMap = block.fBuffer->isCPUBacked();
    317     if (!attemptMap && GrDrawTargetCaps::kNone_MapFlags != fGpu->caps()->mapBufferFlags()) {
    318         if (fFrequentResetHint) {
    319             attemptMap = requestSize > GR_GEOM_BUFFER_MAP_THRESHOLD;
    320         } else {
    321             attemptMap = size > GR_GEOM_BUFFER_MAP_THRESHOLD;
    322         }
    323     }
    324 
    325     if (attemptMap) {
    326         fBufferPtr = block.fBuffer->map();
    327     }
    328 
    329     if (NULL == fBufferPtr) {
    330         fBufferPtr = fCpuData.reset(size);
    331     }
    332 
    333     VALIDATE(true);
    334 
    335     return true;
    336 }
    337 
    338 void GrBufferAllocPool::destroyBlock() {
    339     SkASSERT(!fBlocks.empty());
    340 
    341     BufferBlock& block = fBlocks.back();
    342     if (fPreallocBuffersInUse > 0) {
    343         uint32_t prevPreallocBuffer = (fPreallocBuffersInUse +
    344                                        fPreallocBufferStartIdx +
    345                                        (fPreallocBuffers.count() - 1)) %
    346                                       fPreallocBuffers.count();
    347         if (block.fBuffer == fPreallocBuffers[prevPreallocBuffer]) {
    348             --fPreallocBuffersInUse;
    349         }
    350     }
    351     SkASSERT(!block.fBuffer->isMapped());
    352     block.fBuffer->unref();
    353     fBlocks.pop_back();
    354     fBufferPtr = NULL;
    355 }
    356 
    357 void GrBufferAllocPool::flushCpuData(const BufferBlock& block, size_t flushSize) {
    358     GrGeometryBuffer* buffer = block.fBuffer;
    359     SkASSERT(buffer);
    360     SkASSERT(!buffer->isMapped());
    361     SkASSERT(fCpuData.get() == fBufferPtr);
    362     SkASSERT(flushSize <= buffer->gpuMemorySize());
    363     VALIDATE(true);
    364 
    365     if (GrDrawTargetCaps::kNone_MapFlags != fGpu->caps()->mapBufferFlags() &&
    366         flushSize > GR_GEOM_BUFFER_MAP_THRESHOLD) {
    367         void* data = buffer->map();
    368         if (data) {
    369             memcpy(data, fBufferPtr, flushSize);
    370             UNMAP_BUFFER(block);
    371             return;
    372         }
    373     }
    374     buffer->updateData(fBufferPtr, flushSize);
    375     VALIDATE(true);
    376 }
    377 
    378 GrGeometryBuffer* GrBufferAllocPool::createBuffer(size_t size) {
    379     if (kIndex_BufferType == fBufferType) {
    380         return fGpu->createIndexBuffer(size, true);
    381     } else {
    382         SkASSERT(kVertex_BufferType == fBufferType);
    383         return fGpu->createVertexBuffer(size, true);
    384     }
    385 }
    386 
    387 ////////////////////////////////////////////////////////////////////////////////
    388 
    389 GrVertexBufferAllocPool::GrVertexBufferAllocPool(GrGpu* gpu,
    390                                                  bool frequentResetHint,
    391                                                  size_t bufferSize,
    392                                                  int preallocBufferCnt)
    393 : GrBufferAllocPool(gpu,
    394                     kVertex_BufferType,
    395                     frequentResetHint,
    396                     bufferSize,
    397                     preallocBufferCnt) {
    398 }
    399 
    400 void* GrVertexBufferAllocPool::makeSpace(size_t vertexSize,
    401                                          int vertexCount,
    402                                          const GrVertexBuffer** buffer,
    403                                          int* startVertex) {
    404 
    405     SkASSERT(vertexCount >= 0);
    406     SkASSERT(buffer);
    407     SkASSERT(startVertex);
    408 
    409     size_t offset = 0; // assign to suppress warning
    410     const GrGeometryBuffer* geomBuffer = NULL; // assign to suppress warning
    411     void* ptr = INHERITED::makeSpace(vertexSize * vertexCount,
    412                                      vertexSize,
    413                                      &geomBuffer,
    414                                      &offset);
    415 
    416     *buffer = (const GrVertexBuffer*) geomBuffer;
    417     SkASSERT(0 == offset % vertexSize);
    418     *startVertex = static_cast<int>(offset / vertexSize);
    419     return ptr;
    420 }
    421 
    422 bool GrVertexBufferAllocPool::appendVertices(size_t vertexSize,
    423                                              int vertexCount,
    424                                              const void* vertices,
    425                                              const GrVertexBuffer** buffer,
    426                                              int* startVertex) {
    427     void* space = makeSpace(vertexSize, vertexCount, buffer, startVertex);
    428     if (space) {
    429         memcpy(space,
    430                vertices,
    431                vertexSize * vertexCount);
    432         return true;
    433     } else {
    434         return false;
    435     }
    436 }
    437 
    438 int GrVertexBufferAllocPool::preallocatedBufferVertices(size_t vertexSize) const {
    439     return static_cast<int>(INHERITED::preallocatedBufferSize() / vertexSize);
    440 }
    441 
    442 int GrVertexBufferAllocPool::currentBufferVertices(size_t vertexSize) const {
    443     return currentBufferItems(vertexSize);
    444 }
    445 
    446 ////////////////////////////////////////////////////////////////////////////////
    447 
    448 GrIndexBufferAllocPool::GrIndexBufferAllocPool(GrGpu* gpu,
    449                                                bool frequentResetHint,
    450                                                size_t bufferSize,
    451                                                int preallocBufferCnt)
    452 : GrBufferAllocPool(gpu,
    453                     kIndex_BufferType,
    454                     frequentResetHint,
    455                     bufferSize,
    456                     preallocBufferCnt) {
    457 }
    458 
    459 void* GrIndexBufferAllocPool::makeSpace(int indexCount,
    460                                         const GrIndexBuffer** buffer,
    461                                         int* startIndex) {
    462 
    463     SkASSERT(indexCount >= 0);
    464     SkASSERT(buffer);
    465     SkASSERT(startIndex);
    466 
    467     size_t offset = 0; // assign to suppress warning
    468     const GrGeometryBuffer* geomBuffer = NULL; // assign to suppress warning
    469     void* ptr = INHERITED::makeSpace(indexCount * sizeof(uint16_t),
    470                                      sizeof(uint16_t),
    471                                      &geomBuffer,
    472                                      &offset);
    473 
    474     *buffer = (const GrIndexBuffer*) geomBuffer;
    475     SkASSERT(0 == offset % sizeof(uint16_t));
    476     *startIndex = static_cast<int>(offset / sizeof(uint16_t));
    477     return ptr;
    478 }
    479 
    480 bool GrIndexBufferAllocPool::appendIndices(int indexCount,
    481                                            const void* indices,
    482                                            const GrIndexBuffer** buffer,
    483                                            int* startIndex) {
    484     void* space = makeSpace(indexCount, buffer, startIndex);
    485     if (space) {
    486         memcpy(space, indices, sizeof(uint16_t) * indexCount);
    487         return true;
    488     } else {
    489         return false;
    490     }
    491 }
    492 
    493 int GrIndexBufferAllocPool::preallocatedBufferIndices() const {
    494     return static_cast<int>(INHERITED::preallocatedBufferSize() / sizeof(uint16_t));
    495 }
    496 
    497 int GrIndexBufferAllocPool::currentBufferIndices() const {
    498     return currentBufferItems(sizeof(uint16_t));
    499 }
    500