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