Edit

kc3-lang/angle/src/libGLESv2/IndexDataManager.cpp

Branch :

  • Show log

    Commit

  • Author : jbauman@chromium.org
    Date : 2011-09-02 01:10:47
    Hash : d8f3faad
    Message : Avoid resending lots of D3D state This change uses trivial caching to determines whether to reset shaders, the viewport, and the currently set vertex declaration. It also caches the render target desc to avoid rereading that. Serial numbers are added to vertex and index buffers, so resending those can be avoided. These changes can give a big speedup (30% has been measured) on simple content, particularly when used directly or through pepper/native client. BUG= TEST=bunch of pages using webgl Review URL: http://codereview.appspot.com/4964057 git-svn-id: https://angleproject.googlecode.com/svn/trunk@743 736b8ea6-26fd-11df-bfd4-992fa37f6226

  • src/libGLESv2/IndexDataManager.cpp
  • //
    // Copyright (c) 2002-2010 The ANGLE Project Authors. All rights reserved.
    // Use of this source code is governed by a BSD-style license that can be
    // found in the LICENSE file.
    //
    
    // IndexDataManager.cpp: Defines the IndexDataManager, a class that
    // runs the Buffer translation process for index buffers.
    
    #include "libGLESv2/IndexDataManager.h"
    
    #include "common/debug.h"
    
    #include "libGLESv2/Buffer.h"
    #include "libGLESv2/mathutil.h"
    #include "libGLESv2/main.h"
    
    namespace
    {
        enum { INITIAL_INDEX_BUFFER_SIZE = 4096 * sizeof(GLuint) };
    }
    
    namespace gl
    {
    unsigned int IndexBuffer::mCurrentSerial = 1;
    
    IndexDataManager::IndexDataManager(Context *context, IDirect3DDevice9 *device) : mDevice(device)
    {
        mStreamingBufferShort = new StreamingIndexBuffer(mDevice, INITIAL_INDEX_BUFFER_SIZE, D3DFMT_INDEX16);
    
        if (context->supports32bitIndices())
        {
            mStreamingBufferInt = new StreamingIndexBuffer(mDevice, INITIAL_INDEX_BUFFER_SIZE, D3DFMT_INDEX32);
    
            if (!mStreamingBufferInt)
            {
                // Don't leave it in a half-initialized state
                delete mStreamingBufferShort;
                mStreamingBufferShort = NULL;
            }
        }
        else
        {
            mStreamingBufferInt = NULL;
        }
    
        if (!mStreamingBufferShort)
        {
            ERR("Failed to allocate the streaming index buffer(s).");
        }
    }
    
    IndexDataManager::~IndexDataManager()
    {
        delete mStreamingBufferShort;
        delete mStreamingBufferInt;
    }
    
    void convertIndices(GLenum type, const void *input, GLsizei count, void *output)
    {
        if (type == GL_UNSIGNED_BYTE)
        {
            const GLubyte *in = static_cast<const GLubyte*>(input);
            GLushort *out = static_cast<GLushort*>(output);
    
            for (GLsizei i = 0; i < count; i++)
            {
                out[i] = in[i];
            }
        }
        else if (type == GL_UNSIGNED_INT)
        {
            memcpy(output, input, count * sizeof(GLuint));
        }
        else if (type == GL_UNSIGNED_SHORT)
        {
            memcpy(output, input, count * sizeof(GLushort));
        }
        else UNREACHABLE();
    }
    
    template <class IndexType>
    void computeRange(const IndexType *indices, GLsizei count, GLuint *minIndex, GLuint *maxIndex)
    {
        *minIndex = indices[0];
        *maxIndex = indices[0];
    
        for (GLsizei i = 0; i < count; i++)
        {
            if (*minIndex > indices[i]) *minIndex = indices[i];
            if (*maxIndex < indices[i]) *maxIndex = indices[i];
        }
    }
    
    void computeRange(GLenum type, const void *indices, GLsizei count, GLuint *minIndex, GLuint *maxIndex)
    {
        if (type == GL_UNSIGNED_BYTE)
        {
            computeRange(static_cast<const GLubyte*>(indices), count, minIndex, maxIndex);
        }
        else if (type == GL_UNSIGNED_INT)
        {
            computeRange(static_cast<const GLuint*>(indices), count, minIndex, maxIndex);
        }
        else if (type == GL_UNSIGNED_SHORT)
        {
            computeRange(static_cast<const GLushort*>(indices), count, minIndex, maxIndex);
        }
        else UNREACHABLE();
    }
    
    GLenum IndexDataManager::prepareIndexData(GLenum type, GLsizei count, Buffer *buffer, const void *indices, TranslatedIndexData *translated)
    {
        if (!mStreamingBufferShort)
        {
            return GL_OUT_OF_MEMORY;
        }
    
        D3DFORMAT format = (type == GL_UNSIGNED_INT) ? D3DFMT_INDEX32 : D3DFMT_INDEX16;
        intptr_t offset = reinterpret_cast<intptr_t>(indices);
        bool alignedOffset = false;
    
        if (buffer != NULL)
        {
            switch (type)
            {
              case GL_UNSIGNED_BYTE:  alignedOffset = (offset % sizeof(GLubyte) == 0);  break;
              case GL_UNSIGNED_SHORT: alignedOffset = (offset % sizeof(GLushort) == 0); break;
              case GL_UNSIGNED_INT:   alignedOffset = (offset % sizeof(GLuint) == 0);   break;
              default: UNREACHABLE(); alignedOffset = false;
            }
    
            if (typeSize(type) * count + offset > static_cast<std::size_t>(buffer->size()))
            {
                return GL_INVALID_OPERATION;
            }
    
            indices = static_cast<const GLubyte*>(buffer->data()) + offset;
        }
    
        StreamingIndexBuffer *streamingBuffer = (type == GL_UNSIGNED_INT) ? mStreamingBufferInt : mStreamingBufferShort;
    
        StaticIndexBuffer *staticBuffer = buffer ? buffer->getStaticIndexBuffer() : NULL;
        IndexBuffer *indexBuffer = streamingBuffer;
        UINT streamOffset = 0;
    
        if (staticBuffer && staticBuffer->lookupType(type) && alignedOffset)
        {
            indexBuffer = staticBuffer;
            streamOffset = staticBuffer->lookupRange(offset, count, &translated->minIndex, &translated->maxIndex);
    
            if (streamOffset == -1)
            {
                streamOffset = (offset / typeSize(type)) * indexSize(format);
                computeRange(type, indices, count, &translated->minIndex, &translated->maxIndex);
                staticBuffer->addRange(offset, count, translated->minIndex, translated->maxIndex, streamOffset);
            }
        }
        else
        {
            int convertCount = count;
    
            if (staticBuffer)
            {
                if (staticBuffer->size() == 0 && alignedOffset)
                {
                    indexBuffer = staticBuffer;
                    convertCount = buffer->size() / typeSize(type);
                }
                else
                {
                    buffer->invalidateStaticData();
                    staticBuffer = NULL;
                }
            }
    
            void *output = NULL;
            
            if (indexBuffer)
            {
                indexBuffer->reserveSpace(convertCount * indexSize(format), type);
                output = indexBuffer->map(indexSize(format) * convertCount, &streamOffset);
            }
            
            if (output == NULL)
            {
                ERR("Failed to map index buffer.");
                return GL_OUT_OF_MEMORY;
            }
    
            convertIndices(type, staticBuffer ? buffer->data() : indices, convertCount, output);
            indexBuffer->unmap();
    
            computeRange(type, indices, count, &translated->minIndex, &translated->maxIndex);
    
            if (staticBuffer)
            {
                streamOffset = (offset / typeSize(type)) * indexSize(format);
                staticBuffer->addRange(offset, count, translated->minIndex, translated->maxIndex, streamOffset);
            }
        }
    
        translated->indexBuffer = indexBuffer->getBuffer();
        translated->serial = indexBuffer->getSerial();
        translated->startIndex = streamOffset / indexSize(format);
    
        if (buffer)
        {
            buffer->promoteStaticUsage(count * typeSize(type));
        }
    
        return GL_NO_ERROR;
    }
    
    std::size_t IndexDataManager::indexSize(D3DFORMAT format) const
    {
        return (format == D3DFMT_INDEX32) ? sizeof(unsigned int) : sizeof(unsigned short);
    }
    
    std::size_t IndexDataManager::typeSize(GLenum type) const
    {
        switch (type)
        {
          case GL_UNSIGNED_INT:   return sizeof(GLuint);
          case GL_UNSIGNED_SHORT: return sizeof(GLushort);
          case GL_UNSIGNED_BYTE:  return sizeof(GLubyte);
          default: UNREACHABLE(); return sizeof(GLushort);
        }
    }
    
    IndexBuffer::IndexBuffer(IDirect3DDevice9 *device, UINT size, D3DFORMAT format) : mDevice(device), mBufferSize(size), mIndexBuffer(NULL)
    {
        if (size > 0)
        {
            D3DPOOL pool = getDisplay()->getBufferPool(D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY);
            HRESULT result = device->CreateIndexBuffer(size, D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, format, pool, &mIndexBuffer, NULL);
            mSerial = issueSerial();
    
            if (FAILED(result))
            {
                ERR("Out of memory allocating an index buffer of size %lu.", size);
            }
        }
    }
    
    IndexBuffer::~IndexBuffer()
    {
        if (mIndexBuffer)
        {
            mIndexBuffer->Release();
        }
    }
    
    IDirect3DIndexBuffer9 *IndexBuffer::getBuffer() const
    {
        return mIndexBuffer;
    }
    
    unsigned int IndexBuffer::getSerial() const
    {
        return mSerial;
    }
    
    unsigned int IndexBuffer::issueSerial()
    {
        return mCurrentSerial++;
    }
    
    void IndexBuffer::unmap()
    {
        if (mIndexBuffer)
        {
            mIndexBuffer->Unlock();
        }
    }
    
    StreamingIndexBuffer::StreamingIndexBuffer(IDirect3DDevice9 *device, UINT initialSize, D3DFORMAT format) : IndexBuffer(device, initialSize, format)
    {
        mWritePosition = 0;
    }
    
    StreamingIndexBuffer::~StreamingIndexBuffer()
    {
    }
    
    void *StreamingIndexBuffer::map(UINT requiredSpace, UINT *offset)
    {
        void *mapPtr = NULL;
    
        if (mIndexBuffer)
        {
            HRESULT result = mIndexBuffer->Lock(mWritePosition, requiredSpace, &mapPtr, D3DLOCK_NOOVERWRITE);
         
            if (FAILED(result))
            {
                ERR(" Lock failed with error 0x%08x", result);
                return NULL;
            }
    
            *offset = mWritePosition;
            mWritePosition += requiredSpace;
        }
    
        return mapPtr;
    }
    
    void StreamingIndexBuffer::reserveSpace(UINT requiredSpace, GLenum type)
    {
        if (requiredSpace > mBufferSize)
        {
            if (mIndexBuffer)
            {
                mIndexBuffer->Release();
                mIndexBuffer = NULL;
            }
    
            mBufferSize = std::max(requiredSpace, 2 * mBufferSize);
    
            D3DPOOL pool = getDisplay()->getBufferPool(D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY);
            HRESULT result = mDevice->CreateIndexBuffer(mBufferSize, D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, type == GL_UNSIGNED_INT ? D3DFMT_INDEX32 : D3DFMT_INDEX16, pool, &mIndexBuffer, NULL);
            mSerial = issueSerial();
        
            if (FAILED(result))
            {
                ERR("Out of memory allocating a vertex buffer of size %lu.", mBufferSize);
            }
    
            mWritePosition = 0;
        }
        else if (mWritePosition + requiredSpace > mBufferSize)   // Recycle
        {
            void *dummy;
            mIndexBuffer->Lock(0, 1, &dummy, D3DLOCK_DISCARD);
            mIndexBuffer->Unlock();
    
            mWritePosition = 0;
        }
    }
    
    StaticIndexBuffer::StaticIndexBuffer(IDirect3DDevice9 *device) : IndexBuffer(device, 0, D3DFMT_UNKNOWN)
    {
        mCacheType = GL_NONE;
    }
    
    StaticIndexBuffer::~StaticIndexBuffer()
    {
    }
    
    void *StaticIndexBuffer::map(UINT requiredSpace, UINT *offset)
    {
        void *mapPtr = NULL;
    
        if (mIndexBuffer)
        {
            HRESULT result = mIndexBuffer->Lock(0, requiredSpace, &mapPtr, 0);
         
            if (FAILED(result))
            {
                ERR(" Lock failed with error 0x%08x", result);
                return NULL;
            }
    
            *offset = 0;
        }
    
        return mapPtr;
    }
    
    void StaticIndexBuffer::reserveSpace(UINT requiredSpace, GLenum type)
    {
        if (!mIndexBuffer && mBufferSize == 0)
        {
            D3DPOOL pool = getDisplay()->getBufferPool(D3DUSAGE_WRITEONLY);
            HRESULT result = mDevice->CreateIndexBuffer(requiredSpace, D3DUSAGE_WRITEONLY, type == GL_UNSIGNED_INT ? D3DFMT_INDEX32 : D3DFMT_INDEX16, pool, &mIndexBuffer, NULL);
            mSerial = issueSerial();
        
            if (FAILED(result))
            {
                ERR("Out of memory allocating a vertex buffer of size %lu.", mBufferSize);
            }
    
            mBufferSize = requiredSpace;
            mCacheType = type;
        }
        else if (mIndexBuffer && mBufferSize >= requiredSpace && mCacheType == type)
        {
            // Already allocated
        }
        else UNREACHABLE();   // Static index buffers can't be resized
    }
    
    bool StaticIndexBuffer::lookupType(GLenum type)
    {
        return mCacheType == type;
    }
    
    UINT StaticIndexBuffer::lookupRange(intptr_t offset, GLsizei count, UINT *minIndex, UINT *maxIndex)
    {
        IndexRange range = {offset, count};
    
        std::map<IndexRange, IndexResult>::iterator res = mCache.find(range);
        
        if (res == mCache.end())
        {
            return -1;
        }
    
        *minIndex = res->second.minIndex;
        *maxIndex = res->second.maxIndex;
        return res->second.streamOffset;
    }
    
    void StaticIndexBuffer::addRange(intptr_t offset, GLsizei count, UINT minIndex, UINT maxIndex, UINT streamOffset)
    {
        IndexRange indexRange = {offset, count};
        IndexResult indexResult = {minIndex, maxIndex, streamOffset};
        mCache[indexRange] = indexResult;
    }
    
    }