michael@0: /* michael@0: * Copyright 2013 Google Inc. michael@0: * michael@0: * Use of this source code is governed by a BSD-style license that can be michael@0: * found in the LICENSE file. michael@0: */ michael@0: michael@0: #include "GrGLBufferImpl.h" michael@0: #include "GrGpuGL.h" michael@0: michael@0: #define GL_CALL(GPU, X) GR_GL_CALL(GPU->glInterface(), X) michael@0: michael@0: #ifdef SK_DEBUG michael@0: #define VALIDATE() this->validate() michael@0: #else michael@0: #define VALIDATE() do {} while(false) michael@0: #endif michael@0: michael@0: // GL_STREAM_DRAW triggers an optimization in Chromium's GPU process where a client's vertex buffer michael@0: // objects are implemented as client-side-arrays on tile-deferred architectures. michael@0: #define DYNAMIC_USAGE_PARAM GR_GL_STREAM_DRAW michael@0: michael@0: GrGLBufferImpl::GrGLBufferImpl(GrGpuGL* gpu, const Desc& desc, GrGLenum bufferType) michael@0: : fDesc(desc) michael@0: , fBufferType(bufferType) michael@0: , fLockPtr(NULL) { michael@0: if (0 == desc.fID) { michael@0: fCPUData = sk_malloc_flags(desc.fSizeInBytes, SK_MALLOC_THROW); michael@0: } else { michael@0: fCPUData = NULL; michael@0: } michael@0: VALIDATE(); michael@0: } michael@0: michael@0: void GrGLBufferImpl::release(GrGpuGL* gpu) { michael@0: // make sure we've not been abandoned or already released michael@0: if (NULL != fCPUData) { michael@0: VALIDATE(); michael@0: sk_free(fCPUData); michael@0: fCPUData = NULL; michael@0: } else if (fDesc.fID && !fDesc.fIsWrapped) { michael@0: VALIDATE(); michael@0: GL_CALL(gpu, DeleteBuffers(1, &fDesc.fID)); michael@0: if (GR_GL_ARRAY_BUFFER == fBufferType) { michael@0: gpu->notifyVertexBufferDelete(fDesc.fID); michael@0: } else { michael@0: SkASSERT(GR_GL_ELEMENT_ARRAY_BUFFER == fBufferType); michael@0: gpu->notifyIndexBufferDelete(fDesc.fID); michael@0: } michael@0: fDesc.fID = 0; michael@0: } michael@0: fLockPtr = NULL; michael@0: } michael@0: michael@0: void GrGLBufferImpl::abandon() { michael@0: fDesc.fID = 0; michael@0: fLockPtr = NULL; michael@0: sk_free(fCPUData); michael@0: fCPUData = NULL; michael@0: } michael@0: michael@0: void GrGLBufferImpl::bind(GrGpuGL* gpu) const { michael@0: VALIDATE(); michael@0: if (GR_GL_ARRAY_BUFFER == fBufferType) { michael@0: gpu->bindVertexBuffer(fDesc.fID); michael@0: } else { michael@0: SkASSERT(GR_GL_ELEMENT_ARRAY_BUFFER == fBufferType); michael@0: gpu->bindIndexBufferAndDefaultVertexArray(fDesc.fID); michael@0: } michael@0: } michael@0: michael@0: void* GrGLBufferImpl::lock(GrGpuGL* gpu) { michael@0: VALIDATE(); michael@0: SkASSERT(!this->isLocked()); michael@0: if (0 == fDesc.fID) { michael@0: fLockPtr = fCPUData; michael@0: } else if (gpu->caps()->bufferLockSupport()) { michael@0: this->bind(gpu); michael@0: // Let driver know it can discard the old data michael@0: GL_CALL(gpu, BufferData(fBufferType, michael@0: (GrGLsizeiptr) fDesc.fSizeInBytes, michael@0: NULL, michael@0: fDesc.fDynamic ? DYNAMIC_USAGE_PARAM : GR_GL_STATIC_DRAW)); michael@0: GR_GL_CALL_RET(gpu->glInterface(), michael@0: fLockPtr, michael@0: MapBuffer(fBufferType, GR_GL_WRITE_ONLY)); michael@0: } michael@0: return fLockPtr; michael@0: } michael@0: michael@0: void GrGLBufferImpl::unlock(GrGpuGL* gpu) { michael@0: VALIDATE(); michael@0: SkASSERT(this->isLocked()); michael@0: if (0 != fDesc.fID) { michael@0: SkASSERT(gpu->caps()->bufferLockSupport()); michael@0: this->bind(gpu); michael@0: GL_CALL(gpu, UnmapBuffer(fBufferType)); michael@0: } michael@0: fLockPtr = NULL; michael@0: } michael@0: michael@0: bool GrGLBufferImpl::isLocked() const { michael@0: VALIDATE(); michael@0: return NULL != fLockPtr; michael@0: } michael@0: michael@0: bool GrGLBufferImpl::updateData(GrGpuGL* gpu, const void* src, size_t srcSizeInBytes) { michael@0: SkASSERT(!this->isLocked()); michael@0: VALIDATE(); michael@0: if (srcSizeInBytes > fDesc.fSizeInBytes) { michael@0: return false; michael@0: } michael@0: if (0 == fDesc.fID) { michael@0: memcpy(fCPUData, src, srcSizeInBytes); michael@0: return true; michael@0: } michael@0: this->bind(gpu); michael@0: GrGLenum usage = fDesc.fDynamic ? DYNAMIC_USAGE_PARAM : GR_GL_STATIC_DRAW; michael@0: michael@0: #if GR_GL_USE_BUFFER_DATA_NULL_HINT michael@0: if (fDesc.fSizeInBytes == srcSizeInBytes) { michael@0: GL_CALL(gpu, BufferData(fBufferType, (GrGLsizeiptr) srcSizeInBytes, src, usage)); michael@0: } else { michael@0: // Before we call glBufferSubData we give the driver a hint using michael@0: // glBufferData with NULL. This makes the old buffer contents michael@0: // inaccessible to future draws. The GPU may still be processing michael@0: // draws that reference the old contents. With this hint it can michael@0: // assign a different allocation for the new contents to avoid michael@0: // flushing the gpu past draws consuming the old contents. michael@0: GL_CALL(gpu, BufferData(fBufferType, (GrGLsizeiptr) fDesc.fSizeInBytes, NULL, usage)); michael@0: GL_CALL(gpu, BufferSubData(fBufferType, 0, (GrGLsizeiptr) srcSizeInBytes, src)); michael@0: } michael@0: #else michael@0: // Note that we're cheating on the size here. Currently no methods michael@0: // allow a partial update that preserves contents of non-updated michael@0: // portions of the buffer (lock() does a glBufferData(..size, NULL..)) michael@0: bool doSubData = false; michael@0: #if GR_GL_MAC_BUFFER_OBJECT_PERFOMANCE_WORKAROUND michael@0: static int N = 0; michael@0: // 128 was chosen experimentally. At 256 a slight hitchiness was noticed michael@0: // when dragging a Chromium window around with a canvas tab backgrounded. michael@0: doSubData = 0 == (N % 128); michael@0: ++N; michael@0: #endif michael@0: if (doSubData) { michael@0: // The workaround is to do a glBufferData followed by glBufferSubData. michael@0: // Chromium's command buffer may turn a glBufferSubData where the size michael@0: // exactly matches the buffer size into a glBufferData. So we tack 1 michael@0: // extra byte onto the glBufferData. michael@0: GL_CALL(gpu, BufferData(fBufferType, srcSizeInBytes + 1, NULL, usage)); michael@0: GL_CALL(gpu, BufferSubData(fBufferType, 0, srcSizeInBytes, src)); michael@0: } else { michael@0: GL_CALL(gpu, BufferData(fBufferType, srcSizeInBytes, src, usage)); michael@0: } michael@0: #endif michael@0: return true; michael@0: } michael@0: michael@0: void GrGLBufferImpl::validate() const { michael@0: SkASSERT(GR_GL_ARRAY_BUFFER == fBufferType || GR_GL_ELEMENT_ARRAY_BUFFER == fBufferType); michael@0: // The following assert isn't valid when the buffer has been abandoned: michael@0: // SkASSERT((0 == fDesc.fID) == (NULL != fCPUData)); michael@0: SkASSERT(0 != fDesc.fID || !fDesc.fIsWrapped); michael@0: SkASSERT(NULL == fCPUData || NULL == fLockPtr || fCPUData == fLockPtr); michael@0: }