1 //
2 // Copyright (c) 2002-2010 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6
7 // geometry/IndexDataManager.cpp: Defines the IndexDataManager, a class that
8 // runs the Buffer translation process for index buffers.
9
10 #include "libGLESv2/geometry/IndexDataManager.h"
11
12 #include "common/debug.h"
13
14 #include "libGLESv2/Buffer.h"
15 #include "libGLESv2/mathutil.h"
16 #include "libGLESv2/main.h"
17
18 namespace
19 {
20 enum { INITIAL_INDEX_BUFFER_SIZE = 4096 * sizeof(GLuint) };
21 }
22
23 namespace gl
24 {
25
IndexDataManager(Context * context,IDirect3DDevice9 * device)26 IndexDataManager::IndexDataManager(Context *context, IDirect3DDevice9 *device) : mDevice(device)
27 {
28 mStreamingBufferShort = new StreamingIndexBuffer(mDevice, INITIAL_INDEX_BUFFER_SIZE, D3DFMT_INDEX16);
29
30 if (context->supports32bitIndices())
31 {
32 mStreamingBufferInt = new StreamingIndexBuffer(mDevice, INITIAL_INDEX_BUFFER_SIZE, D3DFMT_INDEX32);
33 }
34 else
35 {
36 mStreamingBufferInt = NULL;
37 }
38 }
39
~IndexDataManager()40 IndexDataManager::~IndexDataManager()
41 {
42 delete mStreamingBufferShort;
43 delete mStreamingBufferInt;
44 }
45
convertIndices(GLenum type,const void * input,GLsizei count,void * output)46 void convertIndices(GLenum type, const void *input, GLsizei count, void *output)
47 {
48 if (type == GL_UNSIGNED_BYTE)
49 {
50 const GLubyte *in = static_cast<const GLubyte*>(input);
51 GLushort *out = static_cast<GLushort*>(output);
52
53 for (GLsizei i = 0; i < count; i++)
54 {
55 out[i] = in[i];
56 }
57 }
58 else if (type == GL_UNSIGNED_INT)
59 {
60 memcpy(output, input, count * sizeof(GLuint));
61 }
62 else if (type == GL_UNSIGNED_SHORT)
63 {
64 memcpy(output, input, count * sizeof(GLushort));
65 }
66 else UNREACHABLE();
67 }
68
69 template <class IndexType>
computeRange(const IndexType * indices,GLsizei count,GLuint * minIndex,GLuint * maxIndex)70 void computeRange(const IndexType *indices, GLsizei count, GLuint *minIndex, GLuint *maxIndex)
71 {
72 *minIndex = indices[0];
73 *maxIndex = indices[0];
74
75 for (GLsizei i = 0; i < count; i++)
76 {
77 if (*minIndex > indices[i]) *minIndex = indices[i];
78 if (*maxIndex < indices[i]) *maxIndex = indices[i];
79 }
80 }
81
computeRange(GLenum type,const void * indices,GLsizei count,GLuint * minIndex,GLuint * maxIndex)82 void computeRange(GLenum type, const void *indices, GLsizei count, GLuint *minIndex, GLuint *maxIndex)
83 {
84 if (type == GL_UNSIGNED_BYTE)
85 {
86 computeRange(static_cast<const GLubyte*>(indices), count, minIndex, maxIndex);
87 }
88 else if (type == GL_UNSIGNED_INT)
89 {
90 computeRange(static_cast<const GLuint*>(indices), count, minIndex, maxIndex);
91 }
92 else if (type == GL_UNSIGNED_SHORT)
93 {
94 computeRange(static_cast<const GLushort*>(indices), count, minIndex, maxIndex);
95 }
96 else UNREACHABLE();
97 }
98
prepareIndexData(GLenum type,GLsizei count,Buffer * buffer,const void * indices,TranslatedIndexData * translated)99 GLenum IndexDataManager::prepareIndexData(GLenum type, GLsizei count, Buffer *buffer, const void *indices, TranslatedIndexData *translated)
100 {
101 D3DFORMAT format = (type == GL_UNSIGNED_INT) ? D3DFMT_INDEX32 : D3DFMT_INDEX16;
102 intptr_t offset = reinterpret_cast<intptr_t>(indices);
103 bool alignedOffset = false;
104
105 if (buffer != NULL)
106 {
107 switch (type)
108 {
109 case GL_UNSIGNED_BYTE: alignedOffset = (offset % sizeof(GLubyte) == 0); break;
110 case GL_UNSIGNED_SHORT: alignedOffset = (offset % sizeof(GLushort) == 0); break;
111 case GL_UNSIGNED_INT: alignedOffset = (offset % sizeof(GLuint) == 0); break;
112 default: UNREACHABLE(); alignedOffset = false;
113 }
114
115 if (typeSize(type) * count + offset > static_cast<std::size_t>(buffer->size()))
116 {
117 return GL_INVALID_OPERATION;
118 }
119
120 indices = static_cast<const GLubyte*>(buffer->data()) + offset;
121 }
122
123 StreamingIndexBuffer *streamingBuffer = (type == GL_UNSIGNED_INT) ? mStreamingBufferInt : mStreamingBufferShort;
124
125 StaticIndexBuffer *staticBuffer = buffer ? buffer->getIndexBuffer() : NULL;
126 IndexBuffer *indexBuffer = streamingBuffer;
127 UINT streamOffset = 0;
128
129 if (staticBuffer && staticBuffer->lookupType(type) && alignedOffset)
130 {
131 indexBuffer = staticBuffer;
132 streamOffset = staticBuffer->lookupRange(offset, count, &translated->minIndex, &translated->maxIndex);
133
134 if (streamOffset == -1)
135 {
136 streamOffset = (offset / typeSize(type)) * indexSize(format);
137 computeRange(type, indices, count, &translated->minIndex, &translated->maxIndex);
138 staticBuffer->addRange(offset, count, translated->minIndex, translated->maxIndex, streamOffset);
139 }
140 }
141 else
142 {
143 int convertCount = count;
144
145 if (staticBuffer)
146 {
147 if (staticBuffer->size() == 0 && alignedOffset)
148 {
149 indexBuffer = staticBuffer;
150 convertCount = buffer->size() / typeSize(type);
151 }
152 else
153 {
154 buffer->invalidateStaticData();
155 staticBuffer = NULL;
156 }
157 }
158
159 void *output = NULL;
160
161 if (indexBuffer)
162 {
163 indexBuffer->reserveSpace(convertCount * indexSize(format), type);
164 output = indexBuffer->map(indexSize(format) * convertCount, &streamOffset);
165 }
166
167 if (output == NULL)
168 {
169 ERR("Failed to map index buffer.");
170 return GL_OUT_OF_MEMORY;
171 }
172
173 convertIndices(type, staticBuffer ? buffer->data() : indices, convertCount, output);
174 indexBuffer->unmap();
175
176 computeRange(type, indices, count, &translated->minIndex, &translated->maxIndex);
177
178 if (staticBuffer)
179 {
180 streamOffset = (offset / typeSize(type)) * indexSize(format);
181 staticBuffer->addRange(offset, count, translated->minIndex, translated->maxIndex, streamOffset);
182 }
183 }
184
185 translated->indexBuffer = indexBuffer->getBuffer();
186 translated->startIndex = streamOffset / indexSize(format);
187
188 return GL_NO_ERROR;
189 }
190
indexSize(D3DFORMAT format) const191 std::size_t IndexDataManager::indexSize(D3DFORMAT format) const
192 {
193 return (format == D3DFMT_INDEX32) ? sizeof(unsigned int) : sizeof(unsigned short);
194 }
195
typeSize(GLenum type) const196 std::size_t IndexDataManager::typeSize(GLenum type) const
197 {
198 switch (type)
199 {
200 case GL_UNSIGNED_INT: return sizeof(GLuint);
201 case GL_UNSIGNED_SHORT: return sizeof(GLushort);
202 case GL_UNSIGNED_BYTE: return sizeof(GLubyte);
203 default: UNREACHABLE(); return sizeof(GLushort);
204 }
205 }
206
IndexBuffer(IDirect3DDevice9 * device,UINT size,D3DFORMAT format)207 IndexBuffer::IndexBuffer(IDirect3DDevice9 *device, UINT size, D3DFORMAT format) : mDevice(device), mBufferSize(size), mIndexBuffer(NULL)
208 {
209 if (size > 0)
210 {
211 D3DPOOL pool = getDisplay()->getBufferPool(D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY);
212 HRESULT result = device->CreateIndexBuffer(size, D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, format, pool, &mIndexBuffer, NULL);
213
214 if (FAILED(result))
215 {
216 ERR("Out of memory allocating an index buffer of size %lu.", size);
217 }
218 }
219 }
220
~IndexBuffer()221 IndexBuffer::~IndexBuffer()
222 {
223 if (mIndexBuffer)
224 {
225 mIndexBuffer->Release();
226 }
227 }
228
getBuffer() const229 IDirect3DIndexBuffer9 *IndexBuffer::getBuffer() const
230 {
231 return mIndexBuffer;
232 }
233
unmap()234 void IndexBuffer::unmap()
235 {
236 if (mIndexBuffer)
237 {
238 mIndexBuffer->Unlock();
239 }
240 }
241
StreamingIndexBuffer(IDirect3DDevice9 * device,UINT initialSize,D3DFORMAT format)242 StreamingIndexBuffer::StreamingIndexBuffer(IDirect3DDevice9 *device, UINT initialSize, D3DFORMAT format) : IndexBuffer(device, initialSize, format)
243 {
244 mWritePosition = 0;
245 }
246
~StreamingIndexBuffer()247 StreamingIndexBuffer::~StreamingIndexBuffer()
248 {
249 }
250
map(UINT requiredSpace,UINT * offset)251 void *StreamingIndexBuffer::map(UINT requiredSpace, UINT *offset)
252 {
253 void *mapPtr = NULL;
254
255 if (mIndexBuffer)
256 {
257 HRESULT result = mIndexBuffer->Lock(mWritePosition, requiredSpace, &mapPtr, D3DLOCK_NOOVERWRITE);
258
259 if (FAILED(result))
260 {
261 ERR(" Lock failed with error 0x%08x", result);
262 return NULL;
263 }
264
265 *offset = mWritePosition;
266 mWritePosition += requiredSpace;
267 }
268
269 return mapPtr;
270 }
271
reserveSpace(UINT requiredSpace,GLenum type)272 void StreamingIndexBuffer::reserveSpace(UINT requiredSpace, GLenum type)
273 {
274 if (requiredSpace > mBufferSize)
275 {
276 if (mIndexBuffer)
277 {
278 mIndexBuffer->Release();
279 mIndexBuffer = NULL;
280 }
281
282 mBufferSize = std::max(requiredSpace, 2 * mBufferSize);
283
284 D3DPOOL pool = getDisplay()->getBufferPool(D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY);
285 HRESULT result = mDevice->CreateIndexBuffer(mBufferSize, D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, type == GL_UNSIGNED_INT ? D3DFMT_INDEX32 : D3DFMT_INDEX16, pool, &mIndexBuffer, NULL);
286
287 if (FAILED(result))
288 {
289 ERR("Out of memory allocating a vertex buffer of size %lu.", mBufferSize);
290 }
291
292 mWritePosition = 0;
293 }
294 else if (mWritePosition + requiredSpace > mBufferSize) // Recycle
295 {
296 void *dummy;
297 mIndexBuffer->Lock(0, 1, &dummy, D3DLOCK_DISCARD);
298 mIndexBuffer->Unlock();
299
300 mWritePosition = 0;
301 }
302 }
303
StaticIndexBuffer(IDirect3DDevice9 * device)304 StaticIndexBuffer::StaticIndexBuffer(IDirect3DDevice9 *device) : IndexBuffer(device, 0, D3DFMT_UNKNOWN)
305 {
306 mCacheType = GL_NONE;
307 }
308
~StaticIndexBuffer()309 StaticIndexBuffer::~StaticIndexBuffer()
310 {
311 }
312
map(UINT requiredSpace,UINT * offset)313 void *StaticIndexBuffer::map(UINT requiredSpace, UINT *offset)
314 {
315 void *mapPtr = NULL;
316
317 if (mIndexBuffer)
318 {
319 HRESULT result = mIndexBuffer->Lock(0, requiredSpace, &mapPtr, 0);
320
321 if (FAILED(result))
322 {
323 ERR(" Lock failed with error 0x%08x", result);
324 return NULL;
325 }
326
327 *offset = 0;
328 }
329
330 return mapPtr;
331 }
332
reserveSpace(UINT requiredSpace,GLenum type)333 void StaticIndexBuffer::reserveSpace(UINT requiredSpace, GLenum type)
334 {
335 if (!mIndexBuffer && mBufferSize == 0)
336 {
337 D3DPOOL pool = getDisplay()->getBufferPool(D3DUSAGE_WRITEONLY);
338 HRESULT result = mDevice->CreateIndexBuffer(requiredSpace, D3DUSAGE_WRITEONLY, type == GL_UNSIGNED_INT ? D3DFMT_INDEX32 : D3DFMT_INDEX16, pool, &mIndexBuffer, NULL);
339
340 if (FAILED(result))
341 {
342 ERR("Out of memory allocating a vertex buffer of size %lu.", mBufferSize);
343 }
344
345 mBufferSize = requiredSpace;
346 mCacheType = type;
347 }
348 else if (mIndexBuffer && mBufferSize >= requiredSpace && mCacheType == type)
349 {
350 // Already allocated
351 }
352 else UNREACHABLE(); // Static index buffers can't be resized
353 }
354
lookupType(GLenum type)355 bool StaticIndexBuffer::lookupType(GLenum type)
356 {
357 return mCacheType == type;
358 }
359
lookupRange(intptr_t offset,GLsizei count,UINT * minIndex,UINT * maxIndex)360 UINT StaticIndexBuffer::lookupRange(intptr_t offset, GLsizei count, UINT *minIndex, UINT *maxIndex)
361 {
362 for (unsigned int range = 0; range < mCache.size(); range++)
363 {
364 if (mCache[range].offset == offset && mCache[range].count == count)
365 {
366 *minIndex = mCache[range].minIndex;
367 *maxIndex = mCache[range].maxIndex;
368
369 return mCache[range].streamOffset;
370 }
371 }
372
373 return -1;
374 }
375
addRange(intptr_t offset,GLsizei count,UINT minIndex,UINT maxIndex,UINT streamOffset)376 void StaticIndexBuffer::addRange(intptr_t offset, GLsizei count, UINT minIndex, UINT maxIndex, UINT streamOffset)
377 {
378 IndexRange indexRange = {offset, count, minIndex, maxIndex, streamOffset};
379 mCache.push_back(indexRange);
380 }
381
382 }
383