blob: d786b02774935a7f62f1427ef17a66b6e2a28bb2 [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
26 #define VALIDATE()
27#endif
28
29#define GrBufferAllocPool_MIN_BLOCK_SIZE ((size_t)1 << 12)
30
31GrBufferAllocPool::GrBufferAllocPool(GrGpu* gpu,
32 BufferType bufferType,
33 bool frequentResetHint,
34 size_t blockSize,
35 int preallocBufferCnt) :
36 fBlocks(GrMax(8, 2*preallocBufferCnt)) {
bsalomon@google.com11f0b512011-03-29 20:52:23 +000037
bsalomon@google.com1c13c962011-02-14 16:51:21 +000038 GrAssert(NULL != gpu);
39 fGpu = gpu;
bsalomon@google.com11f0b512011-03-29 20:52:23 +000040 fGpu->ref();
41 fGpuIsReffed = true;
42
bsalomon@google.com1c13c962011-02-14 16:51:21 +000043 fBufferType = bufferType;
44 fFrequentResetHint = frequentResetHint;
bsalomon@google.com1c13c962011-02-14 16:51:21 +000045 fBufferPtr = NULL;
46 fMinBlockSize = GrMax(GrBufferAllocPool_MIN_BLOCK_SIZE, blockSize);
47
48 fPreallocBuffersInUse = 0;
49 fFirstPreallocBuffer = 0;
50 for (int i = 0; i < preallocBufferCnt; ++i) {
51 GrGeometryBuffer* buffer = this->createBuffer(fMinBlockSize);
52 if (NULL != buffer) {
53 *fPreallocBuffers.append() = buffer;
54 buffer->ref();
55 }
56 }
57}
58
59GrBufferAllocPool::~GrBufferAllocPool() {
60 VALIDATE();
61 if (fBlocks.count()) {
62 GrGeometryBuffer* buffer = fBlocks.back().fBuffer;
63 if (buffer->isLocked()) {
64 buffer->unlock();
65 }
66 }
bsalomon@google.com1c13c962011-02-14 16:51:21 +000067 while (!fBlocks.empty()) {
68 destroyBlock();
69 }
bsalomon@google.com11f0b512011-03-29 20:52:23 +000070 fPreallocBuffers.unrefAll();
71 releaseGpuRef();
72}
73
74void GrBufferAllocPool::releaseGpuRef() {
75 if (fGpuIsReffed) {
76 fGpu->unref();
77 fGpuIsReffed = false;
78 }
bsalomon@google.com1c13c962011-02-14 16:51:21 +000079}
80
81void GrBufferAllocPool::reset() {
82 VALIDATE();
83 if (fBlocks.count()) {
84 GrGeometryBuffer* buffer = fBlocks.back().fBuffer;
85 if (buffer->isLocked()) {
86 buffer->unlock();
87 }
88 }
89 while (!fBlocks.empty()) {
90 destroyBlock();
91 }
92 if (fPreallocBuffers.count()) {
93 // must set this after above loop.
94 fFirstPreallocBuffer = (fFirstPreallocBuffer + fPreallocBuffersInUse) %
95 fPreallocBuffers.count();
96 }
97 fCpuData.realloc(fGpu->supportsBufferLocking() ? 0 : fMinBlockSize);
98 GrAssert(0 == fPreallocBuffersInUse);
99 VALIDATE();
100}
101
102void GrBufferAllocPool::unlock() {
103 VALIDATE();
104
105 if (NULL != fBufferPtr) {
106 BufferBlock& block = fBlocks.back();
107 if (block.fBuffer->isLocked()) {
108 block.fBuffer->unlock();
109 } else {
110 size_t flushSize = block.fBuffer->size() - block.fBytesFree;
111 flushCpuData(fBlocks.back().fBuffer, flushSize);
112 }
113 fBufferPtr = NULL;
114 }
115 VALIDATE();
116}
117
118#if GR_DEBUG
119void GrBufferAllocPool::validate() const {
120 if (NULL != fBufferPtr) {
121 GrAssert(!fBlocks.empty());
122 if (fBlocks.back().fBuffer->isLocked()) {
123 GrGeometryBuffer* buf = fBlocks.back().fBuffer;
124 GrAssert(buf->lockPtr() == fBufferPtr);
125 } else {
126 GrAssert(fCpuData.get() == fBufferPtr);
127 GrAssert(fCpuData.size() == fBlocks.back().fBuffer->size());
128 }
129 } else {
130 GrAssert(fBlocks.empty() || !fBlocks.back().fBuffer->isLocked());
131 }
132 for (int i = 0; i < fBlocks.count() - 1; ++i) {
133 GrAssert(!fBlocks[i].fBuffer->isLocked());
134 }
135}
136#endif
137
138void* GrBufferAllocPool::makeSpace(size_t size,
139 size_t alignment,
140 const GrGeometryBuffer** buffer,
141 size_t* offset) {
142 VALIDATE();
143
144 GrAssert(NULL != buffer);
145 GrAssert(NULL != offset);
146
147 if (NULL != fBufferPtr) {
148 BufferBlock& back = fBlocks.back();
149 size_t usedBytes = back.fBuffer->size() - back.fBytesFree;
150 size_t pad = GrSizeAlignUpPad(usedBytes,
151 alignment);
152 if ((size + pad) <= back.fBytesFree) {
153 usedBytes += pad;
154 *offset = usedBytes;
155 *buffer = back.fBuffer;
156 back.fBytesFree -= size + pad;
157 return (void*)(reinterpret_cast<intptr_t>(fBufferPtr) + usedBytes);
158 }
159 }
160
161 if (!createBlock(size)) {
162 return NULL;
163 }
164 VALIDATE();
165 GrAssert(NULL != fBufferPtr);
166
167 *offset = 0;
168 BufferBlock& back = fBlocks.back();
169 *buffer = back.fBuffer;
170 back.fBytesFree -= size;
171 return fBufferPtr;
172}
173
174int GrBufferAllocPool::currentBufferItems(size_t itemSize) const {
175 VALIDATE();
176 if (NULL != fBufferPtr) {
177 const BufferBlock& back = fBlocks.back();
178 size_t usedBytes = back.fBuffer->size() - back.fBytesFree;
179 size_t pad = GrSizeAlignUpPad(usedBytes, itemSize);
180 return (back.fBytesFree - pad) / itemSize;
181 } else if (fPreallocBuffersInUse < fPreallocBuffers.count()) {
182 return fMinBlockSize / itemSize;
183 }
184 return 0;
185}
186
187int GrBufferAllocPool::preallocatedBuffersRemaining() const {
188 return fPreallocBuffers.count() - fPreallocBuffersInUse;
189}
190
191int GrBufferAllocPool::preallocatedBufferCount() const {
192 return fPreallocBuffers.count();
193}
194
195void GrBufferAllocPool::putBack(size_t bytes) {
196 VALIDATE();
197 if (NULL != fBufferPtr) {
198 BufferBlock& back = fBlocks.back();
199 size_t bytesUsed = back.fBuffer->size() - back.fBytesFree;
200 if (bytes >= bytesUsed) {
201 destroyBlock();
202 bytes -= bytesUsed;
203 } else {
204 back.fBytesFree += bytes;
205 return;
206 }
207 }
208 VALIDATE();
209 GrAssert(NULL == fBufferPtr);
210 // we don't partially roll-back buffers because our VB semantics say locking
211 // a VB discards its previous content.
212 // We could honor it by being sure we use updateSubData and not lock
213 // we will roll-back fully released buffers, though.
214 while (!fBlocks.empty() &&
215 bytes >= fBlocks.back().fBuffer->size()) {
216 bytes -= fBlocks.back().fBuffer->size();
217 destroyBlock();
218 }
219 VALIDATE();
220}
221
222bool GrBufferAllocPool::createBlock(size_t requestSize) {
223
224 size_t size = GrMax(requestSize, fMinBlockSize);
225 GrAssert(size >= GrBufferAllocPool_MIN_BLOCK_SIZE);
226
227 VALIDATE();
228
229 BufferBlock& block = fBlocks.push_back();
230
231 if (size == fMinBlockSize &&
232 fPreallocBuffersInUse < fPreallocBuffers.count()) {
233
234 uint32_t nextBuffer = (fPreallocBuffersInUse + fFirstPreallocBuffer) %
235 fPreallocBuffers.count();
236 block.fBuffer = fPreallocBuffers[nextBuffer];
237 block.fBuffer->ref();
238 ++fPreallocBuffersInUse;
239 } else {
240 block.fBuffer = this->createBuffer(size);
241 if (NULL == block.fBuffer) {
242 fBlocks.pop_back();
243 return false;
244 }
245 }
246
247 block.fBytesFree = size;
248 if (NULL != fBufferPtr) {
249 GrAssert(fBlocks.count() > 1);
250 BufferBlock& prev = fBlocks.fromBack(1);
251 if (prev.fBuffer->isLocked()) {
252 prev.fBuffer->unlock();
253 } else {
254 flushCpuData(prev.fBuffer,
255 prev.fBuffer->size() - prev.fBytesFree);
256 }
257 fBufferPtr = NULL;
258 }
259
260 GrAssert(NULL == fBufferPtr);
261
262 if (fGpu->supportsBufferLocking() &&
263 size > GR_GEOM_BUFFER_LOCK_THRESHOLD &&
264 (!fFrequentResetHint || requestSize > GR_GEOM_BUFFER_LOCK_THRESHOLD)) {
265 fBufferPtr = block.fBuffer->lock();
266 }
267
268 if (NULL == fBufferPtr) {
269 fBufferPtr = fCpuData.realloc(size);
270 }
271
272 VALIDATE();
273
274 return true;
275}
276
277void GrBufferAllocPool::destroyBlock() {
278 GrAssert(!fBlocks.empty());
279
280 BufferBlock& block = fBlocks.back();
281 if (fPreallocBuffersInUse > 0) {
282 uint32_t prevPreallocBuffer = (fPreallocBuffersInUse +
283 fFirstPreallocBuffer +
284 (fPreallocBuffers.count() - 1)) %
285 fPreallocBuffers.count();
286 if (block.fBuffer == fPreallocBuffers[prevPreallocBuffer]) {
287 --fPreallocBuffersInUse;
288 }
289 }
290 GrAssert(!block.fBuffer->isLocked());
291 block.fBuffer->unref();
292 fBlocks.pop_back();
293 fBufferPtr = NULL;
294}
295
296void GrBufferAllocPool::flushCpuData(GrGeometryBuffer* buffer,
297 size_t flushSize) {
298 GrAssert(NULL != buffer);
299 GrAssert(!buffer->isLocked());
300 GrAssert(fCpuData.get() == fBufferPtr);
301 GrAssert(fCpuData.size() == buffer->size());
302 GrAssert(flushSize <= buffer->size());
303
304 bool updated = false;
305 if (fGpu->supportsBufferLocking() &&
306 flushSize > GR_GEOM_BUFFER_LOCK_THRESHOLD) {
307 void* data = buffer->lock();
308 if (NULL != data) {
309 memcpy(data, fBufferPtr, flushSize);
310 buffer->unlock();
311 updated = true;
312 }
313 }
314 buffer->updateData(fBufferPtr, flushSize);
315}
316
317GrGeometryBuffer* GrBufferAllocPool::createBuffer(size_t size) {
318 if (kIndex_BufferType == fBufferType) {
319 return fGpu->createIndexBuffer(size, true);
320 } else {
321 GrAssert(kVertex_BufferType == fBufferType);
322 return fGpu->createVertexBuffer(size, true);
323 }
324}
325
326////////////////////////////////////////////////////////////////////////////////
327
328GrVertexBufferAllocPool::GrVertexBufferAllocPool(GrGpu* gpu,
329 bool frequentResetHint,
330 size_t bufferSize,
331 int preallocBufferCnt)
332: GrBufferAllocPool(gpu,
333 kVertex_BufferType,
334 frequentResetHint,
335 bufferSize,
336 preallocBufferCnt) {
337}
338
339void* GrVertexBufferAllocPool::makeSpace(GrVertexLayout layout,
340 int vertexCount,
341 const GrVertexBuffer** buffer,
342 int* startVertex) {
343
344 GrAssert(vertexCount >= 0);
345 GrAssert(NULL != buffer);
346 GrAssert(NULL != startVertex);
347
348 size_t vSize = GrDrawTarget::VertexSize(layout);
bsalomon@google.com8b484412011-04-18 19:07:44 +0000349 size_t offset = 0; // assign to suppress warning
350 const GrGeometryBuffer* geomBuffer = NULL; // assign to suppress warning
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000351 void* ptr = INHERITED::makeSpace(vSize * vertexCount,
352 vSize,
353 &geomBuffer,
354 &offset);
355
356 *buffer = (const GrVertexBuffer*) geomBuffer;
357 GrAssert(0 == offset % vSize);
358 *startVertex = offset / vSize;
359 return ptr;
360}
361
362bool GrVertexBufferAllocPool::appendVertices(GrVertexLayout layout,
363 int vertexCount,
364 const void* vertices,
365 const GrVertexBuffer** buffer,
366 int* startVertex) {
367 void* space = makeSpace(layout, vertexCount, buffer, startVertex);
368 if (NULL != space) {
369 memcpy(space,
370 vertices,
371 GrDrawTarget::VertexSize(layout) * vertexCount);
372 return true;
373 } else {
374 return false;
375 }
376}
377
378int GrVertexBufferAllocPool::preallocatedBufferVertices(GrVertexLayout layout) const {
379 return INHERITED::preallocatedBufferSize() /
380 GrDrawTarget::VertexSize(layout);
381}
382
383int GrVertexBufferAllocPool::currentBufferVertices(GrVertexLayout layout) const {
384 return currentBufferItems(GrDrawTarget::VertexSize(layout));
385}
386
387////////////////////////////////////////////////////////////////////////////////
388
389GrIndexBufferAllocPool::GrIndexBufferAllocPool(GrGpu* gpu,
390 bool frequentResetHint,
391 size_t bufferSize,
392 int preallocBufferCnt)
393: GrBufferAllocPool(gpu,
394 kIndex_BufferType,
395 frequentResetHint,
396 bufferSize,
397 preallocBufferCnt) {
398}
399
400void* GrIndexBufferAllocPool::makeSpace(int indexCount,
401 const GrIndexBuffer** buffer,
402 int* startIndex) {
403
404 GrAssert(indexCount >= 0);
405 GrAssert(NULL != buffer);
406 GrAssert(NULL != startIndex);
407
bsalomon@google.com8b484412011-04-18 19:07:44 +0000408 size_t offset = 0; // assign to suppress warning
409 const GrGeometryBuffer* geomBuffer = NULL; // assign to suppress warning
bsalomon@google.com1c13c962011-02-14 16:51:21 +0000410 void* ptr = INHERITED::makeSpace(indexCount * sizeof(uint16_t),
411 sizeof(uint16_t),
412 &geomBuffer,
413 &offset);
414
415 *buffer = (const GrIndexBuffer*) geomBuffer;
416 GrAssert(0 == offset % sizeof(uint16_t));
417 *startIndex = offset / sizeof(uint16_t);
418 return ptr;
419}
420
421bool GrIndexBufferAllocPool::appendIndices(int indexCount,
422 const void* indices,
423 const GrIndexBuffer** buffer,
424 int* startIndex) {
425 void* space = makeSpace(indexCount, buffer, startIndex);
426 if (NULL != space) {
427 memcpy(space, indices, sizeof(uint16_t) * indexCount);
428 return true;
429 } else {
430 return false;
431 }
432}
433
434int GrIndexBufferAllocPool::preallocatedBufferIndices() const {
435 return INHERITED::preallocatedBufferSize() / sizeof(uint16_t);
436}
437
438int GrIndexBufferAllocPool::currentBufferIndices() const {
439 return currentBufferItems(sizeof(uint16_t));
440}