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::DynamicDraw, flags,
121 BufferStorage::Immutable);
122 }
123
bufferData(Context * context,BufferBinding target,const void * data,GLsizeiptr size,BufferUsage usage)124 angle::Result Buffer::bufferData(Context *context,
125 BufferBinding target,
126 const void *data,
127 GLsizeiptr size,
128 BufferUsage usage)
129 {
130 GLbitfield flags = (GL_MAP_READ_BIT | GL_MAP_WRITE_BIT | GL_DYNAMIC_STORAGE_BIT_EXT);
131 return bufferDataImpl(context, target, data, size, usage, flags, BufferStorage::Mutable);
132 }
133
bufferDataImpl(Context * context,BufferBinding target,const void * data,GLsizeiptr size,BufferUsage usage,GLbitfield flags,BufferStorage bufferStorage)134 angle::Result Buffer::bufferDataImpl(Context *context,
135 BufferBinding target,
136 const void *data,
137 GLsizeiptr size,
138 BufferUsage usage,
139 GLbitfield flags,
140 BufferStorage bufferStorage)
141 {
142 const void *dataForImpl = data;
143
144 if (mState.isMapped())
145 {
146 // Per the OpenGL ES 3.0 spec, buffers are implicity unmapped when a call to
147 // BufferData happens on a mapped buffer:
148 //
149 // If any portion of the buffer object is mapped in the current context or any context
150 // current to another thread, it is as though UnmapBuffer (see section 2.10.3) is
151 // executed in each such context prior to deleting the existing data store.
152 //
153 GLboolean dontCare = GL_FALSE;
154 ANGLE_TRY(unmap(context, &dontCare));
155 }
156
157 // If we are using robust resource init, make sure the buffer starts cleared.
158 // Note: the Context is checked for nullptr because of some testing code.
159 // TODO(jmadill): Investigate lazier clearing.
160 if (context && context->isRobustResourceInitEnabled() && !data && size > 0)
161 {
162 angle::MemoryBuffer *scratchBuffer = nullptr;
163 ANGLE_CHECK_GL_ALLOC(
164 context, context->getZeroFilledBuffer(static_cast<size_t>(size), &scratchBuffer));
165 dataForImpl = scratchBuffer->data();
166 }
167
168 if (mImpl->setDataWithUsageFlags(context, target, nullptr, dataForImpl, size, usage, flags,
169 bufferStorage) == angle::Result::Stop)
170 {
171 // If setData fails, the buffer contents are undefined. Set a zero size to indicate that.
172 mIndexRangeCache.clear();
173 mState.mSize = 0;
174
175 // Notify when storage changes.
176 onStateChange(angle::SubjectMessage::SubjectChanged);
177
178 return angle::Result::Stop;
179 }
180
181 bool wholeBuffer = size == mState.mSize;
182
183 mIndexRangeCache.clear();
184 mState.mUsage = usage;
185 mState.mSize = size;
186 mState.mImmutable = (bufferStorage == BufferStorage::Immutable);
187 mState.mStorageExtUsageFlags = flags;
188
189 // Notify when storage changes.
190 if (wholeBuffer)
191 {
192 onContentsChange();
193 }
194 else
195 {
196 onStateChange(angle::SubjectMessage::SubjectChanged);
197 }
198
199 return angle::Result::Continue;
200 }
201
bufferExternalDataImpl(Context * context,BufferBinding target,GLeglClientBufferEXT clientBuffer,GLsizeiptr size,GLbitfield flags)202 angle::Result Buffer::bufferExternalDataImpl(Context *context,
203 BufferBinding target,
204 GLeglClientBufferEXT clientBuffer,
205 GLsizeiptr size,
206 GLbitfield flags)
207 {
208 if (mState.isMapped())
209 {
210 // Per the OpenGL ES 3.0 spec, buffers are implicitly unmapped when a call to
211 // BufferData happens on a mapped buffer:
212 //
213 // If any portion of the buffer object is mapped in the current context or any context
214 // current to another thread, it is as though UnmapBuffer (see section 2.10.3) is
215 // executed in each such context prior to deleting the existing data store.
216 //
217 GLboolean dontCare = GL_FALSE;
218 ANGLE_TRY(unmap(context, &dontCare));
219 }
220
221 if (mImpl->setDataWithUsageFlags(context, target, clientBuffer, nullptr, size,
222 BufferUsage::InvalidEnum, flags,
223 BufferStorage::Immutable) == angle::Result::Stop)
224 {
225 // If setData fails, the buffer contents are undefined. Set a zero size to indicate that.
226 mIndexRangeCache.clear();
227 mState.mSize = 0;
228
229 // Notify when storage changes.
230 onStateChange(angle::SubjectMessage::SubjectChanged);
231
232 return angle::Result::Stop;
233 }
234
235 mIndexRangeCache.clear();
236 mState.mUsage = BufferUsage::InvalidEnum;
237 mState.mSize = size;
238 mState.mImmutable = GL_TRUE;
239 mState.mStorageExtUsageFlags = flags;
240 mState.mExternal = GL_TRUE;
241
242 // Notify when storage changes.
243 onStateChange(angle::SubjectMessage::SubjectChanged);
244
245 return angle::Result::Continue;
246 }
247
bufferSubData(const Context * context,BufferBinding target,const void * data,GLsizeiptr size,GLintptr offset)248 angle::Result Buffer::bufferSubData(const Context *context,
249 BufferBinding target,
250 const void *data,
251 GLsizeiptr size,
252 GLintptr offset)
253 {
254 ANGLE_TRY(mImpl->setSubData(context, target, data, size, offset));
255
256 mIndexRangeCache.invalidateRange(static_cast<unsigned int>(offset),
257 static_cast<unsigned int>(size));
258
259 // Notify when data changes.
260 onContentsChange();
261
262 return angle::Result::Continue;
263 }
264
copyBufferSubData(const Context * context,Buffer * source,GLintptr sourceOffset,GLintptr destOffset,GLsizeiptr size)265 angle::Result Buffer::copyBufferSubData(const Context *context,
266 Buffer *source,
267 GLintptr sourceOffset,
268 GLintptr destOffset,
269 GLsizeiptr size)
270 {
271 ANGLE_TRY(
272 mImpl->copySubData(context, source->getImplementation(), sourceOffset, destOffset, size));
273
274 mIndexRangeCache.invalidateRange(static_cast<unsigned int>(destOffset),
275 static_cast<unsigned int>(size));
276
277 // Notify when data changes.
278 onContentsChange();
279
280 return angle::Result::Continue;
281 }
282
map(const Context * context,GLenum access)283 angle::Result Buffer::map(const Context *context, GLenum access)
284 {
285 ASSERT(!mState.mMapped);
286
287 mState.mMapPointer = nullptr;
288 ANGLE_TRY(mImpl->map(context, access, &mState.mMapPointer));
289
290 ASSERT(access == GL_WRITE_ONLY_OES);
291
292 mState.mMapped = GL_TRUE;
293 mState.mMapOffset = 0;
294 mState.mMapLength = mState.mSize;
295 mState.mAccess = access;
296 mState.mAccessFlags = GL_MAP_WRITE_BIT;
297 mIndexRangeCache.clear();
298
299 // Notify when state changes.
300 onStateChange(angle::SubjectMessage::SubjectMapped);
301
302 return angle::Result::Continue;
303 }
304
mapRange(const Context * context,GLintptr offset,GLsizeiptr length,GLbitfield access)305 angle::Result Buffer::mapRange(const Context *context,
306 GLintptr offset,
307 GLsizeiptr length,
308 GLbitfield access)
309 {
310 ASSERT(!mState.mMapped);
311 ASSERT(offset + length <= mState.mSize);
312
313 mState.mMapPointer = nullptr;
314 ANGLE_TRY(mImpl->mapRange(context, offset, length, access, &mState.mMapPointer));
315
316 mState.mMapped = GL_TRUE;
317 mState.mMapOffset = static_cast<GLint64>(offset);
318 mState.mMapLength = static_cast<GLint64>(length);
319 mState.mAccess = GL_WRITE_ONLY_OES;
320 mState.mAccessFlags = access;
321
322 // The OES_mapbuffer extension states that GL_WRITE_ONLY_OES is the only valid
323 // value for GL_BUFFER_ACCESS_OES because it was written against ES2. Since there is
324 // no update for ES3 and the GL_READ_ONLY and GL_READ_WRITE enums don't exist for ES,
325 // we cannot properly set GL_BUFFER_ACCESS_OES when glMapBufferRange is called.
326
327 if ((access & GL_MAP_WRITE_BIT) > 0)
328 {
329 mIndexRangeCache.invalidateRange(static_cast<unsigned int>(offset),
330 static_cast<unsigned int>(length));
331 }
332
333 // Notify when state changes.
334 onStateChange(angle::SubjectMessage::SubjectMapped);
335
336 return angle::Result::Continue;
337 }
338
unmap(const Context * context,GLboolean * result)339 angle::Result Buffer::unmap(const Context *context, GLboolean *result)
340 {
341 ASSERT(mState.mMapped);
342
343 *result = GL_FALSE;
344 ANGLE_TRY(mImpl->unmap(context, result));
345
346 mState.mMapped = GL_FALSE;
347 mState.mMapPointer = nullptr;
348 mState.mMapOffset = 0;
349 mState.mMapLength = 0;
350 mState.mAccess = GL_WRITE_ONLY_OES;
351 mState.mAccessFlags = 0;
352
353 // Notify when data changes.
354 onStateChange(angle::SubjectMessage::SubjectUnmapped);
355
356 return angle::Result::Continue;
357 }
358
onDataChanged()359 void Buffer::onDataChanged()
360 {
361 mIndexRangeCache.clear();
362
363 // Notify when data changes.
364 onContentsChange();
365
366 mImpl->onDataChanged();
367 }
368
getIndexRange(const gl::Context * context,DrawElementsType type,size_t offset,size_t count,bool primitiveRestartEnabled,IndexRange * outRange) const369 angle::Result Buffer::getIndexRange(const gl::Context *context,
370 DrawElementsType type,
371 size_t offset,
372 size_t count,
373 bool primitiveRestartEnabled,
374 IndexRange *outRange) const
375 {
376 if (mIndexRangeCache.findRange(type, offset, count, primitiveRestartEnabled, outRange))
377 {
378 return angle::Result::Continue;
379 }
380
381 ANGLE_TRY(
382 mImpl->getIndexRange(context, type, offset, count, primitiveRestartEnabled, outRange));
383
384 mIndexRangeCache.addRange(type, offset, count, primitiveRestartEnabled, *outRange);
385
386 return angle::Result::Continue;
387 }
388
getMemorySize() const389 GLint64 Buffer::getMemorySize() const
390 {
391 GLint64 implSize = mImpl->getMemorySize();
392 return implSize > 0 ? implSize : mState.mSize;
393 }
394
isDoubleBoundForTransformFeedback() const395 bool Buffer::isDoubleBoundForTransformFeedback() const
396 {
397 return mState.mTransformFeedbackIndexedBindingCount > 1;
398 }
399
onTFBindingChanged(const Context * context,bool bound,bool indexed)400 void Buffer::onTFBindingChanged(const Context *context, bool bound, bool indexed)
401 {
402 ASSERT(bound || mState.mBindingCount > 0);
403 mState.mBindingCount += bound ? 1 : -1;
404 if (indexed)
405 {
406 ASSERT(bound || mState.mTransformFeedbackIndexedBindingCount > 0);
407 mState.mTransformFeedbackIndexedBindingCount += bound ? 1 : -1;
408
409 onStateChange(angle::SubjectMessage::BindingChanged);
410 }
411 else
412 {
413 mState.mTransformFeedbackGenericBindingCount += bound ? 1 : -1;
414 }
415 }
416
getSubData(const gl::Context * context,GLintptr offset,GLsizeiptr size,void * outData)417 angle::Result Buffer::getSubData(const gl::Context *context,
418 GLintptr offset,
419 GLsizeiptr size,
420 void *outData)
421 {
422 return mImpl->getSubData(context, offset, size, outData);
423 }
424
onSubjectStateChange(angle::SubjectIndex index,angle::SubjectMessage message)425 void Buffer::onSubjectStateChange(angle::SubjectIndex index, angle::SubjectMessage message)
426 {
427 // Pass it along!
428 ASSERT(index == kImplementationSubjectIndex);
429 ASSERT(message == angle::SubjectMessage::SubjectChanged ||
430 message == angle::SubjectMessage::InternalMemoryAllocationChanged);
431 onStateChange(message);
432 }
433
getContentsObserverIndex(void * observer,uint32_t bufferIndex) const434 size_t Buffer::getContentsObserverIndex(void *observer, uint32_t bufferIndex) const
435 {
436 ContentsObserver contentsObserver{bufferIndex, observer};
437 for (size_t observerIndex = 0; observerIndex < mContentsObservers.size(); ++observerIndex)
438 {
439 if (mContentsObservers[observerIndex] == contentsObserver)
440 {
441 return observerIndex;
442 }
443 }
444
445 return kInvalidContentsObserverIndex;
446 }
447
addContentsObserver(VertexArray * vertexArray,uint32_t bufferIndex)448 void Buffer::addContentsObserver(VertexArray *vertexArray, uint32_t bufferIndex)
449 {
450 ASSERT(bufferIndex != ContentsObserver::kBufferTextureIndex);
451 if (getContentsObserverIndex(vertexArray, bufferIndex) == kInvalidContentsObserverIndex)
452 {
453 mContentsObservers.push_back({bufferIndex, vertexArray});
454 }
455 }
456
removeContentsObserverImpl(void * observer,uint32_t bufferIndex)457 void Buffer::removeContentsObserverImpl(void *observer, uint32_t bufferIndex)
458 {
459 size_t foundObserver = getContentsObserverIndex(observer, bufferIndex);
460 if (foundObserver != kInvalidContentsObserverIndex)
461 {
462 size_t lastObserverIndex = mContentsObservers.size() - 1;
463 if (foundObserver != lastObserverIndex)
464 {
465 mContentsObservers[foundObserver] = mContentsObservers[lastObserverIndex];
466 }
467 mContentsObservers.pop_back();
468 }
469 }
470
removeContentsObserver(VertexArray * vertexArray,uint32_t bufferIndex)471 void Buffer::removeContentsObserver(VertexArray *vertexArray, uint32_t bufferIndex)
472 {
473 removeContentsObserverImpl(vertexArray, bufferIndex);
474 }
475
addContentsObserver(Texture * texture)476 void Buffer::addContentsObserver(Texture *texture)
477 {
478 if (!hasContentsObserver(texture))
479 {
480 mContentsObservers.push_back({ContentsObserver::kBufferTextureIndex, texture});
481 }
482 }
483
removeContentsObserver(Texture * texture)484 void Buffer::removeContentsObserver(Texture *texture)
485 {
486 removeContentsObserverImpl(texture, ContentsObserver::kBufferTextureIndex);
487 }
488
hasContentsObserver(Texture * texture) const489 bool Buffer::hasContentsObserver(Texture *texture) const
490 {
491 return getContentsObserverIndex(texture, ContentsObserver::kBufferTextureIndex) !=
492 kInvalidContentsObserverIndex;
493 }
494
onContentsChange()495 void Buffer::onContentsChange()
496 {
497 for (const ContentsObserver &contentsObserver : mContentsObservers)
498 {
499 if (contentsObserver.bufferIndex != ContentsObserver::kBufferTextureIndex)
500 {
501 static_cast<VertexArray *>(contentsObserver.observer)
502 ->onBufferContentsChange(contentsObserver.bufferIndex);
503 }
504 else
505 {
506 static_cast<Texture *>(contentsObserver.observer)->onBufferContentsChange();
507 }
508 }
509 }
510 } // namespace gl
511