gfx/skia/trunk/src/gpu/GrBufferAllocPool.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/gfx/skia/trunk/src/gpu/GrBufferAllocPool.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,488 @@
     1.4 +
     1.5 +/*
     1.6 + * Copyright 2010 Google Inc.
     1.7 + *
     1.8 + * Use of this source code is governed by a BSD-style license that can be
     1.9 + * found in the LICENSE file.
    1.10 + */
    1.11 +
    1.12 +
    1.13 +#include "GrBufferAllocPool.h"
    1.14 +#include "GrDrawTargetCaps.h"
    1.15 +#include "GrGpu.h"
    1.16 +#include "GrIndexBuffer.h"
    1.17 +#include "GrTypes.h"
    1.18 +#include "GrVertexBuffer.h"
    1.19 +
    1.20 +#ifdef SK_DEBUG
    1.21 +    #define VALIDATE validate
    1.22 +#else
    1.23 +    static void VALIDATE(bool = false) {}
    1.24 +#endif
    1.25 +
    1.26 +// page size
    1.27 +#define GrBufferAllocPool_MIN_BLOCK_SIZE ((size_t)1 << 12)
    1.28 +
    1.29 +GrBufferAllocPool::GrBufferAllocPool(GrGpu* gpu,
    1.30 +                                     BufferType bufferType,
    1.31 +                                     bool frequentResetHint,
    1.32 +                                     size_t blockSize,
    1.33 +                                     int preallocBufferCnt) :
    1.34 +        fBlocks(GrMax(8, 2*preallocBufferCnt)) {
    1.35 +
    1.36 +    SkASSERT(NULL != gpu);
    1.37 +    fGpu = gpu;
    1.38 +    fGpu->ref();
    1.39 +    fGpuIsReffed = true;
    1.40 +
    1.41 +    fBufferType = bufferType;
    1.42 +    fFrequentResetHint = frequentResetHint;
    1.43 +    fBufferPtr = NULL;
    1.44 +    fMinBlockSize = GrMax(GrBufferAllocPool_MIN_BLOCK_SIZE, blockSize);
    1.45 +
    1.46 +    fBytesInUse = 0;
    1.47 +
    1.48 +    fPreallocBuffersInUse = 0;
    1.49 +    fPreallocBufferStartIdx = 0;
    1.50 +    for (int i = 0; i < preallocBufferCnt; ++i) {
    1.51 +        GrGeometryBuffer* buffer = this->createBuffer(fMinBlockSize);
    1.52 +        if (NULL != buffer) {
    1.53 +            *fPreallocBuffers.append() = buffer;
    1.54 +        }
    1.55 +    }
    1.56 +}
    1.57 +
    1.58 +GrBufferAllocPool::~GrBufferAllocPool() {
    1.59 +    VALIDATE();
    1.60 +    if (fBlocks.count()) {
    1.61 +        GrGeometryBuffer* buffer = fBlocks.back().fBuffer;
    1.62 +        if (buffer->isLocked()) {
    1.63 +            buffer->unlock();
    1.64 +        }
    1.65 +    }
    1.66 +    while (!fBlocks.empty()) {
    1.67 +        destroyBlock();
    1.68 +    }
    1.69 +    fPreallocBuffers.unrefAll();
    1.70 +    releaseGpuRef();
    1.71 +}
    1.72 +
    1.73 +void GrBufferAllocPool::releaseGpuRef() {
    1.74 +    if (fGpuIsReffed) {
    1.75 +        fGpu->unref();
    1.76 +        fGpuIsReffed = false;
    1.77 +    }
    1.78 +}
    1.79 +
    1.80 +void GrBufferAllocPool::reset() {
    1.81 +    VALIDATE();
    1.82 +    fBytesInUse = 0;
    1.83 +    if (fBlocks.count()) {
    1.84 +        GrGeometryBuffer* buffer = fBlocks.back().fBuffer;
    1.85 +        if (buffer->isLocked()) {
    1.86 +            buffer->unlock();
    1.87 +        }
    1.88 +    }
    1.89 +    // fPreallocBuffersInUse will be decremented down to zero in the while loop
    1.90 +    int preallocBuffersInUse = fPreallocBuffersInUse;
    1.91 +    while (!fBlocks.empty()) {
    1.92 +        this->destroyBlock();
    1.93 +    }
    1.94 +    if (fPreallocBuffers.count()) {
    1.95 +        // must set this after above loop.
    1.96 +        fPreallocBufferStartIdx = (fPreallocBufferStartIdx +
    1.97 +                                   preallocBuffersInUse) %
    1.98 +                                  fPreallocBuffers.count();
    1.99 +    }
   1.100 +    // we may have created a large cpu mirror of a large VB. Reset the size
   1.101 +    // to match our pre-allocated VBs.
   1.102 +    fCpuData.reset(fMinBlockSize);
   1.103 +    SkASSERT(0 == fPreallocBuffersInUse);
   1.104 +    VALIDATE();
   1.105 +}
   1.106 +
   1.107 +void GrBufferAllocPool::unlock() {
   1.108 +    VALIDATE();
   1.109 +
   1.110 +    if (NULL != fBufferPtr) {
   1.111 +        BufferBlock& block = fBlocks.back();
   1.112 +        if (block.fBuffer->isLocked()) {
   1.113 +            block.fBuffer->unlock();
   1.114 +        } else {
   1.115 +            size_t flushSize = block.fBuffer->sizeInBytes() - block.fBytesFree;
   1.116 +            flushCpuData(fBlocks.back().fBuffer, flushSize);
   1.117 +        }
   1.118 +        fBufferPtr = NULL;
   1.119 +    }
   1.120 +    VALIDATE();
   1.121 +}
   1.122 +
   1.123 +#ifdef SK_DEBUG
   1.124 +void GrBufferAllocPool::validate(bool unusedBlockAllowed) const {
   1.125 +    if (NULL != fBufferPtr) {
   1.126 +        SkASSERT(!fBlocks.empty());
   1.127 +        if (fBlocks.back().fBuffer->isLocked()) {
   1.128 +            GrGeometryBuffer* buf = fBlocks.back().fBuffer;
   1.129 +            SkASSERT(buf->lockPtr() == fBufferPtr);
   1.130 +        } else {
   1.131 +            SkASSERT(fCpuData.get() == fBufferPtr);
   1.132 +        }
   1.133 +    } else {
   1.134 +        SkASSERT(fBlocks.empty() || !fBlocks.back().fBuffer->isLocked());
   1.135 +    }
   1.136 +    size_t bytesInUse = 0;
   1.137 +    for (int i = 0; i < fBlocks.count() - 1; ++i) {
   1.138 +        SkASSERT(!fBlocks[i].fBuffer->isLocked());
   1.139 +    }
   1.140 +    for (int i = 0; i < fBlocks.count(); ++i) {
   1.141 +        size_t bytes = fBlocks[i].fBuffer->sizeInBytes() - fBlocks[i].fBytesFree;
   1.142 +        bytesInUse += bytes;
   1.143 +        SkASSERT(bytes || unusedBlockAllowed);
   1.144 +    }
   1.145 +
   1.146 +    SkASSERT(bytesInUse == fBytesInUse);
   1.147 +    if (unusedBlockAllowed) {
   1.148 +        SkASSERT((fBytesInUse && !fBlocks.empty()) ||
   1.149 +                 (!fBytesInUse && (fBlocks.count() < 2)));
   1.150 +    } else {
   1.151 +        SkASSERT((0 == fBytesInUse) == fBlocks.empty());
   1.152 +    }
   1.153 +}
   1.154 +#endif
   1.155 +
   1.156 +void* GrBufferAllocPool::makeSpace(size_t size,
   1.157 +                                   size_t alignment,
   1.158 +                                   const GrGeometryBuffer** buffer,
   1.159 +                                   size_t* offset) {
   1.160 +    VALIDATE();
   1.161 +
   1.162 +    SkASSERT(NULL != buffer);
   1.163 +    SkASSERT(NULL != offset);
   1.164 +
   1.165 +    if (NULL != fBufferPtr) {
   1.166 +        BufferBlock& back = fBlocks.back();
   1.167 +        size_t usedBytes = back.fBuffer->sizeInBytes() - back.fBytesFree;
   1.168 +        size_t pad = GrSizeAlignUpPad(usedBytes,
   1.169 +                                      alignment);
   1.170 +        if ((size + pad) <= back.fBytesFree) {
   1.171 +            usedBytes += pad;
   1.172 +            *offset = usedBytes;
   1.173 +            *buffer = back.fBuffer;
   1.174 +            back.fBytesFree -= size + pad;
   1.175 +            fBytesInUse += size + pad;
   1.176 +            VALIDATE();
   1.177 +            return (void*)(reinterpret_cast<intptr_t>(fBufferPtr) + usedBytes);
   1.178 +        }
   1.179 +    }
   1.180 +
   1.181 +    // We could honor the space request using by a partial update of the current
   1.182 +    // VB (if there is room). But we don't currently use draw calls to GL that
   1.183 +    // allow the driver to know that previously issued draws won't read from
   1.184 +    // the part of the buffer we update. Also, the GL buffer implementation
   1.185 +    // may be cheating on the actual buffer size by shrinking the buffer on
   1.186 +    // updateData() if the amount of data passed is less than the full buffer
   1.187 +    // size.
   1.188 +
   1.189 +    if (!createBlock(size)) {
   1.190 +        return NULL;
   1.191 +    }
   1.192 +    SkASSERT(NULL != fBufferPtr);
   1.193 +
   1.194 +    *offset = 0;
   1.195 +    BufferBlock& back = fBlocks.back();
   1.196 +    *buffer = back.fBuffer;
   1.197 +    back.fBytesFree -= size;
   1.198 +    fBytesInUse += size;
   1.199 +    VALIDATE();
   1.200 +    return fBufferPtr;
   1.201 +}
   1.202 +
   1.203 +int GrBufferAllocPool::currentBufferItems(size_t itemSize) const {
   1.204 +    VALIDATE();
   1.205 +    if (NULL != fBufferPtr) {
   1.206 +        const BufferBlock& back = fBlocks.back();
   1.207 +        size_t usedBytes = back.fBuffer->sizeInBytes() - back.fBytesFree;
   1.208 +        size_t pad = GrSizeAlignUpPad(usedBytes, itemSize);
   1.209 +        return static_cast<int>((back.fBytesFree - pad) / itemSize);
   1.210 +    } else if (fPreallocBuffersInUse < fPreallocBuffers.count()) {
   1.211 +        return static_cast<int>(fMinBlockSize / itemSize);
   1.212 +    }
   1.213 +    return 0;
   1.214 +}
   1.215 +
   1.216 +int GrBufferAllocPool::preallocatedBuffersRemaining() const {
   1.217 +    return fPreallocBuffers.count() - fPreallocBuffersInUse;
   1.218 +}
   1.219 +
   1.220 +int GrBufferAllocPool::preallocatedBufferCount() const {
   1.221 +    return fPreallocBuffers.count();
   1.222 +}
   1.223 +
   1.224 +void GrBufferAllocPool::putBack(size_t bytes) {
   1.225 +    VALIDATE();
   1.226 +
   1.227 +    // if the putBack unwinds all the preallocated buffers then we will
   1.228 +    // advance the starting index. As blocks are destroyed fPreallocBuffersInUse
   1.229 +    // will be decremented. I will reach zero if all blocks using preallocated
   1.230 +    // buffers are released.
   1.231 +    int preallocBuffersInUse = fPreallocBuffersInUse;
   1.232 +
   1.233 +    while (bytes) {
   1.234 +        // caller shouldnt try to put back more than they've taken
   1.235 +        SkASSERT(!fBlocks.empty());
   1.236 +        BufferBlock& block = fBlocks.back();
   1.237 +        size_t bytesUsed = block.fBuffer->sizeInBytes() - block.fBytesFree;
   1.238 +        if (bytes >= bytesUsed) {
   1.239 +            bytes -= bytesUsed;
   1.240 +            fBytesInUse -= bytesUsed;
   1.241 +            // if we locked a vb to satisfy the make space and we're releasing
   1.242 +            // beyond it, then unlock it.
   1.243 +            if (block.fBuffer->isLocked()) {
   1.244 +                block.fBuffer->unlock();
   1.245 +            }
   1.246 +            this->destroyBlock();
   1.247 +        } else {
   1.248 +            block.fBytesFree += bytes;
   1.249 +            fBytesInUse -= bytes;
   1.250 +            bytes = 0;
   1.251 +            break;
   1.252 +        }
   1.253 +    }
   1.254 +    if (!fPreallocBuffersInUse && fPreallocBuffers.count()) {
   1.255 +            fPreallocBufferStartIdx = (fPreallocBufferStartIdx +
   1.256 +                                       preallocBuffersInUse) %
   1.257 +                                      fPreallocBuffers.count();
   1.258 +    }
   1.259 +    VALIDATE();
   1.260 +}
   1.261 +
   1.262 +bool GrBufferAllocPool::createBlock(size_t requestSize) {
   1.263 +
   1.264 +    size_t size = GrMax(requestSize, fMinBlockSize);
   1.265 +    SkASSERT(size >= GrBufferAllocPool_MIN_BLOCK_SIZE);
   1.266 +
   1.267 +    VALIDATE();
   1.268 +
   1.269 +    BufferBlock& block = fBlocks.push_back();
   1.270 +
   1.271 +    if (size == fMinBlockSize &&
   1.272 +        fPreallocBuffersInUse < fPreallocBuffers.count()) {
   1.273 +
   1.274 +        uint32_t nextBuffer = (fPreallocBuffersInUse +
   1.275 +                               fPreallocBufferStartIdx) %
   1.276 +                              fPreallocBuffers.count();
   1.277 +        block.fBuffer = fPreallocBuffers[nextBuffer];
   1.278 +        block.fBuffer->ref();
   1.279 +        ++fPreallocBuffersInUse;
   1.280 +    } else {
   1.281 +        block.fBuffer = this->createBuffer(size);
   1.282 +        if (NULL == block.fBuffer) {
   1.283 +            fBlocks.pop_back();
   1.284 +            return false;
   1.285 +        }
   1.286 +    }
   1.287 +
   1.288 +    block.fBytesFree = size;
   1.289 +    if (NULL != fBufferPtr) {
   1.290 +        SkASSERT(fBlocks.count() > 1);
   1.291 +        BufferBlock& prev = fBlocks.fromBack(1);
   1.292 +        if (prev.fBuffer->isLocked()) {
   1.293 +            prev.fBuffer->unlock();
   1.294 +        } else {
   1.295 +            flushCpuData(prev.fBuffer,
   1.296 +                         prev.fBuffer->sizeInBytes() - prev.fBytesFree);
   1.297 +        }
   1.298 +        fBufferPtr = NULL;
   1.299 +    }
   1.300 +
   1.301 +    SkASSERT(NULL == fBufferPtr);
   1.302 +
   1.303 +    // If the buffer is CPU-backed we lock it because it is free to do so and saves a copy.
   1.304 +    // Otherwise when buffer locking is supported:
   1.305 +    //      a) If the frequently reset hint is set we only lock when the requested size meets a
   1.306 +    //      threshold (since we don't expect it is likely that we will see more vertex data)
   1.307 +    //      b) If the hint is not set we lock if the buffer size is greater than the threshold.
   1.308 +    bool attemptLock = block.fBuffer->isCPUBacked();
   1.309 +    if (!attemptLock && fGpu->caps()->bufferLockSupport()) {
   1.310 +        if (fFrequentResetHint) {
   1.311 +            attemptLock = requestSize > GR_GEOM_BUFFER_LOCK_THRESHOLD;
   1.312 +        } else {
   1.313 +            attemptLock = size > GR_GEOM_BUFFER_LOCK_THRESHOLD;
   1.314 +        }
   1.315 +    }
   1.316 +
   1.317 +    if (attemptLock) {
   1.318 +        fBufferPtr = block.fBuffer->lock();
   1.319 +    }
   1.320 +
   1.321 +    if (NULL == fBufferPtr) {
   1.322 +        fBufferPtr = fCpuData.reset(size);
   1.323 +    }
   1.324 +
   1.325 +    VALIDATE(true);
   1.326 +
   1.327 +    return true;
   1.328 +}
   1.329 +
   1.330 +void GrBufferAllocPool::destroyBlock() {
   1.331 +    SkASSERT(!fBlocks.empty());
   1.332 +
   1.333 +    BufferBlock& block = fBlocks.back();
   1.334 +    if (fPreallocBuffersInUse > 0) {
   1.335 +        uint32_t prevPreallocBuffer = (fPreallocBuffersInUse +
   1.336 +                                       fPreallocBufferStartIdx +
   1.337 +                                       (fPreallocBuffers.count() - 1)) %
   1.338 +                                      fPreallocBuffers.count();
   1.339 +        if (block.fBuffer == fPreallocBuffers[prevPreallocBuffer]) {
   1.340 +            --fPreallocBuffersInUse;
   1.341 +        }
   1.342 +    }
   1.343 +    SkASSERT(!block.fBuffer->isLocked());
   1.344 +    block.fBuffer->unref();
   1.345 +    fBlocks.pop_back();
   1.346 +    fBufferPtr = NULL;
   1.347 +}
   1.348 +
   1.349 +void GrBufferAllocPool::flushCpuData(GrGeometryBuffer* buffer,
   1.350 +                                     size_t flushSize) {
   1.351 +    SkASSERT(NULL != buffer);
   1.352 +    SkASSERT(!buffer->isLocked());
   1.353 +    SkASSERT(fCpuData.get() == fBufferPtr);
   1.354 +    SkASSERT(flushSize <= buffer->sizeInBytes());
   1.355 +    VALIDATE(true);
   1.356 +
   1.357 +    if (fGpu->caps()->bufferLockSupport() &&
   1.358 +        flushSize > GR_GEOM_BUFFER_LOCK_THRESHOLD) {
   1.359 +        void* data = buffer->lock();
   1.360 +        if (NULL != data) {
   1.361 +            memcpy(data, fBufferPtr, flushSize);
   1.362 +            buffer->unlock();
   1.363 +            return;
   1.364 +        }
   1.365 +    }
   1.366 +    buffer->updateData(fBufferPtr, flushSize);
   1.367 +    VALIDATE(true);
   1.368 +}
   1.369 +
   1.370 +GrGeometryBuffer* GrBufferAllocPool::createBuffer(size_t size) {
   1.371 +    if (kIndex_BufferType == fBufferType) {
   1.372 +        return fGpu->createIndexBuffer(size, true);
   1.373 +    } else {
   1.374 +        SkASSERT(kVertex_BufferType == fBufferType);
   1.375 +        return fGpu->createVertexBuffer(size, true);
   1.376 +    }
   1.377 +}
   1.378 +
   1.379 +////////////////////////////////////////////////////////////////////////////////
   1.380 +
   1.381 +GrVertexBufferAllocPool::GrVertexBufferAllocPool(GrGpu* gpu,
   1.382 +                                                 bool frequentResetHint,
   1.383 +                                                 size_t bufferSize,
   1.384 +                                                 int preallocBufferCnt)
   1.385 +: GrBufferAllocPool(gpu,
   1.386 +                    kVertex_BufferType,
   1.387 +                    frequentResetHint,
   1.388 +                    bufferSize,
   1.389 +                    preallocBufferCnt) {
   1.390 +}
   1.391 +
   1.392 +void* GrVertexBufferAllocPool::makeSpace(size_t vertexSize,
   1.393 +                                         int vertexCount,
   1.394 +                                         const GrVertexBuffer** buffer,
   1.395 +                                         int* startVertex) {
   1.396 +
   1.397 +    SkASSERT(vertexCount >= 0);
   1.398 +    SkASSERT(NULL != buffer);
   1.399 +    SkASSERT(NULL != startVertex);
   1.400 +
   1.401 +    size_t offset = 0; // assign to suppress warning
   1.402 +    const GrGeometryBuffer* geomBuffer = NULL; // assign to suppress warning
   1.403 +    void* ptr = INHERITED::makeSpace(vertexSize * vertexCount,
   1.404 +                                     vertexSize,
   1.405 +                                     &geomBuffer,
   1.406 +                                     &offset);
   1.407 +
   1.408 +    *buffer = (const GrVertexBuffer*) geomBuffer;
   1.409 +    SkASSERT(0 == offset % vertexSize);
   1.410 +    *startVertex = static_cast<int>(offset / vertexSize);
   1.411 +    return ptr;
   1.412 +}
   1.413 +
   1.414 +bool GrVertexBufferAllocPool::appendVertices(size_t vertexSize,
   1.415 +                                             int vertexCount,
   1.416 +                                             const void* vertices,
   1.417 +                                             const GrVertexBuffer** buffer,
   1.418 +                                             int* startVertex) {
   1.419 +    void* space = makeSpace(vertexSize, vertexCount, buffer, startVertex);
   1.420 +    if (NULL != space) {
   1.421 +        memcpy(space,
   1.422 +               vertices,
   1.423 +               vertexSize * vertexCount);
   1.424 +        return true;
   1.425 +    } else {
   1.426 +        return false;
   1.427 +    }
   1.428 +}
   1.429 +
   1.430 +int GrVertexBufferAllocPool::preallocatedBufferVertices(size_t vertexSize) const {
   1.431 +    return static_cast<int>(INHERITED::preallocatedBufferSize() / vertexSize);
   1.432 +}
   1.433 +
   1.434 +int GrVertexBufferAllocPool::currentBufferVertices(size_t vertexSize) const {
   1.435 +    return currentBufferItems(vertexSize);
   1.436 +}
   1.437 +
   1.438 +////////////////////////////////////////////////////////////////////////////////
   1.439 +
   1.440 +GrIndexBufferAllocPool::GrIndexBufferAllocPool(GrGpu* gpu,
   1.441 +                                               bool frequentResetHint,
   1.442 +                                               size_t bufferSize,
   1.443 +                                               int preallocBufferCnt)
   1.444 +: GrBufferAllocPool(gpu,
   1.445 +                    kIndex_BufferType,
   1.446 +                    frequentResetHint,
   1.447 +                    bufferSize,
   1.448 +                    preallocBufferCnt) {
   1.449 +}
   1.450 +
   1.451 +void* GrIndexBufferAllocPool::makeSpace(int indexCount,
   1.452 +                                        const GrIndexBuffer** buffer,
   1.453 +                                        int* startIndex) {
   1.454 +
   1.455 +    SkASSERT(indexCount >= 0);
   1.456 +    SkASSERT(NULL != buffer);
   1.457 +    SkASSERT(NULL != startIndex);
   1.458 +
   1.459 +    size_t offset = 0; // assign to suppress warning
   1.460 +    const GrGeometryBuffer* geomBuffer = NULL; // assign to suppress warning
   1.461 +    void* ptr = INHERITED::makeSpace(indexCount * sizeof(uint16_t),
   1.462 +                                     sizeof(uint16_t),
   1.463 +                                     &geomBuffer,
   1.464 +                                     &offset);
   1.465 +
   1.466 +    *buffer = (const GrIndexBuffer*) geomBuffer;
   1.467 +    SkASSERT(0 == offset % sizeof(uint16_t));
   1.468 +    *startIndex = static_cast<int>(offset / sizeof(uint16_t));
   1.469 +    return ptr;
   1.470 +}
   1.471 +
   1.472 +bool GrIndexBufferAllocPool::appendIndices(int indexCount,
   1.473 +                                           const void* indices,
   1.474 +                                           const GrIndexBuffer** buffer,
   1.475 +                                           int* startIndex) {
   1.476 +    void* space = makeSpace(indexCount, buffer, startIndex);
   1.477 +    if (NULL != space) {
   1.478 +        memcpy(space, indices, sizeof(uint16_t) * indexCount);
   1.479 +        return true;
   1.480 +    } else {
   1.481 +        return false;
   1.482 +    }
   1.483 +}
   1.484 +
   1.485 +int GrIndexBufferAllocPool::preallocatedBufferIndices() const {
   1.486 +    return static_cast<int>(INHERITED::preallocatedBufferSize() / sizeof(uint16_t));
   1.487 +}
   1.488 +
   1.489 +int GrIndexBufferAllocPool::currentBufferIndices() const {
   1.490 +    return currentBufferItems(sizeof(uint16_t));
   1.491 +}

mercurial