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