blob: e6630b18d709c85bfa03b56755eb27057e1a7182 [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{
jbauman@chromium.orgd8f3faa2011-09-02 01:10:47 +000025unsigned int IndexBuffer::mCurrentSerial = 1;
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +000026
daniel@transgaming.com83921382011-01-08 05:46:00 +000027IndexDataManager::IndexDataManager(Context *context, IDirect3DDevice9 *device) : mDevice(device)
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +000028{
daniel@transgaming.com83921382011-01-08 05:46:00 +000029 mStreamingBufferShort = new StreamingIndexBuffer(mDevice, INITIAL_INDEX_BUFFER_SIZE, D3DFMT_INDEX16);
daniel@transgaming.com81655a72010-05-20 19:18:17 +000030
daniel@transgaming.com83921382011-01-08 05:46:00 +000031 if (context->supports32bitIndices())
daniel@transgaming.com3e4c6002010-05-05 18:50:13 +000032 {
daniel@transgaming.com83921382011-01-08 05:46:00 +000033 mStreamingBufferInt = new StreamingIndexBuffer(mDevice, INITIAL_INDEX_BUFFER_SIZE, D3DFMT_INDEX32);
daniel@transgaming.com72b9e182011-04-13 14:58:33 +000034
35 if (!mStreamingBufferInt)
36 {
37 // Don't leave it in a half-initialized state
38 delete mStreamingBufferShort;
39 mStreamingBufferShort = NULL;
40 }
daniel@transgaming.com3e4c6002010-05-05 18:50:13 +000041 }
42 else
43 {
daniel@transgaming.com83921382011-01-08 05:46:00 +000044 mStreamingBufferInt = NULL;
daniel@transgaming.com3e4c6002010-05-05 18:50:13 +000045 }
daniel@transgaming.com72b9e182011-04-13 14:58:33 +000046
47 if (!mStreamingBufferShort)
48 {
49 ERR("Failed to allocate the streaming index buffer(s).");
50 }
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +000051}
52
53IndexDataManager::~IndexDataManager()
54{
daniel@transgaming.com83921382011-01-08 05:46:00 +000055 delete mStreamingBufferShort;
56 delete mStreamingBufferInt;
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +000057}
58
daniel@transgaming.com83921382011-01-08 05:46:00 +000059void convertIndices(GLenum type, const void *input, GLsizei count, void *output)
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +000060{
daniel@transgaming.com83921382011-01-08 05:46:00 +000061 if (type == GL_UNSIGNED_BYTE)
62 {
63 const GLubyte *in = static_cast<const GLubyte*>(input);
64 GLushort *out = static_cast<GLushort*>(output);
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +000065
daniel@transgaming.com83921382011-01-08 05:46:00 +000066 for (GLsizei i = 0; i < count; i++)
67 {
68 out[i] = in[i];
69 }
70 }
71 else if (type == GL_UNSIGNED_INT)
72 {
73 memcpy(output, input, count * sizeof(GLuint));
74 }
75 else if (type == GL_UNSIGNED_SHORT)
76 {
77 memcpy(output, input, count * sizeof(GLushort));
78 }
79 else UNREACHABLE();
80}
81
82template <class IndexType>
83void computeRange(const IndexType *indices, GLsizei count, GLuint *minIndex, GLuint *maxIndex)
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +000084{
daniel@transgaming.com83921382011-01-08 05:46:00 +000085 *minIndex = indices[0];
86 *maxIndex = indices[0];
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +000087
88 for (GLsizei i = 0; i < count; i++)
89 {
daniel@transgaming.com83921382011-01-08 05:46:00 +000090 if (*minIndex > indices[i]) *minIndex = indices[i];
91 if (*maxIndex < indices[i]) *maxIndex = indices[i];
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +000092 }
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +000093}
94
daniel@transgaming.comd2820bf2012-01-27 15:38:48 +000095void computeRange(GLenum type, const GLvoid *indices, GLsizei count, GLuint *minIndex, GLuint *maxIndex)
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +000096{
daniel@transgaming.com83921382011-01-08 05:46:00 +000097 if (type == GL_UNSIGNED_BYTE)
daniel@transgaming.com41d8dd82010-05-12 03:45:03 +000098 {
daniel@transgaming.com83921382011-01-08 05:46:00 +000099 computeRange(static_cast<const GLubyte*>(indices), count, minIndex, maxIndex);
100 }
101 else if (type == GL_UNSIGNED_INT)
102 {
103 computeRange(static_cast<const GLuint*>(indices), count, minIndex, maxIndex);
104 }
105 else if (type == GL_UNSIGNED_SHORT)
106 {
107 computeRange(static_cast<const GLushort*>(indices), count, minIndex, maxIndex);
108 }
109 else UNREACHABLE();
110}
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +0000111
daniel@transgaming.comd2820bf2012-01-27 15:38:48 +0000112GLenum IndexDataManager::prepareIndexData(GLenum type, GLsizei count, Buffer *buffer, const GLvoid *indices, TranslatedIndexData *translated)
daniel@transgaming.com83921382011-01-08 05:46:00 +0000113{
daniel@transgaming.com72b9e182011-04-13 14:58:33 +0000114 if (!mStreamingBufferShort)
115 {
116 return GL_OUT_OF_MEMORY;
117 }
118
daniel@transgaming.com83921382011-01-08 05:46:00 +0000119 D3DFORMAT format = (type == GL_UNSIGNED_INT) ? D3DFMT_INDEX32 : D3DFMT_INDEX16;
120 intptr_t offset = reinterpret_cast<intptr_t>(indices);
121 bool alignedOffset = false;
122
123 if (buffer != NULL)
124 {
125 switch (type)
126 {
127 case GL_UNSIGNED_BYTE: alignedOffset = (offset % sizeof(GLubyte) == 0); break;
128 case GL_UNSIGNED_SHORT: alignedOffset = (offset % sizeof(GLushort) == 0); break;
129 case GL_UNSIGNED_INT: alignedOffset = (offset % sizeof(GLuint) == 0); break;
130 default: UNREACHABLE(); alignedOffset = false;
131 }
132
133 if (typeSize(type) * count + offset > static_cast<std::size_t>(buffer->size()))
daniel@transgaming.com41d8dd82010-05-12 03:45:03 +0000134 {
135 return GL_INVALID_OPERATION;
136 }
137
daniel@transgaming.com83921382011-01-08 05:46:00 +0000138 indices = static_cast<const GLubyte*>(buffer->data()) + offset;
daniel@transgaming.com41d8dd82010-05-12 03:45:03 +0000139 }
140
daniel@transgaming.com83921382011-01-08 05:46:00 +0000141 StreamingIndexBuffer *streamingBuffer = (type == GL_UNSIGNED_INT) ? mStreamingBufferInt : mStreamingBufferShort;
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +0000142
daniel@transgaming.com78624ca2011-04-22 04:17:57 +0000143 StaticIndexBuffer *staticBuffer = buffer ? buffer->getStaticIndexBuffer() : NULL;
daniel@transgaming.com83921382011-01-08 05:46:00 +0000144 IndexBuffer *indexBuffer = streamingBuffer;
145 UINT streamOffset = 0;
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +0000146
daniel@transgaming.com83921382011-01-08 05:46:00 +0000147 if (staticBuffer && staticBuffer->lookupType(type) && alignedOffset)
apatrick@chromium.orgf99fbb72010-11-16 01:57:05 +0000148 {
daniel@transgaming.com83921382011-01-08 05:46:00 +0000149 indexBuffer = staticBuffer;
150 streamOffset = staticBuffer->lookupRange(offset, count, &translated->minIndex, &translated->maxIndex);
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +0000151
daniel@transgaming.com83921382011-01-08 05:46:00 +0000152 if (streamOffset == -1)
daniel@transgaming.com3e4c6002010-05-05 18:50:13 +0000153 {
daniel@transgaming.com83921382011-01-08 05:46:00 +0000154 streamOffset = (offset / typeSize(type)) * indexSize(format);
155 computeRange(type, indices, count, &translated->minIndex, &translated->maxIndex);
156 staticBuffer->addRange(offset, count, translated->minIndex, translated->maxIndex, streamOffset);
daniel@transgaming.com3e4c6002010-05-05 18:50:13 +0000157 }
158 }
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +0000159 else
160 {
daniel@transgaming.com83921382011-01-08 05:46:00 +0000161 int convertCount = count;
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +0000162
daniel@transgaming.com83921382011-01-08 05:46:00 +0000163 if (staticBuffer)
164 {
165 if (staticBuffer->size() == 0 && alignedOffset)
166 {
167 indexBuffer = staticBuffer;
168 convertCount = buffer->size() / typeSize(type);
169 }
170 else
171 {
172 buffer->invalidateStaticData();
173 staticBuffer = NULL;
174 }
175 }
176
daniel@transgaming.com5ee2ad02011-01-08 05:46:20 +0000177 void *output = NULL;
178
179 if (indexBuffer)
180 {
181 indexBuffer->reserveSpace(convertCount * indexSize(format), type);
182 output = indexBuffer->map(indexSize(format) * convertCount, &streamOffset);
183 }
184
daniel@transgaming.com83921382011-01-08 05:46:00 +0000185 if (output == NULL)
186 {
187 ERR("Failed to map index buffer.");
188 return GL_OUT_OF_MEMORY;
189 }
190
191 convertIndices(type, staticBuffer ? buffer->data() : indices, convertCount, output);
192 indexBuffer->unmap();
193
194 computeRange(type, indices, count, &translated->minIndex, &translated->maxIndex);
195
196 if (staticBuffer)
197 {
198 streamOffset = (offset / typeSize(type)) * indexSize(format);
199 staticBuffer->addRange(offset, count, translated->minIndex, translated->maxIndex, streamOffset);
200 }
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +0000201 }
202
daniel@transgaming.com83921382011-01-08 05:46:00 +0000203 translated->indexBuffer = indexBuffer->getBuffer();
jbauman@chromium.orgd8f3faa2011-09-02 01:10:47 +0000204 translated->serial = indexBuffer->getSerial();
daniel@transgaming.com83921382011-01-08 05:46:00 +0000205 translated->startIndex = streamOffset / indexSize(format);
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +0000206
daniel@transgaming.com78624ca2011-04-22 04:17:57 +0000207 if (buffer)
208 {
209 buffer->promoteStaticUsage(count * typeSize(type));
210 }
211
daniel@transgaming.com41d8dd82010-05-12 03:45:03 +0000212 return GL_NO_ERROR;
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +0000213}
214
daniel@transgaming.com83921382011-01-08 05:46:00 +0000215std::size_t IndexDataManager::indexSize(D3DFORMAT format) const
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +0000216{
daniel@transgaming.com83921382011-01-08 05:46:00 +0000217 return (format == D3DFMT_INDEX32) ? sizeof(unsigned int) : sizeof(unsigned short);
daniel@transgaming.com3e4c6002010-05-05 18:50:13 +0000218}
219
daniel@transgaming.com41d8dd82010-05-12 03:45:03 +0000220std::size_t IndexDataManager::typeSize(GLenum type) const
221{
222 switch (type)
223 {
daniel@transgaming.com83921382011-01-08 05:46:00 +0000224 case GL_UNSIGNED_INT: return sizeof(GLuint);
daniel@transgaming.com41d8dd82010-05-12 03:45:03 +0000225 case GL_UNSIGNED_SHORT: return sizeof(GLushort);
daniel@transgaming.com83921382011-01-08 05:46:00 +0000226 case GL_UNSIGNED_BYTE: return sizeof(GLubyte);
227 default: UNREACHABLE(); return sizeof(GLushort);
daniel@transgaming.com41d8dd82010-05-12 03:45:03 +0000228 }
229}
230
daniel@transgaming.com83921382011-01-08 05:46:00 +0000231IndexBuffer::IndexBuffer(IDirect3DDevice9 *device, UINT size, D3DFORMAT format) : mDevice(device), mBufferSize(size), mIndexBuffer(NULL)
daniel@transgaming.com3e4c6002010-05-05 18:50:13 +0000232{
daniel@transgaming.com83921382011-01-08 05:46:00 +0000233 if (size > 0)
234 {
daniel@transgaming.comee04e452011-01-08 05:46:27 +0000235 D3DPOOL pool = getDisplay()->getBufferPool(D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY);
daniel@transgaming.com37b141e2011-01-08 05:46:13 +0000236 HRESULT result = device->CreateIndexBuffer(size, D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, format, pool, &mIndexBuffer, NULL);
jbauman@chromium.orgd8f3faa2011-09-02 01:10:47 +0000237 mSerial = issueSerial();
daniel@transgaming.com83921382011-01-08 05:46:00 +0000238
239 if (FAILED(result))
240 {
241 ERR("Out of memory allocating an index buffer of size %lu.", size);
242 }
243 }
daniel@transgaming.com3e4c6002010-05-05 18:50:13 +0000244}
245
daniel@transgaming.com83921382011-01-08 05:46:00 +0000246IndexBuffer::~IndexBuffer()
daniel@transgaming.com3e4c6002010-05-05 18:50:13 +0000247{
daniel@transgaming.com83921382011-01-08 05:46:00 +0000248 if (mIndexBuffer)
daniel@transgaming.com3e4c6002010-05-05 18:50:13 +0000249 {
daniel@transgaming.com83921382011-01-08 05:46:00 +0000250 mIndexBuffer->Release();
daniel@transgaming.com3e4c6002010-05-05 18:50:13 +0000251 }
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +0000252}
253
daniel@transgaming.com83921382011-01-08 05:46:00 +0000254IDirect3DIndexBuffer9 *IndexBuffer::getBuffer() const
daniel@transgaming.com81655a72010-05-20 19:18:17 +0000255{
daniel@transgaming.com83921382011-01-08 05:46:00 +0000256 return mIndexBuffer;
257}
daniel@transgaming.com81655a72010-05-20 19:18:17 +0000258
jbauman@chromium.orgd8f3faa2011-09-02 01:10:47 +0000259unsigned int IndexBuffer::getSerial() const
260{
261 return mSerial;
262}
263
264unsigned int IndexBuffer::issueSerial()
265{
266 return mCurrentSerial++;
267}
268
daniel@transgaming.com83921382011-01-08 05:46:00 +0000269void IndexBuffer::unmap()
270{
271 if (mIndexBuffer)
daniel@transgaming.com81655a72010-05-20 19:18:17 +0000272 {
daniel@transgaming.com83921382011-01-08 05:46:00 +0000273 mIndexBuffer->Unlock();
daniel@transgaming.com81655a72010-05-20 19:18:17 +0000274 }
daniel@transgaming.com83921382011-01-08 05:46:00 +0000275}
276
277StreamingIndexBuffer::StreamingIndexBuffer(IDirect3DDevice9 *device, UINT initialSize, D3DFORMAT format) : IndexBuffer(device, initialSize, format)
278{
279 mWritePosition = 0;
280}
281
282StreamingIndexBuffer::~StreamingIndexBuffer()
283{
284}
285
286void *StreamingIndexBuffer::map(UINT requiredSpace, UINT *offset)
287{
288 void *mapPtr = NULL;
289
290 if (mIndexBuffer)
daniel@transgaming.com81655a72010-05-20 19:18:17 +0000291 {
daniel@transgaming.com83921382011-01-08 05:46:00 +0000292 HRESULT result = mIndexBuffer->Lock(mWritePosition, requiredSpace, &mapPtr, D3DLOCK_NOOVERWRITE);
293
294 if (FAILED(result))
apatrick@chromium.orgf99fbb72010-11-16 01:57:05 +0000295 {
daniel@transgaming.com83921382011-01-08 05:46:00 +0000296 ERR(" Lock failed with error 0x%08x", result);
297 return NULL;
apatrick@chromium.orgf99fbb72010-11-16 01:57:05 +0000298 }
daniel@transgaming.com81655a72010-05-20 19:18:17 +0000299
daniel@transgaming.com83921382011-01-08 05:46:00 +0000300 *offset = mWritePosition;
301 mWritePosition += requiredSpace;
daniel@transgaming.com81655a72010-05-20 19:18:17 +0000302 }
303
daniel@transgaming.com83921382011-01-08 05:46:00 +0000304 return mapPtr;
305}
daniel@transgaming.com81655a72010-05-20 19:18:17 +0000306
daniel@transgaming.com83921382011-01-08 05:46:00 +0000307void StreamingIndexBuffer::reserveSpace(UINT requiredSpace, GLenum type)
308{
309 if (requiredSpace > mBufferSize)
310 {
311 if (mIndexBuffer)
312 {
313 mIndexBuffer->Release();
314 mIndexBuffer = NULL;
315 }
316
317 mBufferSize = std::max(requiredSpace, 2 * mBufferSize);
318
daniel@transgaming.comee04e452011-01-08 05:46:27 +0000319 D3DPOOL pool = getDisplay()->getBufferPool(D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY);
daniel@transgaming.com37b141e2011-01-08 05:46:13 +0000320 HRESULT result = mDevice->CreateIndexBuffer(mBufferSize, D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, type == GL_UNSIGNED_INT ? D3DFMT_INDEX32 : D3DFMT_INDEX16, pool, &mIndexBuffer, NULL);
jbauman@chromium.orgd8f3faa2011-09-02 01:10:47 +0000321 mSerial = issueSerial();
daniel@transgaming.com83921382011-01-08 05:46:00 +0000322
323 if (FAILED(result))
324 {
325 ERR("Out of memory allocating a vertex buffer of size %lu.", mBufferSize);
326 }
327
328 mWritePosition = 0;
329 }
330 else if (mWritePosition + requiredSpace > mBufferSize) // Recycle
331 {
332 void *dummy;
333 mIndexBuffer->Lock(0, 1, &dummy, D3DLOCK_DISCARD);
334 mIndexBuffer->Unlock();
335
336 mWritePosition = 0;
337 }
338}
339
340StaticIndexBuffer::StaticIndexBuffer(IDirect3DDevice9 *device) : IndexBuffer(device, 0, D3DFMT_UNKNOWN)
341{
342 mCacheType = GL_NONE;
343}
344
345StaticIndexBuffer::~StaticIndexBuffer()
346{
347}
348
349void *StaticIndexBuffer::map(UINT requiredSpace, UINT *offset)
350{
351 void *mapPtr = NULL;
352
353 if (mIndexBuffer)
354 {
355 HRESULT result = mIndexBuffer->Lock(0, requiredSpace, &mapPtr, 0);
356
357 if (FAILED(result))
358 {
359 ERR(" Lock failed with error 0x%08x", result);
360 return NULL;
361 }
362
363 *offset = 0;
364 }
365
366 return mapPtr;
367}
368
369void StaticIndexBuffer::reserveSpace(UINT requiredSpace, GLenum type)
370{
371 if (!mIndexBuffer && mBufferSize == 0)
372 {
daniel@transgaming.comee04e452011-01-08 05:46:27 +0000373 D3DPOOL pool = getDisplay()->getBufferPool(D3DUSAGE_WRITEONLY);
daniel@transgaming.com37b141e2011-01-08 05:46:13 +0000374 HRESULT result = mDevice->CreateIndexBuffer(requiredSpace, D3DUSAGE_WRITEONLY, type == GL_UNSIGNED_INT ? D3DFMT_INDEX32 : D3DFMT_INDEX16, pool, &mIndexBuffer, NULL);
jbauman@chromium.orgd8f3faa2011-09-02 01:10:47 +0000375 mSerial = issueSerial();
daniel@transgaming.com83921382011-01-08 05:46:00 +0000376
377 if (FAILED(result))
378 {
379 ERR("Out of memory allocating a vertex buffer of size %lu.", mBufferSize);
380 }
381
382 mBufferSize = requiredSpace;
383 mCacheType = type;
384 }
385 else if (mIndexBuffer && mBufferSize >= requiredSpace && mCacheType == type)
386 {
387 // Already allocated
388 }
389 else UNREACHABLE(); // Static index buffers can't be resized
390}
391
392bool StaticIndexBuffer::lookupType(GLenum type)
393{
394 return mCacheType == type;
395}
396
397UINT StaticIndexBuffer::lookupRange(intptr_t offset, GLsizei count, UINT *minIndex, UINT *maxIndex)
398{
jbauman@chromium.org43cbe742011-09-01 22:09:40 +0000399 IndexRange range = {offset, count};
daniel@transgaming.com83921382011-01-08 05:46:00 +0000400
jbauman@chromium.org43cbe742011-09-01 22:09:40 +0000401 std::map<IndexRange, IndexResult>::iterator res = mCache.find(range);
402
403 if (res == mCache.end())
404 {
405 return -1;
daniel@transgaming.com83921382011-01-08 05:46:00 +0000406 }
407
jbauman@chromium.org43cbe742011-09-01 22:09:40 +0000408 *minIndex = res->second.minIndex;
409 *maxIndex = res->second.maxIndex;
410 return res->second.streamOffset;
daniel@transgaming.com83921382011-01-08 05:46:00 +0000411}
412
413void StaticIndexBuffer::addRange(intptr_t offset, GLsizei count, UINT minIndex, UINT maxIndex, UINT streamOffset)
414{
jbauman@chromium.org43cbe742011-09-01 22:09:40 +0000415 IndexRange indexRange = {offset, count};
416 IndexResult indexResult = {minIndex, maxIndex, streamOffset};
417 mCache[indexRange] = indexResult;
daniel@transgaming.com81655a72010-05-20 19:18:17 +0000418}
419
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +0000420}