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 } // anonymous namespace
23
BufferState()24 BufferState::BufferState()
25 : mLabel(),
26 mUsage(BufferUsage::StaticDraw),
27 mSize(0),
28 mAccessFlags(0),
29 mAccess(GL_WRITE_ONLY_OES),
30 mMapped(GL_FALSE),
31 mMapPointer(nullptr),
32 mMapOffset(0),
33 mMapLength(0),
34 mBindingCount(0),
35 mTransformFeedbackIndexedBindingCount(0),
36 mTransformFeedbackGenericBindingCount(0),
37 mImmutable(GL_FALSE),
38 mStorageExtUsageFlags(0),
39 mExternal(GL_FALSE)
40 {}
41
~BufferState()42 BufferState::~BufferState() {}
43
Buffer(rx::GLImplFactory * factory,BufferID id)44 Buffer::Buffer(rx::GLImplFactory *factory, BufferID id)
45 : RefCountObject(factory->generateSerial(), id),
46 mImpl(factory->createBuffer(mState)),
47 mImplObserver(this, kImplementationSubjectIndex)
48 {
49 mImplObserver.bind(mImpl);
50 }
51
~Buffer()52 Buffer::~Buffer()
53 {
54 SafeDelete(mImpl);
55 }
56
onDestroy(const Context * context)57 void Buffer::onDestroy(const Context *context)
58 {
59 // In tests, mImpl might be null.
60 if (mImpl)
61 mImpl->destroy(context);
62 }
63
setLabel(const Context * context,const std::string & label)64 void Buffer::setLabel(const Context *context, const std::string &label)
65 {
66 mState.mLabel = label;
67 }
68
getLabel() const69 const std::string &Buffer::getLabel() const
70 {
71 return mState.mLabel;
72 }
73
bufferStorageExternal(Context * context,BufferBinding target,GLsizeiptr size,GLeglClientBufferEXT clientBuffer,GLbitfield flags)74 angle::Result Buffer::bufferStorageExternal(Context *context,
75 BufferBinding target,
76 GLsizeiptr size,
77 GLeglClientBufferEXT clientBuffer,
78 GLbitfield flags)
79 {
80 return bufferExternalDataImpl(context, target, clientBuffer, size, flags);
81 }
82
bufferStorage(Context * context,BufferBinding target,GLsizeiptr size,const void * data,GLbitfield flags)83 angle::Result Buffer::bufferStorage(Context *context,
84 BufferBinding target,
85 GLsizeiptr size,
86 const void *data,
87 GLbitfield flags)
88 {
89 return bufferDataImpl(context, target, data, size, BufferUsage::InvalidEnum, flags);
90 }
91
bufferData(Context * context,BufferBinding target,const void * data,GLsizeiptr size,BufferUsage usage)92 angle::Result Buffer::bufferData(Context *context,
93 BufferBinding target,
94 const void *data,
95 GLsizeiptr size,
96 BufferUsage usage)
97 {
98 GLbitfield flags = (GL_MAP_READ_BIT | GL_MAP_WRITE_BIT | GL_DYNAMIC_STORAGE_BIT_EXT);
99 return bufferDataImpl(context, target, data, size, usage, flags);
100 }
101
bufferDataImpl(Context * context,BufferBinding target,const void * data,GLsizeiptr size,BufferUsage usage,GLbitfield flags)102 angle::Result Buffer::bufferDataImpl(Context *context,
103 BufferBinding target,
104 const void *data,
105 GLsizeiptr size,
106 BufferUsage usage,
107 GLbitfield flags)
108 {
109 const void *dataForImpl = data;
110
111 if (mState.isMapped())
112 {
113 // Per the OpenGL ES 3.0 spec, buffers are implicity unmapped when a call to
114 // BufferData happens on a mapped buffer:
115 //
116 // If any portion of the buffer object is mapped in the current context or any context
117 // current to another thread, it is as though UnmapBuffer (see section 2.10.3) is
118 // executed in each such context prior to deleting the existing data store.
119 //
120 GLboolean dontCare = GL_FALSE;
121 ANGLE_TRY(unmap(context, &dontCare));
122 }
123
124 // If we are using robust resource init, make sure the buffer starts cleared.
125 // Note: the Context is checked for nullptr because of some testing code.
126 // TODO(jmadill): Investigate lazier clearing.
127 if (context && context->getState().isRobustResourceInitEnabled() && !data && size > 0)
128 {
129 angle::MemoryBuffer *scratchBuffer = nullptr;
130 ANGLE_CHECK_GL_ALLOC(
131 context, context->getZeroFilledBuffer(static_cast<size_t>(size), &scratchBuffer));
132 dataForImpl = scratchBuffer->data();
133 }
134
135 if (mImpl->setDataWithUsageFlags(context, target, nullptr, dataForImpl, size, usage, flags) ==
136 angle::Result::Stop)
137 {
138 // If setData fails, the buffer contents are undefined. Set a zero size to indicate that.
139 mIndexRangeCache.clear();
140 mState.mSize = 0;
141
142 // Notify when storage changes.
143 onStateChange(angle::SubjectMessage::SubjectChanged);
144
145 return angle::Result::Stop;
146 }
147
148 mIndexRangeCache.clear();
149 mState.mUsage = usage;
150 mState.mSize = size;
151 mState.mImmutable = (usage == BufferUsage::InvalidEnum);
152 mState.mStorageExtUsageFlags = flags;
153
154 // Notify when storage changes.
155 onStateChange(angle::SubjectMessage::SubjectChanged);
156
157 return angle::Result::Continue;
158 }
159
bufferExternalDataImpl(Context * context,BufferBinding target,GLeglClientBufferEXT clientBuffer,GLsizeiptr size,GLbitfield flags)160 angle::Result Buffer::bufferExternalDataImpl(Context *context,
161 BufferBinding target,
162 GLeglClientBufferEXT clientBuffer,
163 GLsizeiptr size,
164 GLbitfield flags)
165 {
166 if (mState.isMapped())
167 {
168 // Per the OpenGL ES 3.0 spec, buffers are implicity unmapped when a call to
169 // BufferData happens on a mapped buffer:
170 //
171 // If any portion of the buffer object is mapped in the current context or any context
172 // current to another thread, it is as though UnmapBuffer (see section 2.10.3) is
173 // executed in each such context prior to deleting the existing data store.
174 //
175 GLboolean dontCare = GL_FALSE;
176 ANGLE_TRY(unmap(context, &dontCare));
177 }
178
179 if (mImpl->setDataWithUsageFlags(context, target, clientBuffer, nullptr, size,
180 BufferUsage::InvalidEnum, flags) == angle::Result::Stop)
181 {
182 // If setData fails, the buffer contents are undefined. Set a zero size to indicate that.
183 mIndexRangeCache.clear();
184 mState.mSize = 0;
185
186 // Notify when storage changes.
187 onStateChange(angle::SubjectMessage::SubjectChanged);
188
189 return angle::Result::Stop;
190 }
191
192 mIndexRangeCache.clear();
193 mState.mUsage = BufferUsage::InvalidEnum;
194 mState.mSize = size;
195 mState.mImmutable = GL_TRUE;
196 mState.mStorageExtUsageFlags = flags;
197 mState.mExternal = GL_TRUE;
198
199 // Notify when storage changes.
200 onStateChange(angle::SubjectMessage::SubjectChanged);
201
202 return angle::Result::Continue;
203 }
204
bufferSubData(const Context * context,BufferBinding target,const void * data,GLsizeiptr size,GLintptr offset)205 angle::Result Buffer::bufferSubData(const Context *context,
206 BufferBinding target,
207 const void *data,
208 GLsizeiptr size,
209 GLintptr offset)
210 {
211 ANGLE_TRY(mImpl->setSubData(context, target, data, size, offset));
212
213 mIndexRangeCache.invalidateRange(static_cast<unsigned int>(offset),
214 static_cast<unsigned int>(size));
215
216 // Notify when data changes.
217 onStateChange(angle::SubjectMessage::ContentsChanged);
218
219 return angle::Result::Continue;
220 }
221
copyBufferSubData(const Context * context,Buffer * source,GLintptr sourceOffset,GLintptr destOffset,GLsizeiptr size)222 angle::Result Buffer::copyBufferSubData(const Context *context,
223 Buffer *source,
224 GLintptr sourceOffset,
225 GLintptr destOffset,
226 GLsizeiptr size)
227 {
228 ANGLE_TRY(
229 mImpl->copySubData(context, source->getImplementation(), sourceOffset, destOffset, size));
230
231 mIndexRangeCache.invalidateRange(static_cast<unsigned int>(destOffset),
232 static_cast<unsigned int>(size));
233
234 // Notify when data changes.
235 onStateChange(angle::SubjectMessage::ContentsChanged);
236
237 return angle::Result::Continue;
238 }
239
map(const Context * context,GLenum access)240 angle::Result Buffer::map(const Context *context, GLenum access)
241 {
242 ASSERT(!mState.mMapped);
243
244 mState.mMapPointer = nullptr;
245 ANGLE_TRY(mImpl->map(context, access, &mState.mMapPointer));
246
247 ASSERT(access == GL_WRITE_ONLY_OES);
248
249 mState.mMapped = GL_TRUE;
250 mState.mMapOffset = 0;
251 mState.mMapLength = mState.mSize;
252 mState.mAccess = access;
253 mState.mAccessFlags = GL_MAP_WRITE_BIT;
254 mIndexRangeCache.clear();
255
256 // Notify when state changes.
257 onStateChange(angle::SubjectMessage::SubjectMapped);
258
259 return angle::Result::Continue;
260 }
261
mapRange(const Context * context,GLintptr offset,GLsizeiptr length,GLbitfield access)262 angle::Result Buffer::mapRange(const Context *context,
263 GLintptr offset,
264 GLsizeiptr length,
265 GLbitfield access)
266 {
267 ASSERT(!mState.mMapped);
268 ASSERT(offset + length <= mState.mSize);
269
270 mState.mMapPointer = nullptr;
271 ANGLE_TRY(mImpl->mapRange(context, offset, length, access, &mState.mMapPointer));
272
273 mState.mMapped = GL_TRUE;
274 mState.mMapOffset = static_cast<GLint64>(offset);
275 mState.mMapLength = static_cast<GLint64>(length);
276 mState.mAccess = GL_WRITE_ONLY_OES;
277 mState.mAccessFlags = access;
278
279 // The OES_mapbuffer extension states that GL_WRITE_ONLY_OES is the only valid
280 // value for GL_BUFFER_ACCESS_OES because it was written against ES2. Since there is
281 // no update for ES3 and the GL_READ_ONLY and GL_READ_WRITE enums don't exist for ES,
282 // we cannot properly set GL_BUFFER_ACCESS_OES when glMapBufferRange is called.
283
284 if ((access & GL_MAP_WRITE_BIT) > 0)
285 {
286 mIndexRangeCache.invalidateRange(static_cast<unsigned int>(offset),
287 static_cast<unsigned int>(length));
288 }
289
290 // Notify when state changes.
291 onStateChange(angle::SubjectMessage::SubjectMapped);
292
293 return angle::Result::Continue;
294 }
295
unmap(const Context * context,GLboolean * result)296 angle::Result Buffer::unmap(const Context *context, GLboolean *result)
297 {
298 ASSERT(mState.mMapped);
299
300 *result = GL_FALSE;
301 ANGLE_TRY(mImpl->unmap(context, result));
302
303 mState.mMapped = GL_FALSE;
304 mState.mMapPointer = nullptr;
305 mState.mMapOffset = 0;
306 mState.mMapLength = 0;
307 mState.mAccess = GL_WRITE_ONLY_OES;
308 mState.mAccessFlags = 0;
309
310 // Notify when data changes.
311 onStateChange(angle::SubjectMessage::SubjectUnmapped);
312
313 return angle::Result::Continue;
314 }
315
onDataChanged()316 void Buffer::onDataChanged()
317 {
318 mIndexRangeCache.clear();
319
320 // Notify when data changes.
321 onStateChange(angle::SubjectMessage::ContentsChanged);
322
323 mImpl->onDataChanged();
324 }
325
getIndexRange(const gl::Context * context,DrawElementsType type,size_t offset,size_t count,bool primitiveRestartEnabled,IndexRange * outRange) const326 angle::Result Buffer::getIndexRange(const gl::Context *context,
327 DrawElementsType type,
328 size_t offset,
329 size_t count,
330 bool primitiveRestartEnabled,
331 IndexRange *outRange) const
332 {
333 if (mIndexRangeCache.findRange(type, offset, count, primitiveRestartEnabled, outRange))
334 {
335 return angle::Result::Continue;
336 }
337
338 ANGLE_TRY(
339 mImpl->getIndexRange(context, type, offset, count, primitiveRestartEnabled, outRange));
340
341 mIndexRangeCache.addRange(type, offset, count, primitiveRestartEnabled, *outRange);
342
343 return angle::Result::Continue;
344 }
345
getMemorySize() const346 GLint64 Buffer::getMemorySize() const
347 {
348 GLint64 implSize = mImpl->getMemorySize();
349 return implSize > 0 ? implSize : mState.mSize;
350 }
351
isDoubleBoundForTransformFeedback() const352 bool Buffer::isDoubleBoundForTransformFeedback() const
353 {
354 return mState.mTransformFeedbackIndexedBindingCount > 1;
355 }
356
onTFBindingChanged(const Context * context,bool bound,bool indexed)357 void Buffer::onTFBindingChanged(const Context *context, bool bound, bool indexed)
358 {
359 ASSERT(bound || mState.mBindingCount > 0);
360 mState.mBindingCount += bound ? 1 : -1;
361 if (indexed)
362 {
363 ASSERT(bound || mState.mTransformFeedbackIndexedBindingCount > 0);
364 mState.mTransformFeedbackIndexedBindingCount += bound ? 1 : -1;
365
366 onStateChange(angle::SubjectMessage::BindingChanged);
367 }
368 else
369 {
370 mState.mTransformFeedbackGenericBindingCount += bound ? 1 : -1;
371 }
372 }
373
getSubData(const gl::Context * context,GLintptr offset,GLsizeiptr size,void * outData)374 angle::Result Buffer::getSubData(const gl::Context *context,
375 GLintptr offset,
376 GLsizeiptr size,
377 void *outData)
378 {
379 return mImpl->getSubData(context, offset, size, outData);
380 }
381
onSubjectStateChange(angle::SubjectIndex index,angle::SubjectMessage message)382 void Buffer::onSubjectStateChange(angle::SubjectIndex index, angle::SubjectMessage message)
383 {
384 // Pass it along!
385 ASSERT(index == kImplementationSubjectIndex);
386 ASSERT(message == angle::SubjectMessage::SubjectChanged);
387 onStateChange(angle::SubjectMessage::SubjectChanged);
388 }
389 } // namespace gl
390