blob: 360a086a09eb6b9b512450b114a8bb891f57e834 [file] [log] [blame]
reed@google.comac10a2d2010-12-22 21:39:39 +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
18#include "GrVertexBufferAllocPool.h"
19#include "GrVertexBuffer.h"
20#include "GrGpu.h"
21
22#define GrVertexBufferAllocPool_MIN_BLOCK_SIZE ((size_t)1 << 10)
23
24GrVertexBufferAllocPool::GrVertexBufferAllocPool(GrGpu* gpu,
25 size_t blockSize,
26 int preallocBufferCnt) :
27 fBlocks(GrMax(8, 2*preallocBufferCnt)) {
28 GrAssert(NULL != gpu);
29 fGpu = gpu;
30 fGpu->ref();
31 fBufferPtr = NULL;
32 fMinBlockSize = GrMax(GrVertexBufferAllocPool_MIN_BLOCK_SIZE, blockSize);
33
34 fPreallocBuffersInUse = 0;
35 fFirstPreallocBuffer = 0;
36 for (int i = 0; i < preallocBufferCnt; ++i) {
37 GrVertexBuffer* buffer = gpu->createVertexBuffer(fMinBlockSize, true);
38 if (NULL != buffer) {
39 *fPreallocBuffers.append() = buffer;
40 buffer->ref();
41 }
42 }
43}
44
45GrVertexBufferAllocPool::~GrVertexBufferAllocPool() {
46 fPreallocBuffers.unrefAll();
47 while (!fBlocks.empty()) {
48 destroyBlock();
49 }
50 fGpu->unref();
51}
52
53void GrVertexBufferAllocPool::reset() {
54 while (!fBlocks.empty()) {
55 destroyBlock();
56 }
57 if (fPreallocBuffers.count()) {
58 // must set this after above loop.
59 fFirstPreallocBuffer = (fFirstPreallocBuffer + fPreallocBuffersInUse) %
60 fPreallocBuffers.count();
61 }
62 GrAssert(0 == fPreallocBuffersInUse);
63}
64
65void GrVertexBufferAllocPool::unlock() {
66 GrAssert((NULL == fBufferPtr) ? (!fBlocks.empty() ||
67 !fBlocks.back().fVertexBuffer->isLocked()) :
68 (!fBlocks.empty() &&
69 fBlocks.back().fVertexBuffer->isLocked()));
70 if (NULL != fBufferPtr) {
71 GrAssert(!fBlocks.empty());
72 GrAssert(fBlocks.back().fVertexBuffer->isLocked());
73 fBufferPtr = NULL;
74 fBlocks.back().fVertexBuffer->unlock();
75 }
76#if GR_DEBUG
77 for (uint32_t i = 0; i < fBlocks.count(); ++i) {
78 GrAssert(!fBlocks[i].fVertexBuffer->isLocked());
79 }
80#endif
81}
82
83void* GrVertexBufferAllocPool::alloc(GrVertexLayout layout,
84 uint32_t vertexCount,
85 GrVertexBuffer** buffer,
86 uint32_t* startVertex) {
87 GrAssert(NULL != buffer);
88 size_t vSize = GrDrawTarget::VertexSize(layout);
89 size_t bytes = vSize * vertexCount;
90
91 if (NULL != fBufferPtr) {
92 GrAssert(!fBlocks.empty());
93 GrAssert(fBlocks.back().fVertexBuffer->isLocked());
94 BufferBlock& back = fBlocks.back();
95 uint32_t usedBytes = back.fVertexBuffer->size() - back.fBytesFree;
bsalomon@google.coma827b412011-01-12 21:20:18 +000096 uint32_t pad = GrUIAlignUpPad(usedBytes,
97 GrDrawTarget::VertexSize(layout));
reed@google.comac10a2d2010-12-22 21:39:39 +000098 if ((bytes + pad) <= back.fBytesFree) {
99 usedBytes += pad;
100 *startVertex = usedBytes / vSize;
101 *buffer = back.fVertexBuffer;
102 back.fBytesFree -= bytes + pad;
103 return (void*)((intptr_t)fBufferPtr + usedBytes);
104 }
105 }
106
107 if (!createBlock(GrMax(bytes, fMinBlockSize))) {
108 return NULL;
109 }
110 *startVertex = 0;
111 GrAssert(NULL != fBufferPtr);
112 BufferBlock& back = fBlocks.back();
113 *buffer = back.fVertexBuffer;
114 back.fBytesFree -= bytes;
115 return fBufferPtr;
116}
117
118int GrVertexBufferAllocPool::currentBufferVertices(GrVertexLayout layout) const {
119 if (NULL != fBufferPtr) {
120 GrAssert(!fBlocks.empty());
121 const BufferBlock& back = fBlocks.back();
122 GrAssert(back.fVertexBuffer->isLocked());
123 return back.fBytesFree / GrDrawTarget::VertexSize(layout);
124 } else if (fPreallocBuffersInUse < fPreallocBuffers.count()) {
125 return fMinBlockSize / GrDrawTarget::VertexSize(layout);
126 }
127 return 0;
128}
129
130int GrVertexBufferAllocPool::preallocatedBuffersRemaining() const {
131 return fPreallocBuffers.count() - fPreallocBuffersInUse;
132}
133
134int GrVertexBufferAllocPool::preallocatedBufferVertices(GrVertexLayout layout) const {
135 return fPreallocBuffers.count() ?
136 (fMinBlockSize / GrDrawTarget::VertexSize(layout)) :
137 0;
138}
139
140int GrVertexBufferAllocPool::preallocatedBufferCount() const {
141 return fPreallocBuffers.count();
142}
143
144void GrVertexBufferAllocPool::release(size_t bytes) {
145 if (NULL != fBufferPtr) {
146 GrAssert(!fBlocks.empty());
147 BufferBlock& back = fBlocks.back();
148 GrAssert(back.fVertexBuffer->isLocked());
149 size_t bytesUsed = back.fVertexBuffer->size() - back.fBytesFree;
150 if (bytes >= bytesUsed) {
151 destroyBlock();
152 bytes -= bytesUsed;
153 } else {
154 back.fBytesFree += bytes;
155 return;
156 }
157 }
158 GrAssert(NULL == fBufferPtr);
159 GrAssert(fBlocks.empty() ||
160 !fBlocks.back().fVertexBuffer->isLocked());
161 // we don't honor release if it is within an already unlocked VB
162 // Our VB semantics say locking a VB discards its previous content
163 while (!fBlocks.empty() &&
164 bytes >= fBlocks.back().fVertexBuffer->size()) {
165 bytes -= fBlocks.back().fVertexBuffer->size();
166 destroyBlock();
167 }
168}
169
170bool GrVertexBufferAllocPool::createBlock(size_t size) {
171 GrAssert(size >= GrVertexBufferAllocPool_MIN_BLOCK_SIZE);
172
173 BufferBlock& block = fBlocks.push_back();
174
175 if (size == fMinBlockSize &&
176 fPreallocBuffersInUse < fPreallocBuffers.count()) {
177
178 uint32_t nextBuffer = (fPreallocBuffersInUse + fFirstPreallocBuffer) %
179 fPreallocBuffers.count();
180 block.fVertexBuffer = fPreallocBuffers[nextBuffer];
181 block.fVertexBuffer->ref();
182 ++fPreallocBuffersInUse;
183 } else {
184 block.fVertexBuffer = fGpu->createVertexBuffer(size, true);
185 if (NULL == block.fVertexBuffer) {
186 fBlocks.pop_back();
187 return false;
188 }
189 }
190
191 block.fBytesFree = size;
192 if (NULL != fBufferPtr) {
193 GrAssert(fBlocks.count() > 1);
194 BufferBlock& prev = fBlocks.fromBack(1);
195 GrAssert(prev.fVertexBuffer->isLocked());
196 fBufferPtr = NULL;
197 prev.fVertexBuffer->unlock();
198 }
199 fBufferPtr = block.fVertexBuffer->lock();
200 return true;
201}
202
203void GrVertexBufferAllocPool::destroyBlock() {
204 GrAssert(!fBlocks.empty());
205
206 BufferBlock& block = fBlocks.back();
207 if (fPreallocBuffersInUse > 0) {
208 uint32_t prevPreallocBuffer = (fPreallocBuffersInUse +
209 fFirstPreallocBuffer +
210 (fPreallocBuffers.count() - 1)) %
211 fPreallocBuffers.count();
212 if (block.fVertexBuffer == fPreallocBuffers[prevPreallocBuffer]) {
213 --fPreallocBuffersInUse;
214 }
215 }
216 block.fVertexBuffer->unref();
217 fBlocks.pop_back();
218 fBufferPtr = NULL;
219}
220
221