• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "src/gpu/gl/GrGLBuffer.h"
9 
10 #include "include/core/SkTraceMemoryDump.h"
11 #include "src/core/SkTraceEvent.h"
12 #include "src/gpu/GrGpuResourcePriv.h"
13 #include "src/gpu/gl/GrGLCaps.h"
14 #include "src/gpu/gl/GrGLGpu.h"
15 
16 #define GL_CALL(X) GR_GL_CALL(this->glGpu()->glInterface(), X)
17 #define GL_CALL_RET(RET, X) GR_GL_CALL_RET(this->glGpu()->glInterface(), RET, X)
18 
19 #define GL_ALLOC_CALL(call)                                            \
20     [&] {                                                              \
21         if (this->glGpu()->glCaps().skipErrorChecks()) {               \
22             GR_GL_CALL(this->glGpu()->glInterface(), call);            \
23             return static_cast<GrGLenum>(GR_GL_NO_ERROR);              \
24         } else {                                                       \
25             this->glGpu()->clearErrorsAndCheckForOOM();                \
26             GR_GL_CALL_NOERRCHECK(this->glGpu()->glInterface(), call); \
27             return this->glGpu()->getErrorAndCheckForOOM();            \
28         }                                                              \
29     }()
30 
31 #ifdef SK_DEBUG
32 #define VALIDATE() this->validate()
33 #else
34 #define VALIDATE() do {} while(false)
35 #endif
36 
Make(GrGLGpu * gpu,size_t size,GrGpuBufferType intendedType,GrAccessPattern accessPattern,const void * data)37 sk_sp<GrGLBuffer> GrGLBuffer::Make(GrGLGpu* gpu, size_t size, GrGpuBufferType intendedType,
38                                    GrAccessPattern accessPattern, const void* data) {
39     if (gpu->glCaps().transferBufferType() == GrGLCaps::TransferBufferType::kNone &&
40         (GrGpuBufferType::kXferCpuToGpu == intendedType ||
41          GrGpuBufferType::kXferGpuToCpu == intendedType)) {
42         return nullptr;
43     }
44 
45     sk_sp<GrGLBuffer> buffer(new GrGLBuffer(gpu, size, intendedType, accessPattern, data));
46     if (0 == buffer->bufferID()) {
47         return nullptr;
48     }
49     return buffer;
50 }
51 
52 // GL_STREAM_DRAW triggers an optimization in Chromium's GPU process where a client's vertex buffer
53 // objects are implemented as client-side-arrays on tile-deferred architectures.
54 #define DYNAMIC_DRAW_PARAM GR_GL_STREAM_DRAW
55 
gr_to_gl_access_pattern(GrGpuBufferType bufferType,GrAccessPattern accessPattern,const GrGLCaps & caps)56 inline static GrGLenum gr_to_gl_access_pattern(GrGpuBufferType bufferType,
57                                                GrAccessPattern accessPattern,
58                                                const GrGLCaps& caps) {
59     auto drawUsage = [](GrAccessPattern pattern) {
60         switch (pattern) {
61             case kDynamic_GrAccessPattern:
62                 // TODO: Do we really want to use STREAM_DRAW here on non-Chromium?
63                 return DYNAMIC_DRAW_PARAM;
64             case kStatic_GrAccessPattern:
65                 return GR_GL_STATIC_DRAW;
66             case kStream_GrAccessPattern:
67                 return GR_GL_STREAM_DRAW;
68         }
69         SkUNREACHABLE;
70     };
71 
72     auto readUsage = [](GrAccessPattern pattern) {
73         switch (pattern) {
74             case kDynamic_GrAccessPattern:
75                 return GR_GL_DYNAMIC_READ;
76             case kStatic_GrAccessPattern:
77                 return GR_GL_STATIC_READ;
78             case kStream_GrAccessPattern:
79                 return GR_GL_STREAM_READ;
80         }
81         SkUNREACHABLE;
82     };
83 
84     auto usageType = [&drawUsage, &readUsage, &caps](GrGpuBufferType type,
85                                                      GrAccessPattern pattern) {
86         // GL_NV_pixel_buffer_object adds transfer buffers but not the related <usage> values.
87         if (caps.transferBufferType() == GrGLCaps::TransferBufferType::kNV_PBO) {
88             return drawUsage(pattern);
89         }
90         switch (type) {
91             case GrGpuBufferType::kVertex:
92             case GrGpuBufferType::kIndex:
93             case GrGpuBufferType::kDrawIndirect:
94             case GrGpuBufferType::kXferCpuToGpu:
95             case GrGpuBufferType::kUniform:
96                 return drawUsage(pattern);
97             case GrGpuBufferType::kXferGpuToCpu:
98                 return readUsage(pattern);
99         }
100         SkUNREACHABLE;
101     };
102 
103     return usageType(bufferType, accessPattern);
104 }
105 
GrGLBuffer(GrGLGpu * gpu,size_t size,GrGpuBufferType intendedType,GrAccessPattern accessPattern,const void * data)106 GrGLBuffer::GrGLBuffer(GrGLGpu* gpu, size_t size, GrGpuBufferType intendedType,
107                        GrAccessPattern accessPattern, const void* data)
108         : INHERITED(gpu, size, intendedType, accessPattern)
109         , fIntendedType(intendedType)
110         , fBufferID(0)
111         , fUsage(gr_to_gl_access_pattern(intendedType, accessPattern, gpu->glCaps()))
112         , fGLSizeInBytes(0)
113         , fHasAttachedToTexture(false) {
114     GL_CALL(GenBuffers(1, &fBufferID));
115     if (fBufferID) {
116         GrGLenum target = gpu->bindBuffer(fIntendedType, this);
117         GrGLenum error = GL_ALLOC_CALL(BufferData(target, (GrGLsizeiptr)size, data, fUsage));
118         if (error != GR_GL_NO_ERROR) {
119             GL_CALL(DeleteBuffers(1, &fBufferID));
120             fBufferID = 0;
121         } else {
122             fGLSizeInBytes = size;
123         }
124     }
125     VALIDATE();
126     this->registerWithCache(SkBudgeted::kYes);
127     if (!fBufferID) {
128         this->resourcePriv().removeScratchKey();
129     }
130 }
131 
glGpu() const132 inline GrGLGpu* GrGLBuffer::glGpu() const {
133     SkASSERT(!this->wasDestroyed());
134     return static_cast<GrGLGpu*>(this->getGpu());
135 }
136 
glCaps() const137 inline const GrGLCaps& GrGLBuffer::glCaps() const {
138     return this->glGpu()->glCaps();
139 }
140 
onRelease()141 void GrGLBuffer::onRelease() {
142     TRACE_EVENT0("skia.gpu", TRACE_FUNC);
143 
144     if (!this->wasDestroyed()) {
145         VALIDATE();
146         // make sure we've not been abandoned or already released
147         if (fBufferID) {
148             GL_CALL(DeleteBuffers(1, &fBufferID));
149             fBufferID = 0;
150             fGLSizeInBytes = 0;
151         }
152         fMapPtr = nullptr;
153         VALIDATE();
154     }
155 
156     INHERITED::onRelease();
157 }
158 
onAbandon()159 void GrGLBuffer::onAbandon() {
160     fBufferID = 0;
161     fGLSizeInBytes = 0;
162     fMapPtr = nullptr;
163     VALIDATE();
164     INHERITED::onAbandon();
165 }
166 
onMap()167 void GrGLBuffer::onMap() {
168     SkASSERT(fBufferID);
169     SkASSERT(!this->wasDestroyed());
170     VALIDATE();
171     SkASSERT(!this->isMapped());
172 
173     // TODO: Make this a function parameter.
174     bool readOnly = (GrGpuBufferType::kXferGpuToCpu == fIntendedType);
175 
176     // Handling dirty context is done in the bindBuffer call
177     switch (this->glCaps().mapBufferType()) {
178         case GrGLCaps::kNone_MapBufferType:
179             return;
180         case GrGLCaps::kMapBuffer_MapBufferType: {
181             GrGLenum target = this->glGpu()->bindBuffer(fIntendedType, this);
182             if (!readOnly) {
183                 // Let driver know it can discard the old data
184                 if (this->glCaps().useBufferDataNullHint() || fGLSizeInBytes != this->size()) {
185                     GrGLenum error =
186                             GL_ALLOC_CALL(BufferData(target, this->size(), nullptr, fUsage));
187                     if (error != GR_GL_NO_ERROR) {
188                         return;
189                     }
190                 }
191             }
192             GL_CALL_RET(fMapPtr, MapBuffer(target, readOnly ? GR_GL_READ_ONLY : GR_GL_WRITE_ONLY));
193             break;
194         }
195         case GrGLCaps::kMapBufferRange_MapBufferType: {
196             GrGLenum target = this->glGpu()->bindBuffer(fIntendedType, this);
197             // Make sure the GL buffer size agrees with fDesc before mapping.
198             if (fGLSizeInBytes != this->size()) {
199                 GrGLenum error = GL_ALLOC_CALL(BufferData(target, this->size(), nullptr, fUsage));
200                 if (error != GR_GL_NO_ERROR) {
201                     return;
202                 }
203             }
204             GrGLbitfield access;
205             if (readOnly) {
206                 access = GR_GL_MAP_READ_BIT;
207             } else {
208                 access = GR_GL_MAP_WRITE_BIT;
209                 if (GrGpuBufferType::kXferCpuToGpu != fIntendedType) {
210                     // TODO: Make this a function parameter.
211                     access |= GR_GL_MAP_INVALIDATE_BUFFER_BIT;
212                 }
213             }
214             GL_CALL_RET(fMapPtr, MapBufferRange(target, 0, this->size(), access));
215             break;
216         }
217         case GrGLCaps::kChromium_MapBufferType: {
218             GrGLenum target = this->glGpu()->bindBuffer(fIntendedType, this);
219             // Make sure the GL buffer size agrees with fDesc before mapping.
220             if (fGLSizeInBytes != this->size()) {
221                 GrGLenum error = GL_ALLOC_CALL(BufferData(target, this->size(), nullptr, fUsage));
222                 if (error != GR_GL_NO_ERROR) {
223                     return;
224                 }
225             }
226             GL_CALL_RET(fMapPtr, MapBufferSubData(target, 0, this->size(),
227                                                   readOnly ? GR_GL_READ_ONLY : GR_GL_WRITE_ONLY));
228             break;
229         }
230     }
231     fGLSizeInBytes = this->size();
232     VALIDATE();
233 }
234 
onUnmap()235 void GrGLBuffer::onUnmap() {
236     SkASSERT(fBufferID);
237     VALIDATE();
238     SkASSERT(this->isMapped());
239     if (0 == fBufferID) {
240         fMapPtr = nullptr;
241         return;
242     }
243     // bind buffer handles the dirty context
244     switch (this->glCaps().mapBufferType()) {
245         case GrGLCaps::kNone_MapBufferType:
246             SkDEBUGFAIL("Shouldn't get here.");
247             return;
248         case GrGLCaps::kMapBuffer_MapBufferType: // fall through
249         case GrGLCaps::kMapBufferRange_MapBufferType: {
250             GrGLenum target = this->glGpu()->bindBuffer(fIntendedType, this);
251             GL_CALL(UnmapBuffer(target));
252             break;
253         }
254         case GrGLCaps::kChromium_MapBufferType:
255             this->glGpu()->bindBuffer(fIntendedType, this); // TODO: Is this needed?
256             GL_CALL(UnmapBufferSubData(fMapPtr));
257             break;
258     }
259     fMapPtr = nullptr;
260 }
261 
onUpdateData(const void * src,size_t srcSizeInBytes)262 bool GrGLBuffer::onUpdateData(const void* src, size_t srcSizeInBytes) {
263     SkASSERT(fBufferID);
264     if (this->wasDestroyed()) {
265         return false;
266     }
267 
268     SkASSERT(!this->isMapped());
269     VALIDATE();
270     if (srcSizeInBytes > this->size()) {
271         return false;
272     }
273     SkASSERT(srcSizeInBytes <= this->size());
274     // bindbuffer handles dirty context
275     GrGLenum target = this->glGpu()->bindBuffer(fIntendedType, this);
276 
277     if (this->glCaps().useBufferDataNullHint()) {
278         if (this->size() == srcSizeInBytes) {
279             GrGLenum error =
280                     GL_ALLOC_CALL(BufferData(target, (GrGLsizeiptr)srcSizeInBytes, src, fUsage));
281             if (error != GR_GL_NO_ERROR) {
282                 return false;
283             }
284         } else {
285             // Before we call glBufferSubData we give the driver a hint using
286             // glBufferData with nullptr. This makes the old buffer contents
287             // inaccessible to future draws. The GPU may still be processing
288             // draws that reference the old contents. With this hint it can
289             // assign a different allocation for the new contents to avoid
290             // flushing the gpu past draws consuming the old contents.
291             // TODO I think we actually want to try calling bufferData here
292             GrGLenum error =
293                     GL_ALLOC_CALL(BufferData(target, (GrGLsizeiptr)this->size(), nullptr, fUsage));
294             if (error != GR_GL_NO_ERROR) {
295                 return false;
296             }
297             GL_CALL(BufferSubData(target, 0, (GrGLsizeiptr) srcSizeInBytes, src));
298         }
299         fGLSizeInBytes = this->size();
300     } else {
301         // Note that we're cheating on the size here. Currently no methods
302         // allow a partial update that preserves contents of non-updated
303         // portions of the buffer (map() does a glBufferData(..size, nullptr..))
304         GrGLenum error =
305                 GL_ALLOC_CALL(BufferData(target, (GrGLsizeiptr)srcSizeInBytes, src, fUsage));
306         if (error != GR_GL_NO_ERROR) {
307             return false;
308         }
309         fGLSizeInBytes = srcSizeInBytes;
310     }
311     VALIDATE();
312     return true;
313 }
314 
setMemoryBacking(SkTraceMemoryDump * traceMemoryDump,const SkString & dumpName) const315 void GrGLBuffer::setMemoryBacking(SkTraceMemoryDump* traceMemoryDump,
316                                        const SkString& dumpName) const {
317     SkString buffer_id;
318     buffer_id.appendU32(this->bufferID());
319     traceMemoryDump->setMemoryBacking(dumpName.c_str(), "gl_buffer",
320                                       buffer_id.c_str());
321 }
322 
323 #ifdef SK_DEBUG
324 
validate() const325 void GrGLBuffer::validate() const {
326     SkASSERT(0 != fBufferID || 0 == fGLSizeInBytes);
327     SkASSERT(nullptr == fMapPtr || fGLSizeInBytes <= this->size());
328 }
329 
330 #endif
331