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 23GrGLBufferImpl::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 35void 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 55void GrGLBufferImpl::abandon() { 56 fDesc.fID = 0; 57 fLockPtr = NULL; 58 sk_free(fCPUData); 59 fCPUData = NULL; 60} 61 62void 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 72void* 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 91void 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 102bool GrGLBufferImpl::isLocked() const { 103 VALIDATE(); 104 return NULL != fLockPtr; 105} 106 107bool 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 159void 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