1 /*
2  * Copyright 2016 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 "GrGLBuffer.h"
9 #include "GrGLGpu.h"
10 #include "SkTraceMemoryDump.h"
11 
12 #define GL_CALL(X) GR_GL_CALL(this->glGpu()->glInterface(), X)
13 #define GL_CALL_RET(RET, X) GR_GL_CALL_RET(this->glGpu()->glInterface(), RET, X)
14 
15 #if GR_GL_CHECK_ALLOC_WITH_GET_ERROR
16     #define CLEAR_ERROR_BEFORE_ALLOC(iface)   GrGLClearErr(iface)
17     #define GL_ALLOC_CALL(iface, call)        GR_GL_CALL_NOERRCHECK(iface, call)
18     #define CHECK_ALLOC_ERROR(iface)          GR_GL_GET_ERROR(iface)
19 #else
20     #define CLEAR_ERROR_BEFORE_ALLOC(iface)
21     #define GL_ALLOC_CALL(iface, call)        GR_GL_CALL(iface, call)
22     #define CHECK_ALLOC_ERROR(iface)          GR_GL_NO_ERROR
23 #endif
24 
25 #ifdef SK_DEBUG
26 #define VALIDATE() this->validate()
27 #else
28 #define VALIDATE() do {} while(false)
29 #endif
30 
Create(GrGLGpu * gpu,size_t size,GrBufferType intendedType,GrAccessPattern accessPattern,const void * data)31 GrGLBuffer* GrGLBuffer::Create(GrGLGpu* gpu, size_t size, GrBufferType intendedType,
32                                GrAccessPattern accessPattern, const void* data) {
33     sk_sp<GrGLBuffer> buffer(new GrGLBuffer(gpu, size, intendedType, accessPattern, data));
34     if (0 == buffer->bufferID()) {
35         return nullptr;
36     }
37     return buffer.release();
38 }
39 
40 // GL_STREAM_DRAW triggers an optimization in Chromium's GPU process where a client's vertex buffer
41 // objects are implemented as client-side-arrays on tile-deferred architectures.
42 #define DYNAMIC_DRAW_PARAM GR_GL_STREAM_DRAW
43 
gr_to_gl_access_pattern(GrBufferType bufferType,GrAccessPattern accessPattern)44 inline static GrGLenum gr_to_gl_access_pattern(GrBufferType bufferType,
45                                                GrAccessPattern accessPattern) {
46     static const GrGLenum drawUsages[] = {
47         DYNAMIC_DRAW_PARAM,  // TODO: Do we really want to use STREAM_DRAW here on non-Chromium?
48         GR_GL_STATIC_DRAW,   // kStatic_GrAccessPattern
49         GR_GL_STREAM_DRAW    // kStream_GrAccessPattern
50     };
51 
52     static const GrGLenum readUsages[] = {
53         GR_GL_DYNAMIC_READ,  // kDynamic_GrAccessPattern
54         GR_GL_STATIC_READ,   // kStatic_GrAccessPattern
55         GR_GL_STREAM_READ    // kStream_GrAccessPattern
56     };
57 
58     GR_STATIC_ASSERT(0 == kDynamic_GrAccessPattern);
59     GR_STATIC_ASSERT(1 == kStatic_GrAccessPattern);
60     GR_STATIC_ASSERT(2 == kStream_GrAccessPattern);
61     GR_STATIC_ASSERT(SK_ARRAY_COUNT(drawUsages) == 1 + kLast_GrAccessPattern);
62     GR_STATIC_ASSERT(SK_ARRAY_COUNT(readUsages) == 1 + kLast_GrAccessPattern);
63 
64     static GrGLenum const* const usageTypes[] = {
65         drawUsages,  // kVertex_GrBufferType,
66         drawUsages,  // kIndex_GrBufferType,
67         drawUsages,  // kTexel_GrBufferType,
68         drawUsages,  // kDrawIndirect_GrBufferType,
69         drawUsages,  // kXferCpuToGpu_GrBufferType,
70         readUsages   // kXferGpuToCpu_GrBufferType,
71     };
72 
73     GR_STATIC_ASSERT(0 == kVertex_GrBufferType);
74     GR_STATIC_ASSERT(1 == kIndex_GrBufferType);
75     GR_STATIC_ASSERT(2 == kTexel_GrBufferType);
76     GR_STATIC_ASSERT(3 == kDrawIndirect_GrBufferType);
77     GR_STATIC_ASSERT(4 == kXferCpuToGpu_GrBufferType);
78     GR_STATIC_ASSERT(5 == kXferGpuToCpu_GrBufferType);
79     GR_STATIC_ASSERT(SK_ARRAY_COUNT(usageTypes) == kGrBufferTypeCount);
80 
81     SkASSERT(bufferType >= 0 && bufferType <= kLast_GrBufferType);
82     SkASSERT(accessPattern >= 0 && accessPattern <= kLast_GrAccessPattern);
83 
84     return usageTypes[bufferType][accessPattern];
85 }
86 
GrGLBuffer(GrGLGpu * gpu,size_t size,GrBufferType intendedType,GrAccessPattern accessPattern,const void * data)87 GrGLBuffer::GrGLBuffer(GrGLGpu* gpu, size_t size, GrBufferType intendedType,
88                        GrAccessPattern accessPattern, const void* data)
89     : INHERITED(gpu, size, intendedType, accessPattern),
90       fIntendedType(intendedType),
91       fBufferID(0),
92       fUsage(gr_to_gl_access_pattern(intendedType, accessPattern)),
93       fGLSizeInBytes(0),
94       fHasAttachedToTexture(false) {
95     GL_CALL(GenBuffers(1, &fBufferID));
96     if (fBufferID) {
97         GrGLenum target = gpu->bindBuffer(fIntendedType, this);
98         CLEAR_ERROR_BEFORE_ALLOC(gpu->glInterface());
99         // make sure driver can allocate memory for this buffer
100         GL_ALLOC_CALL(gpu->glInterface(), BufferData(target,
101                                                      (GrGLsizeiptr) size,
102                                                      data,
103                                                      fUsage));
104         if (CHECK_ALLOC_ERROR(gpu->glInterface()) != GR_GL_NO_ERROR) {
105             GL_CALL(DeleteBuffers(1, &fBufferID));
106             fBufferID = 0;
107         } else {
108             fGLSizeInBytes = size;
109         }
110     }
111     VALIDATE();
112     this->registerWithCache(SkBudgeted::kYes);
113 }
114 
glGpu() const115 inline GrGLGpu* GrGLBuffer::glGpu() const {
116     SkASSERT(!this->wasDestroyed());
117     return static_cast<GrGLGpu*>(this->getGpu());
118 }
119 
glCaps() const120 inline const GrGLCaps& GrGLBuffer::glCaps() const {
121     return this->glGpu()->glCaps();
122 }
123 
onRelease()124 void GrGLBuffer::onRelease() {
125     if (!this->wasDestroyed()) {
126         VALIDATE();
127         // make sure we've not been abandoned or already released
128         if (fBufferID) {
129             GL_CALL(DeleteBuffers(1, &fBufferID));
130             fBufferID = 0;
131             fGLSizeInBytes = 0;
132             this->glGpu()->notifyBufferReleased(this);
133         }
134         fMapPtr = nullptr;
135         VALIDATE();
136     }
137 
138     INHERITED::onRelease();
139 }
140 
onAbandon()141 void GrGLBuffer::onAbandon() {
142     fBufferID = 0;
143     fGLSizeInBytes = 0;
144     fMapPtr = nullptr;
145     VALIDATE();
146     INHERITED::onAbandon();
147 }
148 
onMap()149 void GrGLBuffer::onMap() {
150     if (this->wasDestroyed()) {
151         return;
152     }
153 
154     VALIDATE();
155     SkASSERT(!this->isMapped());
156 
157     // TODO: Make this a function parameter.
158     bool readOnly = (kXferGpuToCpu_GrBufferType == fIntendedType);
159 
160     // Handling dirty context is done in the bindBuffer call
161     switch (this->glCaps().mapBufferType()) {
162         case GrGLCaps::kNone_MapBufferType:
163             break;
164         case GrGLCaps::kMapBuffer_MapBufferType: {
165             GrGLenum target = this->glGpu()->bindBuffer(fIntendedType, this);
166             // Let driver know it can discard the old data
167             if (GR_GL_USE_BUFFER_DATA_NULL_HINT || fGLSizeInBytes != this->sizeInBytes()) {
168                 GL_CALL(BufferData(target, this->sizeInBytes(), nullptr, fUsage));
169             }
170             GL_CALL_RET(fMapPtr, MapBuffer(target, readOnly ? GR_GL_READ_ONLY : GR_GL_WRITE_ONLY));
171             break;
172         }
173         case GrGLCaps::kMapBufferRange_MapBufferType: {
174             GrGLenum target = this->glGpu()->bindBuffer(fIntendedType, this);
175             // Make sure the GL buffer size agrees with fDesc before mapping.
176             if (fGLSizeInBytes != this->sizeInBytes()) {
177                 GL_CALL(BufferData(target, this->sizeInBytes(), nullptr, fUsage));
178             }
179             GrGLbitfield writeAccess = GR_GL_MAP_WRITE_BIT;
180             if (kXferCpuToGpu_GrBufferType != fIntendedType) {
181                 // TODO: Make this a function parameter.
182                 writeAccess |= GR_GL_MAP_INVALIDATE_BUFFER_BIT;
183             }
184             GL_CALL_RET(fMapPtr, MapBufferRange(target, 0, this->sizeInBytes(),
185                                                 readOnly ?  GR_GL_MAP_READ_BIT : writeAccess));
186             break;
187         }
188         case GrGLCaps::kChromium_MapBufferType: {
189             GrGLenum target = this->glGpu()->bindBuffer(fIntendedType, this);
190             // Make sure the GL buffer size agrees with fDesc before mapping.
191             if (fGLSizeInBytes != this->sizeInBytes()) {
192                 GL_CALL(BufferData(target, this->sizeInBytes(), nullptr, fUsage));
193             }
194             GL_CALL_RET(fMapPtr, MapBufferSubData(target, 0, this->sizeInBytes(),
195                                                   readOnly ?  GR_GL_READ_ONLY : GR_GL_WRITE_ONLY));
196             break;
197         }
198     }
199     fGLSizeInBytes = this->sizeInBytes();
200     VALIDATE();
201 }
202 
onUnmap()203 void GrGLBuffer::onUnmap() {
204     if (this->wasDestroyed()) {
205         return;
206     }
207 
208     VALIDATE();
209     SkASSERT(this->isMapped());
210     if (0 == fBufferID) {
211         fMapPtr = nullptr;
212         return;
213     }
214     // bind buffer handles the dirty context
215     switch (this->glCaps().mapBufferType()) {
216         case GrGLCaps::kNone_MapBufferType:
217             SkDEBUGFAIL("Shouldn't get here.");
218             return;
219         case GrGLCaps::kMapBuffer_MapBufferType: // fall through
220         case GrGLCaps::kMapBufferRange_MapBufferType: {
221             GrGLenum target = this->glGpu()->bindBuffer(fIntendedType, this);
222             GL_CALL(UnmapBuffer(target));
223             break;
224         }
225         case GrGLCaps::kChromium_MapBufferType:
226             this->glGpu()->bindBuffer(fIntendedType, this); // TODO: Is this needed?
227             GL_CALL(UnmapBufferSubData(fMapPtr));
228             break;
229     }
230     fMapPtr = nullptr;
231 }
232 
onUpdateData(const void * src,size_t srcSizeInBytes)233 bool GrGLBuffer::onUpdateData(const void* src, size_t srcSizeInBytes) {
234     if (this->wasDestroyed()) {
235         return false;
236     }
237 
238     SkASSERT(!this->isMapped());
239     VALIDATE();
240     if (srcSizeInBytes > this->sizeInBytes()) {
241         return false;
242     }
243     SkASSERT(srcSizeInBytes <= this->sizeInBytes());
244     // bindbuffer handles dirty context
245     GrGLenum target = this->glGpu()->bindBuffer(fIntendedType, this);
246 
247 #if GR_GL_USE_BUFFER_DATA_NULL_HINT
248     if (this->sizeInBytes() == srcSizeInBytes) {
249         GL_CALL(BufferData(target, (GrGLsizeiptr) srcSizeInBytes, src, fUsage));
250     } else {
251         // Before we call glBufferSubData we give the driver a hint using
252         // glBufferData with nullptr. This makes the old buffer contents
253         // inaccessible to future draws. The GPU may still be processing
254         // draws that reference the old contents. With this hint it can
255         // assign a different allocation for the new contents to avoid
256         // flushing the gpu past draws consuming the old contents.
257         // TODO I think we actually want to try calling bufferData here
258         GL_CALL(BufferData(target, this->sizeInBytes(), nullptr, fUsage));
259         GL_CALL(BufferSubData(target, 0, (GrGLsizeiptr) srcSizeInBytes, src));
260     }
261     fGLSizeInBytes = this->sizeInBytes();
262 #else
263     // Note that we're cheating on the size here. Currently no methods
264     // allow a partial update that preserves contents of non-updated
265     // portions of the buffer (map() does a glBufferData(..size, nullptr..))
266     GL_CALL(BufferData(target, srcSizeInBytes, src, fUsage));
267     fGLSizeInBytes = srcSizeInBytes;
268 #endif
269     VALIDATE();
270     return true;
271 }
272 
setMemoryBacking(SkTraceMemoryDump * traceMemoryDump,const SkString & dumpName) const273 void GrGLBuffer::setMemoryBacking(SkTraceMemoryDump* traceMemoryDump,
274                                        const SkString& dumpName) const {
275     SkString buffer_id;
276     buffer_id.appendU32(this->bufferID());
277     traceMemoryDump->setMemoryBacking(dumpName.c_str(), "gl_buffer",
278                                       buffer_id.c_str());
279 }
280 
281 #ifdef SK_DEBUG
282 
validate() const283 void GrGLBuffer::validate() const {
284     SkASSERT(0 != fBufferID || 0 == fGLSizeInBytes);
285     SkASSERT(nullptr == fMapPtr || fGLSizeInBytes <= this->sizeInBytes());
286 }
287 
288 #endif
289