| /* |
| Copyright 2010 Google Inc. |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| */ |
| |
| #include "GrBufferAllocPool.h" |
| #include "GrTypes.h" |
| #include "GrVertexBuffer.h" |
| #include "GrIndexBuffer.h" |
| #include "GrGpu.h" |
| |
| #if GR_DEBUG |
| #define VALIDATE validate |
| #else |
| #define VALIDATE() |
| #endif |
| |
| #define GrBufferAllocPool_MIN_BLOCK_SIZE ((size_t)1 << 12) |
| |
| GrBufferAllocPool::GrBufferAllocPool(GrGpu* gpu, |
| BufferType bufferType, |
| bool frequentResetHint, |
| size_t blockSize, |
| int preallocBufferCnt) : |
| fBlocks(GrMax(8, 2*preallocBufferCnt)) { |
| |
| GrAssert(NULL != gpu); |
| fGpu = gpu; |
| fGpu->ref(); |
| fGpuIsReffed = true; |
| |
| fBufferType = bufferType; |
| fFrequentResetHint = frequentResetHint; |
| fBufferPtr = NULL; |
| fMinBlockSize = GrMax(GrBufferAllocPool_MIN_BLOCK_SIZE, blockSize); |
| |
| fPreallocBuffersInUse = 0; |
| fFirstPreallocBuffer = 0; |
| for (int i = 0; i < preallocBufferCnt; ++i) { |
| GrGeometryBuffer* buffer = this->createBuffer(fMinBlockSize); |
| if (NULL != buffer) { |
| *fPreallocBuffers.append() = buffer; |
| buffer->ref(); |
| } |
| } |
| } |
| |
| GrBufferAllocPool::~GrBufferAllocPool() { |
| VALIDATE(); |
| if (fBlocks.count()) { |
| GrGeometryBuffer* buffer = fBlocks.back().fBuffer; |
| if (buffer->isLocked()) { |
| buffer->unlock(); |
| } |
| } |
| while (!fBlocks.empty()) { |
| destroyBlock(); |
| } |
| fPreallocBuffers.unrefAll(); |
| releaseGpuRef(); |
| } |
| |
| void GrBufferAllocPool::releaseGpuRef() { |
| if (fGpuIsReffed) { |
| fGpu->unref(); |
| fGpuIsReffed = false; |
| } |
| } |
| |
| void GrBufferAllocPool::reset() { |
| VALIDATE(); |
| if (fBlocks.count()) { |
| GrGeometryBuffer* buffer = fBlocks.back().fBuffer; |
| if (buffer->isLocked()) { |
| buffer->unlock(); |
| } |
| } |
| while (!fBlocks.empty()) { |
| destroyBlock(); |
| } |
| if (fPreallocBuffers.count()) { |
| // must set this after above loop. |
| fFirstPreallocBuffer = (fFirstPreallocBuffer + fPreallocBuffersInUse) % |
| fPreallocBuffers.count(); |
| } |
| fCpuData.realloc(fGpu->supportsBufferLocking() ? 0 : fMinBlockSize); |
| GrAssert(0 == fPreallocBuffersInUse); |
| VALIDATE(); |
| } |
| |
| void GrBufferAllocPool::unlock() { |
| VALIDATE(); |
| |
| if (NULL != fBufferPtr) { |
| BufferBlock& block = fBlocks.back(); |
| if (block.fBuffer->isLocked()) { |
| block.fBuffer->unlock(); |
| } else { |
| size_t flushSize = block.fBuffer->size() - block.fBytesFree; |
| flushCpuData(fBlocks.back().fBuffer, flushSize); |
| } |
| fBufferPtr = NULL; |
| } |
| VALIDATE(); |
| } |
| |
| #if GR_DEBUG |
| void GrBufferAllocPool::validate() const { |
| if (NULL != fBufferPtr) { |
| GrAssert(!fBlocks.empty()); |
| if (fBlocks.back().fBuffer->isLocked()) { |
| GrGeometryBuffer* buf = fBlocks.back().fBuffer; |
| GrAssert(buf->lockPtr() == fBufferPtr); |
| } else { |
| GrAssert(fCpuData.get() == fBufferPtr); |
| GrAssert(fCpuData.size() == fBlocks.back().fBuffer->size()); |
| } |
| } else { |
| GrAssert(fBlocks.empty() || !fBlocks.back().fBuffer->isLocked()); |
| } |
| for (int i = 0; i < fBlocks.count() - 1; ++i) { |
| GrAssert(!fBlocks[i].fBuffer->isLocked()); |
| } |
| } |
| #endif |
| |
| void* GrBufferAllocPool::makeSpace(size_t size, |
| size_t alignment, |
| const GrGeometryBuffer** buffer, |
| size_t* offset) { |
| VALIDATE(); |
| |
| GrAssert(NULL != buffer); |
| GrAssert(NULL != offset); |
| |
| if (NULL != fBufferPtr) { |
| BufferBlock& back = fBlocks.back(); |
| size_t usedBytes = back.fBuffer->size() - back.fBytesFree; |
| size_t pad = GrSizeAlignUpPad(usedBytes, |
| alignment); |
| if ((size + pad) <= back.fBytesFree) { |
| usedBytes += pad; |
| *offset = usedBytes; |
| *buffer = back.fBuffer; |
| back.fBytesFree -= size + pad; |
| return (void*)(reinterpret_cast<intptr_t>(fBufferPtr) + usedBytes); |
| } |
| } |
| |
| if (!createBlock(size)) { |
| return NULL; |
| } |
| VALIDATE(); |
| GrAssert(NULL != fBufferPtr); |
| |
| *offset = 0; |
| BufferBlock& back = fBlocks.back(); |
| *buffer = back.fBuffer; |
| back.fBytesFree -= size; |
| return fBufferPtr; |
| } |
| |
| int GrBufferAllocPool::currentBufferItems(size_t itemSize) const { |
| VALIDATE(); |
| if (NULL != fBufferPtr) { |
| const BufferBlock& back = fBlocks.back(); |
| size_t usedBytes = back.fBuffer->size() - back.fBytesFree; |
| size_t pad = GrSizeAlignUpPad(usedBytes, itemSize); |
| return (back.fBytesFree - pad) / itemSize; |
| } else if (fPreallocBuffersInUse < fPreallocBuffers.count()) { |
| return fMinBlockSize / itemSize; |
| } |
| return 0; |
| } |
| |
| int GrBufferAllocPool::preallocatedBuffersRemaining() const { |
| return fPreallocBuffers.count() - fPreallocBuffersInUse; |
| } |
| |
| int GrBufferAllocPool::preallocatedBufferCount() const { |
| return fPreallocBuffers.count(); |
| } |
| |
| void GrBufferAllocPool::putBack(size_t bytes) { |
| VALIDATE(); |
| if (NULL != fBufferPtr) { |
| BufferBlock& back = fBlocks.back(); |
| size_t bytesUsed = back.fBuffer->size() - back.fBytesFree; |
| if (bytes >= bytesUsed) { |
| destroyBlock(); |
| bytes -= bytesUsed; |
| } else { |
| back.fBytesFree += bytes; |
| return; |
| } |
| } |
| VALIDATE(); |
| GrAssert(NULL == fBufferPtr); |
| // we don't partially roll-back buffers because our VB semantics say locking |
| // a VB discards its previous content. |
| // We could honor it by being sure we use updateSubData and not lock |
| // we will roll-back fully released buffers, though. |
| while (!fBlocks.empty() && |
| bytes >= fBlocks.back().fBuffer->size()) { |
| bytes -= fBlocks.back().fBuffer->size(); |
| destroyBlock(); |
| } |
| VALIDATE(); |
| } |
| |
| bool GrBufferAllocPool::createBlock(size_t requestSize) { |
| |
| size_t size = GrMax(requestSize, fMinBlockSize); |
| GrAssert(size >= GrBufferAllocPool_MIN_BLOCK_SIZE); |
| |
| VALIDATE(); |
| |
| BufferBlock& block = fBlocks.push_back(); |
| |
| if (size == fMinBlockSize && |
| fPreallocBuffersInUse < fPreallocBuffers.count()) { |
| |
| uint32_t nextBuffer = (fPreallocBuffersInUse + fFirstPreallocBuffer) % |
| fPreallocBuffers.count(); |
| block.fBuffer = fPreallocBuffers[nextBuffer]; |
| block.fBuffer->ref(); |
| ++fPreallocBuffersInUse; |
| } else { |
| block.fBuffer = this->createBuffer(size); |
| if (NULL == block.fBuffer) { |
| fBlocks.pop_back(); |
| return false; |
| } |
| } |
| |
| block.fBytesFree = size; |
| if (NULL != fBufferPtr) { |
| GrAssert(fBlocks.count() > 1); |
| BufferBlock& prev = fBlocks.fromBack(1); |
| if (prev.fBuffer->isLocked()) { |
| prev.fBuffer->unlock(); |
| } else { |
| flushCpuData(prev.fBuffer, |
| prev.fBuffer->size() - prev.fBytesFree); |
| } |
| fBufferPtr = NULL; |
| } |
| |
| GrAssert(NULL == fBufferPtr); |
| |
| if (fGpu->supportsBufferLocking() && |
| size > GR_GEOM_BUFFER_LOCK_THRESHOLD && |
| (!fFrequentResetHint || requestSize > GR_GEOM_BUFFER_LOCK_THRESHOLD)) { |
| fBufferPtr = block.fBuffer->lock(); |
| } |
| |
| if (NULL == fBufferPtr) { |
| fBufferPtr = fCpuData.realloc(size); |
| } |
| |
| VALIDATE(); |
| |
| return true; |
| } |
| |
| void GrBufferAllocPool::destroyBlock() { |
| GrAssert(!fBlocks.empty()); |
| |
| BufferBlock& block = fBlocks.back(); |
| if (fPreallocBuffersInUse > 0) { |
| uint32_t prevPreallocBuffer = (fPreallocBuffersInUse + |
| fFirstPreallocBuffer + |
| (fPreallocBuffers.count() - 1)) % |
| fPreallocBuffers.count(); |
| if (block.fBuffer == fPreallocBuffers[prevPreallocBuffer]) { |
| --fPreallocBuffersInUse; |
| } |
| } |
| GrAssert(!block.fBuffer->isLocked()); |
| block.fBuffer->unref(); |
| fBlocks.pop_back(); |
| fBufferPtr = NULL; |
| } |
| |
| void GrBufferAllocPool::flushCpuData(GrGeometryBuffer* buffer, |
| size_t flushSize) { |
| GrAssert(NULL != buffer); |
| GrAssert(!buffer->isLocked()); |
| GrAssert(fCpuData.get() == fBufferPtr); |
| GrAssert(fCpuData.size() == buffer->size()); |
| GrAssert(flushSize <= buffer->size()); |
| |
| bool updated = false; |
| if (fGpu->supportsBufferLocking() && |
| flushSize > GR_GEOM_BUFFER_LOCK_THRESHOLD) { |
| void* data = buffer->lock(); |
| if (NULL != data) { |
| memcpy(data, fBufferPtr, flushSize); |
| buffer->unlock(); |
| updated = true; |
| } |
| } |
| buffer->updateData(fBufferPtr, flushSize); |
| } |
| |
| GrGeometryBuffer* GrBufferAllocPool::createBuffer(size_t size) { |
| if (kIndex_BufferType == fBufferType) { |
| return fGpu->createIndexBuffer(size, true); |
| } else { |
| GrAssert(kVertex_BufferType == fBufferType); |
| return fGpu->createVertexBuffer(size, true); |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| GrVertexBufferAllocPool::GrVertexBufferAllocPool(GrGpu* gpu, |
| bool frequentResetHint, |
| size_t bufferSize, |
| int preallocBufferCnt) |
| : GrBufferAllocPool(gpu, |
| kVertex_BufferType, |
| frequentResetHint, |
| bufferSize, |
| preallocBufferCnt) { |
| } |
| |
| void* GrVertexBufferAllocPool::makeSpace(GrVertexLayout layout, |
| int vertexCount, |
| const GrVertexBuffer** buffer, |
| int* startVertex) { |
| |
| GrAssert(vertexCount >= 0); |
| GrAssert(NULL != buffer); |
| GrAssert(NULL != startVertex); |
| |
| size_t vSize = GrDrawTarget::VertexSize(layout); |
| size_t offset = 0; // assign to suppress warning |
| const GrGeometryBuffer* geomBuffer = NULL; // assign to suppress warning |
| void* ptr = INHERITED::makeSpace(vSize * vertexCount, |
| vSize, |
| &geomBuffer, |
| &offset); |
| |
| *buffer = (const GrVertexBuffer*) geomBuffer; |
| GrAssert(0 == offset % vSize); |
| *startVertex = offset / vSize; |
| return ptr; |
| } |
| |
| bool GrVertexBufferAllocPool::appendVertices(GrVertexLayout layout, |
| int vertexCount, |
| const void* vertices, |
| const GrVertexBuffer** buffer, |
| int* startVertex) { |
| void* space = makeSpace(layout, vertexCount, buffer, startVertex); |
| if (NULL != space) { |
| memcpy(space, |
| vertices, |
| GrDrawTarget::VertexSize(layout) * vertexCount); |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| int GrVertexBufferAllocPool::preallocatedBufferVertices(GrVertexLayout layout) const { |
| return INHERITED::preallocatedBufferSize() / |
| GrDrawTarget::VertexSize(layout); |
| } |
| |
| int GrVertexBufferAllocPool::currentBufferVertices(GrVertexLayout layout) const { |
| return currentBufferItems(GrDrawTarget::VertexSize(layout)); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| GrIndexBufferAllocPool::GrIndexBufferAllocPool(GrGpu* gpu, |
| bool frequentResetHint, |
| size_t bufferSize, |
| int preallocBufferCnt) |
| : GrBufferAllocPool(gpu, |
| kIndex_BufferType, |
| frequentResetHint, |
| bufferSize, |
| preallocBufferCnt) { |
| } |
| |
| void* GrIndexBufferAllocPool::makeSpace(int indexCount, |
| const GrIndexBuffer** buffer, |
| int* startIndex) { |
| |
| GrAssert(indexCount >= 0); |
| GrAssert(NULL != buffer); |
| GrAssert(NULL != startIndex); |
| |
| size_t offset = 0; // assign to suppress warning |
| const GrGeometryBuffer* geomBuffer = NULL; // assign to suppress warning |
| void* ptr = INHERITED::makeSpace(indexCount * sizeof(uint16_t), |
| sizeof(uint16_t), |
| &geomBuffer, |
| &offset); |
| |
| *buffer = (const GrIndexBuffer*) geomBuffer; |
| GrAssert(0 == offset % sizeof(uint16_t)); |
| *startIndex = offset / sizeof(uint16_t); |
| return ptr; |
| } |
| |
| bool GrIndexBufferAllocPool::appendIndices(int indexCount, |
| const void* indices, |
| const GrIndexBuffer** buffer, |
| int* startIndex) { |
| void* space = makeSpace(indexCount, buffer, startIndex); |
| if (NULL != space) { |
| memcpy(space, indices, sizeof(uint16_t) * indexCount); |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| int GrIndexBufferAllocPool::preallocatedBufferIndices() const { |
| return INHERITED::preallocatedBufferSize() / sizeof(uint16_t); |
| } |
| |
| int GrIndexBufferAllocPool::currentBufferIndices() const { |
| return currentBufferItems(sizeof(uint16_t)); |
| } |