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 +}