• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #ifdef SK_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 
GrGLBufferImpl(GrGpuGL * gpu,const Desc & desc,GrGLenum bufferType)23 GrGLBufferImpl::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 
release(GrGpuGL * gpu)35 void 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             SkASSERT(GR_GL_ELEMENT_ARRAY_BUFFER == fBufferType);
48             gpu->notifyIndexBufferDelete(fDesc.fID);
49         }
50         fDesc.fID = 0;
51     }
52     fLockPtr = NULL;
53 }
54 
abandon()55 void GrGLBufferImpl::abandon() {
56     fDesc.fID = 0;
57     fLockPtr = NULL;
58     sk_free(fCPUData);
59     fCPUData = NULL;
60 }
61 
bind(GrGpuGL * gpu) const62 void GrGLBufferImpl::bind(GrGpuGL* gpu) const {
63     VALIDATE();
64     if (GR_GL_ARRAY_BUFFER == fBufferType) {
65         gpu->bindVertexBuffer(fDesc.fID);
66     } else {
67         SkASSERT(GR_GL_ELEMENT_ARRAY_BUFFER == fBufferType);
68         gpu->bindIndexBufferAndDefaultVertexArray(fDesc.fID);
69     }
70 }
71 
lock(GrGpuGL * gpu)72 void* GrGLBufferImpl::lock(GrGpuGL* gpu) {
73     VALIDATE();
74     SkASSERT(!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                                 (GrGLsizeiptr) 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 
unlock(GrGpuGL * gpu)91 void GrGLBufferImpl::unlock(GrGpuGL* gpu) {
92     VALIDATE();
93     SkASSERT(this->isLocked());
94     if (0 != fDesc.fID) {
95         SkASSERT(gpu->caps()->bufferLockSupport());
96         this->bind(gpu);
97         GL_CALL(gpu, UnmapBuffer(fBufferType));
98     }
99     fLockPtr = NULL;
100 }
101 
isLocked() const102 bool GrGLBufferImpl::isLocked() const {
103     VALIDATE();
104     return NULL != fLockPtr;
105 }
106 
updateData(GrGpuGL * gpu,const void * src,size_t srcSizeInBytes)107 bool GrGLBufferImpl::updateData(GrGpuGL* gpu, const void* src, size_t srcSizeInBytes) {
108     SkASSERT(!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, (GrGLsizeiptr) 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, (GrGLsizeiptr) fDesc.fSizeInBytes, NULL, usage));
131         GL_CALL(gpu, BufferSubData(fBufferType, 0, (GrGLsizeiptr) 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 
validate() const159 void GrGLBufferImpl::validate() const {
160     SkASSERT(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     // SkASSERT((0 == fDesc.fID) == (NULL != fCPUData));
163     SkASSERT(0 != fDesc.fID || !fDesc.fIsWrapped);
164     SkASSERT(NULL == fCPUData || NULL == fLockPtr || fCPUData == fLockPtr);
165 }
166