blob: b6f08c97ee32ccfda80c385d8d80f8f6b48e1a3e [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;
96 uint32_t pad = GrUIAlignUpPad(usedBytes, layout);
97 if ((bytes + pad) <= back.fBytesFree) {
98 usedBytes += pad;
99 *startVertex = usedBytes / vSize;
100 *buffer = back.fVertexBuffer;
101 back.fBytesFree -= bytes + pad;
102 return (void*)((intptr_t)fBufferPtr + usedBytes);
103 }
104 }
105
106 if (!createBlock(GrMax(bytes, fMinBlockSize))) {
107 return NULL;
108 }
109 *startVertex = 0;
110 GrAssert(NULL != fBufferPtr);
111 BufferBlock& back = fBlocks.back();
112 *buffer = back.fVertexBuffer;
113 back.fBytesFree -= bytes;
114 return fBufferPtr;
115}
116
117int GrVertexBufferAllocPool::currentBufferVertices(GrVertexLayout layout) const {
118 if (NULL != fBufferPtr) {
119 GrAssert(!fBlocks.empty());
120 const BufferBlock& back = fBlocks.back();
121 GrAssert(back.fVertexBuffer->isLocked());
122 return back.fBytesFree / GrDrawTarget::VertexSize(layout);
123 } else if (fPreallocBuffersInUse < fPreallocBuffers.count()) {
124 return fMinBlockSize / GrDrawTarget::VertexSize(layout);
125 }
126 return 0;
127}
128
129int GrVertexBufferAllocPool::preallocatedBuffersRemaining() const {
130 return fPreallocBuffers.count() - fPreallocBuffersInUse;
131}
132
133int GrVertexBufferAllocPool::preallocatedBufferVertices(GrVertexLayout layout) const {
134 return fPreallocBuffers.count() ?
135 (fMinBlockSize / GrDrawTarget::VertexSize(layout)) :
136 0;
137}
138
139int GrVertexBufferAllocPool::preallocatedBufferCount() const {
140 return fPreallocBuffers.count();
141}
142
143void GrVertexBufferAllocPool::release(size_t bytes) {
144 if (NULL != fBufferPtr) {
145 GrAssert(!fBlocks.empty());
146 BufferBlock& back = fBlocks.back();
147 GrAssert(back.fVertexBuffer->isLocked());
148 size_t bytesUsed = back.fVertexBuffer->size() - back.fBytesFree;
149 if (bytes >= bytesUsed) {
150 destroyBlock();
151 bytes -= bytesUsed;
152 } else {
153 back.fBytesFree += bytes;
154 return;
155 }
156 }
157 GrAssert(NULL == fBufferPtr);
158 GrAssert(fBlocks.empty() ||
159 !fBlocks.back().fVertexBuffer->isLocked());
160 // we don't honor release if it is within an already unlocked VB
161 // Our VB semantics say locking a VB discards its previous content
162 while (!fBlocks.empty() &&
163 bytes >= fBlocks.back().fVertexBuffer->size()) {
164 bytes -= fBlocks.back().fVertexBuffer->size();
165 destroyBlock();
166 }
167}
168
169bool GrVertexBufferAllocPool::createBlock(size_t size) {
170 GrAssert(size >= GrVertexBufferAllocPool_MIN_BLOCK_SIZE);
171
172 BufferBlock& block = fBlocks.push_back();
173
174 if (size == fMinBlockSize &&
175 fPreallocBuffersInUse < fPreallocBuffers.count()) {
176
177 uint32_t nextBuffer = (fPreallocBuffersInUse + fFirstPreallocBuffer) %
178 fPreallocBuffers.count();
179 block.fVertexBuffer = fPreallocBuffers[nextBuffer];
180 block.fVertexBuffer->ref();
181 ++fPreallocBuffersInUse;
182 } else {
183 block.fVertexBuffer = fGpu->createVertexBuffer(size, true);
184 if (NULL == block.fVertexBuffer) {
185 fBlocks.pop_back();
186 return false;
187 }
188 }
189
190 block.fBytesFree = size;
191 if (NULL != fBufferPtr) {
192 GrAssert(fBlocks.count() > 1);
193 BufferBlock& prev = fBlocks.fromBack(1);
194 GrAssert(prev.fVertexBuffer->isLocked());
195 fBufferPtr = NULL;
196 prev.fVertexBuffer->unlock();
197 }
198 fBufferPtr = block.fVertexBuffer->lock();
199 return true;
200}
201
202void GrVertexBufferAllocPool::destroyBlock() {
203 GrAssert(!fBlocks.empty());
204
205 BufferBlock& block = fBlocks.back();
206 if (fPreallocBuffersInUse > 0) {
207 uint32_t prevPreallocBuffer = (fPreallocBuffersInUse +
208 fFirstPreallocBuffer +
209 (fPreallocBuffers.count() - 1)) %
210 fPreallocBuffers.count();
211 if (block.fVertexBuffer == fPreallocBuffers[prevPreallocBuffer]) {
212 --fPreallocBuffersInUse;
213 }
214 }
215 block.fVertexBuffer->unref();
216 fBlocks.pop_back();
217 fBufferPtr = NULL;
218}
219
220