blob: 3beb7a8277f0edfd1e84d71d58a893e420e2e1df [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.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
159 indexBuffer->reserveSpace(convertCount * indexSize(format), type);
160
161 void *output = indexBuffer->map(indexSize(format) * convertCount, &streamOffset);
162 if (output == NULL)
163 {
164 ERR("Failed to map index buffer.");
165 return GL_OUT_OF_MEMORY;
166 }
167
168 convertIndices(type, staticBuffer ? buffer->data() : indices, convertCount, output);
169 indexBuffer->unmap();
170
171 computeRange(type, indices, count, &translated->minIndex, &translated->maxIndex);
172
173 if (staticBuffer)
174 {
175 streamOffset = (offset / typeSize(type)) * indexSize(format);
176 staticBuffer->addRange(offset, count, translated->minIndex, translated->maxIndex, streamOffset);
177 }
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +0000178 }
179
daniel@transgaming.com83921382011-01-08 05:46:00 +0000180 translated->indexBuffer = indexBuffer->getBuffer();
181 translated->startIndex = streamOffset / indexSize(format);
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +0000182
daniel@transgaming.com41d8dd82010-05-12 03:45:03 +0000183 return GL_NO_ERROR;
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +0000184}
185
daniel@transgaming.com83921382011-01-08 05:46:00 +0000186std::size_t IndexDataManager::indexSize(D3DFORMAT format) const
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +0000187{
daniel@transgaming.com83921382011-01-08 05:46:00 +0000188 return (format == D3DFMT_INDEX32) ? sizeof(unsigned int) : sizeof(unsigned short);
daniel@transgaming.com3e4c6002010-05-05 18:50:13 +0000189}
190
daniel@transgaming.com41d8dd82010-05-12 03:45:03 +0000191std::size_t IndexDataManager::typeSize(GLenum type) const
192{
193 switch (type)
194 {
daniel@transgaming.com83921382011-01-08 05:46:00 +0000195 case GL_UNSIGNED_INT: return sizeof(GLuint);
daniel@transgaming.com41d8dd82010-05-12 03:45:03 +0000196 case GL_UNSIGNED_SHORT: return sizeof(GLushort);
daniel@transgaming.com83921382011-01-08 05:46:00 +0000197 case GL_UNSIGNED_BYTE: return sizeof(GLubyte);
198 default: UNREACHABLE(); return sizeof(GLushort);
daniel@transgaming.com41d8dd82010-05-12 03:45:03 +0000199 }
200}
201
daniel@transgaming.com83921382011-01-08 05:46:00 +0000202IndexBuffer::IndexBuffer(IDirect3DDevice9 *device, UINT size, D3DFORMAT format) : mDevice(device), mBufferSize(size), mIndexBuffer(NULL)
daniel@transgaming.com3e4c6002010-05-05 18:50:13 +0000203{
daniel@transgaming.com83921382011-01-08 05:46:00 +0000204 if (size > 0)
205 {
daniel@transgaming.com37b141e2011-01-08 05:46:13 +0000206 D3DPOOL pool = getDisplay()->isDirect3D9Ex() ? D3DPOOL_DEFAULT : D3DPOOL_MANAGED;
207 HRESULT result = device->CreateIndexBuffer(size, D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, format, pool, &mIndexBuffer, NULL);
daniel@transgaming.com83921382011-01-08 05:46:00 +0000208
209 if (FAILED(result))
210 {
211 ERR("Out of memory allocating an index buffer of size %lu.", size);
212 }
213 }
daniel@transgaming.com3e4c6002010-05-05 18:50:13 +0000214}
215
daniel@transgaming.com83921382011-01-08 05:46:00 +0000216IndexBuffer::~IndexBuffer()
daniel@transgaming.com3e4c6002010-05-05 18:50:13 +0000217{
daniel@transgaming.com83921382011-01-08 05:46:00 +0000218 if (mIndexBuffer)
daniel@transgaming.com3e4c6002010-05-05 18:50:13 +0000219 {
daniel@transgaming.com83921382011-01-08 05:46:00 +0000220 mIndexBuffer->Release();
daniel@transgaming.com3e4c6002010-05-05 18:50:13 +0000221 }
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +0000222}
223
daniel@transgaming.com83921382011-01-08 05:46:00 +0000224IDirect3DIndexBuffer9 *IndexBuffer::getBuffer() const
daniel@transgaming.com81655a72010-05-20 19:18:17 +0000225{
daniel@transgaming.com83921382011-01-08 05:46:00 +0000226 return mIndexBuffer;
227}
daniel@transgaming.com81655a72010-05-20 19:18:17 +0000228
daniel@transgaming.com83921382011-01-08 05:46:00 +0000229void IndexBuffer::unmap()
230{
231 if (mIndexBuffer)
daniel@transgaming.com81655a72010-05-20 19:18:17 +0000232 {
daniel@transgaming.com83921382011-01-08 05:46:00 +0000233 mIndexBuffer->Unlock();
daniel@transgaming.com81655a72010-05-20 19:18:17 +0000234 }
daniel@transgaming.com83921382011-01-08 05:46:00 +0000235}
236
237StreamingIndexBuffer::StreamingIndexBuffer(IDirect3DDevice9 *device, UINT initialSize, D3DFORMAT format) : IndexBuffer(device, initialSize, format)
238{
239 mWritePosition = 0;
240}
241
242StreamingIndexBuffer::~StreamingIndexBuffer()
243{
244}
245
246void *StreamingIndexBuffer::map(UINT requiredSpace, UINT *offset)
247{
248 void *mapPtr = NULL;
249
250 if (mIndexBuffer)
daniel@transgaming.com81655a72010-05-20 19:18:17 +0000251 {
daniel@transgaming.com83921382011-01-08 05:46:00 +0000252 HRESULT result = mIndexBuffer->Lock(mWritePosition, requiredSpace, &mapPtr, D3DLOCK_NOOVERWRITE);
253
254 if (FAILED(result))
apatrick@chromium.orgf99fbb72010-11-16 01:57:05 +0000255 {
daniel@transgaming.com83921382011-01-08 05:46:00 +0000256 ERR(" Lock failed with error 0x%08x", result);
257 return NULL;
apatrick@chromium.orgf99fbb72010-11-16 01:57:05 +0000258 }
daniel@transgaming.com81655a72010-05-20 19:18:17 +0000259
daniel@transgaming.com83921382011-01-08 05:46:00 +0000260 *offset = mWritePosition;
261 mWritePosition += requiredSpace;
daniel@transgaming.com81655a72010-05-20 19:18:17 +0000262 }
263
daniel@transgaming.com83921382011-01-08 05:46:00 +0000264 return mapPtr;
265}
daniel@transgaming.com81655a72010-05-20 19:18:17 +0000266
daniel@transgaming.com83921382011-01-08 05:46:00 +0000267void StreamingIndexBuffer::reserveSpace(UINT requiredSpace, GLenum type)
268{
269 if (requiredSpace > mBufferSize)
270 {
271 if (mIndexBuffer)
272 {
273 mIndexBuffer->Release();
274 mIndexBuffer = NULL;
275 }
276
277 mBufferSize = std::max(requiredSpace, 2 * mBufferSize);
278
daniel@transgaming.com37b141e2011-01-08 05:46:13 +0000279 D3DPOOL pool = getDisplay()->isDirect3D9Ex() ? D3DPOOL_DEFAULT : D3DPOOL_MANAGED;
280 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 +0000281
282 if (FAILED(result))
283 {
284 ERR("Out of memory allocating a vertex buffer of size %lu.", mBufferSize);
285 }
286
287 mWritePosition = 0;
288 }
289 else if (mWritePosition + requiredSpace > mBufferSize) // Recycle
290 {
291 void *dummy;
292 mIndexBuffer->Lock(0, 1, &dummy, D3DLOCK_DISCARD);
293 mIndexBuffer->Unlock();
294
295 mWritePosition = 0;
296 }
297}
298
299StaticIndexBuffer::StaticIndexBuffer(IDirect3DDevice9 *device) : IndexBuffer(device, 0, D3DFMT_UNKNOWN)
300{
301 mCacheType = GL_NONE;
302}
303
304StaticIndexBuffer::~StaticIndexBuffer()
305{
306}
307
308void *StaticIndexBuffer::map(UINT requiredSpace, UINT *offset)
309{
310 void *mapPtr = NULL;
311
312 if (mIndexBuffer)
313 {
314 HRESULT result = mIndexBuffer->Lock(0, requiredSpace, &mapPtr, 0);
315
316 if (FAILED(result))
317 {
318 ERR(" Lock failed with error 0x%08x", result);
319 return NULL;
320 }
321
322 *offset = 0;
323 }
324
325 return mapPtr;
326}
327
328void StaticIndexBuffer::reserveSpace(UINT requiredSpace, GLenum type)
329{
330 if (!mIndexBuffer && mBufferSize == 0)
331 {
daniel@transgaming.com37b141e2011-01-08 05:46:13 +0000332 D3DPOOL pool = getDisplay()->isDirect3D9Ex() ? D3DPOOL_DEFAULT : D3DPOOL_MANAGED;
333 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 +0000334
335 if (FAILED(result))
336 {
337 ERR("Out of memory allocating a vertex buffer of size %lu.", mBufferSize);
338 }
339
340 mBufferSize = requiredSpace;
341 mCacheType = type;
342 }
343 else if (mIndexBuffer && mBufferSize >= requiredSpace && mCacheType == type)
344 {
345 // Already allocated
346 }
347 else UNREACHABLE(); // Static index buffers can't be resized
348}
349
350bool StaticIndexBuffer::lookupType(GLenum type)
351{
352 return mCacheType == type;
353}
354
355UINT StaticIndexBuffer::lookupRange(intptr_t offset, GLsizei count, UINT *minIndex, UINT *maxIndex)
356{
357 for (unsigned int range = 0; range < mCache.size(); range++)
358 {
359 if (mCache[range].offset == offset && mCache[range].count == count)
360 {
361 *minIndex = mCache[range].minIndex;
362 *maxIndex = mCache[range].maxIndex;
363
364 return mCache[range].streamOffset;
365 }
366 }
367
368 return -1;
369}
370
371void StaticIndexBuffer::addRange(intptr_t offset, GLsizei count, UINT minIndex, UINT maxIndex, UINT streamOffset)
372{
373 IndexRange indexRange = {offset, count, minIndex, maxIndex, streamOffset};
374 mCache.push_back(indexRange);
daniel@transgaming.com81655a72010-05-20 19:18:17 +0000375}
376
daniel@transgaming.comf8b58a02010-03-26 04:08:45 +0000377}