/* * Copyright 2011 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "GrGLVertexBuffer.h" #include "GrGpuGL.h" #define GPUGL static_cast<GrGpuGL*>(getGpu()) #define GL_CALL(X) GR_GL_CALL(GPUGL->glInterface(), X) GrGLVertexBuffer::GrGLVertexBuffer(GrGpuGL* gpu, bool isWrapped, GrGLuint id, size_t sizeInBytes, bool dynamic) : INHERITED(gpu, isWrapped, sizeInBytes, dynamic) , fBufferID(id) , fLockPtr(NULL) { } void GrGLVertexBuffer::onRelease() { // make sure we've not been abandoned if (fBufferID && !this->isWrapped()) { GPUGL->notifyVertexBufferDelete(this); GL_CALL(DeleteBuffers(1, &fBufferID)); fBufferID = 0; } INHERITED::onRelease(); } void GrGLVertexBuffer::onAbandon() { fBufferID = 0; fLockPtr = NULL; INHERITED::onAbandon(); } void GrGLVertexBuffer::bind() const { GL_CALL(BindBuffer(GR_GL_ARRAY_BUFFER, fBufferID)); GPUGL->notifyVertexBufferBind(this); } GrGLuint GrGLVertexBuffer::bufferID() const { return fBufferID; } void* GrGLVertexBuffer::lock() { GrAssert(fBufferID); GrAssert(!isLocked()); if (this->getGpu()->getCaps().bufferLockSupport()) { this->bind(); // Let driver know it can discard the old data GL_CALL(BufferData(GR_GL_ARRAY_BUFFER, this->sizeInBytes(), NULL, this->dynamic() ? GR_GL_DYNAMIC_DRAW : GR_GL_STATIC_DRAW)); GR_GL_CALL_RET(GPUGL->glInterface(), fLockPtr, MapBuffer(GR_GL_ARRAY_BUFFER, GR_GL_WRITE_ONLY)); return fLockPtr; } return NULL; } void* GrGLVertexBuffer::lockPtr() const { return fLockPtr; } void GrGLVertexBuffer::unlock() { GrAssert(fBufferID); GrAssert(isLocked()); GrAssert(this->getGpu()->getCaps().bufferLockSupport()); this->bind(); GL_CALL(UnmapBuffer(GR_GL_ARRAY_BUFFER)); fLockPtr = NULL; } bool GrGLVertexBuffer::isLocked() const { GrAssert(!this->isValid() || fBufferID); // this check causes a lot of noise in the gl log #if 0 if (this->isValid() && this->getGpu()->getCaps().fBufferLockSupport) { GrGLint mapped; this->bind(); GL_CALL(GetBufferParameteriv(GR_GL_ARRAY_BUFFER, GR_GL_BUFFER_MAPPED, &mapped)); GrAssert(!!mapped == !!fLockPtr); } #endif return NULL != fLockPtr; } bool GrGLVertexBuffer::updateData(const void* src, size_t srcSizeInBytes) { GrAssert(fBufferID); GrAssert(!isLocked()); if (srcSizeInBytes > this->sizeInBytes()) { return false; } this->bind(); GrGLenum usage = dynamic() ? GR_GL_DYNAMIC_DRAW : GR_GL_STATIC_DRAW; #if GR_GL_USE_BUFFER_DATA_NULL_HINT if (this->sizeInBytes() == srcSizeInBytes) { GL_CALL(BufferData(GR_GL_ARRAY_BUFFER, 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(BufferData(GR_GL_ARRAY_BUFFER, this->sizeInBytes(), NULL, usage)); GL_CALL(BufferSubData(GR_GL_ARRAY_BUFFER, 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(BufferData(GR_GL_ARRAY_BUFFER, srcSizeInBytes + 1, NULL, usage)); GL_CALL(BufferSubData(GR_GL_ARRAY_BUFFER, 0, srcSizeInBytes, src)); } else { GL_CALL(BufferData(GR_GL_ARRAY_BUFFER, srcSizeInBytes, src, usage)); } #endif return true; }