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