blob: 0dc25f9472c5245c43c4623c3f70b171bec055e5 [file] [log] [blame]
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +00001//
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
daniel@transgaming.combbf56f72010-04-20 18:52:13 +000010#include "libGLESv2/geometry/IndexDataManager.h"
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +000011
12#include "common/debug.h"
daniel@transgaming.combbf56f72010-04-20 18:52:13 +000013
14#include "libGLESv2/Buffer.h"
daniel@transgaming.com81655a72010-05-20 19:18:17 +000015#include "libGLESv2/mathutil.h"
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +000016
17namespace
18{
daniel@transgaming.com3e4c6002010-05-05 18:50:13 +000019 enum { INITIAL_INDEX_BUFFER_SIZE = 4096 * sizeof(GLuint) };
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +000020}
21
22namespace gl
23{
24
daniel@transgaming.com83921382011-01-08 05:46:00 +000025IndexDataManager::IndexDataManager(Context *context, IDirect3DDevice9 *device) : mDevice(device)
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +000026{
daniel@transgaming.com83921382011-01-08 05:46:00 +000027 mStreamingBufferShort = new StreamingIndexBuffer(mDevice, INITIAL_INDEX_BUFFER_SIZE, D3DFMT_INDEX16);
daniel@transgaming.com81655a72010-05-20 19:18:17 +000028
daniel@transgaming.com83921382011-01-08 05:46:00 +000029 if (context->supports32bitIndices())
daniel@transgaming.com3e4c6002010-05-05 18:50:13 +000030 {
daniel@transgaming.com83921382011-01-08 05:46:00 +000031 mStreamingBufferInt = new StreamingIndexBuffer(mDevice, INITIAL_INDEX_BUFFER_SIZE, D3DFMT_INDEX32);
daniel@transgaming.com3e4c6002010-05-05 18:50:13 +000032 }
33 else
34 {
daniel@transgaming.com83921382011-01-08 05:46:00 +000035 mStreamingBufferInt = NULL;
daniel@transgaming.com3e4c6002010-05-05 18:50:13 +000036 }
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +000037}
38
39IndexDataManager::~IndexDataManager()
40{
daniel@transgaming.com83921382011-01-08 05:46:00 +000041 delete mStreamingBufferShort;
42 delete mStreamingBufferInt;
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +000043}
44
daniel@transgaming.com83921382011-01-08 05:46:00 +000045void convertIndices(GLenum type, const void *input, GLsizei count, void *output)
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +000046{
daniel@transgaming.com83921382011-01-08 05:46:00 +000047 if (type == GL_UNSIGNED_BYTE)
48 {
49 const GLubyte *in = static_cast<const GLubyte*>(input);
50 GLushort *out = static_cast<GLushort*>(output);
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +000051
daniel@transgaming.com83921382011-01-08 05:46:00 +000052 for (GLsizei i = 0; i < count; i++)
53 {
54 out[i] = in[i];
55 }
56 }
57 else if (type == GL_UNSIGNED_INT)
58 {
59 memcpy(output, input, count * sizeof(GLuint));
60 }
61 else if (type == GL_UNSIGNED_SHORT)
62 {
63 memcpy(output, input, count * sizeof(GLushort));
64 }
65 else UNREACHABLE();
66}
67
68template <class IndexType>
69void computeRange(const IndexType *indices, GLsizei count, GLuint *minIndex, GLuint *maxIndex)
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +000070{
daniel@transgaming.com83921382011-01-08 05:46:00 +000071 *minIndex = indices[0];
72 *maxIndex = indices[0];
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +000073
74 for (GLsizei i = 0; i < count; i++)
75 {
daniel@transgaming.com83921382011-01-08 05:46:00 +000076 if (*minIndex > indices[i]) *minIndex = indices[i];
77 if (*maxIndex < indices[i]) *maxIndex = indices[i];
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +000078 }
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +000079}
80
daniel@transgaming.com83921382011-01-08 05:46:00 +000081void computeRange(GLenum type, const void *indices, GLsizei count, GLuint *minIndex, GLuint *maxIndex)
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +000082{
daniel@transgaming.com83921382011-01-08 05:46:00 +000083 if (type == GL_UNSIGNED_BYTE)
daniel@transgaming.com41d8dd82010-05-12 03:45:03 +000084 {
daniel@transgaming.com83921382011-01-08 05:46:00 +000085 computeRange(static_cast<const GLubyte*>(indices), count, minIndex, maxIndex);
86 }
87 else if (type == GL_UNSIGNED_INT)
88 {
89 computeRange(static_cast<const GLuint*>(indices), count, minIndex, maxIndex);
90 }
91 else if (type == GL_UNSIGNED_SHORT)
92 {
93 computeRange(static_cast<const GLushort*>(indices), count, minIndex, maxIndex);
94 }
95 else UNREACHABLE();
96}
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +000097
daniel@transgaming.com83921382011-01-08 05:46:00 +000098GLenum IndexDataManager::prepareIndexData(GLenum type, GLsizei count, Buffer *buffer, const void *indices, TranslatedIndexData *translated)
99{
100 D3DFORMAT format = (type == GL_UNSIGNED_INT) ? D3DFMT_INDEX32 : D3DFMT_INDEX16;
101 intptr_t offset = reinterpret_cast<intptr_t>(indices);
102 bool alignedOffset = false;
103
104 if (buffer != NULL)
105 {
106 switch (type)
107 {
108 case GL_UNSIGNED_BYTE: alignedOffset = (offset % sizeof(GLubyte) == 0); break;
109 case GL_UNSIGNED_SHORT: alignedOffset = (offset % sizeof(GLushort) == 0); break;
110 case GL_UNSIGNED_INT: alignedOffset = (offset % sizeof(GLuint) == 0); break;
111 default: UNREACHABLE(); alignedOffset = false;
112 }
113
114 if (typeSize(type) * count + offset > static_cast<std::size_t>(buffer->size()))
daniel@transgaming.com41d8dd82010-05-12 03:45:03 +0000115 {
116 return GL_INVALID_OPERATION;
117 }
118
daniel@transgaming.com83921382011-01-08 05:46:00 +0000119 indices = static_cast<const GLubyte*>(buffer->data()) + offset;
daniel@transgaming.com41d8dd82010-05-12 03:45:03 +0000120 }
121
daniel@transgaming.com83921382011-01-08 05:46:00 +0000122 StreamingIndexBuffer *streamingBuffer = (type == GL_UNSIGNED_INT) ? mStreamingBufferInt : mStreamingBufferShort;
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +0000123
daniel@transgaming.com83921382011-01-08 05:46:00 +0000124 StaticIndexBuffer *staticBuffer = buffer ? buffer->getIndexBuffer() : NULL;
125 IndexBuffer *indexBuffer = streamingBuffer;
126 UINT streamOffset = 0;
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +0000127
daniel@transgaming.com83921382011-01-08 05:46:00 +0000128 if (staticBuffer && staticBuffer->lookupType(type) && alignedOffset)
apatrick@chromium.orgf99fbb72010-11-16 01:57:05 +0000129 {
daniel@transgaming.com83921382011-01-08 05:46:00 +0000130 indexBuffer = staticBuffer;
131 streamOffset = staticBuffer->lookupRange(offset, count, &translated->minIndex, &translated->maxIndex);
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +0000132
daniel@transgaming.com83921382011-01-08 05:46:00 +0000133 if (streamOffset == -1)
daniel@transgaming.com3e4c6002010-05-05 18:50:13 +0000134 {
daniel@transgaming.com83921382011-01-08 05:46:00 +0000135 streamOffset = (offset / typeSize(type)) * indexSize(format);
136 computeRange(type, indices, count, &translated->minIndex, &translated->maxIndex);
137 staticBuffer->addRange(offset, count, translated->minIndex, translated->maxIndex, streamOffset);
daniel@transgaming.com3e4c6002010-05-05 18:50:13 +0000138 }
139 }
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +0000140 else
141 {
daniel@transgaming.com83921382011-01-08 05:46:00 +0000142 int convertCount = count;
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +0000143
daniel@transgaming.com83921382011-01-08 05:46:00 +0000144 if (staticBuffer)
145 {
146 if (staticBuffer->size() == 0 && alignedOffset)
147 {
148 indexBuffer = staticBuffer;
149 convertCount = buffer->size() / typeSize(type);
150 }
151 else
152 {
153 buffer->invalidateStaticData();
154 staticBuffer = NULL;
155 }
156 }
157
158 indexBuffer->reserveSpace(convertCount * indexSize(format), type);
159
160 void *output = indexBuffer->map(indexSize(format) * convertCount, &streamOffset);
161 if (output == NULL)
162 {
163 ERR("Failed to map index buffer.");
164 return GL_OUT_OF_MEMORY;
165 }
166
167 convertIndices(type, staticBuffer ? buffer->data() : indices, convertCount, output);
168 indexBuffer->unmap();
169
170 computeRange(type, indices, count, &translated->minIndex, &translated->maxIndex);
171
172 if (staticBuffer)
173 {
174 streamOffset = (offset / typeSize(type)) * indexSize(format);
175 staticBuffer->addRange(offset, count, translated->minIndex, translated->maxIndex, streamOffset);
176 }
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +0000177 }
178
daniel@transgaming.com83921382011-01-08 05:46:00 +0000179 translated->indexBuffer = indexBuffer->getBuffer();
180 translated->startIndex = streamOffset / indexSize(format);
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +0000181
daniel@transgaming.com41d8dd82010-05-12 03:45:03 +0000182 return GL_NO_ERROR;
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +0000183}
184
daniel@transgaming.com83921382011-01-08 05:46:00 +0000185std::size_t IndexDataManager::indexSize(D3DFORMAT format) const
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +0000186{
daniel@transgaming.com83921382011-01-08 05:46:00 +0000187 return (format == D3DFMT_INDEX32) ? sizeof(unsigned int) : sizeof(unsigned short);
daniel@transgaming.com3e4c6002010-05-05 18:50:13 +0000188}
189
daniel@transgaming.com41d8dd82010-05-12 03:45:03 +0000190std::size_t IndexDataManager::typeSize(GLenum type) const
191{
192 switch (type)
193 {
daniel@transgaming.com83921382011-01-08 05:46:00 +0000194 case GL_UNSIGNED_INT: return sizeof(GLuint);
daniel@transgaming.com41d8dd82010-05-12 03:45:03 +0000195 case GL_UNSIGNED_SHORT: return sizeof(GLushort);
daniel@transgaming.com83921382011-01-08 05:46:00 +0000196 case GL_UNSIGNED_BYTE: return sizeof(GLubyte);
197 default: UNREACHABLE(); return sizeof(GLushort);
daniel@transgaming.com41d8dd82010-05-12 03:45:03 +0000198 }
199}
200
daniel@transgaming.com83921382011-01-08 05:46:00 +0000201IndexBuffer::IndexBuffer(IDirect3DDevice9 *device, UINT size, D3DFORMAT format) : mDevice(device), mBufferSize(size), mIndexBuffer(NULL)
daniel@transgaming.com3e4c6002010-05-05 18:50:13 +0000202{
daniel@transgaming.com83921382011-01-08 05:46:00 +0000203 if (size > 0)
204 {
205 HRESULT result = device->CreateIndexBuffer(size, D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, format, D3DPOOL_DEFAULT, &mIndexBuffer, NULL);
206
207 if (FAILED(result))
208 {
209 ERR("Out of memory allocating an index buffer of size %lu.", size);
210 }
211 }
daniel@transgaming.com3e4c6002010-05-05 18:50:13 +0000212}
213
daniel@transgaming.com83921382011-01-08 05:46:00 +0000214IndexBuffer::~IndexBuffer()
daniel@transgaming.com3e4c6002010-05-05 18:50:13 +0000215{
daniel@transgaming.com83921382011-01-08 05:46:00 +0000216 if (mIndexBuffer)
daniel@transgaming.com3e4c6002010-05-05 18:50:13 +0000217 {
daniel@transgaming.com83921382011-01-08 05:46:00 +0000218 mIndexBuffer->Release();
daniel@transgaming.com3e4c6002010-05-05 18:50:13 +0000219 }
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +0000220}
221
daniel@transgaming.com83921382011-01-08 05:46:00 +0000222IDirect3DIndexBuffer9 *IndexBuffer::getBuffer() const
daniel@transgaming.com81655a72010-05-20 19:18:17 +0000223{
daniel@transgaming.com83921382011-01-08 05:46:00 +0000224 return mIndexBuffer;
225}
daniel@transgaming.com81655a72010-05-20 19:18:17 +0000226
daniel@transgaming.com83921382011-01-08 05:46:00 +0000227void IndexBuffer::unmap()
228{
229 if (mIndexBuffer)
daniel@transgaming.com81655a72010-05-20 19:18:17 +0000230 {
daniel@transgaming.com83921382011-01-08 05:46:00 +0000231 mIndexBuffer->Unlock();
daniel@transgaming.com81655a72010-05-20 19:18:17 +0000232 }
daniel@transgaming.com83921382011-01-08 05:46:00 +0000233}
234
235StreamingIndexBuffer::StreamingIndexBuffer(IDirect3DDevice9 *device, UINT initialSize, D3DFORMAT format) : IndexBuffer(device, initialSize, format)
236{
237 mWritePosition = 0;
238}
239
240StreamingIndexBuffer::~StreamingIndexBuffer()
241{
242}
243
244void *StreamingIndexBuffer::map(UINT requiredSpace, UINT *offset)
245{
246 void *mapPtr = NULL;
247
248 if (mIndexBuffer)
daniel@transgaming.com81655a72010-05-20 19:18:17 +0000249 {
daniel@transgaming.com83921382011-01-08 05:46:00 +0000250 HRESULT result = mIndexBuffer->Lock(mWritePosition, requiredSpace, &mapPtr, D3DLOCK_NOOVERWRITE);
251
252 if (FAILED(result))
apatrick@chromium.orgf99fbb72010-11-16 01:57:05 +0000253 {
daniel@transgaming.com83921382011-01-08 05:46:00 +0000254 ERR(" Lock failed with error 0x%08x", result);
255 return NULL;
apatrick@chromium.orgf99fbb72010-11-16 01:57:05 +0000256 }
daniel@transgaming.com81655a72010-05-20 19:18:17 +0000257
daniel@transgaming.com83921382011-01-08 05:46:00 +0000258 *offset = mWritePosition;
259 mWritePosition += requiredSpace;
daniel@transgaming.com81655a72010-05-20 19:18:17 +0000260 }
261
daniel@transgaming.com83921382011-01-08 05:46:00 +0000262 return mapPtr;
263}
daniel@transgaming.com81655a72010-05-20 19:18:17 +0000264
daniel@transgaming.com83921382011-01-08 05:46:00 +0000265void StreamingIndexBuffer::reserveSpace(UINT requiredSpace, GLenum type)
266{
267 if (requiredSpace > mBufferSize)
268 {
269 if (mIndexBuffer)
270 {
271 mIndexBuffer->Release();
272 mIndexBuffer = NULL;
273 }
274
275 mBufferSize = std::max(requiredSpace, 2 * mBufferSize);
276
277 HRESULT result = mDevice->CreateIndexBuffer(mBufferSize, D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, type == GL_UNSIGNED_INT ? D3DFMT_INDEX32 : D3DFMT_INDEX16, D3DPOOL_DEFAULT, &mIndexBuffer, NULL);
278
279 if (FAILED(result))
280 {
281 ERR("Out of memory allocating a vertex buffer of size %lu.", mBufferSize);
282 }
283
284 mWritePosition = 0;
285 }
286 else if (mWritePosition + requiredSpace > mBufferSize) // Recycle
287 {
288 void *dummy;
289 mIndexBuffer->Lock(0, 1, &dummy, D3DLOCK_DISCARD);
290 mIndexBuffer->Unlock();
291
292 mWritePosition = 0;
293 }
294}
295
296StaticIndexBuffer::StaticIndexBuffer(IDirect3DDevice9 *device) : IndexBuffer(device, 0, D3DFMT_UNKNOWN)
297{
298 mCacheType = GL_NONE;
299}
300
301StaticIndexBuffer::~StaticIndexBuffer()
302{
303}
304
305void *StaticIndexBuffer::map(UINT requiredSpace, UINT *offset)
306{
307 void *mapPtr = NULL;
308
309 if (mIndexBuffer)
310 {
311 HRESULT result = mIndexBuffer->Lock(0, requiredSpace, &mapPtr, 0);
312
313 if (FAILED(result))
314 {
315 ERR(" Lock failed with error 0x%08x", result);
316 return NULL;
317 }
318
319 *offset = 0;
320 }
321
322 return mapPtr;
323}
324
325void StaticIndexBuffer::reserveSpace(UINT requiredSpace, GLenum type)
326{
327 if (!mIndexBuffer && mBufferSize == 0)
328 {
329 HRESULT result = mDevice->CreateIndexBuffer(requiredSpace, D3DUSAGE_WRITEONLY, type == GL_UNSIGNED_INT ? D3DFMT_INDEX32 : D3DFMT_INDEX16, D3DPOOL_DEFAULT, &mIndexBuffer, NULL);
330
331 if (FAILED(result))
332 {
333 ERR("Out of memory allocating a vertex buffer of size %lu.", mBufferSize);
334 }
335
336 mBufferSize = requiredSpace;
337 mCacheType = type;
338 }
339 else if (mIndexBuffer && mBufferSize >= requiredSpace && mCacheType == type)
340 {
341 // Already allocated
342 }
343 else UNREACHABLE(); // Static index buffers can't be resized
344}
345
346bool StaticIndexBuffer::lookupType(GLenum type)
347{
348 return mCacheType == type;
349}
350
351UINT StaticIndexBuffer::lookupRange(intptr_t offset, GLsizei count, UINT *minIndex, UINT *maxIndex)
352{
353 for (unsigned int range = 0; range < mCache.size(); range++)
354 {
355 if (mCache[range].offset == offset && mCache[range].count == count)
356 {
357 *minIndex = mCache[range].minIndex;
358 *maxIndex = mCache[range].maxIndex;
359
360 return mCache[range].streamOffset;
361 }
362 }
363
364 return -1;
365}
366
367void StaticIndexBuffer::addRange(intptr_t offset, GLsizei count, UINT minIndex, UINT maxIndex, UINT streamOffset)
368{
369 IndexRange indexRange = {offset, count, minIndex, maxIndex, streamOffset};
370 mCache.push_back(indexRange);
daniel@transgaming.com81655a72010-05-20 19:18:17 +0000371}
372
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +0000373}