blob: afaf1b134c9f1e476dd361a2a0f1d7877873ade0 [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
daniel@transgaming.com8fd34bd2011-02-18 02:52:14 +00007// IndexDataManager.cpp: Defines the IndexDataManager, a class that
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +00008// runs the Buffer translation process for index buffers.
9
daniel@transgaming.com8fd34bd2011-02-18 02:52:14 +000010#include "libGLESv2/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.com37b141e2011-01-08 05:46:13 +000016#include "libGLESv2/main.h"
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +000017
18namespace
19{
daniel@transgaming.com3e4c6002010-05-05 18:50:13 +000020 enum { INITIAL_INDEX_BUFFER_SIZE = 4096 * sizeof(GLuint) };
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +000021}
22
23namespace gl
24{
25
daniel@transgaming.com83921382011-01-08 05:46:00 +000026IndexDataManager::IndexDataManager(Context *context, IDirect3DDevice9 *device) : mDevice(device)
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +000027{
daniel@transgaming.com83921382011-01-08 05:46:00 +000028 mStreamingBufferShort = new StreamingIndexBuffer(mDevice, INITIAL_INDEX_BUFFER_SIZE, D3DFMT_INDEX16);
daniel@transgaming.com81655a72010-05-20 19:18:17 +000029
daniel@transgaming.com83921382011-01-08 05:46:00 +000030 if (context->supports32bitIndices())
daniel@transgaming.com3e4c6002010-05-05 18:50:13 +000031 {
daniel@transgaming.com83921382011-01-08 05:46:00 +000032 mStreamingBufferInt = new StreamingIndexBuffer(mDevice, INITIAL_INDEX_BUFFER_SIZE, D3DFMT_INDEX32);
daniel@transgaming.com3e4c6002010-05-05 18:50:13 +000033 }
34 else
35 {
daniel@transgaming.com83921382011-01-08 05:46:00 +000036 mStreamingBufferInt = NULL;
daniel@transgaming.com3e4c6002010-05-05 18:50:13 +000037 }
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +000038}
39
40IndexDataManager::~IndexDataManager()
41{
daniel@transgaming.com83921382011-01-08 05:46:00 +000042 delete mStreamingBufferShort;
43 delete mStreamingBufferInt;
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +000044}
45
daniel@transgaming.com83921382011-01-08 05:46:00 +000046void convertIndices(GLenum type, const void *input, GLsizei count, void *output)
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +000047{
daniel@transgaming.com83921382011-01-08 05:46:00 +000048 if (type == GL_UNSIGNED_BYTE)
49 {
50 const GLubyte *in = static_cast<const GLubyte*>(input);
51 GLushort *out = static_cast<GLushort*>(output);
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +000052
daniel@transgaming.com83921382011-01-08 05:46:00 +000053 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
69template <class IndexType>
70void computeRange(const IndexType *indices, GLsizei count, GLuint *minIndex, GLuint *maxIndex)
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +000071{
daniel@transgaming.com83921382011-01-08 05:46:00 +000072 *minIndex = indices[0];
73 *maxIndex = indices[0];
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +000074
75 for (GLsizei i = 0; i < count; i++)
76 {
daniel@transgaming.com83921382011-01-08 05:46:00 +000077 if (*minIndex > indices[i]) *minIndex = indices[i];
78 if (*maxIndex < indices[i]) *maxIndex = indices[i];
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +000079 }
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +000080}
81
daniel@transgaming.com83921382011-01-08 05:46:00 +000082void computeRange(GLenum type, const void *indices, GLsizei count, GLuint *minIndex, GLuint *maxIndex)
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +000083{
daniel@transgaming.com83921382011-01-08 05:46:00 +000084 if (type == GL_UNSIGNED_BYTE)
daniel@transgaming.com41d8dd82010-05-12 03:45:03 +000085 {
daniel@transgaming.com83921382011-01-08 05:46:00 +000086 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}
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +000098
daniel@transgaming.com83921382011-01-08 05:46:00 +000099GLenum 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()))
daniel@transgaming.com41d8dd82010-05-12 03:45:03 +0000116 {
117 return GL_INVALID_OPERATION;
118 }
119
daniel@transgaming.com83921382011-01-08 05:46:00 +0000120 indices = static_cast<const GLubyte*>(buffer->data()) + offset;
daniel@transgaming.com41d8dd82010-05-12 03:45:03 +0000121 }
122
daniel@transgaming.com83921382011-01-08 05:46:00 +0000123 StreamingIndexBuffer *streamingBuffer = (type == GL_UNSIGNED_INT) ? mStreamingBufferInt : mStreamingBufferShort;
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +0000124
daniel@transgaming.com83921382011-01-08 05:46:00 +0000125 StaticIndexBuffer *staticBuffer = buffer ? buffer->getIndexBuffer() : NULL;
126 IndexBuffer *indexBuffer = streamingBuffer;
127 UINT streamOffset = 0;
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +0000128
daniel@transgaming.com83921382011-01-08 05:46:00 +0000129 if (staticBuffer && staticBuffer->lookupType(type) && alignedOffset)
apatrick@chromium.orgf99fbb72010-11-16 01:57:05 +0000130 {
daniel@transgaming.com83921382011-01-08 05:46:00 +0000131 indexBuffer = staticBuffer;
132 streamOffset = staticBuffer->lookupRange(offset, count, &translated->minIndex, &translated->maxIndex);
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +0000133
daniel@transgaming.com83921382011-01-08 05:46:00 +0000134 if (streamOffset == -1)
daniel@transgaming.com3e4c6002010-05-05 18:50:13 +0000135 {
daniel@transgaming.com83921382011-01-08 05:46:00 +0000136 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);
daniel@transgaming.com3e4c6002010-05-05 18:50:13 +0000139 }
140 }
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +0000141 else
142 {
daniel@transgaming.com83921382011-01-08 05:46:00 +0000143 int convertCount = count;
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +0000144
daniel@transgaming.com83921382011-01-08 05:46:00 +0000145 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
daniel@transgaming.com5ee2ad02011-01-08 05:46:20 +0000159 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
daniel@transgaming.com83921382011-01-08 05:46:00 +0000167 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 }
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +0000183 }
184
daniel@transgaming.com83921382011-01-08 05:46:00 +0000185 translated->indexBuffer = indexBuffer->getBuffer();
186 translated->startIndex = streamOffset / indexSize(format);
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +0000187
daniel@transgaming.com41d8dd82010-05-12 03:45:03 +0000188 return GL_NO_ERROR;
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +0000189}
190
daniel@transgaming.com83921382011-01-08 05:46:00 +0000191std::size_t IndexDataManager::indexSize(D3DFORMAT format) const
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +0000192{
daniel@transgaming.com83921382011-01-08 05:46:00 +0000193 return (format == D3DFMT_INDEX32) ? sizeof(unsigned int) : sizeof(unsigned short);
daniel@transgaming.com3e4c6002010-05-05 18:50:13 +0000194}
195
daniel@transgaming.com41d8dd82010-05-12 03:45:03 +0000196std::size_t IndexDataManager::typeSize(GLenum type) const
197{
198 switch (type)
199 {
daniel@transgaming.com83921382011-01-08 05:46:00 +0000200 case GL_UNSIGNED_INT: return sizeof(GLuint);
daniel@transgaming.com41d8dd82010-05-12 03:45:03 +0000201 case GL_UNSIGNED_SHORT: return sizeof(GLushort);
daniel@transgaming.com83921382011-01-08 05:46:00 +0000202 case GL_UNSIGNED_BYTE: return sizeof(GLubyte);
203 default: UNREACHABLE(); return sizeof(GLushort);
daniel@transgaming.com41d8dd82010-05-12 03:45:03 +0000204 }
205}
206
daniel@transgaming.com83921382011-01-08 05:46:00 +0000207IndexBuffer::IndexBuffer(IDirect3DDevice9 *device, UINT size, D3DFORMAT format) : mDevice(device), mBufferSize(size), mIndexBuffer(NULL)
daniel@transgaming.com3e4c6002010-05-05 18:50:13 +0000208{
daniel@transgaming.com83921382011-01-08 05:46:00 +0000209 if (size > 0)
210 {
daniel@transgaming.comee04e452011-01-08 05:46:27 +0000211 D3DPOOL pool = getDisplay()->getBufferPool(D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY);
daniel@transgaming.com37b141e2011-01-08 05:46:13 +0000212 HRESULT result = device->CreateIndexBuffer(size, D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, format, pool, &mIndexBuffer, NULL);
daniel@transgaming.com83921382011-01-08 05:46:00 +0000213
214 if (FAILED(result))
215 {
216 ERR("Out of memory allocating an index buffer of size %lu.", size);
217 }
218 }
daniel@transgaming.com3e4c6002010-05-05 18:50:13 +0000219}
220
daniel@transgaming.com83921382011-01-08 05:46:00 +0000221IndexBuffer::~IndexBuffer()
daniel@transgaming.com3e4c6002010-05-05 18:50:13 +0000222{
daniel@transgaming.com83921382011-01-08 05:46:00 +0000223 if (mIndexBuffer)
daniel@transgaming.com3e4c6002010-05-05 18:50:13 +0000224 {
daniel@transgaming.com83921382011-01-08 05:46:00 +0000225 mIndexBuffer->Release();
daniel@transgaming.com3e4c6002010-05-05 18:50:13 +0000226 }
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +0000227}
228
daniel@transgaming.com83921382011-01-08 05:46:00 +0000229IDirect3DIndexBuffer9 *IndexBuffer::getBuffer() const
daniel@transgaming.com81655a72010-05-20 19:18:17 +0000230{
daniel@transgaming.com83921382011-01-08 05:46:00 +0000231 return mIndexBuffer;
232}
daniel@transgaming.com81655a72010-05-20 19:18:17 +0000233
daniel@transgaming.com83921382011-01-08 05:46:00 +0000234void IndexBuffer::unmap()
235{
236 if (mIndexBuffer)
daniel@transgaming.com81655a72010-05-20 19:18:17 +0000237 {
daniel@transgaming.com83921382011-01-08 05:46:00 +0000238 mIndexBuffer->Unlock();
daniel@transgaming.com81655a72010-05-20 19:18:17 +0000239 }
daniel@transgaming.com83921382011-01-08 05:46:00 +0000240}
241
242StreamingIndexBuffer::StreamingIndexBuffer(IDirect3DDevice9 *device, UINT initialSize, D3DFORMAT format) : IndexBuffer(device, initialSize, format)
243{
244 mWritePosition = 0;
245}
246
247StreamingIndexBuffer::~StreamingIndexBuffer()
248{
249}
250
251void *StreamingIndexBuffer::map(UINT requiredSpace, UINT *offset)
252{
253 void *mapPtr = NULL;
254
255 if (mIndexBuffer)
daniel@transgaming.com81655a72010-05-20 19:18:17 +0000256 {
daniel@transgaming.com83921382011-01-08 05:46:00 +0000257 HRESULT result = mIndexBuffer->Lock(mWritePosition, requiredSpace, &mapPtr, D3DLOCK_NOOVERWRITE);
258
259 if (FAILED(result))
apatrick@chromium.orgf99fbb72010-11-16 01:57:05 +0000260 {
daniel@transgaming.com83921382011-01-08 05:46:00 +0000261 ERR(" Lock failed with error 0x%08x", result);
262 return NULL;
apatrick@chromium.orgf99fbb72010-11-16 01:57:05 +0000263 }
daniel@transgaming.com81655a72010-05-20 19:18:17 +0000264
daniel@transgaming.com83921382011-01-08 05:46:00 +0000265 *offset = mWritePosition;
266 mWritePosition += requiredSpace;
daniel@transgaming.com81655a72010-05-20 19:18:17 +0000267 }
268
daniel@transgaming.com83921382011-01-08 05:46:00 +0000269 return mapPtr;
270}
daniel@transgaming.com81655a72010-05-20 19:18:17 +0000271
daniel@transgaming.com83921382011-01-08 05:46:00 +0000272void 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
daniel@transgaming.comee04e452011-01-08 05:46:27 +0000284 D3DPOOL pool = getDisplay()->getBufferPool(D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY);
daniel@transgaming.com37b141e2011-01-08 05:46:13 +0000285 HRESULT result = mDevice->CreateIndexBuffer(mBufferSize, D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, type == GL_UNSIGNED_INT ? D3DFMT_INDEX32 : D3DFMT_INDEX16, pool, &mIndexBuffer, NULL);
daniel@transgaming.com83921382011-01-08 05:46:00 +0000286
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
304StaticIndexBuffer::StaticIndexBuffer(IDirect3DDevice9 *device) : IndexBuffer(device, 0, D3DFMT_UNKNOWN)
305{
306 mCacheType = GL_NONE;
307}
308
309StaticIndexBuffer::~StaticIndexBuffer()
310{
311}
312
313void *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
333void StaticIndexBuffer::reserveSpace(UINT requiredSpace, GLenum type)
334{
335 if (!mIndexBuffer && mBufferSize == 0)
336 {
daniel@transgaming.comee04e452011-01-08 05:46:27 +0000337 D3DPOOL pool = getDisplay()->getBufferPool(D3DUSAGE_WRITEONLY);
daniel@transgaming.com37b141e2011-01-08 05:46:13 +0000338 HRESULT result = mDevice->CreateIndexBuffer(requiredSpace, D3DUSAGE_WRITEONLY, type == GL_UNSIGNED_INT ? D3DFMT_INDEX32 : D3DFMT_INDEX16, pool, &mIndexBuffer, NULL);
daniel@transgaming.com83921382011-01-08 05:46:00 +0000339
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
355bool StaticIndexBuffer::lookupType(GLenum type)
356{
357 return mCacheType == type;
358}
359
360UINT 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
376void 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);
daniel@transgaming.com81655a72010-05-20 19:18:17 +0000380}
381
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +0000382}