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