blob: 1d041f461c5fe80a3d3bff206ca56ea4b804887b [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;
bsalomon@google.com1c13c962011-02-14 16:51:21 +000050 }
51 }
52}
53
54GrBufferAllocPool::~GrBufferAllocPool() {
55 VALIDATE();
56 if (fBlocks.count()) {
57 GrGeometryBuffer* buffer = fBlocks.back().fBuffer;
58 if (buffer->isLocked()) {
59 buffer->unlock();
60 }
61 }
bsalomon@google.com1c13c962011-02-14 16:51:21 +000062 while (!fBlocks.empty()) {
63 destroyBlock();
64 }
bsalomon@google.com11f0b512011-03-29 20:52:23 +000065 fPreallocBuffers.unrefAll();
66 releaseGpuRef();
67}
68
69void GrBufferAllocPool::releaseGpuRef() {
70 if (fGpuIsReffed) {
71 fGpu->unref();
72 fGpuIsReffed = false;
73 }
bsalomon@google.com1c13c962011-02-14 16:51:21 +000074}
75
76void GrBufferAllocPool::reset() {
77 VALIDATE();
bsalomon@google.com25fb21f2011-06-21 18:17:25 +000078 fBytesInUse = 0;
bsalomon@google.com1c13c962011-02-14 16:51:21 +000079 if (fBlocks.count()) {
80 GrGeometryBuffer* buffer = fBlocks.back().fBuffer;
81 if (buffer->isLocked()) {
82 buffer->unlock();
83 }
84 }
85 while (!fBlocks.empty()) {
86 destroyBlock();
87 }
88 if (fPreallocBuffers.count()) {
89 // must set this after above loop.
90 fFirstPreallocBuffer = (fFirstPreallocBuffer + fPreallocBuffersInUse) %
91 fPreallocBuffers.count();
92 }
bsalomon@google.com987dbc02011-12-14 14:44:19 +000093 // we may have created a large cpu mirror of a large VB. Reset the size
94 // to match our pre-allocated VBs.
95 fCpuData.reset(fMinBlockSize);
bsalomon@google.com1c13c962011-02-14 16:51:21 +000096 GrAssert(0 == fPreallocBuffersInUse);
97 VALIDATE();
98}
99
100void GrBufferAllocPool::unlock() {
101 VALIDATE();
102
103 if (NULL != fBufferPtr) {
104 BufferBlock& block = fBlocks.back();
105 if (block.fBuffer->isLocked()) {
106 block.fBuffer->unlock();
107 } else {
bsalomon@google.comcee661a2011-07-26 12:32:36 +0000108 size_t flushSize = block.fBuffer->sizeInBytes() - block.fBytesFree;
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000109 flushCpuData(fBlocks.back().fBuffer, flushSize);
110 }
111 fBufferPtr = NULL;
112 }
113 VALIDATE();
114}
115
116#if GR_DEBUG
bsalomon@google.com25fb21f2011-06-21 18:17:25 +0000117void GrBufferAllocPool::validate(bool unusedBlockAllowed) const {
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000118 if (NULL != fBufferPtr) {
119 GrAssert(!fBlocks.empty());
120 if (fBlocks.back().fBuffer->isLocked()) {
121 GrGeometryBuffer* buf = fBlocks.back().fBuffer;
122 GrAssert(buf->lockPtr() == fBufferPtr);
123 } else {
124 GrAssert(fCpuData.get() == fBufferPtr);
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000125 }
126 } else {
127 GrAssert(fBlocks.empty() || !fBlocks.back().fBuffer->isLocked());
128 }
bsalomon@google.com25fb21f2011-06-21 18:17:25 +0000129 size_t bytesInUse = 0;
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000130 for (int i = 0; i < fBlocks.count() - 1; ++i) {
131 GrAssert(!fBlocks[i].fBuffer->isLocked());
132 }
bsalomon@google.com25fb21f2011-06-21 18:17:25 +0000133 for (int i = 0; i < fBlocks.count(); ++i) {
bsalomon@google.comcee661a2011-07-26 12:32:36 +0000134 size_t bytes = fBlocks[i].fBuffer->sizeInBytes() - fBlocks[i].fBytesFree;
bsalomon@google.com25fb21f2011-06-21 18:17:25 +0000135 bytesInUse += bytes;
136 GrAssert(bytes || unusedBlockAllowed);
137 }
138
139 GrAssert(bytesInUse == fBytesInUse);
140 if (unusedBlockAllowed) {
141 GrAssert((fBytesInUse && !fBlocks.empty()) ||
142 (!fBytesInUse && (fBlocks.count() < 2)));
143 } else {
144 GrAssert((0 == fBytesInUse) == fBlocks.empty());
145 }
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000146}
147#endif
148
149void* GrBufferAllocPool::makeSpace(size_t size,
150 size_t alignment,
151 const GrGeometryBuffer** buffer,
152 size_t* offset) {
153 VALIDATE();
154
155 GrAssert(NULL != buffer);
156 GrAssert(NULL != offset);
157
158 if (NULL != fBufferPtr) {
159 BufferBlock& back = fBlocks.back();
bsalomon@google.comcee661a2011-07-26 12:32:36 +0000160 size_t usedBytes = back.fBuffer->sizeInBytes() - back.fBytesFree;
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000161 size_t pad = GrSizeAlignUpPad(usedBytes,
162 alignment);
163 if ((size + pad) <= back.fBytesFree) {
164 usedBytes += pad;
165 *offset = usedBytes;
166 *buffer = back.fBuffer;
167 back.fBytesFree -= size + pad;
bsalomon@google.com25fb21f2011-06-21 18:17:25 +0000168 fBytesInUse += size;
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000169 return (void*)(reinterpret_cast<intptr_t>(fBufferPtr) + usedBytes);
170 }
171 }
172
bsalomon@google.com96e96df2011-10-10 14:49:29 +0000173 // We could honor the space request using by a partial update of the current
174 // VB (if there is room). But we don't currently use draw calls to GL that
bsalomon@google.com25fb21f2011-06-21 18:17:25 +0000175 // allow the driver to know that previously issued draws won't read from
bsalomon@google.com96e96df2011-10-10 14:49:29 +0000176 // the part of the buffer we update. Also, the GL buffer implementation
177 // may be cheating on the actual buffer size by shrinking the buffer on
178 // updateData() if the amount of data passed is less than the full buffer
179 // size.
bsalomon@google.com25fb21f2011-06-21 18:17:25 +0000180
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000181 if (!createBlock(size)) {
182 return NULL;
183 }
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000184 GrAssert(NULL != fBufferPtr);
185
186 *offset = 0;
187 BufferBlock& back = fBlocks.back();
188 *buffer = back.fBuffer;
189 back.fBytesFree -= size;
bsalomon@google.com25fb21f2011-06-21 18:17:25 +0000190 fBytesInUse += size;
191 VALIDATE();
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000192 return fBufferPtr;
193}
194
195int GrBufferAllocPool::currentBufferItems(size_t itemSize) const {
196 VALIDATE();
197 if (NULL != fBufferPtr) {
198 const BufferBlock& back = fBlocks.back();
bsalomon@google.comcee661a2011-07-26 12:32:36 +0000199 size_t usedBytes = back.fBuffer->sizeInBytes() - back.fBytesFree;
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000200 size_t pad = GrSizeAlignUpPad(usedBytes, itemSize);
201 return (back.fBytesFree - pad) / itemSize;
202 } else if (fPreallocBuffersInUse < fPreallocBuffers.count()) {
203 return fMinBlockSize / itemSize;
204 }
205 return 0;
206}
207
208int GrBufferAllocPool::preallocatedBuffersRemaining() const {
209 return fPreallocBuffers.count() - fPreallocBuffersInUse;
210}
211
212int GrBufferAllocPool::preallocatedBufferCount() const {
213 return fPreallocBuffers.count();
214}
215
216void GrBufferAllocPool::putBack(size_t bytes) {
217 VALIDATE();
bsalomon@google.com25fb21f2011-06-21 18:17:25 +0000218
219 while (bytes) {
220 // caller shouldnt try to put back more than they've taken
221 GrAssert(!fBlocks.empty());
222 BufferBlock& block = fBlocks.back();
bsalomon@google.comcee661a2011-07-26 12:32:36 +0000223 size_t bytesUsed = block.fBuffer->sizeInBytes() - block.fBytesFree;
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000224 if (bytes >= bytesUsed) {
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000225 bytes -= bytesUsed;
bsalomon@google.com25fb21f2011-06-21 18:17:25 +0000226 fBytesInUse -= bytesUsed;
bsalomon@google.com6513cd02011-08-05 20:12:30 +0000227 // if we locked a vb to satisfy the make space and we're releasing
228 // beyond it, then unlock it.
229 if (block.fBuffer->isLocked()) {
230 block.fBuffer->unlock();
231 }
232 this->destroyBlock();
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000233 } else {
bsalomon@google.com25fb21f2011-06-21 18:17:25 +0000234 block.fBytesFree += bytes;
235 fBytesInUse -= bytes;
236 bytes = 0;
237 break;
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000238 }
239 }
240 VALIDATE();
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000241}
242
243bool GrBufferAllocPool::createBlock(size_t requestSize) {
244
245 size_t size = GrMax(requestSize, fMinBlockSize);
246 GrAssert(size >= GrBufferAllocPool_MIN_BLOCK_SIZE);
247
248 VALIDATE();
249
250 BufferBlock& block = fBlocks.push_back();
251
252 if (size == fMinBlockSize &&
253 fPreallocBuffersInUse < fPreallocBuffers.count()) {
254
255 uint32_t nextBuffer = (fPreallocBuffersInUse + fFirstPreallocBuffer) %
256 fPreallocBuffers.count();
257 block.fBuffer = fPreallocBuffers[nextBuffer];
258 block.fBuffer->ref();
259 ++fPreallocBuffersInUse;
260 } else {
261 block.fBuffer = this->createBuffer(size);
262 if (NULL == block.fBuffer) {
263 fBlocks.pop_back();
264 return false;
265 }
266 }
267
268 block.fBytesFree = size;
269 if (NULL != fBufferPtr) {
270 GrAssert(fBlocks.count() > 1);
271 BufferBlock& prev = fBlocks.fromBack(1);
272 if (prev.fBuffer->isLocked()) {
273 prev.fBuffer->unlock();
274 } else {
275 flushCpuData(prev.fBuffer,
bsalomon@google.comcee661a2011-07-26 12:32:36 +0000276 prev.fBuffer->sizeInBytes() - prev.fBytesFree);
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000277 }
278 fBufferPtr = NULL;
279 }
280
281 GrAssert(NULL == fBufferPtr);
282
bsalomon@google.com18c9c192011-09-22 21:01:31 +0000283 if (fGpu->getCaps().fBufferLockSupport &&
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000284 size > GR_GEOM_BUFFER_LOCK_THRESHOLD &&
285 (!fFrequentResetHint || requestSize > GR_GEOM_BUFFER_LOCK_THRESHOLD)) {
286 fBufferPtr = block.fBuffer->lock();
287 }
288
289 if (NULL == fBufferPtr) {
bsalomon@google.com7d4679a2011-09-02 22:06:24 +0000290 fBufferPtr = fCpuData.reset(size);
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000291 }
292
bsalomon@google.com25fb21f2011-06-21 18:17:25 +0000293 VALIDATE(true);
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000294
295 return true;
296}
297
298void GrBufferAllocPool::destroyBlock() {
299 GrAssert(!fBlocks.empty());
300
301 BufferBlock& block = fBlocks.back();
302 if (fPreallocBuffersInUse > 0) {
303 uint32_t prevPreallocBuffer = (fPreallocBuffersInUse +
304 fFirstPreallocBuffer +
305 (fPreallocBuffers.count() - 1)) %
306 fPreallocBuffers.count();
307 if (block.fBuffer == fPreallocBuffers[prevPreallocBuffer]) {
308 --fPreallocBuffersInUse;
309 }
310 }
311 GrAssert(!block.fBuffer->isLocked());
312 block.fBuffer->unref();
313 fBlocks.pop_back();
314 fBufferPtr = NULL;
315}
316
317void GrBufferAllocPool::flushCpuData(GrGeometryBuffer* buffer,
318 size_t flushSize) {
319 GrAssert(NULL != buffer);
320 GrAssert(!buffer->isLocked());
321 GrAssert(fCpuData.get() == fBufferPtr);
bsalomon@google.comcee661a2011-07-26 12:32:36 +0000322 GrAssert(flushSize <= buffer->sizeInBytes());
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000323
bsalomon@google.com18c9c192011-09-22 21:01:31 +0000324 if (fGpu->getCaps().fBufferLockSupport &&
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000325 flushSize > GR_GEOM_BUFFER_LOCK_THRESHOLD) {
326 void* data = buffer->lock();
327 if (NULL != data) {
328 memcpy(data, fBufferPtr, flushSize);
329 buffer->unlock();
bsalomon@google.com71bd1ef2011-12-12 20:42:26 +0000330 return;
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000331 }
332 }
333 buffer->updateData(fBufferPtr, flushSize);
334}
335
336GrGeometryBuffer* GrBufferAllocPool::createBuffer(size_t size) {
337 if (kIndex_BufferType == fBufferType) {
338 return fGpu->createIndexBuffer(size, true);
339 } else {
340 GrAssert(kVertex_BufferType == fBufferType);
341 return fGpu->createVertexBuffer(size, true);
342 }
343}
344
345////////////////////////////////////////////////////////////////////////////////
346
347GrVertexBufferAllocPool::GrVertexBufferAllocPool(GrGpu* gpu,
348 bool frequentResetHint,
349 size_t bufferSize,
350 int preallocBufferCnt)
351: GrBufferAllocPool(gpu,
352 kVertex_BufferType,
353 frequentResetHint,
354 bufferSize,
355 preallocBufferCnt) {
356}
357
358void* GrVertexBufferAllocPool::makeSpace(GrVertexLayout layout,
359 int vertexCount,
360 const GrVertexBuffer** buffer,
361 int* startVertex) {
362
363 GrAssert(vertexCount >= 0);
364 GrAssert(NULL != buffer);
365 GrAssert(NULL != startVertex);
366
367 size_t vSize = GrDrawTarget::VertexSize(layout);
bsalomon@google.com8b484412011-04-18 19:07:44 +0000368 size_t offset = 0; // assign to suppress warning
369 const GrGeometryBuffer* geomBuffer = NULL; // assign to suppress warning
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000370 void* ptr = INHERITED::makeSpace(vSize * vertexCount,
371 vSize,
372 &geomBuffer,
373 &offset);
374
375 *buffer = (const GrVertexBuffer*) geomBuffer;
376 GrAssert(0 == offset % vSize);
377 *startVertex = offset / vSize;
378 return ptr;
379}
380
381bool GrVertexBufferAllocPool::appendVertices(GrVertexLayout layout,
382 int vertexCount,
383 const void* vertices,
384 const GrVertexBuffer** buffer,
385 int* startVertex) {
386 void* space = makeSpace(layout, vertexCount, buffer, startVertex);
387 if (NULL != space) {
388 memcpy(space,
389 vertices,
390 GrDrawTarget::VertexSize(layout) * vertexCount);
391 return true;
392 } else {
393 return false;
394 }
395}
396
397int GrVertexBufferAllocPool::preallocatedBufferVertices(GrVertexLayout layout) const {
398 return INHERITED::preallocatedBufferSize() /
399 GrDrawTarget::VertexSize(layout);
400}
401
402int GrVertexBufferAllocPool::currentBufferVertices(GrVertexLayout layout) const {
403 return currentBufferItems(GrDrawTarget::VertexSize(layout));
404}
405
406////////////////////////////////////////////////////////////////////////////////
407
408GrIndexBufferAllocPool::GrIndexBufferAllocPool(GrGpu* gpu,
409 bool frequentResetHint,
410 size_t bufferSize,
411 int preallocBufferCnt)
412: GrBufferAllocPool(gpu,
413 kIndex_BufferType,
414 frequentResetHint,
415 bufferSize,
416 preallocBufferCnt) {
417}
418
419void* GrIndexBufferAllocPool::makeSpace(int indexCount,
420 const GrIndexBuffer** buffer,
421 int* startIndex) {
422
423 GrAssert(indexCount >= 0);
424 GrAssert(NULL != buffer);
425 GrAssert(NULL != startIndex);
426
bsalomon@google.com8b484412011-04-18 19:07:44 +0000427 size_t offset = 0; // assign to suppress warning
428 const GrGeometryBuffer* geomBuffer = NULL; // assign to suppress warning
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000429 void* ptr = INHERITED::makeSpace(indexCount * sizeof(uint16_t),
430 sizeof(uint16_t),
431 &geomBuffer,
432 &offset);
433
434 *buffer = (const GrIndexBuffer*) geomBuffer;
435 GrAssert(0 == offset % sizeof(uint16_t));
436 *startIndex = offset / sizeof(uint16_t);
437 return ptr;
438}
439
440bool GrIndexBufferAllocPool::appendIndices(int indexCount,
441 const void* indices,
442 const GrIndexBuffer** buffer,
443 int* startIndex) {
444 void* space = makeSpace(indexCount, buffer, startIndex);
445 if (NULL != space) {
446 memcpy(space, indices, sizeof(uint16_t) * indexCount);
447 return true;
448 } else {
449 return false;
450 }
451}
452
453int GrIndexBufferAllocPool::preallocatedBufferIndices() const {
454 return INHERITED::preallocatedBufferSize() / sizeof(uint16_t);
455}
456
457int GrIndexBufferAllocPool::currentBufferIndices() const {
458 return currentBufferItems(sizeof(uint16_t));
459}