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