blob: 700f76fae1038e6fbe75da35e34f3449b304e4a6 [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.com72b9e182011-04-13 14:58:33 +000033
34 if (!mStreamingBufferInt)
35 {
36 // Don't leave it in a half-initialized state
37 delete mStreamingBufferShort;
38 mStreamingBufferShort = NULL;
39 }
daniel@transgaming.com3e4c6002010-05-05 18:50:13 +000040 }
41 else
42 {
daniel@transgaming.com83921382011-01-08 05:46:00 +000043 mStreamingBufferInt = NULL;
daniel@transgaming.com3e4c6002010-05-05 18:50:13 +000044 }
daniel@transgaming.com72b9e182011-04-13 14:58:33 +000045
46 if (!mStreamingBufferShort)
47 {
48 ERR("Failed to allocate the streaming index buffer(s).");
49 }
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +000050}
51
52IndexDataManager::~IndexDataManager()
53{
daniel@transgaming.com83921382011-01-08 05:46:00 +000054 delete mStreamingBufferShort;
55 delete mStreamingBufferInt;
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +000056}
57
daniel@transgaming.com83921382011-01-08 05:46:00 +000058void convertIndices(GLenum type, const void *input, GLsizei count, void *output)
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +000059{
daniel@transgaming.com83921382011-01-08 05:46:00 +000060 if (type == GL_UNSIGNED_BYTE)
61 {
62 const GLubyte *in = static_cast<const GLubyte*>(input);
63 GLushort *out = static_cast<GLushort*>(output);
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +000064
daniel@transgaming.com83921382011-01-08 05:46:00 +000065 for (GLsizei i = 0; i < count; i++)
66 {
67 out[i] = in[i];
68 }
69 }
70 else if (type == GL_UNSIGNED_INT)
71 {
72 memcpy(output, input, count * sizeof(GLuint));
73 }
74 else if (type == GL_UNSIGNED_SHORT)
75 {
76 memcpy(output, input, count * sizeof(GLushort));
77 }
78 else UNREACHABLE();
79}
80
81template <class IndexType>
82void computeRange(const IndexType *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 *minIndex = indices[0];
85 *maxIndex = indices[0];
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +000086
87 for (GLsizei i = 0; i < count; i++)
88 {
daniel@transgaming.com83921382011-01-08 05:46:00 +000089 if (*minIndex > indices[i]) *minIndex = indices[i];
90 if (*maxIndex < indices[i]) *maxIndex = indices[i];
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +000091 }
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +000092}
93
daniel@transgaming.com83921382011-01-08 05:46:00 +000094void computeRange(GLenum type, const void *indices, GLsizei count, GLuint *minIndex, GLuint *maxIndex)
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +000095{
daniel@transgaming.com83921382011-01-08 05:46:00 +000096 if (type == GL_UNSIGNED_BYTE)
daniel@transgaming.com41d8dd82010-05-12 03:45:03 +000097 {
daniel@transgaming.com83921382011-01-08 05:46:00 +000098 computeRange(static_cast<const GLubyte*>(indices), count, minIndex, maxIndex);
99 }
100 else if (type == GL_UNSIGNED_INT)
101 {
102 computeRange(static_cast<const GLuint*>(indices), count, minIndex, maxIndex);
103 }
104 else if (type == GL_UNSIGNED_SHORT)
105 {
106 computeRange(static_cast<const GLushort*>(indices), count, minIndex, maxIndex);
107 }
108 else UNREACHABLE();
109}
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +0000110
daniel@transgaming.com83921382011-01-08 05:46:00 +0000111GLenum IndexDataManager::prepareIndexData(GLenum type, GLsizei count, Buffer *buffer, const void *indices, TranslatedIndexData *translated)
112{
daniel@transgaming.com72b9e182011-04-13 14:58:33 +0000113 if (!mStreamingBufferShort)
114 {
115 return GL_OUT_OF_MEMORY;
116 }
117
daniel@transgaming.com83921382011-01-08 05:46:00 +0000118 D3DFORMAT format = (type == GL_UNSIGNED_INT) ? D3DFMT_INDEX32 : D3DFMT_INDEX16;
119 intptr_t offset = reinterpret_cast<intptr_t>(indices);
120 bool alignedOffset = false;
121
122 if (buffer != NULL)
123 {
124 switch (type)
125 {
126 case GL_UNSIGNED_BYTE: alignedOffset = (offset % sizeof(GLubyte) == 0); break;
127 case GL_UNSIGNED_SHORT: alignedOffset = (offset % sizeof(GLushort) == 0); break;
128 case GL_UNSIGNED_INT: alignedOffset = (offset % sizeof(GLuint) == 0); break;
129 default: UNREACHABLE(); alignedOffset = false;
130 }
131
132 if (typeSize(type) * count + offset > static_cast<std::size_t>(buffer->size()))
daniel@transgaming.com41d8dd82010-05-12 03:45:03 +0000133 {
134 return GL_INVALID_OPERATION;
135 }
136
daniel@transgaming.com83921382011-01-08 05:46:00 +0000137 indices = static_cast<const GLubyte*>(buffer->data()) + offset;
daniel@transgaming.com41d8dd82010-05-12 03:45:03 +0000138 }
139
daniel@transgaming.com83921382011-01-08 05:46:00 +0000140 StreamingIndexBuffer *streamingBuffer = (type == GL_UNSIGNED_INT) ? mStreamingBufferInt : mStreamingBufferShort;
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +0000141
daniel@transgaming.com83921382011-01-08 05:46:00 +0000142 StaticIndexBuffer *staticBuffer = buffer ? buffer->getIndexBuffer() : NULL;
143 IndexBuffer *indexBuffer = streamingBuffer;
144 UINT streamOffset = 0;
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +0000145
daniel@transgaming.com83921382011-01-08 05:46:00 +0000146 if (staticBuffer && staticBuffer->lookupType(type) && alignedOffset)
apatrick@chromium.orgf99fbb72010-11-16 01:57:05 +0000147 {
daniel@transgaming.com83921382011-01-08 05:46:00 +0000148 indexBuffer = staticBuffer;
149 streamOffset = staticBuffer->lookupRange(offset, count, &translated->minIndex, &translated->maxIndex);
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +0000150
daniel@transgaming.com83921382011-01-08 05:46:00 +0000151 if (streamOffset == -1)
daniel@transgaming.com3e4c6002010-05-05 18:50:13 +0000152 {
daniel@transgaming.com83921382011-01-08 05:46:00 +0000153 streamOffset = (offset / typeSize(type)) * indexSize(format);
154 computeRange(type, indices, count, &translated->minIndex, &translated->maxIndex);
155 staticBuffer->addRange(offset, count, translated->minIndex, translated->maxIndex, streamOffset);
daniel@transgaming.com3e4c6002010-05-05 18:50:13 +0000156 }
157 }
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +0000158 else
159 {
daniel@transgaming.com83921382011-01-08 05:46:00 +0000160 int convertCount = count;
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +0000161
daniel@transgaming.com83921382011-01-08 05:46:00 +0000162 if (staticBuffer)
163 {
164 if (staticBuffer->size() == 0 && alignedOffset)
165 {
166 indexBuffer = staticBuffer;
167 convertCount = buffer->size() / typeSize(type);
168 }
169 else
170 {
171 buffer->invalidateStaticData();
172 staticBuffer = NULL;
173 }
174 }
175
daniel@transgaming.com5ee2ad02011-01-08 05:46:20 +0000176 void *output = NULL;
177
178 if (indexBuffer)
179 {
180 indexBuffer->reserveSpace(convertCount * indexSize(format), type);
181 output = indexBuffer->map(indexSize(format) * convertCount, &streamOffset);
182 }
183
daniel@transgaming.com83921382011-01-08 05:46:00 +0000184 if (output == NULL)
185 {
186 ERR("Failed to map index buffer.");
187 return GL_OUT_OF_MEMORY;
188 }
189
190 convertIndices(type, staticBuffer ? buffer->data() : indices, convertCount, output);
191 indexBuffer->unmap();
192
193 computeRange(type, indices, count, &translated->minIndex, &translated->maxIndex);
194
195 if (staticBuffer)
196 {
197 streamOffset = (offset / typeSize(type)) * indexSize(format);
198 staticBuffer->addRange(offset, count, translated->minIndex, translated->maxIndex, streamOffset);
199 }
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +0000200 }
201
daniel@transgaming.com83921382011-01-08 05:46:00 +0000202 translated->indexBuffer = indexBuffer->getBuffer();
203 translated->startIndex = streamOffset / indexSize(format);
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +0000204
daniel@transgaming.com41d8dd82010-05-12 03:45:03 +0000205 return GL_NO_ERROR;
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +0000206}
207
daniel@transgaming.com83921382011-01-08 05:46:00 +0000208std::size_t IndexDataManager::indexSize(D3DFORMAT format) const
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +0000209{
daniel@transgaming.com83921382011-01-08 05:46:00 +0000210 return (format == D3DFMT_INDEX32) ? sizeof(unsigned int) : sizeof(unsigned short);
daniel@transgaming.com3e4c6002010-05-05 18:50:13 +0000211}
212
daniel@transgaming.com41d8dd82010-05-12 03:45:03 +0000213std::size_t IndexDataManager::typeSize(GLenum type) const
214{
215 switch (type)
216 {
daniel@transgaming.com83921382011-01-08 05:46:00 +0000217 case GL_UNSIGNED_INT: return sizeof(GLuint);
daniel@transgaming.com41d8dd82010-05-12 03:45:03 +0000218 case GL_UNSIGNED_SHORT: return sizeof(GLushort);
daniel@transgaming.com83921382011-01-08 05:46:00 +0000219 case GL_UNSIGNED_BYTE: return sizeof(GLubyte);
220 default: UNREACHABLE(); return sizeof(GLushort);
daniel@transgaming.com41d8dd82010-05-12 03:45:03 +0000221 }
222}
223
daniel@transgaming.com83921382011-01-08 05:46:00 +0000224IndexBuffer::IndexBuffer(IDirect3DDevice9 *device, UINT size, D3DFORMAT format) : mDevice(device), mBufferSize(size), mIndexBuffer(NULL)
daniel@transgaming.com3e4c6002010-05-05 18:50:13 +0000225{
daniel@transgaming.com83921382011-01-08 05:46:00 +0000226 if (size > 0)
227 {
daniel@transgaming.comee04e452011-01-08 05:46:27 +0000228 D3DPOOL pool = getDisplay()->getBufferPool(D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY);
daniel@transgaming.com37b141e2011-01-08 05:46:13 +0000229 HRESULT result = device->CreateIndexBuffer(size, D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, format, pool, &mIndexBuffer, NULL);
daniel@transgaming.com83921382011-01-08 05:46:00 +0000230
231 if (FAILED(result))
232 {
233 ERR("Out of memory allocating an index buffer of size %lu.", size);
234 }
235 }
daniel@transgaming.com3e4c6002010-05-05 18:50:13 +0000236}
237
daniel@transgaming.com83921382011-01-08 05:46:00 +0000238IndexBuffer::~IndexBuffer()
daniel@transgaming.com3e4c6002010-05-05 18:50:13 +0000239{
daniel@transgaming.com83921382011-01-08 05:46:00 +0000240 if (mIndexBuffer)
daniel@transgaming.com3e4c6002010-05-05 18:50:13 +0000241 {
daniel@transgaming.com83921382011-01-08 05:46:00 +0000242 mIndexBuffer->Release();
daniel@transgaming.com3e4c6002010-05-05 18:50:13 +0000243 }
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +0000244}
245
daniel@transgaming.com83921382011-01-08 05:46:00 +0000246IDirect3DIndexBuffer9 *IndexBuffer::getBuffer() const
daniel@transgaming.com81655a72010-05-20 19:18:17 +0000247{
daniel@transgaming.com83921382011-01-08 05:46:00 +0000248 return mIndexBuffer;
249}
daniel@transgaming.com81655a72010-05-20 19:18:17 +0000250
daniel@transgaming.com83921382011-01-08 05:46:00 +0000251void IndexBuffer::unmap()
252{
253 if (mIndexBuffer)
daniel@transgaming.com81655a72010-05-20 19:18:17 +0000254 {
daniel@transgaming.com83921382011-01-08 05:46:00 +0000255 mIndexBuffer->Unlock();
daniel@transgaming.com81655a72010-05-20 19:18:17 +0000256 }
daniel@transgaming.com83921382011-01-08 05:46:00 +0000257}
258
259StreamingIndexBuffer::StreamingIndexBuffer(IDirect3DDevice9 *device, UINT initialSize, D3DFORMAT format) : IndexBuffer(device, initialSize, format)
260{
261 mWritePosition = 0;
262}
263
264StreamingIndexBuffer::~StreamingIndexBuffer()
265{
266}
267
268void *StreamingIndexBuffer::map(UINT requiredSpace, UINT *offset)
269{
270 void *mapPtr = NULL;
271
272 if (mIndexBuffer)
daniel@transgaming.com81655a72010-05-20 19:18:17 +0000273 {
daniel@transgaming.com83921382011-01-08 05:46:00 +0000274 HRESULT result = mIndexBuffer->Lock(mWritePosition, requiredSpace, &mapPtr, D3DLOCK_NOOVERWRITE);
275
276 if (FAILED(result))
apatrick@chromium.orgf99fbb72010-11-16 01:57:05 +0000277 {
daniel@transgaming.com83921382011-01-08 05:46:00 +0000278 ERR(" Lock failed with error 0x%08x", result);
279 return NULL;
apatrick@chromium.orgf99fbb72010-11-16 01:57:05 +0000280 }
daniel@transgaming.com81655a72010-05-20 19:18:17 +0000281
daniel@transgaming.com83921382011-01-08 05:46:00 +0000282 *offset = mWritePosition;
283 mWritePosition += requiredSpace;
daniel@transgaming.com81655a72010-05-20 19:18:17 +0000284 }
285
daniel@transgaming.com83921382011-01-08 05:46:00 +0000286 return mapPtr;
287}
daniel@transgaming.com81655a72010-05-20 19:18:17 +0000288
daniel@transgaming.com83921382011-01-08 05:46:00 +0000289void StreamingIndexBuffer::reserveSpace(UINT requiredSpace, GLenum type)
290{
291 if (requiredSpace > mBufferSize)
292 {
293 if (mIndexBuffer)
294 {
295 mIndexBuffer->Release();
296 mIndexBuffer = NULL;
297 }
298
299 mBufferSize = std::max(requiredSpace, 2 * mBufferSize);
300
daniel@transgaming.comee04e452011-01-08 05:46:27 +0000301 D3DPOOL pool = getDisplay()->getBufferPool(D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY);
daniel@transgaming.com37b141e2011-01-08 05:46:13 +0000302 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 +0000303
304 if (FAILED(result))
305 {
306 ERR("Out of memory allocating a vertex buffer of size %lu.", mBufferSize);
307 }
308
309 mWritePosition = 0;
310 }
311 else if (mWritePosition + requiredSpace > mBufferSize) // Recycle
312 {
313 void *dummy;
314 mIndexBuffer->Lock(0, 1, &dummy, D3DLOCK_DISCARD);
315 mIndexBuffer->Unlock();
316
317 mWritePosition = 0;
318 }
319}
320
321StaticIndexBuffer::StaticIndexBuffer(IDirect3DDevice9 *device) : IndexBuffer(device, 0, D3DFMT_UNKNOWN)
322{
323 mCacheType = GL_NONE;
324}
325
326StaticIndexBuffer::~StaticIndexBuffer()
327{
328}
329
330void *StaticIndexBuffer::map(UINT requiredSpace, UINT *offset)
331{
332 void *mapPtr = NULL;
333
334 if (mIndexBuffer)
335 {
336 HRESULT result = mIndexBuffer->Lock(0, requiredSpace, &mapPtr, 0);
337
338 if (FAILED(result))
339 {
340 ERR(" Lock failed with error 0x%08x", result);
341 return NULL;
342 }
343
344 *offset = 0;
345 }
346
347 return mapPtr;
348}
349
350void StaticIndexBuffer::reserveSpace(UINT requiredSpace, GLenum type)
351{
352 if (!mIndexBuffer && mBufferSize == 0)
353 {
daniel@transgaming.comee04e452011-01-08 05:46:27 +0000354 D3DPOOL pool = getDisplay()->getBufferPool(D3DUSAGE_WRITEONLY);
daniel@transgaming.com37b141e2011-01-08 05:46:13 +0000355 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 +0000356
357 if (FAILED(result))
358 {
359 ERR("Out of memory allocating a vertex buffer of size %lu.", mBufferSize);
360 }
361
362 mBufferSize = requiredSpace;
363 mCacheType = type;
364 }
365 else if (mIndexBuffer && mBufferSize >= requiredSpace && mCacheType == type)
366 {
367 // Already allocated
368 }
369 else UNREACHABLE(); // Static index buffers can't be resized
370}
371
372bool StaticIndexBuffer::lookupType(GLenum type)
373{
374 return mCacheType == type;
375}
376
377UINT StaticIndexBuffer::lookupRange(intptr_t offset, GLsizei count, UINT *minIndex, UINT *maxIndex)
378{
379 for (unsigned int range = 0; range < mCache.size(); range++)
380 {
381 if (mCache[range].offset == offset && mCache[range].count == count)
382 {
383 *minIndex = mCache[range].minIndex;
384 *maxIndex = mCache[range].maxIndex;
385
386 return mCache[range].streamOffset;
387 }
388 }
389
390 return -1;
391}
392
393void StaticIndexBuffer::addRange(intptr_t offset, GLsizei count, UINT minIndex, UINT maxIndex, UINT streamOffset)
394{
395 IndexRange indexRange = {offset, count, minIndex, maxIndex, streamOffset};
396 mCache.push_back(indexRange);
daniel@transgaming.com81655a72010-05-20 19:18:17 +0000397}
398
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +0000399}