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