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