1 //
2 // Copyright 2002 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6
7 // Buffer.cpp: Implements the gl::Buffer class, representing storage of vertex and/or
8 // index data. Implements GL buffer objects and related functionality.
9 // [OpenGL ES 2.0.24] section 2.9 page 21.
10
11 #include "libANGLE/Buffer.h"
12
13 #include "libANGLE/Context.h"
14 #include "libANGLE/renderer/BufferImpl.h"
15 #include "libANGLE/renderer/GLImplFactory.h"
16
17 namespace gl
18 {
19 namespace
20 {
21 constexpr angle::SubjectIndex kImplementationSubjectIndex = 0;
22 constexpr size_t kInvalidContentsObserverIndex = std::numeric_limits<size_t>::max();
23 } // anonymous namespace
24
BufferState()25 BufferState::BufferState()
26 : mLabel(),
27 mUsage(BufferUsage::StaticDraw),
28 mSize(0),
29 mAccessFlags(0),
30 mAccess(GL_WRITE_ONLY_OES),
31 mMapped(GL_FALSE),
32 mMapPointer(nullptr),
33 mMapOffset(0),
34 mMapLength(0),
35 mBindingCount(0),
36 mTransformFeedbackIndexedBindingCount(0),
37 mTransformFeedbackGenericBindingCount(0),
38 mImmutable(GL_FALSE),
39 mStorageExtUsageFlags(0),
40 mExternal(GL_FALSE),
41 mWebGLType(WebGLBufferType::Undefined)
42 {}
43
~BufferState()44 BufferState::~BufferState() {}
45
Buffer(rx::GLImplFactory * factory,BufferID id)46 Buffer::Buffer(rx::GLImplFactory *factory, BufferID id)
47 : RefCountObject(factory->generateSerial(), id),
48 mImpl(factory->createBuffer(mState)),
49 mImplObserver(this, kImplementationSubjectIndex)
50 {
51 mImplObserver.bind(mImpl);
52 }
53
~Buffer()54 Buffer::~Buffer()
55 {
56 SafeDelete(mImpl);
57 }
58
onDestroy(const Context * context)59 void Buffer::onDestroy(const Context *context)
60 {
61 mContentsObservers.clear();
62
63 // In tests, mImpl might be null.
64 if (mImpl)
65 mImpl->destroy(context);
66 }
67
onBind(const Context * context,BufferBinding target)68 void Buffer::onBind(const Context *context, BufferBinding target)
69 {
70 // Note: this function is called from glBindBuffer, which does not hold the share group lock.
71 // However, it only affects webgl contexts, where browsers already guarantees thread safety.
72 if (!context->isWebGL())
73 {
74 return;
75 }
76
77 if (mState.mWebGLType == WebGLBufferType::Undefined)
78 {
79 if (target == BufferBinding::ElementArray)
80 {
81 mState.mWebGLType = WebGLBufferType::ElementArray;
82 }
83 else
84 {
85 mState.mWebGLType = WebGLBufferType::OtherData;
86 }
87 }
88 }
89
setLabel(const Context * context,const std::string & label)90 angle::Result Buffer::setLabel(const Context *context, const std::string &label)
91 {
92 mState.mLabel = label;
93 if (mImpl)
94 {
95 return mImpl->onLabelUpdate(context);
96 }
97 return angle::Result::Continue;
98 }
99
getLabel() const100 const std::string &Buffer::getLabel() const
101 {
102 return mState.mLabel;
103 }
104
bufferStorageExternal(Context * context,BufferBinding target,GLsizeiptr size,GLeglClientBufferEXT clientBuffer,GLbitfield flags)105 angle::Result Buffer::bufferStorageExternal(Context *context,
106 BufferBinding target,
107 GLsizeiptr size,
108 GLeglClientBufferEXT clientBuffer,
109 GLbitfield flags)
110 {
111 return bufferExternalDataImpl(context, target, clientBuffer, size, flags);
112 }
113
bufferStorage(Context * context,BufferBinding target,GLsizeiptr size,const void * data,GLbitfield flags)114 angle::Result Buffer::bufferStorage(Context *context,
115 BufferBinding target,
116 GLsizeiptr size,
117 const void *data,
118 GLbitfield flags)
119 {
120 return bufferDataImpl(context, target, data, size, BufferUsage::InvalidEnum, flags);
121 }
122
bufferData(Context * context,BufferBinding target,const void * data,GLsizeiptr size,BufferUsage usage)123 angle::Result Buffer::bufferData(Context *context,
124 BufferBinding target,
125 const void *data,
126 GLsizeiptr size,
127 BufferUsage usage)
128 {
129 GLbitfield flags = (GL_MAP_READ_BIT | GL_MAP_WRITE_BIT | GL_DYNAMIC_STORAGE_BIT_EXT);
130 return bufferDataImpl(context, target, data, size, usage, flags);
131 }
132
bufferDataImpl(Context * context,BufferBinding target,const void * data,GLsizeiptr size,BufferUsage usage,GLbitfield flags)133 angle::Result Buffer::bufferDataImpl(Context *context,
134 BufferBinding target,
135 const void *data,
136 GLsizeiptr size,
137 BufferUsage usage,
138 GLbitfield flags)
139 {
140 const void *dataForImpl = data;
141
142 if (mState.isMapped())
143 {
144 // Per the OpenGL ES 3.0 spec, buffers are implicity unmapped when a call to
145 // BufferData happens on a mapped buffer:
146 //
147 // If any portion of the buffer object is mapped in the current context or any context
148 // current to another thread, it is as though UnmapBuffer (see section 2.10.3) is
149 // executed in each such context prior to deleting the existing data store.
150 //
151 GLboolean dontCare = GL_FALSE;
152 ANGLE_TRY(unmap(context, &dontCare));
153 }
154
155 // If we are using robust resource init, make sure the buffer starts cleared.
156 // Note: the Context is checked for nullptr because of some testing code.
157 // TODO(jmadill): Investigate lazier clearing.
158 if (context && context->isRobustResourceInitEnabled() && !data && size > 0)
159 {
160 angle::MemoryBuffer *scratchBuffer = nullptr;
161 ANGLE_CHECK_GL_ALLOC(
162 context, context->getZeroFilledBuffer(static_cast<size_t>(size), &scratchBuffer));
163 dataForImpl = scratchBuffer->data();
164 }
165
166 if (mImpl->setDataWithUsageFlags(context, target, nullptr, dataForImpl, size, usage, flags) ==
167 angle::Result::Stop)
168 {
169 // If setData fails, the buffer contents are undefined. Set a zero size to indicate that.
170 mIndexRangeCache.clear();
171 mState.mSize = 0;
172
173 // Notify when storage changes.
174 onStateChange(angle::SubjectMessage::SubjectChanged);
175
176 return angle::Result::Stop;
177 }
178
179 bool wholeBuffer = size == mState.mSize;
180
181 mIndexRangeCache.clear();
182 mState.mUsage = usage;
183 mState.mSize = size;
184 mState.mImmutable = (usage == BufferUsage::InvalidEnum);
185 mState.mStorageExtUsageFlags = flags;
186
187 // Notify when storage changes.
188 if (wholeBuffer)
189 {
190 onContentsChange();
191 }
192 else
193 {
194 onStateChange(angle::SubjectMessage::SubjectChanged);
195 }
196
197 return angle::Result::Continue;
198 }
199
bufferExternalDataImpl(Context * context,BufferBinding target,GLeglClientBufferEXT clientBuffer,GLsizeiptr size,GLbitfield flags)200 angle::Result Buffer::bufferExternalDataImpl(Context *context,
201 BufferBinding target,
202 GLeglClientBufferEXT clientBuffer,
203 GLsizeiptr size,
204 GLbitfield flags)
205 {
206 if (mState.isMapped())
207 {
208 // Per the OpenGL ES 3.0 spec, buffers are implicitly unmapped when a call to
209 // BufferData happens on a mapped buffer:
210 //
211 // If any portion of the buffer object is mapped in the current context or any context
212 // current to another thread, it is as though UnmapBuffer (see section 2.10.3) is
213 // executed in each such context prior to deleting the existing data store.
214 //
215 GLboolean dontCare = GL_FALSE;
216 ANGLE_TRY(unmap(context, &dontCare));
217 }
218
219 if (mImpl->setDataWithUsageFlags(context, target, clientBuffer, nullptr, size,
220 BufferUsage::InvalidEnum, flags) == angle::Result::Stop)
221 {
222 // If setData fails, the buffer contents are undefined. Set a zero size to indicate that.
223 mIndexRangeCache.clear();
224 mState.mSize = 0;
225
226 // Notify when storage changes.
227 onStateChange(angle::SubjectMessage::SubjectChanged);
228
229 return angle::Result::Stop;
230 }
231
232 mIndexRangeCache.clear();
233 mState.mUsage = BufferUsage::InvalidEnum;
234 mState.mSize = size;
235 mState.mImmutable = GL_TRUE;
236 mState.mStorageExtUsageFlags = flags;
237 mState.mExternal = GL_TRUE;
238
239 // Notify when storage changes.
240 onStateChange(angle::SubjectMessage::SubjectChanged);
241
242 return angle::Result::Continue;
243 }
244
bufferSubData(const Context * context,BufferBinding target,const void * data,GLsizeiptr size,GLintptr offset)245 angle::Result Buffer::bufferSubData(const Context *context,
246 BufferBinding target,
247 const void *data,
248 GLsizeiptr size,
249 GLintptr offset)
250 {
251 ANGLE_TRY(mImpl->setSubData(context, target, data, size, offset));
252
253 mIndexRangeCache.invalidateRange(static_cast<unsigned int>(offset),
254 static_cast<unsigned int>(size));
255
256 // Notify when data changes.
257 onContentsChange();
258
259 return angle::Result::Continue;
260 }
261
copyBufferSubData(const Context * context,Buffer * source,GLintptr sourceOffset,GLintptr destOffset,GLsizeiptr size)262 angle::Result Buffer::copyBufferSubData(const Context *context,
263 Buffer *source,
264 GLintptr sourceOffset,
265 GLintptr destOffset,
266 GLsizeiptr size)
267 {
268 ANGLE_TRY(
269 mImpl->copySubData(context, source->getImplementation(), sourceOffset, destOffset, size));
270
271 mIndexRangeCache.invalidateRange(static_cast<unsigned int>(destOffset),
272 static_cast<unsigned int>(size));
273
274 // Notify when data changes.
275 onContentsChange();
276
277 return angle::Result::Continue;
278 }
279
map(const Context * context,GLenum access)280 angle::Result Buffer::map(const Context *context, GLenum access)
281 {
282 ASSERT(!mState.mMapped);
283
284 mState.mMapPointer = nullptr;
285 ANGLE_TRY(mImpl->map(context, access, &mState.mMapPointer));
286
287 ASSERT(access == GL_WRITE_ONLY_OES);
288
289 mState.mMapped = GL_TRUE;
290 mState.mMapOffset = 0;
291 mState.mMapLength = mState.mSize;
292 mState.mAccess = access;
293 mState.mAccessFlags = GL_MAP_WRITE_BIT;
294 mIndexRangeCache.clear();
295
296 // Notify when state changes.
297 onStateChange(angle::SubjectMessage::SubjectMapped);
298
299 return angle::Result::Continue;
300 }
301
mapRange(const Context * context,GLintptr offset,GLsizeiptr length,GLbitfield access)302 angle::Result Buffer::mapRange(const Context *context,
303 GLintptr offset,
304 GLsizeiptr length,
305 GLbitfield access)
306 {
307 ASSERT(!mState.mMapped);
308 ASSERT(offset + length <= mState.mSize);
309
310 mState.mMapPointer = nullptr;
311 ANGLE_TRY(mImpl->mapRange(context, offset, length, access, &mState.mMapPointer));
312
313 mState.mMapped = GL_TRUE;
314 mState.mMapOffset = static_cast<GLint64>(offset);
315 mState.mMapLength = static_cast<GLint64>(length);
316 mState.mAccess = GL_WRITE_ONLY_OES;
317 mState.mAccessFlags = access;
318
319 // The OES_mapbuffer extension states that GL_WRITE_ONLY_OES is the only valid
320 // value for GL_BUFFER_ACCESS_OES because it was written against ES2. Since there is
321 // no update for ES3 and the GL_READ_ONLY and GL_READ_WRITE enums don't exist for ES,
322 // we cannot properly set GL_BUFFER_ACCESS_OES when glMapBufferRange is called.
323
324 if ((access & GL_MAP_WRITE_BIT) > 0)
325 {
326 mIndexRangeCache.invalidateRange(static_cast<unsigned int>(offset),
327 static_cast<unsigned int>(length));
328 }
329
330 // Notify when state changes.
331 onStateChange(angle::SubjectMessage::SubjectMapped);
332
333 return angle::Result::Continue;
334 }
335
unmap(const Context * context,GLboolean * result)336 angle::Result Buffer::unmap(const Context *context, GLboolean *result)
337 {
338 ASSERT(mState.mMapped);
339
340 *result = GL_FALSE;
341 ANGLE_TRY(mImpl->unmap(context, result));
342
343 mState.mMapped = GL_FALSE;
344 mState.mMapPointer = nullptr;
345 mState.mMapOffset = 0;
346 mState.mMapLength = 0;
347 mState.mAccess = GL_WRITE_ONLY_OES;
348 mState.mAccessFlags = 0;
349
350 // Notify when data changes.
351 onStateChange(angle::SubjectMessage::SubjectUnmapped);
352
353 return angle::Result::Continue;
354 }
355
onDataChanged()356 void Buffer::onDataChanged()
357 {
358 mIndexRangeCache.clear();
359
360 // Notify when data changes.
361 onContentsChange();
362
363 mImpl->onDataChanged();
364 }
365
getIndexRange(const gl::Context * context,DrawElementsType type,size_t offset,size_t count,bool primitiveRestartEnabled,IndexRange * outRange) const366 angle::Result Buffer::getIndexRange(const gl::Context *context,
367 DrawElementsType type,
368 size_t offset,
369 size_t count,
370 bool primitiveRestartEnabled,
371 IndexRange *outRange) const
372 {
373 if (mIndexRangeCache.findRange(type, offset, count, primitiveRestartEnabled, outRange))
374 {
375 return angle::Result::Continue;
376 }
377
378 ANGLE_TRY(
379 mImpl->getIndexRange(context, type, offset, count, primitiveRestartEnabled, outRange));
380
381 mIndexRangeCache.addRange(type, offset, count, primitiveRestartEnabled, *outRange);
382
383 return angle::Result::Continue;
384 }
385
getMemorySize() const386 GLint64 Buffer::getMemorySize() const
387 {
388 GLint64 implSize = mImpl->getMemorySize();
389 return implSize > 0 ? implSize : mState.mSize;
390 }
391
isDoubleBoundForTransformFeedback() const392 bool Buffer::isDoubleBoundForTransformFeedback() const
393 {
394 return mState.mTransformFeedbackIndexedBindingCount > 1;
395 }
396
onTFBindingChanged(const Context * context,bool bound,bool indexed)397 void Buffer::onTFBindingChanged(const Context *context, bool bound, bool indexed)
398 {
399 ASSERT(bound || mState.mBindingCount > 0);
400 mState.mBindingCount += bound ? 1 : -1;
401 if (indexed)
402 {
403 ASSERT(bound || mState.mTransformFeedbackIndexedBindingCount > 0);
404 mState.mTransformFeedbackIndexedBindingCount += bound ? 1 : -1;
405
406 onStateChange(angle::SubjectMessage::BindingChanged);
407 }
408 else
409 {
410 mState.mTransformFeedbackGenericBindingCount += bound ? 1 : -1;
411 }
412 }
413
getSubData(const gl::Context * context,GLintptr offset,GLsizeiptr size,void * outData)414 angle::Result Buffer::getSubData(const gl::Context *context,
415 GLintptr offset,
416 GLsizeiptr size,
417 void *outData)
418 {
419 return mImpl->getSubData(context, offset, size, outData);
420 }
421
onSubjectStateChange(angle::SubjectIndex index,angle::SubjectMessage message)422 void Buffer::onSubjectStateChange(angle::SubjectIndex index, angle::SubjectMessage message)
423 {
424 // Pass it along!
425 ASSERT(index == kImplementationSubjectIndex);
426 ASSERT(message == angle::SubjectMessage::SubjectChanged ||
427 message == angle::SubjectMessage::InternalMemoryAllocationChanged);
428 onStateChange(message);
429 }
430
getContentsObserverIndex(void * observer,uint32_t bufferIndex) const431 size_t Buffer::getContentsObserverIndex(void *observer, uint32_t bufferIndex) const
432 {
433 ContentsObserver contentsObserver{bufferIndex, observer};
434 for (size_t observerIndex = 0; observerIndex < mContentsObservers.size(); ++observerIndex)
435 {
436 if (mContentsObservers[observerIndex] == contentsObserver)
437 {
438 return observerIndex;
439 }
440 }
441
442 return kInvalidContentsObserverIndex;
443 }
444
addContentsObserver(VertexArray * vertexArray,uint32_t bufferIndex)445 void Buffer::addContentsObserver(VertexArray *vertexArray, uint32_t bufferIndex)
446 {
447 ASSERT(bufferIndex != ContentsObserver::kBufferTextureIndex);
448 if (getContentsObserverIndex(vertexArray, bufferIndex) == kInvalidContentsObserverIndex)
449 {
450 mContentsObservers.push_back({bufferIndex, vertexArray});
451 }
452 }
453
removeContentsObserverImpl(void * observer,uint32_t bufferIndex)454 void Buffer::removeContentsObserverImpl(void *observer, uint32_t bufferIndex)
455 {
456 size_t foundObserver = getContentsObserverIndex(observer, bufferIndex);
457 if (foundObserver != kInvalidContentsObserverIndex)
458 {
459 size_t lastObserverIndex = mContentsObservers.size() - 1;
460 if (foundObserver != lastObserverIndex)
461 {
462 mContentsObservers[foundObserver] = mContentsObservers[lastObserverIndex];
463 }
464 mContentsObservers.pop_back();
465 }
466 }
467
removeContentsObserver(VertexArray * vertexArray,uint32_t bufferIndex)468 void Buffer::removeContentsObserver(VertexArray *vertexArray, uint32_t bufferIndex)
469 {
470 removeContentsObserverImpl(vertexArray, bufferIndex);
471 }
472
addContentsObserver(Texture * texture)473 void Buffer::addContentsObserver(Texture *texture)
474 {
475 if (!hasContentsObserver(texture))
476 {
477 mContentsObservers.push_back({ContentsObserver::kBufferTextureIndex, texture});
478 }
479 }
480
removeContentsObserver(Texture * texture)481 void Buffer::removeContentsObserver(Texture *texture)
482 {
483 removeContentsObserverImpl(texture, ContentsObserver::kBufferTextureIndex);
484 }
485
hasContentsObserver(Texture * texture) const486 bool Buffer::hasContentsObserver(Texture *texture) const
487 {
488 return getContentsObserverIndex(texture, ContentsObserver::kBufferTextureIndex) !=
489 kInvalidContentsObserverIndex;
490 }
491
onContentsChange()492 void Buffer::onContentsChange()
493 {
494 for (const ContentsObserver &contentsObserver : mContentsObservers)
495 {
496 if (contentsObserver.bufferIndex != ContentsObserver::kBufferTextureIndex)
497 {
498 static_cast<VertexArray *>(contentsObserver.observer)
499 ->onBufferContentsChange(contentsObserver.bufferIndex);
500 }
501 else
502 {
503 static_cast<Texture *>(contentsObserver.observer)->onBufferContentsChange();
504 }
505 }
506 }
507 } // namespace gl
508