blob: d029471a9e52ef7a29502bee86878e0462444c37 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
bsalomon@google.com1c13c962011-02-14 16:51:21 +00002/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00003 * 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.
bsalomon@google.com1c13c962011-02-14 16:51:21 +00007 */
8
epoger@google.comec3ed6a2011-07-28 14:26:00 +00009
bsalomon@google.com1c13c962011-02-14 16:51:21 +000010#include "GrBufferAllocPool.h"
11#include "GrTypes.h"
12#include "GrVertexBuffer.h"
13#include "GrIndexBuffer.h"
14#include "GrGpu.h"
15
16#if GR_DEBUG
17 #define VALIDATE validate
18#else
bsalomon@google.com25fb21f2011-06-21 18:17:25 +000019 static void VALIDATE(bool x = false) {}
bsalomon@google.com1c13c962011-02-14 16:51:21 +000020#endif
21
bsalomon@google.com25fb21f2011-06-21 18:17:25 +000022// page size
bsalomon@google.com1c13c962011-02-14 16:51:21 +000023#define GrBufferAllocPool_MIN_BLOCK_SIZE ((size_t)1 << 12)
24
25GrBufferAllocPool::GrBufferAllocPool(GrGpu* gpu,
26 BufferType bufferType,
27 bool frequentResetHint,
28 size_t blockSize,
29 int preallocBufferCnt) :
30 fBlocks(GrMax(8, 2*preallocBufferCnt)) {
bsalomon@google.com11f0b512011-03-29 20:52:23 +000031
bsalomon@google.com1c13c962011-02-14 16:51:21 +000032 GrAssert(NULL != gpu);
33 fGpu = gpu;
bsalomon@google.com11f0b512011-03-29 20:52:23 +000034 fGpu->ref();
35 fGpuIsReffed = true;
36
bsalomon@google.com1c13c962011-02-14 16:51:21 +000037 fBufferType = bufferType;
38 fFrequentResetHint = frequentResetHint;
bsalomon@google.com1c13c962011-02-14 16:51:21 +000039 fBufferPtr = NULL;
40 fMinBlockSize = GrMax(GrBufferAllocPool_MIN_BLOCK_SIZE, blockSize);
41
bsalomon@google.com25fb21f2011-06-21 18:17:25 +000042 fBytesInUse = 0;
43
bsalomon@google.com1c13c962011-02-14 16:51:21 +000044 fPreallocBuffersInUse = 0;
45 fFirstPreallocBuffer = 0;
46 for (int i = 0; i < preallocBufferCnt; ++i) {
47 GrGeometryBuffer* buffer = this->createBuffer(fMinBlockSize);
48 if (NULL != buffer) {
49 *fPreallocBuffers.append() = buffer;
50 buffer->ref();
51 }
52 }
53}
54
55GrBufferAllocPool::~GrBufferAllocPool() {
56 VALIDATE();
57 if (fBlocks.count()) {
58 GrGeometryBuffer* buffer = fBlocks.back().fBuffer;
59 if (buffer->isLocked()) {
60 buffer->unlock();
61 }
62 }
bsalomon@google.com1c13c962011-02-14 16:51:21 +000063 while (!fBlocks.empty()) {
64 destroyBlock();
65 }
bsalomon@google.com11f0b512011-03-29 20:52:23 +000066 fPreallocBuffers.unrefAll();
67 releaseGpuRef();
68}
69
70void GrBufferAllocPool::releaseGpuRef() {
71 if (fGpuIsReffed) {
72 fGpu->unref();
73 fGpuIsReffed = false;
74 }
bsalomon@google.com1c13c962011-02-14 16:51:21 +000075}
76
77void GrBufferAllocPool::reset() {
78 VALIDATE();
bsalomon@google.com25fb21f2011-06-21 18:17:25 +000079 fBytesInUse = 0;
bsalomon@google.com1c13c962011-02-14 16:51:21 +000080 if (fBlocks.count()) {
81 GrGeometryBuffer* buffer = fBlocks.back().fBuffer;
82 if (buffer->isLocked()) {
83 buffer->unlock();
84 }
85 }
86 while (!fBlocks.empty()) {
87 destroyBlock();
88 }
89 if (fPreallocBuffers.count()) {
90 // must set this after above loop.
91 fFirstPreallocBuffer = (fFirstPreallocBuffer + fPreallocBuffersInUse) %
92 fPreallocBuffers.count();
93 }
bsalomon@google.com987dbc02011-12-14 14:44:19 +000094 // we may have created a large cpu mirror of a large VB. Reset the size
95 // to match our pre-allocated VBs.
96 fCpuData.reset(fMinBlockSize);
bsalomon@google.com1c13c962011-02-14 16:51:21 +000097 GrAssert(0 == fPreallocBuffersInUse);
98 VALIDATE();
99}
100
101void GrBufferAllocPool::unlock() {
102 VALIDATE();
103
104 if (NULL != fBufferPtr) {
105 BufferBlock& block = fBlocks.back();
106 if (block.fBuffer->isLocked()) {
107 block.fBuffer->unlock();
108 } else {
bsalomon@google.comcee661a2011-07-26 12:32:36 +0000109 size_t flushSize = block.fBuffer->sizeInBytes() - block.fBytesFree;
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000110 flushCpuData(fBlocks.back().fBuffer, flushSize);
111 }
112 fBufferPtr = NULL;
113 }
114 VALIDATE();
115}
116
117#if GR_DEBUG
bsalomon@google.com25fb21f2011-06-21 18:17:25 +0000118void GrBufferAllocPool::validate(bool unusedBlockAllowed) const {
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000119 if (NULL != fBufferPtr) {
120 GrAssert(!fBlocks.empty());
121 if (fBlocks.back().fBuffer->isLocked()) {
122 GrGeometryBuffer* buf = fBlocks.back().fBuffer;
123 GrAssert(buf->lockPtr() == fBufferPtr);
124 } else {
125 GrAssert(fCpuData.get() == fBufferPtr);
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000126 }
127 } else {
128 GrAssert(fBlocks.empty() || !fBlocks.back().fBuffer->isLocked());
129 }
bsalomon@google.com25fb21f2011-06-21 18:17:25 +0000130 size_t bytesInUse = 0;
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000131 for (int i = 0; i < fBlocks.count() - 1; ++i) {
132 GrAssert(!fBlocks[i].fBuffer->isLocked());
133 }
bsalomon@google.com25fb21f2011-06-21 18:17:25 +0000134 for (int i = 0; i < fBlocks.count(); ++i) {
bsalomon@google.comcee661a2011-07-26 12:32:36 +0000135 size_t bytes = fBlocks[i].fBuffer->sizeInBytes() - fBlocks[i].fBytesFree;
bsalomon@google.com25fb21f2011-06-21 18:17:25 +0000136 bytesInUse += bytes;
137 GrAssert(bytes || unusedBlockAllowed);
138 }
139
140 GrAssert(bytesInUse == fBytesInUse);
141 if (unusedBlockAllowed) {
142 GrAssert((fBytesInUse && !fBlocks.empty()) ||
143 (!fBytesInUse && (fBlocks.count() < 2)));
144 } else {
145 GrAssert((0 == fBytesInUse) == fBlocks.empty());
146 }
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000147}
148#endif
149
150void* GrBufferAllocPool::makeSpace(size_t size,
151 size_t alignment,
152 const GrGeometryBuffer** buffer,
153 size_t* offset) {
154 VALIDATE();
155
156 GrAssert(NULL != buffer);
157 GrAssert(NULL != offset);
158
159 if (NULL != fBufferPtr) {
160 BufferBlock& back = fBlocks.back();
bsalomon@google.comcee661a2011-07-26 12:32:36 +0000161 size_t usedBytes = back.fBuffer->sizeInBytes() - back.fBytesFree;
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000162 size_t pad = GrSizeAlignUpPad(usedBytes,
163 alignment);
164 if ((size + pad) <= back.fBytesFree) {
165 usedBytes += pad;
166 *offset = usedBytes;
167 *buffer = back.fBuffer;
168 back.fBytesFree -= size + pad;
bsalomon@google.com25fb21f2011-06-21 18:17:25 +0000169 fBytesInUse += size;
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000170 return (void*)(reinterpret_cast<intptr_t>(fBufferPtr) + usedBytes);
171 }
172 }
173
bsalomon@google.com96e96df2011-10-10 14:49:29 +0000174 // We could honor the space request using by a partial update of the current
175 // VB (if there is room). But we don't currently use draw calls to GL that
bsalomon@google.com25fb21f2011-06-21 18:17:25 +0000176 // allow the driver to know that previously issued draws won't read from
bsalomon@google.com96e96df2011-10-10 14:49:29 +0000177 // the part of the buffer we update. Also, the GL buffer implementation
178 // may be cheating on the actual buffer size by shrinking the buffer on
179 // updateData() if the amount of data passed is less than the full buffer
180 // size.
bsalomon@google.com25fb21f2011-06-21 18:17:25 +0000181
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000182 if (!createBlock(size)) {
183 return NULL;
184 }
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000185 GrAssert(NULL != fBufferPtr);
186
187 *offset = 0;
188 BufferBlock& back = fBlocks.back();
189 *buffer = back.fBuffer;
190 back.fBytesFree -= size;
bsalomon@google.com25fb21f2011-06-21 18:17:25 +0000191 fBytesInUse += size;
192 VALIDATE();
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000193 return fBufferPtr;
194}
195
196int GrBufferAllocPool::currentBufferItems(size_t itemSize) const {
197 VALIDATE();
198 if (NULL != fBufferPtr) {
199 const BufferBlock& back = fBlocks.back();
bsalomon@google.comcee661a2011-07-26 12:32:36 +0000200 size_t usedBytes = back.fBuffer->sizeInBytes() - back.fBytesFree;
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000201 size_t pad = GrSizeAlignUpPad(usedBytes, itemSize);
202 return (back.fBytesFree - pad) / itemSize;
203 } else if (fPreallocBuffersInUse < fPreallocBuffers.count()) {
204 return fMinBlockSize / itemSize;
205 }
206 return 0;
207}
208
209int GrBufferAllocPool::preallocatedBuffersRemaining() const {
210 return fPreallocBuffers.count() - fPreallocBuffersInUse;
211}
212
213int GrBufferAllocPool::preallocatedBufferCount() const {
214 return fPreallocBuffers.count();
215}
216
217void GrBufferAllocPool::putBack(size_t bytes) {
218 VALIDATE();
bsalomon@google.com25fb21f2011-06-21 18:17:25 +0000219
220 while (bytes) {
221 // caller shouldnt try to put back more than they've taken
222 GrAssert(!fBlocks.empty());
223 BufferBlock& block = fBlocks.back();
bsalomon@google.comcee661a2011-07-26 12:32:36 +0000224 size_t bytesUsed = block.fBuffer->sizeInBytes() - block.fBytesFree;
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000225 if (bytes >= bytesUsed) {
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000226 bytes -= bytesUsed;
bsalomon@google.com25fb21f2011-06-21 18:17:25 +0000227 fBytesInUse -= bytesUsed;
bsalomon@google.com6513cd02011-08-05 20:12:30 +0000228 // if we locked a vb to satisfy the make space and we're releasing
229 // beyond it, then unlock it.
230 if (block.fBuffer->isLocked()) {
231 block.fBuffer->unlock();
232 }
233 this->destroyBlock();
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000234 } else {
bsalomon@google.com25fb21f2011-06-21 18:17:25 +0000235 block.fBytesFree += bytes;
236 fBytesInUse -= bytes;
237 bytes = 0;
238 break;
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000239 }
240 }
241 VALIDATE();
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000242}
243
244bool GrBufferAllocPool::createBlock(size_t requestSize) {
245
246 size_t size = GrMax(requestSize, fMinBlockSize);
247 GrAssert(size >= GrBufferAllocPool_MIN_BLOCK_SIZE);
248
249 VALIDATE();
250
251 BufferBlock& block = fBlocks.push_back();
252
253 if (size == fMinBlockSize &&
254 fPreallocBuffersInUse < fPreallocBuffers.count()) {
255
256 uint32_t nextBuffer = (fPreallocBuffersInUse + fFirstPreallocBuffer) %
257 fPreallocBuffers.count();
258 block.fBuffer = fPreallocBuffers[nextBuffer];
259 block.fBuffer->ref();
260 ++fPreallocBuffersInUse;
261 } else {
262 block.fBuffer = this->createBuffer(size);
263 if (NULL == block.fBuffer) {
264 fBlocks.pop_back();
265 return false;
266 }
267 }
268
269 block.fBytesFree = size;
270 if (NULL != fBufferPtr) {
271 GrAssert(fBlocks.count() > 1);
272 BufferBlock& prev = fBlocks.fromBack(1);
273 if (prev.fBuffer->isLocked()) {
274 prev.fBuffer->unlock();
275 } else {
276 flushCpuData(prev.fBuffer,
bsalomon@google.comcee661a2011-07-26 12:32:36 +0000277 prev.fBuffer->sizeInBytes() - prev.fBytesFree);
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000278 }
279 fBufferPtr = NULL;
280 }
281
282 GrAssert(NULL == fBufferPtr);
283
bsalomon@google.com18c9c192011-09-22 21:01:31 +0000284 if (fGpu->getCaps().fBufferLockSupport &&
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000285 size > GR_GEOM_BUFFER_LOCK_THRESHOLD &&
286 (!fFrequentResetHint || requestSize > GR_GEOM_BUFFER_LOCK_THRESHOLD)) {
287 fBufferPtr = block.fBuffer->lock();
288 }
289
290 if (NULL == fBufferPtr) {
bsalomon@google.com7d4679a2011-09-02 22:06:24 +0000291 fBufferPtr = fCpuData.reset(size);
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000292 }
293
bsalomon@google.com25fb21f2011-06-21 18:17:25 +0000294 VALIDATE(true);
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000295
296 return true;
297}
298
299void GrBufferAllocPool::destroyBlock() {
300 GrAssert(!fBlocks.empty());
301
302 BufferBlock& block = fBlocks.back();
303 if (fPreallocBuffersInUse > 0) {
304 uint32_t prevPreallocBuffer = (fPreallocBuffersInUse +
305 fFirstPreallocBuffer +
306 (fPreallocBuffers.count() - 1)) %
307 fPreallocBuffers.count();
308 if (block.fBuffer == fPreallocBuffers[prevPreallocBuffer]) {
309 --fPreallocBuffersInUse;
310 }
311 }
312 GrAssert(!block.fBuffer->isLocked());
313 block.fBuffer->unref();
314 fBlocks.pop_back();
315 fBufferPtr = NULL;
316}
317
318void GrBufferAllocPool::flushCpuData(GrGeometryBuffer* buffer,
319 size_t flushSize) {
320 GrAssert(NULL != buffer);
321 GrAssert(!buffer->isLocked());
322 GrAssert(fCpuData.get() == fBufferPtr);
bsalomon@google.comcee661a2011-07-26 12:32:36 +0000323 GrAssert(flushSize <= buffer->sizeInBytes());
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000324
bsalomon@google.com18c9c192011-09-22 21:01:31 +0000325 if (fGpu->getCaps().fBufferLockSupport &&
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000326 flushSize > GR_GEOM_BUFFER_LOCK_THRESHOLD) {
327 void* data = buffer->lock();
328 if (NULL != data) {
329 memcpy(data, fBufferPtr, flushSize);
330 buffer->unlock();
bsalomon@google.com71bd1ef2011-12-12 20:42:26 +0000331 return;
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000332 }
333 }
334 buffer->updateData(fBufferPtr, flushSize);
335}
336
337GrGeometryBuffer* GrBufferAllocPool::createBuffer(size_t size) {
338 if (kIndex_BufferType == fBufferType) {
339 return fGpu->createIndexBuffer(size, true);
340 } else {
341 GrAssert(kVertex_BufferType == fBufferType);
342 return fGpu->createVertexBuffer(size, true);
343 }
344}
345
346////////////////////////////////////////////////////////////////////////////////
347
348GrVertexBufferAllocPool::GrVertexBufferAllocPool(GrGpu* gpu,
349 bool frequentResetHint,
350 size_t bufferSize,
351 int preallocBufferCnt)
352: GrBufferAllocPool(gpu,
353 kVertex_BufferType,
354 frequentResetHint,
355 bufferSize,
356 preallocBufferCnt) {
357}
358
359void* GrVertexBufferAllocPool::makeSpace(GrVertexLayout layout,
360 int vertexCount,
361 const GrVertexBuffer** buffer,
362 int* startVertex) {
363
364 GrAssert(vertexCount >= 0);
365 GrAssert(NULL != buffer);
366 GrAssert(NULL != startVertex);
367
368 size_t vSize = GrDrawTarget::VertexSize(layout);
bsalomon@google.com8b484412011-04-18 19:07:44 +0000369 size_t offset = 0; // assign to suppress warning
370 const GrGeometryBuffer* geomBuffer = NULL; // assign to suppress warning
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000371 void* ptr = INHERITED::makeSpace(vSize * vertexCount,
372 vSize,
373 &geomBuffer,
374 &offset);
375
376 *buffer = (const GrVertexBuffer*) geomBuffer;
377 GrAssert(0 == offset % vSize);
378 *startVertex = offset / vSize;
379 return ptr;
380}
381
382bool GrVertexBufferAllocPool::appendVertices(GrVertexLayout layout,
383 int vertexCount,
384 const void* vertices,
385 const GrVertexBuffer** buffer,
386 int* startVertex) {
387 void* space = makeSpace(layout, vertexCount, buffer, startVertex);
388 if (NULL != space) {
389 memcpy(space,
390 vertices,
391 GrDrawTarget::VertexSize(layout) * vertexCount);
392 return true;
393 } else {
394 return false;
395 }
396}
397
398int GrVertexBufferAllocPool::preallocatedBufferVertices(GrVertexLayout layout) const {
399 return INHERITED::preallocatedBufferSize() /
400 GrDrawTarget::VertexSize(layout);
401}
402
403int GrVertexBufferAllocPool::currentBufferVertices(GrVertexLayout layout) const {
404 return currentBufferItems(GrDrawTarget::VertexSize(layout));
405}
406
407////////////////////////////////////////////////////////////////////////////////
408
409GrIndexBufferAllocPool::GrIndexBufferAllocPool(GrGpu* gpu,
410 bool frequentResetHint,
411 size_t bufferSize,
412 int preallocBufferCnt)
413: GrBufferAllocPool(gpu,
414 kIndex_BufferType,
415 frequentResetHint,
416 bufferSize,
417 preallocBufferCnt) {
418}
419
420void* GrIndexBufferAllocPool::makeSpace(int indexCount,
421 const GrIndexBuffer** buffer,
422 int* startIndex) {
423
424 GrAssert(indexCount >= 0);
425 GrAssert(NULL != buffer);
426 GrAssert(NULL != startIndex);
427
bsalomon@google.com8b484412011-04-18 19:07:44 +0000428 size_t offset = 0; // assign to suppress warning
429 const GrGeometryBuffer* geomBuffer = NULL; // assign to suppress warning
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000430 void* ptr = INHERITED::makeSpace(indexCount * sizeof(uint16_t),
431 sizeof(uint16_t),
432 &geomBuffer,
433 &offset);
434
435 *buffer = (const GrIndexBuffer*) geomBuffer;
436 GrAssert(0 == offset % sizeof(uint16_t));
437 *startIndex = offset / sizeof(uint16_t);
438 return ptr;
439}
440
441bool GrIndexBufferAllocPool::appendIndices(int indexCount,
442 const void* indices,
443 const GrIndexBuffer** buffer,
444 int* startIndex) {
445 void* space = makeSpace(indexCount, buffer, startIndex);
446 if (NULL != space) {
447 memcpy(space, indices, sizeof(uint16_t) * indexCount);
448 return true;
449 } else {
450 return false;
451 }
452}
453
454int GrIndexBufferAllocPool::preallocatedBufferIndices() const {
455 return INHERITED::preallocatedBufferSize() / sizeof(uint16_t);
456}
457
458int GrIndexBufferAllocPool::currentBufferIndices() const {
459 return currentBufferItems(sizeof(uint16_t));
460}