Home | History | Annotate | Download | only in gl
      1 /*
      2  * Copyright 2011 Google Inc.
      3  *
      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 
      9 
     10 #include "GrGLVertexBuffer.h"
     11 #include "GrGpuGL.h"
     12 
     13 #define GPUGL static_cast<GrGpuGL*>(getGpu())
     14 
     15 #define GL_CALL(X) GR_GL_CALL(GPUGL->glInterface(), X)
     16 
     17 GrGLVertexBuffer::GrGLVertexBuffer(GrGpuGL* gpu,
     18                                    bool isWrapped,
     19                                    GrGLuint id,
     20                                    size_t sizeInBytes,
     21                                    bool dynamic)
     22     : INHERITED(gpu, isWrapped, sizeInBytes, dynamic)
     23     , fBufferID(id)
     24     , fLockPtr(NULL) {
     25 }
     26 
     27 void GrGLVertexBuffer::onRelease() {
     28     // make sure we've not been abandoned
     29     if (fBufferID && !this->isWrapped()) {
     30         GPUGL->notifyVertexBufferDelete(this);
     31         GL_CALL(DeleteBuffers(1, &fBufferID));
     32         fBufferID = 0;
     33     }
     34 
     35     INHERITED::onRelease();
     36 }
     37 
     38 void GrGLVertexBuffer::onAbandon() {
     39     fBufferID = 0;
     40     fLockPtr = NULL;
     41 
     42     INHERITED::onAbandon();
     43 }
     44 
     45 void GrGLVertexBuffer::bind() const {
     46     GL_CALL(BindBuffer(GR_GL_ARRAY_BUFFER, fBufferID));
     47     GPUGL->notifyVertexBufferBind(this);
     48 }
     49 
     50 GrGLuint GrGLVertexBuffer::bufferID() const {
     51     return fBufferID;
     52 }
     53 
     54 void* GrGLVertexBuffer::lock() {
     55     GrAssert(fBufferID);
     56     GrAssert(!isLocked());
     57     if (this->getGpu()->getCaps().bufferLockSupport()) {
     58         this->bind();
     59         // Let driver know it can discard the old data
     60         GL_CALL(BufferData(GR_GL_ARRAY_BUFFER, this->sizeInBytes(), NULL,
     61                            this->dynamic() ? GR_GL_DYNAMIC_DRAW :
     62                                              GR_GL_STATIC_DRAW));
     63         GR_GL_CALL_RET(GPUGL->glInterface(),
     64                        fLockPtr,
     65                        MapBuffer(GR_GL_ARRAY_BUFFER, GR_GL_WRITE_ONLY));
     66         return fLockPtr;
     67     }
     68     return NULL;
     69 }
     70 
     71 void* GrGLVertexBuffer::lockPtr() const {
     72     return fLockPtr;
     73 }
     74 
     75 void GrGLVertexBuffer::unlock() {
     76 
     77     GrAssert(fBufferID);
     78     GrAssert(isLocked());
     79     GrAssert(this->getGpu()->getCaps().bufferLockSupport());
     80 
     81     this->bind();
     82     GL_CALL(UnmapBuffer(GR_GL_ARRAY_BUFFER));
     83     fLockPtr = NULL;
     84 }
     85 
     86 bool GrGLVertexBuffer::isLocked() const {
     87     GrAssert(!this->isValid() || fBufferID);
     88     // this check causes a lot of noise in the gl log
     89 #if 0
     90     if (this->isValid() && this->getGpu()->getCaps().fBufferLockSupport) {
     91         GrGLint mapped;
     92         this->bind();
     93         GL_CALL(GetBufferParameteriv(GR_GL_ARRAY_BUFFER,
     94                                      GR_GL_BUFFER_MAPPED, &mapped));
     95         GrAssert(!!mapped == !!fLockPtr);
     96     }
     97 #endif
     98     return NULL != fLockPtr;
     99 }
    100 
    101 bool GrGLVertexBuffer::updateData(const void* src, size_t srcSizeInBytes) {
    102     GrAssert(fBufferID);
    103     GrAssert(!isLocked());
    104     if (srcSizeInBytes > this->sizeInBytes()) {
    105         return false;
    106     }
    107     this->bind();
    108     GrGLenum usage = dynamic() ? GR_GL_DYNAMIC_DRAW : GR_GL_STATIC_DRAW;
    109 
    110 #if GR_GL_USE_BUFFER_DATA_NULL_HINT
    111     if (this->sizeInBytes() == srcSizeInBytes) {
    112         GL_CALL(BufferData(GR_GL_ARRAY_BUFFER, srcSizeInBytes, src, usage));
    113     } else {
    114         // Before we call glBufferSubData we give the driver a hint using
    115         // glBufferData with NULL. This makes the old buffer contents
    116         // inaccessible to future draws. The GPU may still be processing
    117         // draws that reference the old contents. With this hint it can
    118         // assign a different allocation for the new contents to avoid
    119         // flushing the gpu past draws consuming the old contents.
    120         GL_CALL(BufferData(GR_GL_ARRAY_BUFFER,
    121                            this->sizeInBytes(), NULL, usage));
    122         GL_CALL(BufferSubData(GR_GL_ARRAY_BUFFER, 0, srcSizeInBytes, src));
    123     }
    124 #else
    125     // Note that we're cheating on the size here. Currently no methods
    126     // allow a partial update that preserves contents of non-updated
    127     // portions of the buffer (lock() does a glBufferData(..size, NULL..))
    128     bool doSubData = false;
    129 #if GR_GL_MAC_BUFFER_OBJECT_PERFOMANCE_WORKAROUND
    130     static int N = 0;
    131     // 128 was chosen experimentally. At 256 a slight hitchiness was noticed
    132     // when dragging a Chromium window around with a canvas tab backgrounded.
    133     doSubData = 0 == (N % 128);
    134     ++N;
    135 #endif
    136     if (doSubData) {
    137         // The workaround is to do a glBufferData followed by glBufferSubData.
    138         // Chromium's command buffer may turn a glBufferSubData where the size
    139         // exactly matches the buffer size into a glBufferData. So we tack 1
    140         // extra byte onto the glBufferData.
    141         GL_CALL(BufferData(GR_GL_ARRAY_BUFFER, srcSizeInBytes + 1,
    142                            NULL, usage));
    143         GL_CALL(BufferSubData(GR_GL_ARRAY_BUFFER, 0, srcSizeInBytes, src));
    144     } else {
    145         GL_CALL(BufferData(GR_GL_ARRAY_BUFFER, srcSizeInBytes, src, usage));
    146     }
    147 #endif
    148     return true;
    149 }
    150