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(GrMax(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 = GrMax(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->isLocked()) { 60 buffer->unlock(); 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->isLocked()) { 83 buffer->unlock(); 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::unlock() { 105 VALIDATE(); 106 107 if (NULL != fBufferPtr) { 108 BufferBlock& block = fBlocks.back(); 109 if (block.fBuffer->isLocked()) { 110 block.fBuffer->unlock(); 111 } else { 112 size_t flushSize = block.fBuffer->sizeInBytes() - block.fBytesFree; 113 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->isLocked()) { 125 GrGeometryBuffer* buf = fBlocks.back().fBuffer; 126 SkASSERT(buf->lockPtr() == fBufferPtr); 127 } else { 128 SkASSERT(fCpuData.get() == fBufferPtr); 129 } 130 } else { 131 SkASSERT(fBlocks.empty() || !fBlocks.back().fBuffer->isLocked()); 132 } 133 size_t bytesInUse = 0; 134 for (int i = 0; i < fBlocks.count() - 1; ++i) { 135 SkASSERT(!fBlocks[i].fBuffer->isLocked()); 136 } 137 for (int i = 0; i < fBlocks.count(); ++i) { 138 size_t bytes = fBlocks[i].fBuffer->sizeInBytes() - 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->sizeInBytes() - 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->sizeInBytes() - 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->sizeInBytes() - 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 unlock it. 240 if (block.fBuffer->isLocked()) { 241 block.fBuffer->unlock(); 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 = GrMax(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->isLocked()) { 290 prev.fBuffer->unlock(); 291 } else { 292 flushCpuData(prev.fBuffer, 293 prev.fBuffer->sizeInBytes() - prev.fBytesFree); 294 } 295 fBufferPtr = NULL; 296 } 297 298 SkASSERT(NULL == fBufferPtr); 299 300 // If the buffer is CPU-backed we lock it because it is free to do so and saves a copy. 301 // Otherwise when buffer locking is supported: 302 // a) If the frequently reset hint is set we only lock 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 lock if the buffer size is greater than the threshold. 305 bool attemptLock = block.fBuffer->isCPUBacked(); 306 if (!attemptLock && fGpu->caps()->bufferLockSupport()) { 307 if (fFrequentResetHint) { 308 attemptLock = requestSize > GR_GEOM_BUFFER_LOCK_THRESHOLD; 309 } else { 310 attemptLock = size > GR_GEOM_BUFFER_LOCK_THRESHOLD; 311 } 312 } 313 314 if (attemptLock) { 315 fBufferPtr = block.fBuffer->lock(); 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->isLocked()); 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->isLocked()); 350 SkASSERT(fCpuData.get() == fBufferPtr); 351 SkASSERT(flushSize <= buffer->sizeInBytes()); 352 VALIDATE(true); 353 354 if (fGpu->caps()->bufferLockSupport() && 355 flushSize > GR_GEOM_BUFFER_LOCK_THRESHOLD) { 356 void* data = buffer->lock(); 357 if (NULL != data) { 358 memcpy(data, fBufferPtr, flushSize); 359 buffer->unlock(); 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