blob: 73d707ff91486cc9ed76e685ab5aee6824c9efce [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 }
101 fCpuData.realloc(fGpu->supportsBufferLocking() ? 0 : fMinBlockSize);
102 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 {
114 size_t flushSize = block.fBuffer->size() - block.fBytesFree;
115 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);
131 GrAssert(fCpuData.size() == fBlocks.back().fBuffer->size());
132 }
133 } else {
134 GrAssert(fBlocks.empty() || !fBlocks.back().fBuffer->isLocked());
135 }
bsalomon@google.com25fb21f2011-06-21 18:17:25 +0000136 size_t bytesInUse = 0;
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000137 for (int i = 0; i < fBlocks.count() - 1; ++i) {
138 GrAssert(!fBlocks[i].fBuffer->isLocked());
139 }
bsalomon@google.com25fb21f2011-06-21 18:17:25 +0000140 for (int i = 0; i < fBlocks.count(); ++i) {
141 size_t bytes = fBlocks[i].fBuffer->size() - fBlocks[i].fBytesFree;
142 bytesInUse += bytes;
143 GrAssert(bytes || unusedBlockAllowed);
144 }
145
146 GrAssert(bytesInUse == fBytesInUse);
147 if (unusedBlockAllowed) {
148 GrAssert((fBytesInUse && !fBlocks.empty()) ||
149 (!fBytesInUse && (fBlocks.count() < 2)));
150 } else {
151 GrAssert((0 == fBytesInUse) == fBlocks.empty());
152 }
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000153}
154#endif
155
156void* GrBufferAllocPool::makeSpace(size_t size,
157 size_t alignment,
158 const GrGeometryBuffer** buffer,
159 size_t* offset) {
160 VALIDATE();
161
162 GrAssert(NULL != buffer);
163 GrAssert(NULL != offset);
164
165 if (NULL != fBufferPtr) {
166 BufferBlock& back = fBlocks.back();
167 size_t usedBytes = back.fBuffer->size() - back.fBytesFree;
168 size_t pad = GrSizeAlignUpPad(usedBytes,
169 alignment);
170 if ((size + pad) <= back.fBytesFree) {
171 usedBytes += pad;
172 *offset = usedBytes;
173 *buffer = back.fBuffer;
174 back.fBytesFree -= size + pad;
bsalomon@google.com25fb21f2011-06-21 18:17:25 +0000175 fBytesInUse += size;
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000176 return (void*)(reinterpret_cast<intptr_t>(fBufferPtr) + usedBytes);
177 }
178 }
179
bsalomon@google.com25fb21f2011-06-21 18:17:25 +0000180 // We could honor the space request using updateSubData on the current VB
181 // (if there is room). But we don't currently use draw calls to GL that
182 // allow the driver to know that previously issued draws won't read from
183 // the part of the buffer we update.
184
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000185 if (!createBlock(size)) {
186 return NULL;
187 }
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000188 GrAssert(NULL != fBufferPtr);
189
190 *offset = 0;
191 BufferBlock& back = fBlocks.back();
192 *buffer = back.fBuffer;
193 back.fBytesFree -= size;
bsalomon@google.com25fb21f2011-06-21 18:17:25 +0000194 fBytesInUse += size;
195 VALIDATE();
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000196 return fBufferPtr;
197}
198
199int GrBufferAllocPool::currentBufferItems(size_t itemSize) const {
200 VALIDATE();
201 if (NULL != fBufferPtr) {
202 const BufferBlock& back = fBlocks.back();
203 size_t usedBytes = back.fBuffer->size() - back.fBytesFree;
204 size_t pad = GrSizeAlignUpPad(usedBytes, itemSize);
205 return (back.fBytesFree - pad) / itemSize;
206 } else if (fPreallocBuffersInUse < fPreallocBuffers.count()) {
207 return fMinBlockSize / itemSize;
208 }
209 return 0;
210}
211
212int GrBufferAllocPool::preallocatedBuffersRemaining() const {
213 return fPreallocBuffers.count() - fPreallocBuffersInUse;
214}
215
216int GrBufferAllocPool::preallocatedBufferCount() const {
217 return fPreallocBuffers.count();
218}
219
220void GrBufferAllocPool::putBack(size_t bytes) {
221 VALIDATE();
bsalomon@google.com25fb21f2011-06-21 18:17:25 +0000222
223 while (bytes) {
224 // caller shouldnt try to put back more than they've taken
225 GrAssert(!fBlocks.empty());
226 BufferBlock& block = fBlocks.back();
227 size_t bytesUsed = block.fBuffer->size() - block.fBytesFree;
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000228 if (bytes >= bytesUsed) {
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000229 bytes -= bytesUsed;
bsalomon@google.com25fb21f2011-06-21 18:17:25 +0000230 fBytesInUse -= bytesUsed;
231 destroyBlock();
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000232 } else {
bsalomon@google.com25fb21f2011-06-21 18:17:25 +0000233 block.fBytesFree += bytes;
234 fBytesInUse -= bytes;
235 bytes = 0;
236 break;
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000237 }
238 }
239 VALIDATE();
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000240}
241
242bool GrBufferAllocPool::createBlock(size_t requestSize) {
243
244 size_t size = GrMax(requestSize, fMinBlockSize);
245 GrAssert(size >= GrBufferAllocPool_MIN_BLOCK_SIZE);
246
247 VALIDATE();
248
249 BufferBlock& block = fBlocks.push_back();
250
251 if (size == fMinBlockSize &&
252 fPreallocBuffersInUse < fPreallocBuffers.count()) {
253
254 uint32_t nextBuffer = (fPreallocBuffersInUse + fFirstPreallocBuffer) %
255 fPreallocBuffers.count();
256 block.fBuffer = fPreallocBuffers[nextBuffer];
257 block.fBuffer->ref();
258 ++fPreallocBuffersInUse;
259 } else {
260 block.fBuffer = this->createBuffer(size);
261 if (NULL == block.fBuffer) {
262 fBlocks.pop_back();
263 return false;
264 }
265 }
266
267 block.fBytesFree = size;
268 if (NULL != fBufferPtr) {
269 GrAssert(fBlocks.count() > 1);
270 BufferBlock& prev = fBlocks.fromBack(1);
271 if (prev.fBuffer->isLocked()) {
272 prev.fBuffer->unlock();
273 } else {
274 flushCpuData(prev.fBuffer,
275 prev.fBuffer->size() - prev.fBytesFree);
276 }
277 fBufferPtr = NULL;
278 }
279
280 GrAssert(NULL == fBufferPtr);
281
282 if (fGpu->supportsBufferLocking() &&
283 size > GR_GEOM_BUFFER_LOCK_THRESHOLD &&
284 (!fFrequentResetHint || requestSize > GR_GEOM_BUFFER_LOCK_THRESHOLD)) {
285 fBufferPtr = block.fBuffer->lock();
286 }
287
288 if (NULL == fBufferPtr) {
289 fBufferPtr = fCpuData.realloc(size);
290 }
291
bsalomon@google.com25fb21f2011-06-21 18:17:25 +0000292 VALIDATE(true);
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000293
294 return true;
295}
296
297void GrBufferAllocPool::destroyBlock() {
298 GrAssert(!fBlocks.empty());
299
300 BufferBlock& block = fBlocks.back();
301 if (fPreallocBuffersInUse > 0) {
302 uint32_t prevPreallocBuffer = (fPreallocBuffersInUse +
303 fFirstPreallocBuffer +
304 (fPreallocBuffers.count() - 1)) %
305 fPreallocBuffers.count();
306 if (block.fBuffer == fPreallocBuffers[prevPreallocBuffer]) {
307 --fPreallocBuffersInUse;
308 }
309 }
310 GrAssert(!block.fBuffer->isLocked());
311 block.fBuffer->unref();
312 fBlocks.pop_back();
313 fBufferPtr = NULL;
314}
315
316void GrBufferAllocPool::flushCpuData(GrGeometryBuffer* buffer,
317 size_t flushSize) {
318 GrAssert(NULL != buffer);
319 GrAssert(!buffer->isLocked());
320 GrAssert(fCpuData.get() == fBufferPtr);
321 GrAssert(fCpuData.size() == buffer->size());
322 GrAssert(flushSize <= buffer->size());
323
324 bool updated = false;
325 if (fGpu->supportsBufferLocking() &&
326 flushSize > GR_GEOM_BUFFER_LOCK_THRESHOLD) {
327 void* data = buffer->lock();
328 if (NULL != data) {
329 memcpy(data, fBufferPtr, flushSize);
330 buffer->unlock();
331 updated = true;
332 }
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}