1#include "precompiled.h"
2//
3// Copyright (c) 2002-2014 The ANGLE Project Authors. All rights reserved.
4// Use of this source code is governed by a BSD-style license that can be
5// found in the LICENSE file.
6//
7
8// IndexDataManager.cpp: Defines the IndexDataManager, a class that
9// runs the Buffer translation process for index buffers.
10
11#include "libGLESv2/renderer/IndexDataManager.h"
12#include "libGLESv2/renderer/BufferStorage.h"
13
14#include "libGLESv2/Buffer.h"
15#include "libGLESv2/main.h"
16#include "libGLESv2/formatutils.h"
17#include "libGLESv2/renderer/IndexBuffer.h"
18#include "libGLESv2/renderer/Renderer.h"
19
20namespace rx
21{
22
23IndexDataManager::IndexDataManager(Renderer *renderer) : mRenderer(renderer)
24{
25    mStreamingBufferShort = new StreamingIndexBufferInterface(mRenderer);
26    if (!mStreamingBufferShort->reserveBufferSpace(INITIAL_INDEX_BUFFER_SIZE, GL_UNSIGNED_SHORT))
27    {
28        delete mStreamingBufferShort;
29        mStreamingBufferShort = NULL;
30    }
31
32    mStreamingBufferInt = new StreamingIndexBufferInterface(mRenderer);
33    if (!mStreamingBufferInt->reserveBufferSpace(INITIAL_INDEX_BUFFER_SIZE, GL_UNSIGNED_INT))
34    {
35        delete mStreamingBufferInt;
36        mStreamingBufferInt = NULL;
37    }
38
39    if (!mStreamingBufferShort)
40    {
41        // Make sure both buffers are deleted.
42        delete mStreamingBufferInt;
43        mStreamingBufferInt = NULL;
44
45        ERR("Failed to allocate the streaming index buffer(s).");
46    }
47
48    mCountingBuffer = NULL;
49}
50
51IndexDataManager::~IndexDataManager()
52{
53    delete mStreamingBufferShort;
54    delete mStreamingBufferInt;
55    delete mCountingBuffer;
56}
57
58static void convertIndices(GLenum sourceType, GLenum destinationType, const void *input, GLsizei count, void *output)
59{
60    if (sourceType == GL_UNSIGNED_BYTE)
61    {
62        ASSERT(destinationType == GL_UNSIGNED_SHORT);
63        const GLubyte *in = static_cast<const GLubyte*>(input);
64        GLushort *out = static_cast<GLushort*>(output);
65
66        for (GLsizei i = 0; i < count; i++)
67        {
68            out[i] = in[i];
69        }
70    }
71    else if (sourceType == GL_UNSIGNED_INT)
72    {
73        ASSERT(destinationType == GL_UNSIGNED_INT);
74        memcpy(output, input, count * sizeof(GLuint));
75    }
76    else if (sourceType == GL_UNSIGNED_SHORT)
77    {
78        if (destinationType == GL_UNSIGNED_SHORT)
79        {
80            memcpy(output, input, count * sizeof(GLushort));
81        }
82        else if (destinationType == GL_UNSIGNED_INT)
83        {
84            const GLushort *in = static_cast<const GLushort*>(input);
85            GLuint *out = static_cast<GLuint*>(output);
86
87            for (GLsizei i = 0; i < count; i++)
88            {
89                out[i] = in[i];
90            }
91        }
92        else UNREACHABLE();
93    }
94    else UNREACHABLE();
95}
96
97template <class IndexType>
98static void computeRange(const IndexType *indices, GLsizei count, GLuint *minIndex, GLuint *maxIndex)
99{
100    *minIndex = indices[0];
101    *maxIndex = indices[0];
102
103    for (GLsizei i = 0; i < count; i++)
104    {
105        if (*minIndex > indices[i]) *minIndex = indices[i];
106        if (*maxIndex < indices[i]) *maxIndex = indices[i];
107    }
108}
109
110static void computeRange(GLenum type, const GLvoid *indices, GLsizei count, GLuint *minIndex, GLuint *maxIndex)
111{
112    if (type == GL_UNSIGNED_BYTE)
113    {
114        computeRange(static_cast<const GLubyte*>(indices), count, minIndex, maxIndex);
115    }
116    else if (type == GL_UNSIGNED_INT)
117    {
118        computeRange(static_cast<const GLuint*>(indices), count, minIndex, maxIndex);
119    }
120    else if (type == GL_UNSIGNED_SHORT)
121    {
122        computeRange(static_cast<const GLushort*>(indices), count, minIndex, maxIndex);
123    }
124    else UNREACHABLE();
125}
126
127GLenum IndexDataManager::prepareIndexData(GLenum type, GLsizei count, gl::Buffer *buffer, const GLvoid *indices, TranslatedIndexData *translated)
128{
129    if (!mStreamingBufferShort)
130    {
131        return GL_OUT_OF_MEMORY;
132    }
133
134    GLenum destinationIndexType = (type == GL_UNSIGNED_INT) ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT;
135    unsigned int offset = 0;
136    bool alignedOffset = false;
137
138    BufferStorage *storage = NULL;
139
140    if (buffer != NULL)
141    {
142        if (reinterpret_cast<uintptr_t>(indices) > std::numeric_limits<unsigned int>::max())
143        {
144            return GL_OUT_OF_MEMORY;
145        }
146        offset = static_cast<unsigned int>(reinterpret_cast<uintptr_t>(indices));
147
148        storage = buffer->getStorage();
149
150        switch (type)
151        {
152          case GL_UNSIGNED_BYTE:  alignedOffset = (offset % sizeof(GLubyte) == 0);  break;
153          case GL_UNSIGNED_SHORT: alignedOffset = (offset % sizeof(GLushort) == 0); break;
154          case GL_UNSIGNED_INT:   alignedOffset = (offset % sizeof(GLuint) == 0);   break;
155          default: UNREACHABLE(); alignedOffset = false;
156        }
157
158        unsigned int typeSize = gl::GetTypeBytes(type);
159
160        // check for integer overflows
161        if (static_cast<unsigned int>(count) > (std::numeric_limits<unsigned int>::max() / typeSize) ||
162            typeSize * static_cast<unsigned int>(count) + offset < offset)
163        {
164            return GL_OUT_OF_MEMORY;
165        }
166
167        if (typeSize * static_cast<unsigned int>(count) + offset > storage->getSize())
168        {
169            return GL_INVALID_OPERATION;
170        }
171
172        indices = static_cast<const GLubyte*>(storage->getData()) + offset;
173    }
174
175    StaticIndexBufferInterface *staticBuffer = buffer ? buffer->getStaticIndexBuffer() : NULL;
176    IndexBufferInterface *indexBuffer = NULL;
177    bool directStorage = alignedOffset && storage && storage->supportsDirectBinding() &&
178                         destinationIndexType == type;
179    unsigned int streamOffset = 0;
180
181    if (directStorage)
182    {
183        streamOffset = offset;
184
185        if (!buffer->getIndexRangeCache()->findRange(type, offset, count, &translated->minIndex,
186            &translated->maxIndex, NULL))
187        {
188            computeRange(type, indices, count, &translated->minIndex, &translated->maxIndex);
189            buffer->getIndexRangeCache()->addRange(type, offset, count, translated->minIndex,
190                                                   translated->maxIndex, offset);
191        }
192    }
193    else if (staticBuffer && staticBuffer->getBufferSize() != 0 && staticBuffer->getIndexType() == type && alignedOffset)
194    {
195        indexBuffer = staticBuffer;
196
197        if (!staticBuffer->getIndexRangeCache()->findRange(type, offset, count, &translated->minIndex,
198                                                           &translated->maxIndex, &streamOffset))
199        {
200            streamOffset = (offset / gl::GetTypeBytes(type)) * gl::GetTypeBytes(destinationIndexType);
201            computeRange(type, indices, count, &translated->minIndex, &translated->maxIndex);
202            staticBuffer->getIndexRangeCache()->addRange(type, offset, count, translated->minIndex,
203                                                         translated->maxIndex, streamOffset);
204        }
205    }
206    else
207    {
208        computeRange(type, indices, count, &translated->minIndex, &translated->maxIndex);
209    }
210
211    // Avoid D3D11's primitive restart index value
212    // see http://msdn.microsoft.com/en-us/library/windows/desktop/bb205124(v=vs.85).aspx
213    if (translated->maxIndex == 0xFFFF && type == GL_UNSIGNED_SHORT && mRenderer->getMajorShaderModel() > 3)
214    {
215        destinationIndexType = GL_UNSIGNED_INT;
216        directStorage = false;
217        indexBuffer = NULL;
218    }
219
220    if (!directStorage && !indexBuffer)
221    {
222        indexBuffer = (destinationIndexType == GL_UNSIGNED_INT) ? mStreamingBufferInt : mStreamingBufferShort;
223
224        unsigned int convertCount = count;
225
226        if (staticBuffer)
227        {
228            if (staticBuffer->getBufferSize() == 0 && alignedOffset)
229            {
230                indexBuffer = staticBuffer;
231                convertCount = storage->getSize() / gl::GetTypeBytes(type);
232            }
233            else
234            {
235                buffer->invalidateStaticData();
236                staticBuffer = NULL;
237            }
238        }
239
240        if (!indexBuffer)
241        {
242            ERR("No valid index buffer.");
243            return GL_INVALID_OPERATION;
244        }
245
246        unsigned int indexTypeSize = gl::GetTypeBytes(destinationIndexType);
247        if (convertCount > std::numeric_limits<unsigned int>::max() / indexTypeSize)
248        {
249            ERR("Reserving %u indicies of %u bytes each exceeds the maximum buffer size.", convertCount, indexTypeSize);
250            return GL_OUT_OF_MEMORY;
251        }
252
253        unsigned int bufferSizeRequired = convertCount * indexTypeSize;
254        if (!indexBuffer->reserveBufferSpace(bufferSizeRequired, type))
255        {
256            ERR("Failed to reserve %u bytes in an index buffer.", bufferSizeRequired);
257            return GL_OUT_OF_MEMORY;
258        }
259
260        void* output = NULL;
261        if (!indexBuffer->mapBuffer(bufferSizeRequired, &output, &streamOffset))
262        {
263            ERR("Failed to map index buffer.");
264            return GL_OUT_OF_MEMORY;
265        }
266
267        convertIndices(type, destinationIndexType, staticBuffer ? storage->getData() : indices, convertCount, output);
268
269        if (!indexBuffer->unmapBuffer())
270        {
271            ERR("Failed to unmap index buffer.");
272            return GL_OUT_OF_MEMORY;
273        }
274
275        if (staticBuffer)
276        {
277            streamOffset = (offset / gl::GetTypeBytes(type)) * gl::GetTypeBytes(destinationIndexType);
278            staticBuffer->getIndexRangeCache()->addRange(type, offset, count, translated->minIndex,
279                                                         translated->maxIndex, streamOffset);
280        }
281    }
282
283    translated->storage = directStorage ? storage : NULL;
284    translated->indexBuffer = indexBuffer ? indexBuffer->getIndexBuffer() : NULL;
285    translated->serial = directStorage ? storage->getSerial() : indexBuffer->getSerial();
286    translated->startIndex = streamOffset / gl::GetTypeBytes(destinationIndexType);
287    translated->startOffset = streamOffset;
288    translated->indexType = destinationIndexType;
289
290    if (buffer)
291    {
292        buffer->promoteStaticUsage(count * gl::GetTypeBytes(type));
293    }
294
295    return GL_NO_ERROR;
296}
297
298StaticIndexBufferInterface *IndexDataManager::getCountingIndices(GLsizei count)
299{
300    if (count <= 65536)   // 16-bit indices
301    {
302        const unsigned int spaceNeeded = count * sizeof(unsigned short);
303
304        if (!mCountingBuffer || mCountingBuffer->getBufferSize() < spaceNeeded)
305        {
306            delete mCountingBuffer;
307            mCountingBuffer = new StaticIndexBufferInterface(mRenderer);
308            mCountingBuffer->reserveBufferSpace(spaceNeeded, GL_UNSIGNED_SHORT);
309
310            void* mappedMemory = NULL;
311            if (!mCountingBuffer->mapBuffer(spaceNeeded, &mappedMemory, NULL))
312            {
313                ERR("Failed to map counting buffer.");
314                return NULL;
315            }
316
317            unsigned short *data = reinterpret_cast<unsigned short*>(mappedMemory);
318            for(int i = 0; i < count; i++)
319            {
320                data[i] = i;
321            }
322
323            if (!mCountingBuffer->unmapBuffer())
324            {
325                ERR("Failed to unmap counting buffer.");
326                return NULL;
327            }
328        }
329    }
330    else if (mStreamingBufferInt)   // 32-bit indices supported
331    {
332        const unsigned int spaceNeeded = count * sizeof(unsigned int);
333
334        if (!mCountingBuffer || mCountingBuffer->getBufferSize() < spaceNeeded)
335        {
336            delete mCountingBuffer;
337            mCountingBuffer = new StaticIndexBufferInterface(mRenderer);
338            mCountingBuffer->reserveBufferSpace(spaceNeeded, GL_UNSIGNED_INT);
339
340            void* mappedMemory = NULL;
341            if (!mCountingBuffer->mapBuffer(spaceNeeded, &mappedMemory, NULL))
342            {
343                ERR("Failed to map counting buffer.");
344                return NULL;
345            }
346
347            unsigned int *data = reinterpret_cast<unsigned int*>(mappedMemory);
348            for(int i = 0; i < count; i++)
349            {
350                data[i] = i;
351            }
352
353            if (!mCountingBuffer->unmapBuffer())
354            {
355                ERR("Failed to unmap counting buffer.");
356                return NULL;
357            }
358        }
359    }
360    else
361    {
362        return NULL;
363    }
364
365    return mCountingBuffer;
366}
367
368}
369