1//
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
10#include "libGLESv2/geometry/IndexDataManager.h"
11
12#include "common/debug.h"
13
14#include "libGLESv2/Buffer.h"
15#include "libGLESv2/mathutil.h"
16#include "libGLESv2/main.h"
17
18namespace
19{
20    enum { INITIAL_INDEX_BUFFER_SIZE = 4096 * sizeof(GLuint) };
21}
22
23namespace gl
24{
25
26IndexDataManager::IndexDataManager(Context *context, IDirect3DDevice9 *device) : mDevice(device)
27{
28    mStreamingBufferShort = new StreamingIndexBuffer(mDevice, INITIAL_INDEX_BUFFER_SIZE, D3DFMT_INDEX16);
29
30    if (context->supports32bitIndices())
31    {
32        mStreamingBufferInt = new StreamingIndexBuffer(mDevice, INITIAL_INDEX_BUFFER_SIZE, D3DFMT_INDEX32);
33    }
34    else
35    {
36        mStreamingBufferInt = NULL;
37    }
38}
39
40IndexDataManager::~IndexDataManager()
41{
42    delete mStreamingBufferShort;
43    delete mStreamingBufferInt;
44}
45
46void convertIndices(GLenum type, const void *input, GLsizei count, void *output)
47{
48    if (type == GL_UNSIGNED_BYTE)
49    {
50        const GLubyte *in = static_cast<const GLubyte*>(input);
51        GLushort *out = static_cast<GLushort*>(output);
52
53        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)
71{
72    *minIndex = indices[0];
73    *maxIndex = indices[0];
74
75    for (GLsizei i = 0; i < count; i++)
76    {
77        if (*minIndex > indices[i]) *minIndex = indices[i];
78        if (*maxIndex < indices[i]) *maxIndex = indices[i];
79    }
80}
81
82void computeRange(GLenum type, const void *indices, GLsizei count, GLuint *minIndex, GLuint *maxIndex)
83{
84    if (type == GL_UNSIGNED_BYTE)
85    {
86        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}
98
99GLenum 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()))
116        {
117            return GL_INVALID_OPERATION;
118        }
119
120        indices = static_cast<const GLubyte*>(buffer->data()) + offset;
121    }
122
123    StreamingIndexBuffer *streamingBuffer = (type == GL_UNSIGNED_INT) ? mStreamingBufferInt : mStreamingBufferShort;
124
125    StaticIndexBuffer *staticBuffer = buffer ? buffer->getIndexBuffer() : NULL;
126    IndexBuffer *indexBuffer = streamingBuffer;
127    UINT streamOffset = 0;
128
129    if (staticBuffer && staticBuffer->lookupType(type) && alignedOffset)
130    {
131        indexBuffer = staticBuffer;
132        streamOffset = staticBuffer->lookupRange(offset, count, &translated->minIndex, &translated->maxIndex);
133
134        if (streamOffset == -1)
135        {
136            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);
139        }
140    }
141    else
142    {
143        int convertCount = count;
144
145        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        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
167        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        }
183    }
184
185    translated->indexBuffer = indexBuffer->getBuffer();
186    translated->startIndex = streamOffset / indexSize(format);
187
188    return GL_NO_ERROR;
189}
190
191std::size_t IndexDataManager::indexSize(D3DFORMAT format) const
192{
193    return (format == D3DFMT_INDEX32) ? sizeof(unsigned int) : sizeof(unsigned short);
194}
195
196std::size_t IndexDataManager::typeSize(GLenum type) const
197{
198    switch (type)
199    {
200      case GL_UNSIGNED_INT:   return sizeof(GLuint);
201      case GL_UNSIGNED_SHORT: return sizeof(GLushort);
202      case GL_UNSIGNED_BYTE:  return sizeof(GLubyte);
203      default: UNREACHABLE(); return sizeof(GLushort);
204    }
205}
206
207IndexBuffer::IndexBuffer(IDirect3DDevice9 *device, UINT size, D3DFORMAT format) : mDevice(device), mBufferSize(size), mIndexBuffer(NULL)
208{
209    if (size > 0)
210    {
211        D3DPOOL pool = getDisplay()->getBufferPool(D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY);
212        HRESULT result = device->CreateIndexBuffer(size, D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, format, pool, &mIndexBuffer, NULL);
213
214        if (FAILED(result))
215        {
216            ERR("Out of memory allocating an index buffer of size %lu.", size);
217        }
218    }
219}
220
221IndexBuffer::~IndexBuffer()
222{
223    if (mIndexBuffer)
224    {
225        mIndexBuffer->Release();
226    }
227}
228
229IDirect3DIndexBuffer9 *IndexBuffer::getBuffer() const
230{
231    return mIndexBuffer;
232}
233
234void IndexBuffer::unmap()
235{
236    if (mIndexBuffer)
237    {
238        mIndexBuffer->Unlock();
239    }
240}
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)
256    {
257        HRESULT result = mIndexBuffer->Lock(mWritePosition, requiredSpace, &mapPtr, D3DLOCK_NOOVERWRITE);
258
259        if (FAILED(result))
260        {
261            ERR(" Lock failed with error 0x%08x", result);
262            return NULL;
263        }
264
265        *offset = mWritePosition;
266        mWritePosition += requiredSpace;
267    }
268
269    return mapPtr;
270}
271
272void 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
284        D3DPOOL pool = getDisplay()->getBufferPool(D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY);
285        HRESULT result = mDevice->CreateIndexBuffer(mBufferSize, D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, type == GL_UNSIGNED_INT ? D3DFMT_INDEX32 : D3DFMT_INDEX16, pool, &mIndexBuffer, NULL);
286
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    {
337        D3DPOOL pool = getDisplay()->getBufferPool(D3DUSAGE_WRITEONLY);
338        HRESULT result = mDevice->CreateIndexBuffer(requiredSpace, D3DUSAGE_WRITEONLY, type == GL_UNSIGNED_INT ? D3DFMT_INDEX32 : D3DFMT_INDEX16, pool, &mIndexBuffer, NULL);
339
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);
380}
381
382}
383