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
17GrGLVertexBuffer::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
27void 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
38void GrGLVertexBuffer::onAbandon() {
39    fBufferID = 0;
40    fLockPtr = NULL;
41
42    INHERITED::onAbandon();
43}
44
45void GrGLVertexBuffer::bind() const {
46    GL_CALL(BindBuffer(GR_GL_ARRAY_BUFFER, fBufferID));
47    GPUGL->notifyVertexBufferBind(this);
48}
49
50GrGLuint GrGLVertexBuffer::bufferID() const {
51    return fBufferID;
52}
53
54void* 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
71void* GrGLVertexBuffer::lockPtr() const {
72    return fLockPtr;
73}
74
75void 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
86bool 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
101bool 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