Home | History | Annotate | Download | only in geometry
      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 
     18 namespace
     19 {
     20     enum { INITIAL_INDEX_BUFFER_SIZE = 4096 * sizeof(GLuint) };
     21 }
     22 
     23 namespace gl
     24 {
     25 
     26 IndexDataManager::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 
     40 IndexDataManager::~IndexDataManager()
     41 {
     42     delete mStreamingBufferShort;
     43     delete mStreamingBufferInt;
     44 }
     45 
     46 void 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 
     69 template <class IndexType>
     70 void 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 
     82 void 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 
     99 GLenum 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 
    191 std::size_t IndexDataManager::indexSize(D3DFORMAT format) const
    192 {
    193     return (format == D3DFMT_INDEX32) ? sizeof(unsigned int) : sizeof(unsigned short);
    194 }
    195 
    196 std::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 
    207 IndexBuffer::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 
    221 IndexBuffer::~IndexBuffer()
    222 {
    223     if (mIndexBuffer)
    224     {
    225         mIndexBuffer->Release();
    226     }
    227 }
    228 
    229 IDirect3DIndexBuffer9 *IndexBuffer::getBuffer() const
    230 {
    231     return mIndexBuffer;
    232 }
    233 
    234 void IndexBuffer::unmap()
    235 {
    236     if (mIndexBuffer)
    237     {
    238         mIndexBuffer->Unlock();
    239     }
    240 }
    241 
    242 StreamingIndexBuffer::StreamingIndexBuffer(IDirect3DDevice9 *device, UINT initialSize, D3DFORMAT format) : IndexBuffer(device, initialSize, format)
    243 {
    244     mWritePosition = 0;
    245 }
    246 
    247 StreamingIndexBuffer::~StreamingIndexBuffer()
    248 {
    249 }
    250 
    251 void *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 
    272 void 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 
    304 StaticIndexBuffer::StaticIndexBuffer(IDirect3DDevice9 *device) : IndexBuffer(device, 0, D3DFMT_UNKNOWN)
    305 {
    306     mCacheType = GL_NONE;
    307 }
    308 
    309 StaticIndexBuffer::~StaticIndexBuffer()
    310 {
    311 }
    312 
    313 void *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 
    333 void 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 
    355 bool StaticIndexBuffer::lookupType(GLenum type)
    356 {
    357     return mCacheType == type;
    358 }
    359 
    360 UINT 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 
    376 void 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