blob: 5b939b8a93fbcdfb47559833ad284bb68adfbd65 [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.com78624ca2011-04-22 04:17:57 +0000142 StaticIndexBuffer *staticBuffer = buffer ? buffer->getStaticIndexBuffer() : NULL;
daniel@transgaming.com83921382011-01-08 05:46:00 +0000143 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.com78624ca2011-04-22 04:17:57 +0000205 if (buffer)
206 {
207 buffer->promoteStaticUsage(count * typeSize(type));
208 }
209
daniel@transgaming.com41d8dd82010-05-12 03:45:03 +0000210 return GL_NO_ERROR;
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +0000211}
212
daniel@transgaming.com83921382011-01-08 05:46:00 +0000213std::size_t IndexDataManager::indexSize(D3DFORMAT format) const
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +0000214{
daniel@transgaming.com83921382011-01-08 05:46:00 +0000215 return (format == D3DFMT_INDEX32) ? sizeof(unsigned int) : sizeof(unsigned short);
daniel@transgaming.com3e4c6002010-05-05 18:50:13 +0000216}
217
daniel@transgaming.com41d8dd82010-05-12 03:45:03 +0000218std::size_t IndexDataManager::typeSize(GLenum type) const
219{
220 switch (type)
221 {
daniel@transgaming.com83921382011-01-08 05:46:00 +0000222 case GL_UNSIGNED_INT: return sizeof(GLuint);
daniel@transgaming.com41d8dd82010-05-12 03:45:03 +0000223 case GL_UNSIGNED_SHORT: return sizeof(GLushort);
daniel@transgaming.com83921382011-01-08 05:46:00 +0000224 case GL_UNSIGNED_BYTE: return sizeof(GLubyte);
225 default: UNREACHABLE(); return sizeof(GLushort);
daniel@transgaming.com41d8dd82010-05-12 03:45:03 +0000226 }
227}
228
daniel@transgaming.com83921382011-01-08 05:46:00 +0000229IndexBuffer::IndexBuffer(IDirect3DDevice9 *device, UINT size, D3DFORMAT format) : mDevice(device), mBufferSize(size), mIndexBuffer(NULL)
daniel@transgaming.com3e4c6002010-05-05 18:50:13 +0000230{
daniel@transgaming.com83921382011-01-08 05:46:00 +0000231 if (size > 0)
232 {
daniel@transgaming.comee04e452011-01-08 05:46:27 +0000233 D3DPOOL pool = getDisplay()->getBufferPool(D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY);
daniel@transgaming.com37b141e2011-01-08 05:46:13 +0000234 HRESULT result = device->CreateIndexBuffer(size, D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, format, pool, &mIndexBuffer, NULL);
daniel@transgaming.com83921382011-01-08 05:46:00 +0000235
236 if (FAILED(result))
237 {
238 ERR("Out of memory allocating an index buffer of size %lu.", size);
239 }
240 }
daniel@transgaming.com3e4c6002010-05-05 18:50:13 +0000241}
242
daniel@transgaming.com83921382011-01-08 05:46:00 +0000243IndexBuffer::~IndexBuffer()
daniel@transgaming.com3e4c6002010-05-05 18:50:13 +0000244{
daniel@transgaming.com83921382011-01-08 05:46:00 +0000245 if (mIndexBuffer)
daniel@transgaming.com3e4c6002010-05-05 18:50:13 +0000246 {
daniel@transgaming.com83921382011-01-08 05:46:00 +0000247 mIndexBuffer->Release();
daniel@transgaming.com3e4c6002010-05-05 18:50:13 +0000248 }
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +0000249}
250
daniel@transgaming.com83921382011-01-08 05:46:00 +0000251IDirect3DIndexBuffer9 *IndexBuffer::getBuffer() const
daniel@transgaming.com81655a72010-05-20 19:18:17 +0000252{
daniel@transgaming.com83921382011-01-08 05:46:00 +0000253 return mIndexBuffer;
254}
daniel@transgaming.com81655a72010-05-20 19:18:17 +0000255
daniel@transgaming.com83921382011-01-08 05:46:00 +0000256void IndexBuffer::unmap()
257{
258 if (mIndexBuffer)
daniel@transgaming.com81655a72010-05-20 19:18:17 +0000259 {
daniel@transgaming.com83921382011-01-08 05:46:00 +0000260 mIndexBuffer->Unlock();
daniel@transgaming.com81655a72010-05-20 19:18:17 +0000261 }
daniel@transgaming.com83921382011-01-08 05:46:00 +0000262}
263
264StreamingIndexBuffer::StreamingIndexBuffer(IDirect3DDevice9 *device, UINT initialSize, D3DFORMAT format) : IndexBuffer(device, initialSize, format)
265{
266 mWritePosition = 0;
267}
268
269StreamingIndexBuffer::~StreamingIndexBuffer()
270{
271}
272
273void *StreamingIndexBuffer::map(UINT requiredSpace, UINT *offset)
274{
275 void *mapPtr = NULL;
276
277 if (mIndexBuffer)
daniel@transgaming.com81655a72010-05-20 19:18:17 +0000278 {
daniel@transgaming.com83921382011-01-08 05:46:00 +0000279 HRESULT result = mIndexBuffer->Lock(mWritePosition, requiredSpace, &mapPtr, D3DLOCK_NOOVERWRITE);
280
281 if (FAILED(result))
apatrick@chromium.orgf99fbb72010-11-16 01:57:05 +0000282 {
daniel@transgaming.com83921382011-01-08 05:46:00 +0000283 ERR(" Lock failed with error 0x%08x", result);
284 return NULL;
apatrick@chromium.orgf99fbb72010-11-16 01:57:05 +0000285 }
daniel@transgaming.com81655a72010-05-20 19:18:17 +0000286
daniel@transgaming.com83921382011-01-08 05:46:00 +0000287 *offset = mWritePosition;
288 mWritePosition += requiredSpace;
daniel@transgaming.com81655a72010-05-20 19:18:17 +0000289 }
290
daniel@transgaming.com83921382011-01-08 05:46:00 +0000291 return mapPtr;
292}
daniel@transgaming.com81655a72010-05-20 19:18:17 +0000293
daniel@transgaming.com83921382011-01-08 05:46:00 +0000294void StreamingIndexBuffer::reserveSpace(UINT requiredSpace, GLenum type)
295{
296 if (requiredSpace > mBufferSize)
297 {
298 if (mIndexBuffer)
299 {
300 mIndexBuffer->Release();
301 mIndexBuffer = NULL;
302 }
303
304 mBufferSize = std::max(requiredSpace, 2 * mBufferSize);
305
daniel@transgaming.comee04e452011-01-08 05:46:27 +0000306 D3DPOOL pool = getDisplay()->getBufferPool(D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY);
daniel@transgaming.com37b141e2011-01-08 05:46:13 +0000307 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 +0000308
309 if (FAILED(result))
310 {
311 ERR("Out of memory allocating a vertex buffer of size %lu.", mBufferSize);
312 }
313
314 mWritePosition = 0;
315 }
316 else if (mWritePosition + requiredSpace > mBufferSize) // Recycle
317 {
318 void *dummy;
319 mIndexBuffer->Lock(0, 1, &dummy, D3DLOCK_DISCARD);
320 mIndexBuffer->Unlock();
321
322 mWritePosition = 0;
323 }
324}
325
326StaticIndexBuffer::StaticIndexBuffer(IDirect3DDevice9 *device) : IndexBuffer(device, 0, D3DFMT_UNKNOWN)
327{
328 mCacheType = GL_NONE;
329}
330
331StaticIndexBuffer::~StaticIndexBuffer()
332{
333}
334
335void *StaticIndexBuffer::map(UINT requiredSpace, UINT *offset)
336{
337 void *mapPtr = NULL;
338
339 if (mIndexBuffer)
340 {
341 HRESULT result = mIndexBuffer->Lock(0, requiredSpace, &mapPtr, 0);
342
343 if (FAILED(result))
344 {
345 ERR(" Lock failed with error 0x%08x", result);
346 return NULL;
347 }
348
349 *offset = 0;
350 }
351
352 return mapPtr;
353}
354
355void StaticIndexBuffer::reserveSpace(UINT requiredSpace, GLenum type)
356{
357 if (!mIndexBuffer && mBufferSize == 0)
358 {
daniel@transgaming.comee04e452011-01-08 05:46:27 +0000359 D3DPOOL pool = getDisplay()->getBufferPool(D3DUSAGE_WRITEONLY);
daniel@transgaming.com37b141e2011-01-08 05:46:13 +0000360 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 +0000361
362 if (FAILED(result))
363 {
364 ERR("Out of memory allocating a vertex buffer of size %lu.", mBufferSize);
365 }
366
367 mBufferSize = requiredSpace;
368 mCacheType = type;
369 }
370 else if (mIndexBuffer && mBufferSize >= requiredSpace && mCacheType == type)
371 {
372 // Already allocated
373 }
374 else UNREACHABLE(); // Static index buffers can't be resized
375}
376
377bool StaticIndexBuffer::lookupType(GLenum type)
378{
379 return mCacheType == type;
380}
381
382UINT StaticIndexBuffer::lookupRange(intptr_t offset, GLsizei count, UINT *minIndex, UINT *maxIndex)
383{
384 for (unsigned int range = 0; range < mCache.size(); range++)
385 {
386 if (mCache[range].offset == offset && mCache[range].count == count)
387 {
388 *minIndex = mCache[range].minIndex;
389 *maxIndex = mCache[range].maxIndex;
390
391 return mCache[range].streamOffset;
392 }
393 }
394
395 return -1;
396}
397
398void StaticIndexBuffer::addRange(intptr_t offset, GLsizei count, UINT minIndex, UINT maxIndex, UINT streamOffset)
399{
400 IndexRange indexRange = {offset, count, minIndex, maxIndex, streamOffset};
401 mCache.push_back(indexRange);
daniel@transgaming.com81655a72010-05-20 19:18:17 +0000402}
403
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +0000404}