blob: 42556322647773c6749c69be01070738acab6398 [file] [log] [blame]
bsalomon@google.com1c13c962011-02-14 16:51:21 +00001/*
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
bsalomon@google.com25fb21f2011-06-21 18:17:25 +000026 static void VALIDATE(bool x = false) {}
bsalomon@google.com1c13c962011-02-14 16:51:21 +000027#endif
28
bsalomon@google.com25fb21f2011-06-21 18:17:25 +000029// page size
bsalomon@google.com1c13c962011-02-14 16:51:21 +000030#define GrBufferAllocPool_MIN_BLOCK_SIZE ((size_t)1 << 12)
31
32GrBufferAllocPool::GrBufferAllocPool(GrGpu* gpu,
33 BufferType bufferType,
34 bool frequentResetHint,
35 size_t blockSize,
36 int preallocBufferCnt) :
37 fBlocks(GrMax(8, 2*preallocBufferCnt)) {
bsalomon@google.com11f0b512011-03-29 20:52:23 +000038
bsalomon@google.com1c13c962011-02-14 16:51:21 +000039 GrAssert(NULL != gpu);
40 fGpu = gpu;
bsalomon@google.com11f0b512011-03-29 20:52:23 +000041 fGpu->ref();
42 fGpuIsReffed = true;
43
bsalomon@google.com1c13c962011-02-14 16:51:21 +000044 fBufferType = bufferType;
45 fFrequentResetHint = frequentResetHint;
bsalomon@google.com1c13c962011-02-14 16:51:21 +000046 fBufferPtr = NULL;
47 fMinBlockSize = GrMax(GrBufferAllocPool_MIN_BLOCK_SIZE, blockSize);
48
bsalomon@google.com25fb21f2011-06-21 18:17:25 +000049 fBytesInUse = 0;
50
bsalomon@google.com1c13c962011-02-14 16:51:21 +000051 fPreallocBuffersInUse = 0;
52 fFirstPreallocBuffer = 0;
53 for (int i = 0; i < preallocBufferCnt; ++i) {
54 GrGeometryBuffer* buffer = this->createBuffer(fMinBlockSize);
55 if (NULL != buffer) {
56 *fPreallocBuffers.append() = buffer;
57 buffer->ref();
58 }
59 }
60}
61
62GrBufferAllocPool::~GrBufferAllocPool() {
63 VALIDATE();
64 if (fBlocks.count()) {
65 GrGeometryBuffer* buffer = fBlocks.back().fBuffer;
66 if (buffer->isLocked()) {
67 buffer->unlock();
68 }
69 }
bsalomon@google.com1c13c962011-02-14 16:51:21 +000070 while (!fBlocks.empty()) {
71 destroyBlock();
72 }
bsalomon@google.com11f0b512011-03-29 20:52:23 +000073 fPreallocBuffers.unrefAll();
74 releaseGpuRef();
75}
76
77void GrBufferAllocPool::releaseGpuRef() {
78 if (fGpuIsReffed) {
79 fGpu->unref();
80 fGpuIsReffed = false;
81 }
bsalomon@google.com1c13c962011-02-14 16:51:21 +000082}
83
84void GrBufferAllocPool::reset() {
85 VALIDATE();
bsalomon@google.com25fb21f2011-06-21 18:17:25 +000086 fBytesInUse = 0;
bsalomon@google.com1c13c962011-02-14 16:51:21 +000087 if (fBlocks.count()) {
88 GrGeometryBuffer* buffer = fBlocks.back().fBuffer;
89 if (buffer->isLocked()) {
90 buffer->unlock();
91 }
92 }
93 while (!fBlocks.empty()) {
94 destroyBlock();
95 }
96 if (fPreallocBuffers.count()) {
97 // must set this after above loop.
98 fFirstPreallocBuffer = (fFirstPreallocBuffer + fPreallocBuffersInUse) %
99 fPreallocBuffers.count();
100 }
bsalomon@google.com3582bf92011-06-30 21:32:31 +0000101 fCpuData.alloc(fGpu->supportsBufferLocking() ? 0 : fMinBlockSize);
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000102 GrAssert(0 == fPreallocBuffersInUse);
103 VALIDATE();
104}
105
106void GrBufferAllocPool::unlock() {
107 VALIDATE();
108
109 if (NULL != fBufferPtr) {
110 BufferBlock& block = fBlocks.back();
111 if (block.fBuffer->isLocked()) {
112 block.fBuffer->unlock();
113 } else {
bsalomon@google.comcee661a2011-07-26 12:32:36 +0000114 size_t flushSize = block.fBuffer->sizeInBytes() - block.fBytesFree;
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000115 flushCpuData(fBlocks.back().fBuffer, flushSize);
116 }
117 fBufferPtr = NULL;
118 }
119 VALIDATE();
120}
121
122#if GR_DEBUG
bsalomon@google.com25fb21f2011-06-21 18:17:25 +0000123void GrBufferAllocPool::validate(bool unusedBlockAllowed) const {
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000124 if (NULL != fBufferPtr) {
125 GrAssert(!fBlocks.empty());
126 if (fBlocks.back().fBuffer->isLocked()) {
127 GrGeometryBuffer* buf = fBlocks.back().fBuffer;
128 GrAssert(buf->lockPtr() == fBufferPtr);
129 } else {
130 GrAssert(fCpuData.get() == fBufferPtr);
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000131 }
132 } else {
133 GrAssert(fBlocks.empty() || !fBlocks.back().fBuffer->isLocked());
134 }
bsalomon@google.com25fb21f2011-06-21 18:17:25 +0000135 size_t bytesInUse = 0;
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000136 for (int i = 0; i < fBlocks.count() - 1; ++i) {
137 GrAssert(!fBlocks[i].fBuffer->isLocked());
138 }
bsalomon@google.com25fb21f2011-06-21 18:17:25 +0000139 for (int i = 0; i < fBlocks.count(); ++i) {
bsalomon@google.comcee661a2011-07-26 12:32:36 +0000140 size_t bytes = fBlocks[i].fBuffer->sizeInBytes() - fBlocks[i].fBytesFree;
bsalomon@google.com25fb21f2011-06-21 18:17:25 +0000141 bytesInUse += bytes;
142 GrAssert(bytes || unusedBlockAllowed);
143 }
144
145 GrAssert(bytesInUse == fBytesInUse);
146 if (unusedBlockAllowed) {
147 GrAssert((fBytesInUse && !fBlocks.empty()) ||
148 (!fBytesInUse && (fBlocks.count() < 2)));
149 } else {
150 GrAssert((0 == fBytesInUse) == fBlocks.empty());
151 }
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000152}
153#endif
154
155void* GrBufferAllocPool::makeSpace(size_t size,
156 size_t alignment,
157 const GrGeometryBuffer** buffer,
158 size_t* offset) {
159 VALIDATE();
160
161 GrAssert(NULL != buffer);
162 GrAssert(NULL != offset);
163
164 if (NULL != fBufferPtr) {
165 BufferBlock& back = fBlocks.back();
bsalomon@google.comcee661a2011-07-26 12:32:36 +0000166 size_t usedBytes = back.fBuffer->sizeInBytes() - back.fBytesFree;
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000167 size_t pad = GrSizeAlignUpPad(usedBytes,
168 alignment);
169 if ((size + pad) <= back.fBytesFree) {
170 usedBytes += pad;
171 *offset = usedBytes;
172 *buffer = back.fBuffer;
173 back.fBytesFree -= size + pad;
bsalomon@google.com25fb21f2011-06-21 18:17:25 +0000174 fBytesInUse += size;
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000175 return (void*)(reinterpret_cast<intptr_t>(fBufferPtr) + usedBytes);
176 }
177 }
178
bsalomon@google.com25fb21f2011-06-21 18:17:25 +0000179 // We could honor the space request using updateSubData on the current VB
180 // (if there is room). But we don't currently use draw calls to GL that
181 // allow the driver to know that previously issued draws won't read from
182 // the part of the buffer we update.
183
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000184 if (!createBlock(size)) {
185 return NULL;
186 }
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000187 GrAssert(NULL != fBufferPtr);
188
189 *offset = 0;
190 BufferBlock& back = fBlocks.back();
191 *buffer = back.fBuffer;
192 back.fBytesFree -= size;
bsalomon@google.com25fb21f2011-06-21 18:17:25 +0000193 fBytesInUse += size;
194 VALIDATE();
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000195 return fBufferPtr;
196}
197
198int GrBufferAllocPool::currentBufferItems(size_t itemSize) const {
199 VALIDATE();
200 if (NULL != fBufferPtr) {
201 const BufferBlock& back = fBlocks.back();
bsalomon@google.comcee661a2011-07-26 12:32:36 +0000202 size_t usedBytes = back.fBuffer->sizeInBytes() - back.fBytesFree;
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000203 size_t pad = GrSizeAlignUpPad(usedBytes, itemSize);
204 return (back.fBytesFree - pad) / itemSize;
205 } else if (fPreallocBuffersInUse < fPreallocBuffers.count()) {
206 return fMinBlockSize / itemSize;
207 }
208 return 0;
209}
210
211int GrBufferAllocPool::preallocatedBuffersRemaining() const {
212 return fPreallocBuffers.count() - fPreallocBuffersInUse;
213}
214
215int GrBufferAllocPool::preallocatedBufferCount() const {
216 return fPreallocBuffers.count();
217}
218
219void GrBufferAllocPool::putBack(size_t bytes) {
220 VALIDATE();
bsalomon@google.com25fb21f2011-06-21 18:17:25 +0000221
222 while (bytes) {
223 // caller shouldnt try to put back more than they've taken
224 GrAssert(!fBlocks.empty());
225 BufferBlock& block = fBlocks.back();
bsalomon@google.comcee661a2011-07-26 12:32:36 +0000226 size_t bytesUsed = block.fBuffer->sizeInBytes() - block.fBytesFree;
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000227 if (bytes >= bytesUsed) {
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000228 bytes -= bytesUsed;
bsalomon@google.com25fb21f2011-06-21 18:17:25 +0000229 fBytesInUse -= bytesUsed;
230 destroyBlock();
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000231 } else {
bsalomon@google.com25fb21f2011-06-21 18:17:25 +0000232 block.fBytesFree += bytes;
233 fBytesInUse -= bytes;
234 bytes = 0;
235 break;
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000236 }
237 }
238 VALIDATE();
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000239}
240
241bool GrBufferAllocPool::createBlock(size_t requestSize) {
242
243 size_t size = GrMax(requestSize, fMinBlockSize);
244 GrAssert(size >= GrBufferAllocPool_MIN_BLOCK_SIZE);
245
246 VALIDATE();
247
248 BufferBlock& block = fBlocks.push_back();
249
250 if (size == fMinBlockSize &&
251 fPreallocBuffersInUse < fPreallocBuffers.count()) {
252
253 uint32_t nextBuffer = (fPreallocBuffersInUse + fFirstPreallocBuffer) %
254 fPreallocBuffers.count();
255 block.fBuffer = fPreallocBuffers[nextBuffer];
256 block.fBuffer->ref();
257 ++fPreallocBuffersInUse;
258 } else {
259 block.fBuffer = this->createBuffer(size);
260 if (NULL == block.fBuffer) {
261 fBlocks.pop_back();
262 return false;
263 }
264 }
265
266 block.fBytesFree = size;
267 if (NULL != fBufferPtr) {
268 GrAssert(fBlocks.count() > 1);
269 BufferBlock& prev = fBlocks.fromBack(1);
270 if (prev.fBuffer->isLocked()) {
271 prev.fBuffer->unlock();
272 } else {
273 flushCpuData(prev.fBuffer,
bsalomon@google.comcee661a2011-07-26 12:32:36 +0000274 prev.fBuffer->sizeInBytes() - prev.fBytesFree);
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000275 }
276 fBufferPtr = NULL;
277 }
278
279 GrAssert(NULL == fBufferPtr);
280
281 if (fGpu->supportsBufferLocking() &&
282 size > GR_GEOM_BUFFER_LOCK_THRESHOLD &&
283 (!fFrequentResetHint || requestSize > GR_GEOM_BUFFER_LOCK_THRESHOLD)) {
284 fBufferPtr = block.fBuffer->lock();
285 }
286
287 if (NULL == fBufferPtr) {
bsalomon@google.com3582bf92011-06-30 21:32:31 +0000288 fBufferPtr = fCpuData.alloc(size);
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000289 }
290
bsalomon@google.com25fb21f2011-06-21 18:17:25 +0000291 VALIDATE(true);
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000292
293 return true;
294}
295
296void GrBufferAllocPool::destroyBlock() {
297 GrAssert(!fBlocks.empty());
298
299 BufferBlock& block = fBlocks.back();
300 if (fPreallocBuffersInUse > 0) {
301 uint32_t prevPreallocBuffer = (fPreallocBuffersInUse +
302 fFirstPreallocBuffer +
303 (fPreallocBuffers.count() - 1)) %
304 fPreallocBuffers.count();
305 if (block.fBuffer == fPreallocBuffers[prevPreallocBuffer]) {
306 --fPreallocBuffersInUse;
307 }
308 }
309 GrAssert(!block.fBuffer->isLocked());
310 block.fBuffer->unref();
311 fBlocks.pop_back();
312 fBufferPtr = NULL;
313}
314
315void GrBufferAllocPool::flushCpuData(GrGeometryBuffer* buffer,
316 size_t flushSize) {
317 GrAssert(NULL != buffer);
318 GrAssert(!buffer->isLocked());
319 GrAssert(fCpuData.get() == fBufferPtr);
bsalomon@google.comcee661a2011-07-26 12:32:36 +0000320 GrAssert(flushSize <= buffer->sizeInBytes());
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000321
322 bool updated = false;
323 if (fGpu->supportsBufferLocking() &&
324 flushSize > GR_GEOM_BUFFER_LOCK_THRESHOLD) {
325 void* data = buffer->lock();
326 if (NULL != data) {
327 memcpy(data, fBufferPtr, flushSize);
328 buffer->unlock();
329 updated = true;
330 }
331 }
332 buffer->updateData(fBufferPtr, flushSize);
333}
334
335GrGeometryBuffer* GrBufferAllocPool::createBuffer(size_t size) {
336 if (kIndex_BufferType == fBufferType) {
337 return fGpu->createIndexBuffer(size, true);
338 } else {
339 GrAssert(kVertex_BufferType == fBufferType);
340 return fGpu->createVertexBuffer(size, true);
341 }
342}
343
344////////////////////////////////////////////////////////////////////////////////
345
346GrVertexBufferAllocPool::GrVertexBufferAllocPool(GrGpu* gpu,
347 bool frequentResetHint,
348 size_t bufferSize,
349 int preallocBufferCnt)
350: GrBufferAllocPool(gpu,
351 kVertex_BufferType,
352 frequentResetHint,
353 bufferSize,
354 preallocBufferCnt) {
355}
356
357void* GrVertexBufferAllocPool::makeSpace(GrVertexLayout layout,
358 int vertexCount,
359 const GrVertexBuffer** buffer,
360 int* startVertex) {
361
362 GrAssert(vertexCount >= 0);
363 GrAssert(NULL != buffer);
364 GrAssert(NULL != startVertex);
365
366 size_t vSize = GrDrawTarget::VertexSize(layout);
bsalomon@google.com8b484412011-04-18 19:07:44 +0000367 size_t offset = 0; // assign to suppress warning
368 const GrGeometryBuffer* geomBuffer = NULL; // assign to suppress warning
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000369 void* ptr = INHERITED::makeSpace(vSize * vertexCount,
370 vSize,
371 &geomBuffer,
372 &offset);
373
374 *buffer = (const GrVertexBuffer*) geomBuffer;
375 GrAssert(0 == offset % vSize);
376 *startVertex = offset / vSize;
377 return ptr;
378}
379
380bool GrVertexBufferAllocPool::appendVertices(GrVertexLayout layout,
381 int vertexCount,
382 const void* vertices,
383 const GrVertexBuffer** buffer,
384 int* startVertex) {
385 void* space = makeSpace(layout, vertexCount, buffer, startVertex);
386 if (NULL != space) {
387 memcpy(space,
388 vertices,
389 GrDrawTarget::VertexSize(layout) * vertexCount);
390 return true;
391 } else {
392 return false;
393 }
394}
395
396int GrVertexBufferAllocPool::preallocatedBufferVertices(GrVertexLayout layout) const {
397 return INHERITED::preallocatedBufferSize() /
398 GrDrawTarget::VertexSize(layout);
399}
400
401int GrVertexBufferAllocPool::currentBufferVertices(GrVertexLayout layout) const {
402 return currentBufferItems(GrDrawTarget::VertexSize(layout));
403}
404
405////////////////////////////////////////////////////////////////////////////////
406
407GrIndexBufferAllocPool::GrIndexBufferAllocPool(GrGpu* gpu,
408 bool frequentResetHint,
409 size_t bufferSize,
410 int preallocBufferCnt)
411: GrBufferAllocPool(gpu,
412 kIndex_BufferType,
413 frequentResetHint,
414 bufferSize,
415 preallocBufferCnt) {
416}
417
418void* GrIndexBufferAllocPool::makeSpace(int indexCount,
419 const GrIndexBuffer** buffer,
420 int* startIndex) {
421
422 GrAssert(indexCount >= 0);
423 GrAssert(NULL != buffer);
424 GrAssert(NULL != startIndex);
425
bsalomon@google.com8b484412011-04-18 19:07:44 +0000426 size_t offset = 0; // assign to suppress warning
427 const GrGeometryBuffer* geomBuffer = NULL; // assign to suppress warning
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000428 void* ptr = INHERITED::makeSpace(indexCount * sizeof(uint16_t),
429 sizeof(uint16_t),
430 &geomBuffer,
431 &offset);
432
433 *buffer = (const GrIndexBuffer*) geomBuffer;
434 GrAssert(0 == offset % sizeof(uint16_t));
435 *startIndex = offset / sizeof(uint16_t);
436 return ptr;
437}
438
439bool GrIndexBufferAllocPool::appendIndices(int indexCount,
440 const void* indices,
441 const GrIndexBuffer** buffer,
442 int* startIndex) {
443 void* space = makeSpace(indexCount, buffer, startIndex);
444 if (NULL != space) {
445 memcpy(space, indices, sizeof(uint16_t) * indexCount);
446 return true;
447 } else {
448 return false;
449 }
450}
451
452int GrIndexBufferAllocPool::preallocatedBufferIndices() const {
453 return INHERITED::preallocatedBufferSize() / sizeof(uint16_t);
454}
455
456int GrIndexBufferAllocPool::currentBufferIndices() const {
457 return currentBufferItems(sizeof(uint16_t));
458}