Changeset 52205 in webkit


Ignore:
Timestamp:
Dec 16, 2009 10:50:55 AM (14 years ago)
Author:
eric@webkit.org
Message:

2009-12-16 Kenneth Russell <kbr@google.com>

Reviewed by Darin Fisher.

Performance problems with index validation code for drawElements
https://bugs.webkit.org/show_bug.cgi?id=32466

Added a cache of the maximum index for each element type to
WebGLBuffer, querying it before iterating through the indices in
the client-side copy of the buffer's data. Hoisted checks of the
size of the element array itself into validateElementArraySize to
avoid duplicating code.

The performance improvement has been measured with manual tests.
Existing layout tests cover the index validation functionality
and continue to pass as before.

  • html/canvas/WebGLBuffer.cpp: (WebCore::WebGLBuffer::WebGLBuffer): (WebCore::WebGLBuffer::associateBufferData): (WebCore::WebGLBuffer::associateBufferSubData): (WebCore::WebGLBuffer::getCachedMaxIndex): (WebCore::WebGLBuffer::setCachedMaxIndex): (WebCore::WebGLBuffer::clearCachedMaxIndices):
  • html/canvas/WebGLBuffer.h:
  • html/canvas/WebGLRenderingContext.cpp: (WebCore::WebGLRenderingContext::validateElementArraySize): (WebCore::WebGLRenderingContext::validateIndexArrayConservative): (WebCore::WebGLRenderingContext::validateIndexArrayPrecise): (WebCore::WebGLRenderingContext::validateRenderingState): (WebCore::WebGLRenderingContext::drawElements):
  • html/canvas/WebGLRenderingContext.h:
