diff options
Diffstat (limited to 'src/gpu/gl/GrGLBufferImpl.cpp')
-rw-r--r-- | src/gpu/gl/GrGLBufferImpl.cpp | 131 |
1 files changed, 131 insertions, 0 deletions
diff --git a/src/gpu/gl/GrGLBufferImpl.cpp b/src/gpu/gl/GrGLBufferImpl.cpp new file mode 100644 index 0000000000..0ab83fb77a --- /dev/null +++ b/src/gpu/gl/GrGLBufferImpl.cpp @@ -0,0 +1,131 @@ +/* + * Copyright 2013 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "GrGLBufferImpl.h" +#include "GrGpuGL.h" + +#define GL_CALL(GPU, X) GR_GL_CALL(GPU->glInterface(), X) + +GrGLBufferImpl::GrGLBufferImpl(GrGpuGL* gpu, const Desc& desc, GrGLenum bufferType) + : fDesc(desc) + , fBufferType(bufferType) + , fLockPtr(NULL) { + GrAssert(GR_GL_ARRAY_BUFFER == bufferType || GR_GL_ELEMENT_ARRAY_BUFFER == bufferType); +} + +void GrGLBufferImpl::release(GrGpuGL* gpu) { + // make sure we've not been abandoned + if (fDesc.fID && !fDesc.fIsWrapped) { + GL_CALL(gpu, DeleteBuffers(1, &fDesc.fID)); + if (GR_GL_ARRAY_BUFFER == fBufferType) { + gpu->notifyVertexBufferDelete(fDesc.fID); + } else { + GrAssert(GR_GL_ELEMENT_ARRAY_BUFFER == fBufferType); + gpu->notifyIndexBufferDelete(fDesc.fID); + } + fDesc.fID = 0; + } +} + +void GrGLBufferImpl::abandon() { + fDesc.fID = 0; + fLockPtr = NULL; +} + +void GrGLBufferImpl::bind(GrGpuGL* gpu) const { + GL_CALL(gpu, BindBuffer(fBufferType, fDesc.fID)); + if (GR_GL_ARRAY_BUFFER == fBufferType) { + gpu->notifyVertexBufferBind(fDesc.fID); + } else { + GrAssert(GR_GL_ELEMENT_ARRAY_BUFFER == fBufferType); + gpu->notifyIndexBufferBind(fDesc.fID); + } +} + +void* GrGLBufferImpl::lock(GrGpuGL* gpu) { + GrAssert(0 != fDesc.fID); + GrAssert(!this->isLocked()); + if (gpu->getCaps().bufferLockSupport()) { + this->bind(gpu); + // Let driver know it can discard the old data + GL_CALL(gpu, BufferData(fBufferType, + fDesc.fSizeInBytes, + NULL, + fDesc.fDynamic ? GR_GL_DYNAMIC_DRAW : GR_GL_STATIC_DRAW)); + GR_GL_CALL_RET(gpu->glInterface(), + fLockPtr, + MapBuffer(fBufferType, GR_GL_WRITE_ONLY)); + return fLockPtr; + } + return NULL; +} + +void GrGLBufferImpl::unlock(GrGpuGL* gpu) { + + GrAssert(0 != fDesc.fID); + GrAssert(this->isLocked()); + GrAssert(gpu->getCaps().bufferLockSupport()); + + this->bind(gpu); + GL_CALL(gpu, UnmapBuffer(fBufferType)); + fLockPtr = NULL; +} + +bool GrGLBufferImpl::isLocked() const { + GrAssert(0 != fDesc.fID); + return NULL != fLockPtr; +} + +bool GrGLBufferImpl::updateData(GrGpuGL* gpu, const void* src, size_t srcSizeInBytes) { + GrAssert(!this->isLocked()); + if (srcSizeInBytes > fDesc.fSizeInBytes) { + return false; + } + if (0 == fDesc.fID) { + return false; + } + this->bind(gpu); + GrGLenum usage = fDesc.fDynamic ? GR_GL_DYNAMIC_DRAW : GR_GL_STATIC_DRAW; + +#if GR_GL_USE_BUFFER_DATA_NULL_HINT + if (fDesc.fSizeInBytes == srcSizeInBytes) { + GL_CALL(gpu, BufferData(fBufferType, srcSizeInBytes, src, usage)); + } else { + // Before we call glBufferSubData we give the driver a hint using + // glBufferData with NULL. This makes the old buffer contents + // inaccessible to future draws. The GPU may still be processing + // draws that reference the old contents. With this hint it can + // assign a different allocation for the new contents to avoid + // flushing the gpu past draws consuming the old contents. + GL_CALL(gpu, BufferData(fBufferType, fDesc.fSizeInBytes, NULL, usage)); + GL_CALL(gpu, BufferSubData(fBufferType, 0, srcSizeInBytes, src)); + } +#else + // Note that we're cheating on the size here. Currently no methods + // allow a partial update that preserves contents of non-updated + // portions of the buffer (lock() does a glBufferData(..size, NULL..)) + bool doSubData = false; +#if GR_GL_MAC_BUFFER_OBJECT_PERFOMANCE_WORKAROUND + static int N = 0; + // 128 was chosen experimentally. At 256 a slight hitchiness was noticed + // when dragging a Chromium window around with a canvas tab backgrounded. + doSubData = 0 == (N % 128); + ++N; +#endif + if (doSubData) { + // The workaround is to do a glBufferData followed by glBufferSubData. + // Chromium's command buffer may turn a glBufferSubData where the size + // exactly matches the buffer size into a glBufferData. So we tack 1 + // extra byte onto the glBufferData. + GL_CALL(gpu, BufferData(fBufferType, srcSizeInBytes + 1, NULL, usage)); + GL_CALL(gpu, BufferSubData(fBufferType, 0, srcSizeInBytes, src)); + } else { + GL_CALL(gpu, BufferData(fBufferType, srcSizeInBytes, src, usage)); + } +#endif + return true; +} |