/src/skia/src/gpu/gl/GrGLBuffer.cpp
Line | Count | Source (jump to first uncovered line) |
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 | 0 | #define GL_CALL(X) GR_GL_CALL(this->glGpu()->glInterface(), X) |
17 | 0 | #define GL_CALL_RET(RET, X) GR_GL_CALL_RET(this->glGpu()->glInterface(), RET, X) |
18 | | |
19 | | #define GL_ALLOC_CALL(call) \ |
20 | 0 | [&] { \ |
21 | 0 | if (this->glGpu()->glCaps().skipErrorChecks()) { \ |
22 | 0 | GR_GL_CALL(this->glGpu()->glInterface(), call); \ |
23 | 0 | return static_cast<GrGLenum>(GR_GL_NO_ERROR); \ |
24 | 0 | } else { \ |
25 | 0 | this->glGpu()->clearErrorsAndCheckForOOM(); \ |
26 | 0 | GR_GL_CALL_NOERRCHECK(this->glGpu()->glInterface(), call); \ |
27 | 0 | return this->glGpu()->getErrorAndCheckForOOM(); \ |
28 | 0 | } \ |
29 | 0 | }() Unexecuted instantiation: GrGLBuffer.cpp:GrGLBuffer::GrGLBuffer(GrGLGpu*, unsigned long, GrGpuBufferType, GrAccessPattern, void const*)::$_0::operator()() const Unexecuted instantiation: GrGLBuffer.cpp:GrGLBuffer::onMap()::$_1::operator()() const Unexecuted instantiation: GrGLBuffer.cpp:GrGLBuffer::onMap()::$_2::operator()() const Unexecuted instantiation: GrGLBuffer.cpp:GrGLBuffer::onMap()::$_3::operator()() const Unexecuted instantiation: GrGLBuffer.cpp:GrGLBuffer::onUpdateData(void const*, unsigned long)::$_4::operator()() const Unexecuted instantiation: GrGLBuffer.cpp:GrGLBuffer::onUpdateData(void const*, unsigned long)::$_5::operator()() const Unexecuted instantiation: GrGLBuffer.cpp:GrGLBuffer::onUpdateData(void const*, unsigned long)::$_6::operator()() const Unexecuted instantiation: GrGLBuffer.cpp:GrGLBuffer::GrGLBuffer(GrGLGpu*, unsigned long, GrGpuBufferType, GrAccessPattern, void const*)::$_0::operator()() const Unexecuted instantiation: GrGLBuffer.cpp:GrGLBuffer::onMap()::$_4::operator()() const Unexecuted instantiation: GrGLBuffer.cpp:GrGLBuffer::onMap()::$_5::operator()() const Unexecuted instantiation: GrGLBuffer.cpp:GrGLBuffer::onMap()::$_6::operator()() const Unexecuted instantiation: GrGLBuffer.cpp:GrGLBuffer::onUpdateData(void const*, unsigned long)::$_12::operator()() const Unexecuted instantiation: GrGLBuffer.cpp:GrGLBuffer::onUpdateData(void const*, unsigned long)::$_13::operator()() const Unexecuted instantiation: GrGLBuffer.cpp:GrGLBuffer::onUpdateData(void const*, unsigned long)::$_14::operator()() const |
30 | | |
31 | | #ifdef SK_DEBUG |
32 | 0 | #define VALIDATE() this->validate() |
33 | | #else |
34 | 0 | #define VALIDATE() do {} while(false) |
35 | | #endif |
36 | | |
37 | | sk_sp<GrGLBuffer> GrGLBuffer::Make(GrGLGpu* gpu, size_t size, GrGpuBufferType intendedType, |
38 | 0 | GrAccessPattern accessPattern, const void* data) { |
39 | 0 | if (gpu->glCaps().transferBufferType() == GrGLCaps::TransferBufferType::kNone && |
40 | 0 | (GrGpuBufferType::kXferCpuToGpu == intendedType || |
41 | 0 | GrGpuBufferType::kXferGpuToCpu == intendedType)) { |
42 | 0 | return nullptr; |
43 | 0 | } |
44 | | |
45 | 0 | sk_sp<GrGLBuffer> buffer(new GrGLBuffer(gpu, size, intendedType, accessPattern, data)); |
46 | 0 | if (0 == buffer->bufferID()) { |
47 | 0 | return nullptr; |
48 | 0 | } |
49 | 0 | return buffer; |
50 | 0 | } |
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 | 0 | #define DYNAMIC_DRAW_PARAM GR_GL_STREAM_DRAW |
55 | | |
56 | | inline static GrGLenum gr_to_gl_access_pattern(GrGpuBufferType bufferType, |
57 | | GrAccessPattern accessPattern, |
58 | 0 | const GrGLCaps& caps) { |
59 | 0 | auto drawUsage = [](GrAccessPattern pattern) { |
60 | 0 | switch (pattern) { |
61 | 0 | case kDynamic_GrAccessPattern: |
62 | | // TODO: Do we really want to use STREAM_DRAW here on non-Chromium? |
63 | 0 | return DYNAMIC_DRAW_PARAM; |
64 | 0 | case kStatic_GrAccessPattern: |
65 | 0 | return GR_GL_STATIC_DRAW; |
66 | 0 | case kStream_GrAccessPattern: |
67 | 0 | return GR_GL_STREAM_DRAW; |
68 | 0 | } |
69 | 0 | SkUNREACHABLE; |
70 | 0 | }; |
71 | |
|
72 | 0 | auto readUsage = [](GrAccessPattern pattern) { |
73 | 0 | switch (pattern) { |
74 | 0 | case kDynamic_GrAccessPattern: |
75 | 0 | return GR_GL_DYNAMIC_READ; |
76 | 0 | case kStatic_GrAccessPattern: |
77 | 0 | return GR_GL_STATIC_READ; |
78 | 0 | case kStream_GrAccessPattern: |
79 | 0 | return GR_GL_STREAM_READ; |
80 | 0 | } |
81 | 0 | SkUNREACHABLE; |
82 | 0 | }; |
83 | |
|
84 | 0 | auto usageType = [&drawUsage, &readUsage, &caps](GrGpuBufferType type, |
85 | 0 | GrAccessPattern pattern) { |
86 | | // GL_NV_pixel_buffer_object adds transfer buffers but not the related <usage> values. |
87 | 0 | if (caps.transferBufferType() == GrGLCaps::TransferBufferType::kNV_PBO) { |
88 | 0 | return drawUsage(pattern); |
89 | 0 | } |
90 | 0 | switch (type) { |
91 | 0 | case GrGpuBufferType::kVertex: |
92 | 0 | case GrGpuBufferType::kIndex: |
93 | 0 | case GrGpuBufferType::kDrawIndirect: |
94 | 0 | case GrGpuBufferType::kXferCpuToGpu: |
95 | 0 | case GrGpuBufferType::kUniform: |
96 | 0 | return drawUsage(pattern); |
97 | 0 | case GrGpuBufferType::kXferGpuToCpu: |
98 | 0 | return readUsage(pattern); |
99 | 0 | } |
100 | 0 | SkUNREACHABLE; |
101 | 0 | }; |
102 | |
|
103 | 0 | return usageType(bufferType, accessPattern); |
104 | 0 | } |
105 | | |
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 | 0 | , fHasAttachedToTexture(false) { |
114 | 0 | GL_CALL(GenBuffers(1, &fBufferID)); |
115 | 0 | if (fBufferID) { |
116 | 0 | GrGLenum target = gpu->bindBuffer(fIntendedType, this); |
117 | 0 | GrGLenum error = GL_ALLOC_CALL(BufferData(target, (GrGLsizeiptr)size, data, fUsage)); |
118 | 0 | if (error != GR_GL_NO_ERROR) { |
119 | 0 | GL_CALL(DeleteBuffers(1, &fBufferID)); |
120 | 0 | fBufferID = 0; |
121 | 0 | } else { |
122 | 0 | fGLSizeInBytes = size; |
123 | 0 | } |
124 | 0 | } |
125 | 0 | VALIDATE(); |
126 | 0 | this->registerWithCache(SkBudgeted::kYes); |
127 | 0 | if (!fBufferID) { |
128 | 0 | this->resourcePriv().removeScratchKey(); |
129 | 0 | } |
130 | 0 | } Unexecuted instantiation: GrGLBuffer::GrGLBuffer(GrGLGpu*, unsigned long, GrGpuBufferType, GrAccessPattern, void const*) Unexecuted instantiation: GrGLBuffer::GrGLBuffer(GrGLGpu*, unsigned long, GrGpuBufferType, GrAccessPattern, void const*) |
131 | | |
132 | 0 | inline GrGLGpu* GrGLBuffer::glGpu() const { |
133 | 0 | SkASSERT(!this->wasDestroyed()); |
134 | 0 | return static_cast<GrGLGpu*>(this->getGpu()); |
135 | 0 | } Unexecuted instantiation: GrGLBuffer::glGpu() const Unexecuted instantiation: GrGLBuffer::glGpu() const |
136 | | |
137 | 0 | inline const GrGLCaps& GrGLBuffer::glCaps() const { |
138 | 0 | return this->glGpu()->glCaps(); |
139 | 0 | } |
140 | | |
141 | 0 | void GrGLBuffer::onRelease() { |
142 | 0 | TRACE_EVENT0("skia.gpu", TRACE_FUNC); |
143 | |
|
144 | 0 | if (!this->wasDestroyed()) { |
145 | 0 | VALIDATE(); |
146 | | // make sure we've not been abandoned or already released |
147 | 0 | if (fBufferID) { |
148 | 0 | GL_CALL(DeleteBuffers(1, &fBufferID)); |
149 | 0 | fBufferID = 0; |
150 | 0 | fGLSizeInBytes = 0; |
151 | 0 | } |
152 | 0 | fMapPtr = nullptr; |
153 | 0 | VALIDATE(); |
154 | 0 | } |
155 | |
|
156 | 0 | INHERITED::onRelease(); |
157 | 0 | } Unexecuted instantiation: GrGLBuffer::onRelease() Unexecuted instantiation: GrGLBuffer::onRelease() |
158 | | |
159 | 0 | void GrGLBuffer::onAbandon() { |
160 | 0 | fBufferID = 0; |
161 | 0 | fGLSizeInBytes = 0; |
162 | 0 | fMapPtr = nullptr; |
163 | 0 | VALIDATE(); |
164 | 0 | INHERITED::onAbandon(); |
165 | 0 | } |
166 | | |
167 | 0 | void GrGLBuffer::onMap() { |
168 | 0 | SkASSERT(fBufferID); |
169 | 0 | SkASSERT(!this->wasDestroyed()); |
170 | 0 | VALIDATE(); |
171 | 0 | SkASSERT(!this->isMapped()); |
172 | | |
173 | | // TODO: Make this a function parameter. |
174 | 0 | bool readOnly = (GrGpuBufferType::kXferGpuToCpu == fIntendedType); |
175 | | |
176 | | // Handling dirty context is done in the bindBuffer call |
177 | 0 | switch (this->glCaps().mapBufferType()) { |
178 | 0 | case GrGLCaps::kNone_MapBufferType: |
179 | 0 | return; |
180 | 0 | case GrGLCaps::kMapBuffer_MapBufferType: { |
181 | 0 | GrGLenum target = this->glGpu()->bindBuffer(fIntendedType, this); |
182 | 0 | if (!readOnly) { |
183 | | // Let driver know it can discard the old data |
184 | 0 | if (this->glCaps().useBufferDataNullHint() || fGLSizeInBytes != this->size()) { |
185 | 0 | GrGLenum error = |
186 | 0 | GL_ALLOC_CALL(BufferData(target, this->size(), nullptr, fUsage)); |
187 | 0 | if (error != GR_GL_NO_ERROR) { |
188 | 0 | return; |
189 | 0 | } |
190 | 0 | } |
191 | 0 | } |
192 | 0 | GL_CALL_RET(fMapPtr, MapBuffer(target, readOnly ? GR_GL_READ_ONLY : GR_GL_WRITE_ONLY)); |
193 | 0 | break; |
194 | 0 | } |
195 | 0 | case GrGLCaps::kMapBufferRange_MapBufferType: { |
196 | 0 | GrGLenum target = this->glGpu()->bindBuffer(fIntendedType, this); |
197 | | // Make sure the GL buffer size agrees with fDesc before mapping. |
198 | 0 | if (fGLSizeInBytes != this->size()) { |
199 | 0 | GrGLenum error = GL_ALLOC_CALL(BufferData(target, this->size(), nullptr, fUsage)); |
200 | 0 | if (error != GR_GL_NO_ERROR) { |
201 | 0 | return; |
202 | 0 | } |
203 | 0 | } |
204 | 0 | GrGLbitfield access; |
205 | 0 | if (readOnly) { |
206 | 0 | access = GR_GL_MAP_READ_BIT; |
207 | 0 | } else { |
208 | 0 | access = GR_GL_MAP_WRITE_BIT; |
209 | 0 | if (GrGpuBufferType::kXferCpuToGpu != fIntendedType) { |
210 | | // TODO: Make this a function parameter. |
211 | 0 | access |= GR_GL_MAP_INVALIDATE_BUFFER_BIT; |
212 | 0 | } |
213 | 0 | } |
214 | 0 | GL_CALL_RET(fMapPtr, MapBufferRange(target, 0, this->size(), access)); |
215 | 0 | break; |
216 | 0 | } |
217 | 0 | case GrGLCaps::kChromium_MapBufferType: { |
218 | 0 | GrGLenum target = this->glGpu()->bindBuffer(fIntendedType, this); |
219 | | // Make sure the GL buffer size agrees with fDesc before mapping. |
220 | 0 | if (fGLSizeInBytes != this->size()) { |
221 | 0 | GrGLenum error = GL_ALLOC_CALL(BufferData(target, this->size(), nullptr, fUsage)); |
222 | 0 | if (error != GR_GL_NO_ERROR) { |
223 | 0 | return; |
224 | 0 | } |
225 | 0 | } |
226 | 0 | GL_CALL_RET(fMapPtr, MapBufferSubData(target, 0, this->size(), |
227 | 0 | readOnly ? GR_GL_READ_ONLY : GR_GL_WRITE_ONLY)); |
228 | 0 | break; |
229 | 0 | } |
230 | 0 | } |
231 | 0 | fGLSizeInBytes = this->size(); |
232 | 0 | VALIDATE(); |
233 | 0 | } Unexecuted instantiation: GrGLBuffer::onMap() Unexecuted instantiation: GrGLBuffer::onMap() |
234 | | |
235 | 0 | void GrGLBuffer::onUnmap() { |
236 | 0 | SkASSERT(fBufferID); |
237 | 0 | VALIDATE(); |
238 | 0 | SkASSERT(this->isMapped()); |
239 | 0 | if (0 == fBufferID) { |
240 | 0 | fMapPtr = nullptr; |
241 | 0 | return; |
242 | 0 | } |
243 | | // bind buffer handles the dirty context |
244 | 0 | switch (this->glCaps().mapBufferType()) { |
245 | 0 | case GrGLCaps::kNone_MapBufferType: |
246 | 0 | SkDEBUGFAIL("Shouldn't get here."); |
247 | 0 | return; |
248 | 0 | case GrGLCaps::kMapBuffer_MapBufferType: // fall through |
249 | 0 | case GrGLCaps::kMapBufferRange_MapBufferType: { |
250 | 0 | GrGLenum target = this->glGpu()->bindBuffer(fIntendedType, this); |
251 | 0 | GL_CALL(UnmapBuffer(target)); |
252 | 0 | break; |
253 | 0 | } |
254 | 0 | case GrGLCaps::kChromium_MapBufferType: |
255 | 0 | this->glGpu()->bindBuffer(fIntendedType, this); // TODO: Is this needed? |
256 | 0 | GL_CALL(UnmapBufferSubData(fMapPtr)); |
257 | 0 | break; |
258 | 0 | } |
259 | 0 | fMapPtr = nullptr; |
260 | 0 | } Unexecuted instantiation: GrGLBuffer::onUnmap() Unexecuted instantiation: GrGLBuffer::onUnmap() |
261 | | |
262 | 0 | bool GrGLBuffer::onUpdateData(const void* src, size_t srcSizeInBytes) { |
263 | 0 | SkASSERT(fBufferID); |
264 | 0 | if (this->wasDestroyed()) { |
265 | 0 | return false; |
266 | 0 | } |
267 | | |
268 | 0 | SkASSERT(!this->isMapped()); |
269 | 0 | VALIDATE(); |
270 | 0 | if (srcSizeInBytes > this->size()) { |
271 | 0 | return false; |
272 | 0 | } |
273 | 0 | SkASSERT(srcSizeInBytes <= this->size()); |
274 | | // bindbuffer handles dirty context |
275 | 0 | GrGLenum target = this->glGpu()->bindBuffer(fIntendedType, this); |
276 | |
|
277 | 0 | if (this->glCaps().useBufferDataNullHint()) { |
278 | 0 | if (this->size() == srcSizeInBytes) { |
279 | 0 | GrGLenum error = |
280 | 0 | GL_ALLOC_CALL(BufferData(target, (GrGLsizeiptr)srcSizeInBytes, src, fUsage)); |
281 | 0 | if (error != GR_GL_NO_ERROR) { |
282 | 0 | return false; |
283 | 0 | } |
284 | 0 | } 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 | 0 | GrGLenum error = |
293 | 0 | GL_ALLOC_CALL(BufferData(target, (GrGLsizeiptr)this->size(), nullptr, fUsage)); |
294 | 0 | if (error != GR_GL_NO_ERROR) { |
295 | 0 | return false; |
296 | 0 | } |
297 | 0 | GL_CALL(BufferSubData(target, 0, (GrGLsizeiptr) srcSizeInBytes, src)); |
298 | 0 | } |
299 | 0 | fGLSizeInBytes = this->size(); |
300 | 0 | } 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 | 0 | GrGLenum error = |
305 | 0 | GL_ALLOC_CALL(BufferData(target, (GrGLsizeiptr)srcSizeInBytes, src, fUsage)); |
306 | 0 | if (error != GR_GL_NO_ERROR) { |
307 | 0 | return false; |
308 | 0 | } |
309 | 0 | fGLSizeInBytes = srcSizeInBytes; |
310 | 0 | } |
311 | 0 | VALIDATE(); |
312 | 0 | return true; |
313 | 0 | } Unexecuted instantiation: GrGLBuffer::onUpdateData(void const*, unsigned long) Unexecuted instantiation: GrGLBuffer::onUpdateData(void const*, unsigned long) |
314 | | |
315 | | void GrGLBuffer::setMemoryBacking(SkTraceMemoryDump* traceMemoryDump, |
316 | 0 | const SkString& dumpName) const { |
317 | 0 | SkString buffer_id; |
318 | 0 | buffer_id.appendU32(this->bufferID()); |
319 | 0 | traceMemoryDump->setMemoryBacking(dumpName.c_str(), "gl_buffer", |
320 | 0 | buffer_id.c_str()); |
321 | 0 | } |
322 | | |
323 | | #ifdef SK_DEBUG |
324 | | |
325 | 0 | void GrGLBuffer::validate() const { |
326 | 0 | SkASSERT(0 != fBufferID || 0 == fGLSizeInBytes); |
327 | 0 | SkASSERT(nullptr == fMapPtr || fGLSizeInBytes <= this->size()); |
328 | 0 | } |
329 | | |
330 | | #endif |