Home | History | Annotate | Download | only in gl
      1 /*
      2  * Copyright 2013 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 #include "GrGLBufferImpl.h"
      9 #include "GrGpuGL.h"
     10 
     11 #define GL_CALL(GPU, X) GR_GL_CALL(GPU->glInterface(), X)
     12 
     13 #if GR_DEBUG
     14 #define VALIDATE() this->validate()
     15 #else
     16 #define VALIDATE() do {} while(false)
     17 #endif
     18 
     19 // GL_STREAM_DRAW triggers an optimization in Chromium's GPU process where a client's vertex buffer
     20 // objects are implemented as client-side-arrays on tile-deferred architectures.
     21 #define DYNAMIC_USAGE_PARAM GR_GL_STREAM_DRAW
     22 
     23 GrGLBufferImpl::GrGLBufferImpl(GrGpuGL* gpu, const Desc& desc, GrGLenum bufferType)
     24     : fDesc(desc)
     25     , fBufferType(bufferType)
     26     , fLockPtr(NULL) {
     27     if (0 == desc.fID) {
     28         fCPUData = sk_malloc_flags(desc.fSizeInBytes, SK_MALLOC_THROW);
     29     } else {
     30         fCPUData = NULL;
     31     }
     32     VALIDATE();
     33 }
     34 
     35 void GrGLBufferImpl::release(GrGpuGL* gpu) {
     36     // make sure we've not been abandoned or already released
     37     if (NULL != fCPUData) {
     38         VALIDATE();
     39         sk_free(fCPUData);
     40         fCPUData = NULL;
     41     } else if (fDesc.fID && !fDesc.fIsWrapped) {
     42         VALIDATE();
     43         GL_CALL(gpu, DeleteBuffers(1, &fDesc.fID));
     44         if (GR_GL_ARRAY_BUFFER == fBufferType) {
     45             gpu->notifyVertexBufferDelete(fDesc.fID);
     46         } else {
     47             GrAssert(GR_GL_ELEMENT_ARRAY_BUFFER == fBufferType);
     48             gpu->notifyIndexBufferDelete(fDesc.fID);
     49         }
     50         fDesc.fID = 0;
     51     }
     52     fLockPtr = NULL;
     53 }
     54 
     55 void GrGLBufferImpl::abandon() {
     56     fDesc.fID = 0;
     57     fLockPtr = NULL;
     58     sk_free(fCPUData);
     59     fCPUData = NULL;
     60 }
     61 
     62 void GrGLBufferImpl::bind(GrGpuGL* gpu) const {
     63     VALIDATE();
     64     if (GR_GL_ARRAY_BUFFER == fBufferType) {
     65         gpu->bindVertexBuffer(fDesc.fID);
     66     } else {
     67         GrAssert(GR_GL_ELEMENT_ARRAY_BUFFER == fBufferType);
     68         gpu->bindIndexBufferAndDefaultVertexArray(fDesc.fID);
     69     }
     70 }
     71 
     72 void* GrGLBufferImpl::lock(GrGpuGL* gpu) {
     73     VALIDATE();
     74     GrAssert(!this->isLocked());
     75     if (0 == fDesc.fID) {
     76         fLockPtr = fCPUData;
     77     } else if (gpu->caps()->bufferLockSupport()) {
     78         this->bind(gpu);
     79         // Let driver know it can discard the old data
     80         GL_CALL(gpu, BufferData(fBufferType,
     81                                 fDesc.fSizeInBytes,
     82                                 NULL,
     83                                 fDesc.fDynamic ? DYNAMIC_USAGE_PARAM : GR_GL_STATIC_DRAW));
     84         GR_GL_CALL_RET(gpu->glInterface(),
     85                        fLockPtr,
     86                        MapBuffer(fBufferType, GR_GL_WRITE_ONLY));
     87     }
     88     return fLockPtr;
     89 }
     90 
     91 void GrGLBufferImpl::unlock(GrGpuGL* gpu) {
     92     VALIDATE();
     93     GrAssert(this->isLocked());
     94     if (0 != fDesc.fID) {
     95         GrAssert(gpu->caps()->bufferLockSupport());
     96         this->bind(gpu);
     97         GL_CALL(gpu, UnmapBuffer(fBufferType));
     98     }
     99     fLockPtr = NULL;
    100 }
    101 
    102 bool GrGLBufferImpl::isLocked() const {
    103     VALIDATE();
    104     return NULL != fLockPtr;
    105 }
    106 
    107 bool GrGLBufferImpl::updateData(GrGpuGL* gpu, const void* src, size_t srcSizeInBytes) {
    108     GrAssert(!this->isLocked());
    109     VALIDATE();
    110     if (srcSizeInBytes > fDesc.fSizeInBytes) {
    111         return false;
    112     }
    113     if (0 == fDesc.fID) {
    114         memcpy(fCPUData, src, srcSizeInBytes);
    115         return true;
    116     }
    117     this->bind(gpu);
    118     GrGLenum usage = fDesc.fDynamic ? DYNAMIC_USAGE_PARAM : GR_GL_STATIC_DRAW;
    119 
    120 #if GR_GL_USE_BUFFER_DATA_NULL_HINT
    121     if (fDesc.fSizeInBytes == srcSizeInBytes) {
    122         GL_CALL(gpu, BufferData(fBufferType, srcSizeInBytes, src, usage));
    123     } else {
    124         // Before we call glBufferSubData we give the driver a hint using
    125         // glBufferData with NULL. This makes the old buffer contents
    126         // inaccessible to future draws. The GPU may still be processing
    127         // draws that reference the old contents. With this hint it can
    128         // assign a different allocation for the new contents to avoid
    129         // flushing the gpu past draws consuming the old contents.
    130         GL_CALL(gpu, BufferData(fBufferType, fDesc.fSizeInBytes, NULL, usage));
    131         GL_CALL(gpu, BufferSubData(fBufferType, 0, srcSizeInBytes, src));
    132     }
    133 #else
    134     // Note that we're cheating on the size here. Currently no methods
    135     // allow a partial update that preserves contents of non-updated
    136     // portions of the buffer (lock() does a glBufferData(..size, NULL..))
    137     bool doSubData = false;
    138 #if GR_GL_MAC_BUFFER_OBJECT_PERFOMANCE_WORKAROUND
    139     static int N = 0;
    140     // 128 was chosen experimentally. At 256 a slight hitchiness was noticed
    141     // when dragging a Chromium window around with a canvas tab backgrounded.
    142     doSubData = 0 == (N % 128);
    143     ++N;
    144 #endif
    145     if (doSubData) {
    146         // The workaround is to do a glBufferData followed by glBufferSubData.
    147         // Chromium's command buffer may turn a glBufferSubData where the size
    148         // exactly matches the buffer size into a glBufferData. So we tack 1
    149         // extra byte onto the glBufferData.
    150         GL_CALL(gpu, BufferData(fBufferType, srcSizeInBytes + 1, NULL, usage));
    151         GL_CALL(gpu, BufferSubData(fBufferType, 0, srcSizeInBytes, src));
    152     } else {
    153         GL_CALL(gpu, BufferData(fBufferType, srcSizeInBytes, src, usage));
    154     }
    155 #endif
    156     return true;
    157 }
    158 
    159 void GrGLBufferImpl::validate() const {
    160     GrAssert(GR_GL_ARRAY_BUFFER == fBufferType || GR_GL_ELEMENT_ARRAY_BUFFER == fBufferType);
    161     // The following assert isn't valid when the buffer has been abandoned:
    162     // GrAssert((0 == fDesc.fID) == (NULL != fCPUData));
    163     GrAssert(0 != fDesc.fID || !fDesc.fIsWrapped);
    164     GrAssert(NULL == fCPUData || NULL == fLockPtr || fCPUData == fLockPtr);
    165 }
    166