1 /* 2 Copyright 2010 Google Inc. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 #include "GrBufferAllocPool.h" 18 #include "GrTypes.h" 19 #include "GrVertexBuffer.h" 20 #include "GrIndexBuffer.h" 21 #include "GrGpu.h" 22 23 #if GR_DEBUG 24 #define VALIDATE validate 25 #else 26 #define VALIDATE() 27 #endif 28 29 #define GrBufferAllocPool_MIN_BLOCK_SIZE ((size_t)1 << 12) 30 31 GrBufferAllocPool::GrBufferAllocPool(GrGpu* gpu, 32 BufferType bufferType, 33 bool frequentResetHint, 34 size_t blockSize, 35 int preallocBufferCnt) : 36 fBlocks(GrMax(8, 2*preallocBufferCnt)) { 37 38 GrAssert(NULL != gpu); 39 fGpu = gpu; 40 fGpu->ref(); 41 fGpuIsReffed = true; 42 43 fBufferType = bufferType; 44 fFrequentResetHint = frequentResetHint; 45 fBufferPtr = NULL; 46 fMinBlockSize = GrMax(GrBufferAllocPool_MIN_BLOCK_SIZE, blockSize); 47 48 fPreallocBuffersInUse = 0; 49 fFirstPreallocBuffer = 0; 50 for (int i = 0; i < preallocBufferCnt; ++i) { 51 GrGeometryBuffer* buffer = this->createBuffer(fMinBlockSize); 52 if (NULL != buffer) { 53 *fPreallocBuffers.append() = buffer; 54 buffer->ref(); 55 } 56 } 57 } 58 59 GrBufferAllocPool::~GrBufferAllocPool() { 60 VALIDATE(); 61 if (fBlocks.count()) { 62 GrGeometryBuffer* buffer = fBlocks.back().fBuffer; 63 if (buffer->isLocked()) { 64 buffer->unlock(); 65 } 66 } 67 while (!fBlocks.empty()) { 68 destroyBlock(); 69 } 70 fPreallocBuffers.unrefAll(); 71 releaseGpuRef(); 72 } 73 74 void GrBufferAllocPool::releaseGpuRef() { 75 if (fGpuIsReffed) { 76 fGpu->unref(); 77 fGpuIsReffed = false; 78 } 79 } 80 81 void GrBufferAllocPool::reset() { 82 VALIDATE(); 83 if (fBlocks.count()) { 84 GrGeometryBuffer* buffer = fBlocks.back().fBuffer; 85 if (buffer->isLocked()) { 86 buffer->unlock(); 87 } 88 } 89 while (!fBlocks.empty()) { 90 destroyBlock(); 91 } 92 if (fPreallocBuffers.count()) { 93 // must set this after above loop. 94 fFirstPreallocBuffer = (fFirstPreallocBuffer + fPreallocBuffersInUse) % 95 fPreallocBuffers.count(); 96 } 97 fCpuData.realloc(fGpu->supportsBufferLocking() ? 0 : fMinBlockSize); 98 GrAssert(0 == fPreallocBuffersInUse); 99 VALIDATE(); 100 } 101 102 void GrBufferAllocPool::unlock() { 103 VALIDATE(); 104 105 if (NULL != fBufferPtr) { 106 BufferBlock& block = fBlocks.back(); 107 if (block.fBuffer->isLocked()) { 108 block.fBuffer->unlock(); 109 } else { 110 size_t flushSize = block.fBuffer->size() - block.fBytesFree; 111 flushCpuData(fBlocks.back().fBuffer, flushSize); 112 } 113 fBufferPtr = NULL; 114 } 115 VALIDATE(); 116 } 117 118 #if GR_DEBUG 119 void GrBufferAllocPool::validate() const { 120 if (NULL != fBufferPtr) { 121 GrAssert(!fBlocks.empty()); 122 if (fBlocks.back().fBuffer->isLocked()) { 123 GrGeometryBuffer* buf = fBlocks.back().fBuffer; 124 GrAssert(buf->lockPtr() == fBufferPtr); 125 } else { 126 GrAssert(fCpuData.get() == fBufferPtr); 127 GrAssert(fCpuData.size() == fBlocks.back().fBuffer->size()); 128 } 129 } else { 130 GrAssert(fBlocks.empty() || !fBlocks.back().fBuffer->isLocked()); 131 } 132 for (int i = 0; i < fBlocks.count() - 1; ++i) { 133 GrAssert(!fBlocks[i].fBuffer->isLocked()); 134 } 135 } 136 #endif 137 138 void* GrBufferAllocPool::makeSpace(size_t size, 139 size_t alignment, 140 const GrGeometryBuffer** buffer, 141 size_t* offset) { 142 VALIDATE(); 143 144 GrAssert(NULL != buffer); 145 GrAssert(NULL != offset); 146 147 if (NULL != fBufferPtr) { 148 BufferBlock& back = fBlocks.back(); 149 size_t usedBytes = back.fBuffer->size() - back.fBytesFree; 150 size_t pad = GrSizeAlignUpPad(usedBytes, 151 alignment); 152 if ((size + pad) <= back.fBytesFree) { 153 usedBytes += pad; 154 *offset = usedBytes; 155 *buffer = back.fBuffer; 156 back.fBytesFree -= size + pad; 157 return (void*)(reinterpret_cast<intptr_t>(fBufferPtr) + usedBytes); 158 } 159 } 160 161 if (!createBlock(size)) { 162 return NULL; 163 } 164 VALIDATE(); 165 GrAssert(NULL != fBufferPtr); 166 167 *offset = 0; 168 BufferBlock& back = fBlocks.back(); 169 *buffer = back.fBuffer; 170 back.fBytesFree -= size; 171 return fBufferPtr; 172 } 173 174 int GrBufferAllocPool::currentBufferItems(size_t itemSize) const { 175 VALIDATE(); 176 if (NULL != fBufferPtr) { 177 const BufferBlock& back = fBlocks.back(); 178 size_t usedBytes = back.fBuffer->size() - back.fBytesFree; 179 size_t pad = GrSizeAlignUpPad(usedBytes, itemSize); 180 return (back.fBytesFree - pad) / itemSize; 181 } else if (fPreallocBuffersInUse < fPreallocBuffers.count()) { 182 return fMinBlockSize / itemSize; 183 } 184 return 0; 185 } 186 187 int GrBufferAllocPool::preallocatedBuffersRemaining() const { 188 return fPreallocBuffers.count() - fPreallocBuffersInUse; 189 } 190 191 int GrBufferAllocPool::preallocatedBufferCount() const { 192 return fPreallocBuffers.count(); 193 } 194 195 void GrBufferAllocPool::putBack(size_t bytes) { 196 VALIDATE(); 197 if (NULL != fBufferPtr) { 198 BufferBlock& back = fBlocks.back(); 199 size_t bytesUsed = back.fBuffer->size() - back.fBytesFree; 200 if (bytes >= bytesUsed) { 201 destroyBlock(); 202 bytes -= bytesUsed; 203 } else { 204 back.fBytesFree += bytes; 205 return; 206 } 207 } 208 VALIDATE(); 209 GrAssert(NULL == fBufferPtr); 210 // we don't partially roll-back buffers because our VB semantics say locking 211 // a VB discards its previous content. 212 // We could honor it by being sure we use updateSubData and not lock 213 // we will roll-back fully released buffers, though. 214 while (!fBlocks.empty() && 215 bytes >= fBlocks.back().fBuffer->size()) { 216 bytes -= fBlocks.back().fBuffer->size(); 217 destroyBlock(); 218 } 219 VALIDATE(); 220 } 221 222 bool GrBufferAllocPool::createBlock(size_t requestSize) { 223 224 size_t size = GrMax(requestSize, fMinBlockSize); 225 GrAssert(size >= GrBufferAllocPool_MIN_BLOCK_SIZE); 226 227 VALIDATE(); 228 229 BufferBlock& block = fBlocks.push_back(); 230 231 if (size == fMinBlockSize && 232 fPreallocBuffersInUse < fPreallocBuffers.count()) { 233 234 uint32_t nextBuffer = (fPreallocBuffersInUse + fFirstPreallocBuffer) % 235 fPreallocBuffers.count(); 236 block.fBuffer = fPreallocBuffers[nextBuffer]; 237 block.fBuffer->ref(); 238 ++fPreallocBuffersInUse; 239 } else { 240 block.fBuffer = this->createBuffer(size); 241 if (NULL == block.fBuffer) { 242 fBlocks.pop_back(); 243 return false; 244 } 245 } 246 247 block.fBytesFree = size; 248 if (NULL != fBufferPtr) { 249 GrAssert(fBlocks.count() > 1); 250 BufferBlock& prev = fBlocks.fromBack(1); 251 if (prev.fBuffer->isLocked()) { 252 prev.fBuffer->unlock(); 253 } else { 254 flushCpuData(prev.fBuffer, 255 prev.fBuffer->size() - prev.fBytesFree); 256 } 257 fBufferPtr = NULL; 258 } 259 260 GrAssert(NULL == fBufferPtr); 261 262 if (fGpu->supportsBufferLocking() && 263 size > GR_GEOM_BUFFER_LOCK_THRESHOLD && 264 (!fFrequentResetHint || requestSize > GR_GEOM_BUFFER_LOCK_THRESHOLD)) { 265 fBufferPtr = block.fBuffer->lock(); 266 } 267 268 if (NULL == fBufferPtr) { 269 fBufferPtr = fCpuData.realloc(size); 270 } 271 272 VALIDATE(); 273 274 return true; 275 } 276 277 void GrBufferAllocPool::destroyBlock() { 278 GrAssert(!fBlocks.empty()); 279 280 BufferBlock& block = fBlocks.back(); 281 if (fPreallocBuffersInUse > 0) { 282 uint32_t prevPreallocBuffer = (fPreallocBuffersInUse + 283 fFirstPreallocBuffer + 284 (fPreallocBuffers.count() - 1)) % 285 fPreallocBuffers.count(); 286 if (block.fBuffer == fPreallocBuffers[prevPreallocBuffer]) { 287 --fPreallocBuffersInUse; 288 } 289 } 290 GrAssert(!block.fBuffer->isLocked()); 291 block.fBuffer->unref(); 292 fBlocks.pop_back(); 293 fBufferPtr = NULL; 294 } 295 296 void GrBufferAllocPool::flushCpuData(GrGeometryBuffer* buffer, 297 size_t flushSize) { 298 GrAssert(NULL != buffer); 299 GrAssert(!buffer->isLocked()); 300 GrAssert(fCpuData.get() == fBufferPtr); 301 GrAssert(fCpuData.size() == buffer->size()); 302 GrAssert(flushSize <= buffer->size()); 303 304 bool updated = false; 305 if (fGpu->supportsBufferLocking() && 306 flushSize > GR_GEOM_BUFFER_LOCK_THRESHOLD) { 307 void* data = buffer->lock(); 308 if (NULL != data) { 309 memcpy(data, fBufferPtr, flushSize); 310 buffer->unlock(); 311 updated = true; 312 } 313 } 314 buffer->updateData(fBufferPtr, flushSize); 315 } 316 317 GrGeometryBuffer* GrBufferAllocPool::createBuffer(size_t size) { 318 if (kIndex_BufferType == fBufferType) { 319 return fGpu->createIndexBuffer(size, true); 320 } else { 321 GrAssert(kVertex_BufferType == fBufferType); 322 return fGpu->createVertexBuffer(size, true); 323 } 324 } 325 326 //////////////////////////////////////////////////////////////////////////////// 327 328 GrVertexBufferAllocPool::GrVertexBufferAllocPool(GrGpu* gpu, 329 bool frequentResetHint, 330 size_t bufferSize, 331 int preallocBufferCnt) 332 : GrBufferAllocPool(gpu, 333 kVertex_BufferType, 334 frequentResetHint, 335 bufferSize, 336 preallocBufferCnt) { 337 } 338 339 void* GrVertexBufferAllocPool::makeSpace(GrVertexLayout layout, 340 int vertexCount, 341 const GrVertexBuffer** buffer, 342 int* startVertex) { 343 344 GrAssert(vertexCount >= 0); 345 GrAssert(NULL != buffer); 346 GrAssert(NULL != startVertex); 347 348 size_t vSize = GrDrawTarget::VertexSize(layout); 349 size_t offset = 0; // assign to suppress warning 350 const GrGeometryBuffer* geomBuffer = NULL; // assign to suppress warning 351 void* ptr = INHERITED::makeSpace(vSize * vertexCount, 352 vSize, 353 &geomBuffer, 354 &offset); 355 356 *buffer = (const GrVertexBuffer*) geomBuffer; 357 GrAssert(0 == offset % vSize); 358 *startVertex = offset / vSize; 359 return ptr; 360 } 361 362 bool GrVertexBufferAllocPool::appendVertices(GrVertexLayout layout, 363 int vertexCount, 364 const void* vertices, 365 const GrVertexBuffer** buffer, 366 int* startVertex) { 367 void* space = makeSpace(layout, vertexCount, buffer, startVertex); 368 if (NULL != space) { 369 memcpy(space, 370 vertices, 371 GrDrawTarget::VertexSize(layout) * vertexCount); 372 return true; 373 } else { 374 return false; 375 } 376 } 377 378 int GrVertexBufferAllocPool::preallocatedBufferVertices(GrVertexLayout layout) const { 379 return INHERITED::preallocatedBufferSize() / 380 GrDrawTarget::VertexSize(layout); 381 } 382 383 int GrVertexBufferAllocPool::currentBufferVertices(GrVertexLayout layout) const { 384 return currentBufferItems(GrDrawTarget::VertexSize(layout)); 385 } 386 387 //////////////////////////////////////////////////////////////////////////////// 388 389 GrIndexBufferAllocPool::GrIndexBufferAllocPool(GrGpu* gpu, 390 bool frequentResetHint, 391 size_t bufferSize, 392 int preallocBufferCnt) 393 : GrBufferAllocPool(gpu, 394 kIndex_BufferType, 395 frequentResetHint, 396 bufferSize, 397 preallocBufferCnt) { 398 } 399 400 void* GrIndexBufferAllocPool::makeSpace(int indexCount, 401 const GrIndexBuffer** buffer, 402 int* startIndex) { 403 404 GrAssert(indexCount >= 0); 405 GrAssert(NULL != buffer); 406 GrAssert(NULL != startIndex); 407 408 size_t offset = 0; // assign to suppress warning 409 const GrGeometryBuffer* geomBuffer = NULL; // assign to suppress warning 410 void* ptr = INHERITED::makeSpace(indexCount * sizeof(uint16_t), 411 sizeof(uint16_t), 412 &geomBuffer, 413 &offset); 414 415 *buffer = (const GrIndexBuffer*) geomBuffer; 416 GrAssert(0 == offset % sizeof(uint16_t)); 417 *startIndex = offset / sizeof(uint16_t); 418 return ptr; 419 } 420 421 bool GrIndexBufferAllocPool::appendIndices(int indexCount, 422 const void* indices, 423 const GrIndexBuffer** buffer, 424 int* startIndex) { 425 void* space = makeSpace(indexCount, buffer, startIndex); 426 if (NULL != space) { 427 memcpy(space, indices, sizeof(uint16_t) * indexCount); 428 return true; 429 } else { 430 return false; 431 } 432 } 433 434 int GrIndexBufferAllocPool::preallocatedBufferIndices() const { 435 return INHERITED::preallocatedBufferSize() / sizeof(uint16_t); 436 } 437 438 int GrIndexBufferAllocPool::currentBufferIndices() const { 439 return currentBufferItems(sizeof(uint16_t)); 440 } 441