Location:
trunk/WebCore
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/WebCore/ChangeLog

    r52204 r52205  
     12009-12-16  Kenneth Russell  <kbr@google.com>
     2
     3        Reviewed by Darin Fisher.
     4
     5        Performance problems with index validation code for drawElements
     6        https://bugs.webkit.org/show_bug.cgi?id=32466
     7
     8        Added a cache of the maximum index for each element type to
     9        WebGLBuffer, querying it before iterating through the indices in
     10        the client-side copy of the buffer's data. Hoisted checks of the
     11        size of the element array itself into validateElementArraySize to
     12        avoid duplicating code.
     13
     14        The performance improvement has been measured with manual tests.
     15        Existing layout tests cover the index validation functionality
     16        and continue to pass as before.
     17
     18        * html/canvas/WebGLBuffer.cpp:
     19        (WebCore::WebGLBuffer::WebGLBuffer):
     20        (WebCore::WebGLBuffer::associateBufferData):
     21        (WebCore::WebGLBuffer::associateBufferSubData):
     22        (WebCore::WebGLBuffer::getCachedMaxIndex):
     23        (WebCore::WebGLBuffer::setCachedMaxIndex):
     24        (WebCore::WebGLBuffer::clearCachedMaxIndices):
     25        * html/canvas/WebGLBuffer.h:
     26        * html/canvas/WebGLRenderingContext.cpp:
     27        (WebCore::WebGLRenderingContext::validateElementArraySize):
     28        (WebCore::WebGLRenderingContext::validateIndexArrayConservative):
     29        (WebCore::WebGLRenderingContext::validateIndexArrayPrecise):
     30        (WebCore::WebGLRenderingContext::validateRenderingState):
     31        (WebCore::WebGLRenderingContext::drawElements):
     32        * html/canvas/WebGLRenderingContext.h:
     33
    1342009-12-16  Zelidrag Hornung  <zelidrag@chromium.org>
    235
  • trunk/WebCore/html/canvas/WebGLBuffer.cpp

    r51348 r52205  
    4848    , m_arrayBufferByteLength(0)
    4949    , m_elementArrayBufferCloned(false)
     50    , m_nextAvailableCacheEntry(0)
    5051{
    5152    setObject(context()->graphicsContext3D()->createBuffer());
     53    clearCachedMaxIndices();
    5254}
    5355
    5456WebGLBuffer::WebGLBuffer(WebGLRenderingContext* ctx, Platform3DObject obj)
    5557    : CanvasObject(ctx)
     58    , m_nextAvailableCacheEntry(0)
    5659{
    5760    setObject(obj, false);
     61    clearCachedMaxIndices();
    5862}
    5963
     
    8488       
    8589    if (target == GraphicsContext3D::ELEMENT_ARRAY_BUFFER) {
     90        clearCachedMaxIndices();
    8691        m_elementArrayBufferByteLength = array->byteLength();
    8792        m_elementArrayBuffer = array->buffer();
     
    104109       
    105110    if (target == GraphicsContext3D::ELEMENT_ARRAY_BUFFER) {
     111        clearCachedMaxIndices();
     112
    106113        // We need to protect against integer overflow with these tests
    107114        if (offset < 0)
     
    133140}
    134141
     142long WebGLBuffer::getCachedMaxIndex(unsigned long type)
     143{
     144    size_t numEntries = sizeof(m_maxIndexCache) / sizeof(MaxIndexCacheEntry);
     145    for (size_t i = 0; i < numEntries; i++)
     146        if (m_maxIndexCache[i].type == type)
     147            return m_maxIndexCache[i].maxIndex;
     148    return -1;
     149}
     150
     151void WebGLBuffer::setCachedMaxIndex(unsigned long type, long value)
     152{
     153    int numEntries = sizeof(m_maxIndexCache) / sizeof(MaxIndexCacheEntry);
     154    for (int i = 0; i < numEntries; i++)
     155        if (m_maxIndexCache[i].type == type) {
     156            m_maxIndexCache[i].maxIndex = value;
     157            return;
     158        }
     159    m_maxIndexCache[m_nextAvailableCacheEntry].type = type;
     160    m_maxIndexCache[m_nextAvailableCacheEntry].maxIndex = value;
     161    m_nextAvailableCacheEntry = (m_nextAvailableCacheEntry + 1) % numEntries;
     162}
     163
     164void WebGLBuffer::clearCachedMaxIndices()
     165{
     166    memset(m_maxIndexCache, 0, sizeof(m_maxIndexCache));
     167}
     168
    135169}
    136170
  • trunk/WebCore/html/canvas/WebGLBuffer.h

    r51348 r52205  
    5252        const WebGLArrayBuffer* elementArrayBuffer() const { return m_elementArrayBuffer.get(); }
    5353                       
     54        // Gets the cached max index for the given type. Returns -1 if
     55        // none has been set.
     56        long getCachedMaxIndex(unsigned long type);
     57        // Sets the cached max index for the given type.
     58        void setCachedMaxIndex(unsigned long type, long value);
     59
    5460    protected:
    5561        WebGLBuffer(WebGLRenderingContext*);
     
    6369        unsigned m_arrayBufferByteLength;
    6470        bool m_elementArrayBufferCloned;
     71
     72        // Optimization for index validation. For each type of index
     73        // (i.e., UNSIGNED_SHORT), cache the maximum index in the
     74        // entire buffer.
     75        //
     76        // This is sufficient to eliminate a lot of work upon each
     77        // draw call as long as all bound array buffers are at least
     78        // that size.
     79        struct MaxIndexCacheEntry {
     80            unsigned long type;
     81            long maxIndex;
     82        };
     83        // OpenGL ES 2.0 only has two valid index types (UNSIGNED_BYTE
     84        // and UNSIGNED_SHORT), but might as well leave open the
     85        // possibility of adding others.
     86        MaxIndexCacheEntry m_maxIndexCache[4];
     87        unsigned m_nextAvailableCacheEntry;
     88
     89        // Clears all of the cached max indices.
     90        void clearCachedMaxIndices();
    6591    };
    6692   
  • trunk/WebCore/html/canvas/WebGLRenderingContext.cpp

    r52164 r52205  
    575575}
    576576
    577 bool WebGLRenderingContext::validateIndexArray(unsigned long count, unsigned long type, long offset, long& numElements)
    578 {
    579     long lastIndex = -1;
     577bool WebGLRenderingContext::validateElementArraySize(unsigned long count, unsigned long type, long offset)
     578{
    580579    if (!m_boundElementArrayBuffer)
    581580        return false;
    582        
     581
    583582    if (offset < 0)
    584583        return false;
    585        
    586     // The GL spec says that count must be "greater
    587    
     584
    588585    unsigned long uoffset = static_cast<unsigned long>(offset);
    589    
     586
    590587    if (type == GraphicsContext3D::UNSIGNED_SHORT) {
    591588        // For an unsigned short array, offset must be divisible by 2 for alignment reasons.
    592589        if (uoffset & 1)
    593590            return false;
    594        
     591
    595592        // Make uoffset an element offset.
    596593        uoffset /= 2;
    597    
     594
    598595        unsigned long n = m_boundElementArrayBuffer->byteLength(GraphicsContext3D::ELEMENT_ARRAY_BUFFER) / 2;
    599596        if (uoffset > n || count > n - uoffset)
    600597            return false;
    601            
     598    } else if (type == GraphicsContext3D::UNSIGNED_BYTE) {
     599        unsigned long n = m_boundElementArrayBuffer->byteLength(GraphicsContext3D::ELEMENT_ARRAY_BUFFER);
     600        if (uoffset > n || count > n - uoffset)
     601            return false;
     602    }
     603    return true;
     604}
     605
     606bool WebGLRenderingContext::validateIndexArrayConservative(unsigned long type, long& numElementsRequired)
     607{
     608    // Performs conservative validation by caching a maximum index of
     609    // the given type per element array buffer. If all of the bound
     610    // array buffers have enough elements to satisfy that maximum
     611    // index, skips the expensive per-draw-call iteration in
     612    // validateIndexArrayPrecise.
     613
     614    long maxIndex = m_boundElementArrayBuffer->getCachedMaxIndex(type);
     615    if (maxIndex < 0) {
     616        // Compute the maximum index in the entire buffer for the given type of index.
     617        switch (type) {
     618        case GraphicsContext3D::UNSIGNED_BYTE: {
     619            unsigned numElements = m_boundElementArrayBuffer->byteLength(GraphicsContext3D::ELEMENT_ARRAY_BUFFER);
     620            const unsigned char* p = static_cast<const unsigned char*>(m_boundElementArrayBuffer->elementArrayBuffer()->data());
     621            for (unsigned i = 0; i < numElements; i++)
     622                maxIndex = max(maxIndex, static_cast<long>(p[i]));
     623            break;
     624        }
     625        case GraphicsContext3D::UNSIGNED_SHORT: {
     626            unsigned numElements = m_boundElementArrayBuffer->byteLength(GraphicsContext3D::ELEMENT_ARRAY_BUFFER) / sizeof(unsigned short);
     627            const unsigned short* p = static_cast<const unsigned short*>(m_boundElementArrayBuffer->elementArrayBuffer()->data());
     628            for (unsigned i = 0; i < numElements; i++)
     629                maxIndex = max(maxIndex, static_cast<long>(p[i]));
     630            break;
     631        }
     632        default:
     633            return false;
     634        }
     635        m_boundElementArrayBuffer->setCachedMaxIndex(type, maxIndex);
     636    }
     637
     638    if (maxIndex >= 0) {
     639        // The number of required elements is one more than the maximum
     640        // index that will be accessed.
     641        numElementsRequired = maxIndex + 1;
     642        return true;
     643    }
     644
     645    return false;
     646}
     647
     648bool WebGLRenderingContext::validateIndexArrayPrecise(unsigned long count, unsigned long type, long offset, long& numElementsRequired)
     649{
     650    // FIXME: "count" should need to be used in the computation below
     651    UNUSED_PARAM(count);
     652    long lastIndex = -1;
     653
     654    if (!m_boundElementArrayBuffer)
     655        return false;
     656       
     657    // The GL spec says that count must be "greater
     658   
     659    unsigned long uoffset = static_cast<unsigned long>(offset);
     660   
     661    if (type == GraphicsContext3D::UNSIGNED_SHORT) {
     662        // Make uoffset an element offset.
     663        uoffset /= 2;
     664   
     665        unsigned long n = m_boundElementArrayBuffer->byteLength(GraphicsContext3D::ELEMENT_ARRAY_BUFFER) / 2;
    602666        const unsigned short* p = static_cast<const unsigned short*>(m_boundElementArrayBuffer->elementArrayBuffer()->data());
    603667        while (n-- > 0) {
     
    608672    } else if (type == GraphicsContext3D::UNSIGNED_BYTE) {
    609673        unsigned long n = m_boundElementArrayBuffer->byteLength(GraphicsContext3D::ELEMENT_ARRAY_BUFFER);
    610         if (uoffset > n || count > n - uoffset)
    611             return false;
    612            
    613674        const unsigned char* p = static_cast<const unsigned char*>(m_boundElementArrayBuffer->elementArrayBuffer()->data());
    614675        while (n-- > 0) {
     
    620681       
    621682    // Then set the last index in the index array and make sure it is valid.
    622     numElements = lastIndex + 1;
    623     return numElements > 0;
    624 }
    625 
    626 bool WebGLRenderingContext::validateRenderingState(long numElements)
     683    numElementsRequired = lastIndex + 1;
     684    return numElementsRequired > 0;
     685}
     686
     687bool WebGLRenderingContext::validateRenderingState(long numElementsRequired)
    627688{
    628689    // Look in each enabled vertex attrib and find the smallest buffer size
     
    637698        smallestNumElements = 0;
    638699   
    639     return numElements <= smallestNumElements;
     700    return numElementsRequired <= smallestNumElements;
    640701}
    641702
     
    659720    long numElements;
    660721   
    661     if (offset < 0 || !validateIndexArray(count, type, offset, numElements) || !validateRenderingState(numElements)) {
    662         m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION);
    663         return;
    664     }
     722    if (offset < 0 || !validateElementArraySize(count, type, offset)) {
     723        m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION);
     724        return;
     725    }
     726
     727    if (!validateIndexArrayConservative(type, numElements) || !validateRenderingState(numElements))
     728        if (!validateIndexArrayPrecise(count, type, offset, numElements) || !validateRenderingState(numElements)) {
     729            m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION);
     730            return;
     731        }
    665732   
    666733    m_context->drawElements(mode, count, type, offset);
  • trunk/WebCore/html/canvas/WebGLRenderingContext.h

    r51970 r52205  
    302302        }
    303303       
    304         bool validateIndexArray(unsigned long count, unsigned long type, long offset, long& numElements);
     304        // Basic validation of count and offset against number of elements in element array buffer
     305        bool validateElementArraySize(unsigned long count, unsigned long type, long offset);
     306
     307        // Conservative but quick index validation
     308        bool validateIndexArrayConservative(unsigned long type, long& numElementsRequired);
     309
     310        // Precise but slow index validation -- only done if conservative checks fail
     311        bool validateIndexArrayPrecise(unsigned long count, unsigned long type, long offset, long& numElementsRequired);
    305312        bool validateRenderingState(long numElements);
    306313
Note: See TracChangeset for help on using the changeset viewer.