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