1 //
2 // Copyright 2015 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 // VertexArrayGL.cpp: Implements the class methods for VertexArrayGL.
8
9 #include "libANGLE/renderer/gl/VertexArrayGL.h"
10
11 #include "common/bitset_utils.h"
12 #include "common/debug.h"
13 #include "common/mathutil.h"
14 #include "common/utilities.h"
15 #include "libANGLE/Buffer.h"
16 #include "libANGLE/Context.h"
17 #include "libANGLE/angletypes.h"
18 #include "libANGLE/formatutils.h"
19 #include "libANGLE/renderer/gl/BufferGL.h"
20 #include "libANGLE/renderer/gl/ContextGL.h"
21 #include "libANGLE/renderer/gl/FunctionsGL.h"
22 #include "libANGLE/renderer/gl/StateManagerGL.h"
23 #include "libANGLE/renderer/gl/renderergl_utils.h"
24
25 using namespace gl;
26
27 namespace rx
28 {
29 namespace
30 {
SameVertexAttribFormat(const VertexAttribute & a,const VertexAttribute & b)31 bool SameVertexAttribFormat(const VertexAttribute &a, const VertexAttribute &b)
32 {
33 return a.format == b.format && a.relativeOffset == b.relativeOffset;
34 }
35
SameVertexBuffer(const VertexBinding & a,const VertexBinding & b)36 bool SameVertexBuffer(const VertexBinding &a, const VertexBinding &b)
37 {
38 return a.getStride() == b.getStride() && a.getOffset() == b.getOffset() &&
39 a.getBuffer().get() == b.getBuffer().get();
40 }
41
IsVertexAttribPointerSupported(size_t attribIndex,const VertexAttribute & attrib)42 bool IsVertexAttribPointerSupported(size_t attribIndex, const VertexAttribute &attrib)
43 {
44 return (attribIndex == attrib.bindingIndex && attrib.relativeOffset == 0);
45 }
46
GetAdjustedDivisor(GLuint numViews,GLuint divisor)47 GLuint GetAdjustedDivisor(GLuint numViews, GLuint divisor)
48 {
49 return numViews * divisor;
50 }
51
52 } // anonymous namespace
53
VertexArrayGL(const VertexArrayState & state,const FunctionsGL * functions,StateManagerGL * stateManager)54 VertexArrayGL::VertexArrayGL(const VertexArrayState &state,
55 const FunctionsGL *functions,
56 StateManagerGL *stateManager)
57 : VertexArrayImpl(state),
58 mFunctions(functions),
59 mStateManager(stateManager),
60 mVertexArrayID(0),
61 mAppliedNumViews(1),
62 mAppliedElementArrayBuffer(),
63 mAppliedBindings(state.getMaxBindings()),
64 mStreamingElementArrayBufferSize(0),
65 mStreamingElementArrayBuffer(0),
66 mStreamingArrayBufferSize(0),
67 mStreamingArrayBuffer(0)
68 {
69 ASSERT(mFunctions);
70 ASSERT(mStateManager);
71 mFunctions->genVertexArrays(1, &mVertexArrayID);
72
73 // Set the cached vertex attribute array and vertex attribute binding array size
74 GLuint maxVertexAttribs = static_cast<GLuint>(state.getMaxAttribs());
75 for (GLuint i = 0; i < maxVertexAttribs; i++)
76 {
77 mAppliedAttributes.emplace_back(i);
78 }
79 }
80
~VertexArrayGL()81 VertexArrayGL::~VertexArrayGL() {}
82
destroy(const gl::Context * context)83 void VertexArrayGL::destroy(const gl::Context *context)
84 {
85 mStateManager->deleteVertexArray(mVertexArrayID);
86 mVertexArrayID = 0;
87 mAppliedNumViews = 1;
88
89 mStateManager->deleteBuffer(mStreamingElementArrayBuffer);
90 mStreamingElementArrayBufferSize = 0;
91 mStreamingElementArrayBuffer = 0;
92
93 mStateManager->deleteBuffer(mStreamingArrayBuffer);
94 mStreamingArrayBufferSize = 0;
95 mStreamingArrayBuffer = 0;
96
97 mAppliedElementArrayBuffer.set(context, nullptr);
98 for (auto &binding : mAppliedBindings)
99 {
100 binding.setBuffer(context, nullptr);
101 }
102 }
103
syncClientSideData(const gl::Context * context,const gl::AttributesMask & activeAttributesMask,GLint first,GLsizei count,GLsizei instanceCount) const104 angle::Result VertexArrayGL::syncClientSideData(const gl::Context *context,
105 const gl::AttributesMask &activeAttributesMask,
106 GLint first,
107 GLsizei count,
108 GLsizei instanceCount) const
109 {
110 return syncDrawState(context, activeAttributesMask, first, count,
111 gl::DrawElementsType::InvalidEnum, nullptr, instanceCount, false, nullptr);
112 }
113
updateElementArrayBufferBinding(const gl::Context * context) const114 void VertexArrayGL::updateElementArrayBufferBinding(const gl::Context *context) const
115 {
116 gl::Buffer *elementArrayBuffer = mState.getElementArrayBuffer();
117 if (elementArrayBuffer != nullptr && elementArrayBuffer != mAppliedElementArrayBuffer.get())
118 {
119 const BufferGL *bufferGL = GetImplAs<BufferGL>(elementArrayBuffer);
120 mStateManager->bindBuffer(gl::BufferBinding::ElementArray, bufferGL->getBufferID());
121 mAppliedElementArrayBuffer.set(context, elementArrayBuffer);
122 }
123 }
124
syncDrawState(const gl::Context * context,const gl::AttributesMask & activeAttributesMask,GLint first,GLsizei count,gl::DrawElementsType type,const void * indices,GLsizei instanceCount,bool primitiveRestartEnabled,const void ** outIndices) const125 angle::Result VertexArrayGL::syncDrawState(const gl::Context *context,
126 const gl::AttributesMask &activeAttributesMask,
127 GLint first,
128 GLsizei count,
129 gl::DrawElementsType type,
130 const void *indices,
131 GLsizei instanceCount,
132 bool primitiveRestartEnabled,
133 const void **outIndices) const
134 {
135 // Check if any attributes need to be streamed, determines if the index range needs to be
136 // computed
137 const gl::AttributesMask &needsStreamingAttribs =
138 context->getStateCache().getActiveClientAttribsMask();
139
140 // Determine if an index buffer needs to be streamed and the range of vertices that need to be
141 // copied
142 IndexRange indexRange;
143 if (type != gl::DrawElementsType::InvalidEnum)
144 {
145 ANGLE_TRY(syncIndexData(context, count, type, indices, primitiveRestartEnabled,
146 needsStreamingAttribs.any(), &indexRange, outIndices));
147 }
148 else
149 {
150 // Not an indexed call, set the range to [first, first + count - 1]
151 indexRange.start = first;
152 indexRange.end = first + count - 1;
153 }
154
155 if (needsStreamingAttribs.any())
156 {
157 ANGLE_TRY(streamAttributes(context, needsStreamingAttribs, instanceCount, indexRange));
158 }
159
160 return angle::Result::Continue;
161 }
162
syncIndexData(const gl::Context * context,GLsizei count,gl::DrawElementsType type,const void * indices,bool primitiveRestartEnabled,bool attributesNeedStreaming,IndexRange * outIndexRange,const void ** outIndices) const163 angle::Result VertexArrayGL::syncIndexData(const gl::Context *context,
164 GLsizei count,
165 gl::DrawElementsType type,
166 const void *indices,
167 bool primitiveRestartEnabled,
168 bool attributesNeedStreaming,
169 IndexRange *outIndexRange,
170 const void **outIndices) const
171 {
172 ASSERT(outIndices);
173
174 gl::Buffer *elementArrayBuffer = mState.getElementArrayBuffer();
175
176 // Need to check the range of indices if attributes need to be streamed
177 if (elementArrayBuffer != nullptr)
178 {
179 ASSERT(elementArrayBuffer == mAppliedElementArrayBuffer.get());
180 // Only compute the index range if the attributes also need to be streamed
181 if (attributesNeedStreaming)
182 {
183 ptrdiff_t elementArrayBufferOffset = reinterpret_cast<ptrdiff_t>(indices);
184 ANGLE_TRY(mState.getElementArrayBuffer()->getIndexRange(
185 context, type, elementArrayBufferOffset, count, primitiveRestartEnabled,
186 outIndexRange));
187 }
188
189 // Indices serves as an offset into the index buffer in this case, use the same value for
190 // the draw call
191 *outIndices = indices;
192 }
193 else
194 {
195 // Need to stream the index buffer
196 // TODO: if GLES, nothing needs to be streamed
197
198 // Only compute the index range if the attributes also need to be streamed
199 if (attributesNeedStreaming)
200 {
201 *outIndexRange = ComputeIndexRange(type, indices, count, primitiveRestartEnabled);
202 }
203
204 // Allocate the streaming element array buffer
205 if (mStreamingElementArrayBuffer == 0)
206 {
207 mFunctions->genBuffers(1, &mStreamingElementArrayBuffer);
208 mStreamingElementArrayBufferSize = 0;
209 }
210
211 mStateManager->bindVertexArray(mVertexArrayID, getAppliedElementArrayBufferID());
212
213 mStateManager->bindBuffer(gl::BufferBinding::ElementArray, mStreamingElementArrayBuffer);
214 mAppliedElementArrayBuffer.set(context, nullptr);
215
216 // Make sure the element array buffer is large enough
217 const GLuint indexTypeBytes = gl::GetDrawElementsTypeSize(type);
218 size_t requiredStreamingBufferSize = indexTypeBytes * count;
219 if (requiredStreamingBufferSize > mStreamingElementArrayBufferSize)
220 {
221 // Copy the indices in while resizing the buffer
222 mFunctions->bufferData(GL_ELEMENT_ARRAY_BUFFER, requiredStreamingBufferSize, indices,
223 GL_DYNAMIC_DRAW);
224 mStreamingElementArrayBufferSize = requiredStreamingBufferSize;
225 }
226 else
227 {
228 // Put the indices at the beginning of the buffer
229 mFunctions->bufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, requiredStreamingBufferSize,
230 indices);
231 }
232
233 // Set the index offset for the draw call to zero since the supplied index pointer is to
234 // client data
235 *outIndices = nullptr;
236 }
237
238 return angle::Result::Continue;
239 }
240
computeStreamingAttributeSizes(const gl::AttributesMask & attribsToStream,GLsizei instanceCount,const gl::IndexRange & indexRange,size_t * outStreamingDataSize,size_t * outMaxAttributeDataSize) const241 void VertexArrayGL::computeStreamingAttributeSizes(const gl::AttributesMask &attribsToStream,
242 GLsizei instanceCount,
243 const gl::IndexRange &indexRange,
244 size_t *outStreamingDataSize,
245 size_t *outMaxAttributeDataSize) const
246 {
247 *outStreamingDataSize = 0;
248 *outMaxAttributeDataSize = 0;
249
250 ASSERT(attribsToStream.any());
251
252 const auto &attribs = mState.getVertexAttributes();
253 const auto &bindings = mState.getVertexBindings();
254
255 for (auto idx : attribsToStream)
256 {
257 const auto &attrib = attribs[idx];
258 const auto &binding = bindings[attrib.bindingIndex];
259
260 // If streaming is going to be required, compute the size of the required buffer
261 // and how much slack space at the beginning of the buffer will be required by determining
262 // the attribute with the largest data size.
263 size_t typeSize = ComputeVertexAttributeTypeSize(attrib);
264 GLuint adjustedDivisor = GetAdjustedDivisor(mAppliedNumViews, binding.getDivisor());
265 *outStreamingDataSize +=
266 typeSize * ComputeVertexBindingElementCount(adjustedDivisor, indexRange.vertexCount(),
267 instanceCount);
268 *outMaxAttributeDataSize = std::max(*outMaxAttributeDataSize, typeSize);
269 }
270 }
271
streamAttributes(const gl::Context * context,const gl::AttributesMask & attribsToStream,GLsizei instanceCount,const gl::IndexRange & indexRange) const272 angle::Result VertexArrayGL::streamAttributes(const gl::Context *context,
273 const gl::AttributesMask &attribsToStream,
274 GLsizei instanceCount,
275 const gl::IndexRange &indexRange) const
276 {
277 // Sync the vertex attribute state and track what data needs to be streamed
278 size_t streamingDataSize = 0;
279 size_t maxAttributeDataSize = 0;
280
281 computeStreamingAttributeSizes(attribsToStream, instanceCount, indexRange, &streamingDataSize,
282 &maxAttributeDataSize);
283
284 if (streamingDataSize == 0)
285 {
286 return angle::Result::Continue;
287 }
288
289 if (mStreamingArrayBuffer == 0)
290 {
291 mFunctions->genBuffers(1, &mStreamingArrayBuffer);
292 mStreamingArrayBufferSize = 0;
293 }
294
295 // If first is greater than zero, a slack space needs to be left at the beginning of the buffer
296 // so that the same 'first' argument can be passed into the draw call.
297 const size_t bufferEmptySpace = maxAttributeDataSize * indexRange.start;
298 const size_t requiredBufferSize = streamingDataSize + bufferEmptySpace;
299
300 mStateManager->bindBuffer(gl::BufferBinding::Array, mStreamingArrayBuffer);
301 if (requiredBufferSize > mStreamingArrayBufferSize)
302 {
303 mFunctions->bufferData(GL_ARRAY_BUFFER, requiredBufferSize, nullptr, GL_DYNAMIC_DRAW);
304 mStreamingArrayBufferSize = requiredBufferSize;
305 }
306
307 mStateManager->bindVertexArray(mVertexArrayID, getAppliedElementArrayBufferID());
308
309 // Unmapping a buffer can return GL_FALSE to indicate that the system has corrupted the data
310 // somehow (such as by a screen change), retry writing the data a few times and return
311 // OUT_OF_MEMORY if that fails.
312 GLboolean unmapResult = GL_FALSE;
313 size_t unmapRetryAttempts = 5;
314 while (unmapResult != GL_TRUE && --unmapRetryAttempts > 0)
315 {
316 uint8_t *bufferPointer = MapBufferRangeWithFallback(mFunctions, GL_ARRAY_BUFFER, 0,
317 requiredBufferSize, GL_MAP_WRITE_BIT);
318 size_t curBufferOffset = bufferEmptySpace;
319
320 const auto &attribs = mState.getVertexAttributes();
321 const auto &bindings = mState.getVertexBindings();
322
323 for (auto idx : attribsToStream)
324 {
325 const auto &attrib = attribs[idx];
326 ASSERT(IsVertexAttribPointerSupported(idx, attrib));
327
328 const auto &binding = bindings[attrib.bindingIndex];
329
330 GLuint adjustedDivisor = GetAdjustedDivisor(mAppliedNumViews, binding.getDivisor());
331 const size_t streamedVertexCount = ComputeVertexBindingElementCount(
332 adjustedDivisor, indexRange.vertexCount(), instanceCount);
333
334 const size_t sourceStride = ComputeVertexAttributeStride(attrib, binding);
335 const size_t destStride = ComputeVertexAttributeTypeSize(attrib);
336
337 // Vertices do not apply the 'start' offset when the divisor is non-zero even when doing
338 // a non-instanced draw call
339 const size_t firstIndex = adjustedDivisor == 0 ? indexRange.start : 0;
340
341 // Attributes using client memory ignore the VERTEX_ATTRIB_BINDING state.
342 // https://www.opengl.org/registry/specs/ARB/vertex_attrib_binding.txt
343 const uint8_t *inputPointer = static_cast<const uint8_t *>(attrib.pointer);
344
345 // Pack the data when copying it, user could have supplied a very large stride that
346 // would cause the buffer to be much larger than needed.
347 if (destStride == sourceStride)
348 {
349 // Can copy in one go, the data is packed
350 memcpy(bufferPointer + curBufferOffset, inputPointer + (sourceStride * firstIndex),
351 destStride * streamedVertexCount);
352 }
353 else
354 {
355 // Copy each vertex individually
356 for (size_t vertexIdx = 0; vertexIdx < streamedVertexCount; vertexIdx++)
357 {
358 uint8_t *out = bufferPointer + curBufferOffset + (destStride * vertexIdx);
359 const uint8_t *in = inputPointer + sourceStride * (vertexIdx + firstIndex);
360 memcpy(out, in, destStride);
361 }
362 }
363
364 // Compute where the 0-index vertex would be.
365 const size_t vertexStartOffset = curBufferOffset - (firstIndex * destStride);
366
367 callVertexAttribPointer(static_cast<GLuint>(idx), attrib,
368 static_cast<GLsizei>(destStride),
369 static_cast<GLintptr>(vertexStartOffset));
370
371 curBufferOffset += destStride * streamedVertexCount;
372 }
373
374 unmapResult = mFunctions->unmapBuffer(GL_ARRAY_BUFFER);
375 }
376
377 ANGLE_CHECK(GetImplAs<ContextGL>(context), unmapResult == GL_TRUE,
378 "Failed to unmap the client data streaming buffer.", GL_OUT_OF_MEMORY);
379 return angle::Result::Continue;
380 }
381
getVertexArrayID() const382 GLuint VertexArrayGL::getVertexArrayID() const
383 {
384 return mVertexArrayID;
385 }
386
getAppliedElementArrayBufferID() const387 GLuint VertexArrayGL::getAppliedElementArrayBufferID() const
388 {
389 if (mAppliedElementArrayBuffer.get() == nullptr)
390 {
391 return mStreamingElementArrayBuffer;
392 }
393
394 return GetImplAs<BufferGL>(mAppliedElementArrayBuffer.get())->getBufferID();
395 }
396
updateAttribEnabled(size_t attribIndex)397 void VertexArrayGL::updateAttribEnabled(size_t attribIndex)
398 {
399 const bool enabled = mState.getVertexAttribute(attribIndex).enabled &
400 mProgramActiveAttribLocationsMask.test(attribIndex);
401 if (mAppliedAttributes[attribIndex].enabled == enabled)
402 {
403 return;
404 }
405
406 if (enabled)
407 {
408 mFunctions->enableVertexAttribArray(static_cast<GLuint>(attribIndex));
409 }
410 else
411 {
412 mFunctions->disableVertexAttribArray(static_cast<GLuint>(attribIndex));
413 }
414
415 mAppliedAttributes[attribIndex].enabled = enabled;
416 }
417
updateAttribPointer(const gl::Context * context,size_t attribIndex)418 void VertexArrayGL::updateAttribPointer(const gl::Context *context, size_t attribIndex)
419 {
420 const VertexAttribute &attrib = mState.getVertexAttribute(attribIndex);
421
422 // According to SPEC, VertexAttribPointer should update the binding indexed attribIndex instead
423 // of the binding indexed attrib.bindingIndex (unless attribIndex == attrib.bindingIndex).
424 const VertexBinding &binding = mState.getVertexBinding(attribIndex);
425
426 // Early return when the vertex attribute isn't using a buffer object:
427 // - If we need to stream, defer the attribPointer to the draw call.
428 // - Skip the attribute that is disabled and uses a client memory pointer.
429 // - Skip the attribute whose buffer is detached by BindVertexBuffer. Since it cannot have a
430 // client memory pointer either, it must be disabled and shouldn't affect the draw.
431 const auto &bindingBuffer = binding.getBuffer();
432 const Buffer *arrayBuffer = bindingBuffer.get();
433 if (arrayBuffer == nullptr)
434 {
435 // Mark the applied binding isn't using a buffer by setting its buffer to nullptr so that if
436 // it starts to use a buffer later, there is no chance that the caching will skip it.
437 mAppliedBindings[attribIndex].setBuffer(context, nullptr);
438 return;
439 }
440
441 // We do not need to compare attrib.pointer because when we use a different client memory
442 // pointer, we don't need to update mAttributesNeedStreaming by binding.buffer and we won't
443 // update attribPointer in this function.
444 if ((SameVertexAttribFormat(mAppliedAttributes[attribIndex], attrib)) &&
445 (mAppliedAttributes[attribIndex].bindingIndex == attrib.bindingIndex) &&
446 (SameVertexBuffer(mAppliedBindings[attribIndex], binding)))
447 {
448 return;
449 }
450
451 // Since ANGLE always uses a non-zero VAO, we cannot use a client memory pointer on it:
452 // [OpenGL ES 3.0.2] Section 2.8 page 24:
453 // An INVALID_OPERATION error is generated when a non-zero vertex array object is bound,
454 // zero is bound to the ARRAY_BUFFER buffer object binding point, and the pointer argument
455 // is not NULL.
456
457 const BufferGL *arrayBufferGL = GetImplAs<BufferGL>(arrayBuffer);
458 mStateManager->bindBuffer(gl::BufferBinding::Array, arrayBufferGL->getBufferID());
459 callVertexAttribPointer(static_cast<GLuint>(attribIndex), attrib, binding.getStride(),
460 binding.getOffset());
461
462 mAppliedAttributes[attribIndex].format = attrib.format;
463
464 // After VertexAttribPointer, attrib.relativeOffset is set to 0 and attrib.bindingIndex is set
465 // to attribIndex in driver. If attrib.relativeOffset != 0 or attrib.bindingIndex !=
466 // attribIndex, they should be set in updateAttribFormat and updateAttribBinding. The cache
467 // should be consistent with driver so that we won't miss anything.
468 mAppliedAttributes[attribIndex].relativeOffset = 0;
469 mAppliedAttributes[attribIndex].bindingIndex = static_cast<GLuint>(attribIndex);
470
471 mAppliedBindings[attribIndex].setStride(binding.getStride());
472 mAppliedBindings[attribIndex].setOffset(binding.getOffset());
473 mAppliedBindings[attribIndex].setBuffer(context, binding.getBuffer().get());
474 }
475
callVertexAttribPointer(GLuint attribIndex,const VertexAttribute & attrib,GLsizei stride,GLintptr offset) const476 void VertexArrayGL::callVertexAttribPointer(GLuint attribIndex,
477 const VertexAttribute &attrib,
478 GLsizei stride,
479 GLintptr offset) const
480 {
481 const GLvoid *pointer = reinterpret_cast<const GLvoid *>(offset);
482 const angle::Format &format = *attrib.format;
483 if (format.isPureInt())
484 {
485 ASSERT(!format.isNorm());
486 mFunctions->vertexAttribIPointer(attribIndex, format.channelCount,
487 gl::ToGLenum(format.vertexAttribType), stride, pointer);
488 }
489 else
490 {
491 mFunctions->vertexAttribPointer(attribIndex, format.channelCount,
492 gl::ToGLenum(format.vertexAttribType), format.isNorm(),
493 stride, pointer);
494 }
495 }
496
supportVertexAttribBinding() const497 bool VertexArrayGL::supportVertexAttribBinding() const
498 {
499 ASSERT(mFunctions);
500 return (mFunctions->vertexAttribBinding != nullptr);
501 }
502
updateAttribFormat(size_t attribIndex)503 void VertexArrayGL::updateAttribFormat(size_t attribIndex)
504 {
505 ASSERT(supportVertexAttribBinding());
506
507 const VertexAttribute &attrib = mState.getVertexAttribute(attribIndex);
508 if (SameVertexAttribFormat(mAppliedAttributes[attribIndex], attrib))
509 {
510 return;
511 }
512
513 const angle::Format &format = *attrib.format;
514 if (format.isPureInt())
515 {
516 ASSERT(!format.isNorm());
517 mFunctions->vertexAttribIFormat(static_cast<GLuint>(attribIndex), format.channelCount,
518 gl::ToGLenum(format.vertexAttribType),
519 attrib.relativeOffset);
520 }
521 else
522 {
523 mFunctions->vertexAttribFormat(static_cast<GLuint>(attribIndex), format.channelCount,
524 gl::ToGLenum(format.vertexAttribType), format.isNorm(),
525 attrib.relativeOffset);
526 }
527
528 mAppliedAttributes[attribIndex].format = attrib.format;
529 mAppliedAttributes[attribIndex].relativeOffset = attrib.relativeOffset;
530 }
531
updateAttribBinding(size_t attribIndex)532 void VertexArrayGL::updateAttribBinding(size_t attribIndex)
533 {
534 ASSERT(supportVertexAttribBinding());
535
536 GLuint bindingIndex = mState.getVertexAttribute(attribIndex).bindingIndex;
537 if (mAppliedAttributes[attribIndex].bindingIndex == bindingIndex)
538 {
539 return;
540 }
541
542 mFunctions->vertexAttribBinding(static_cast<GLuint>(attribIndex), bindingIndex);
543
544 mAppliedAttributes[attribIndex].bindingIndex = bindingIndex;
545 }
546
updateBindingBuffer(const gl::Context * context,size_t bindingIndex)547 void VertexArrayGL::updateBindingBuffer(const gl::Context *context, size_t bindingIndex)
548 {
549 ASSERT(supportVertexAttribBinding());
550
551 const VertexBinding &binding = mState.getVertexBinding(bindingIndex);
552 if (SameVertexBuffer(mAppliedBindings[bindingIndex], binding))
553 {
554 return;
555 }
556
557 const Buffer *arrayBuffer = binding.getBuffer().get();
558 GLuint bufferId = 0;
559 if (arrayBuffer != nullptr)
560 {
561 bufferId = GetImplAs<BufferGL>(arrayBuffer)->getBufferID();
562 }
563
564 mFunctions->bindVertexBuffer(static_cast<GLuint>(bindingIndex), bufferId, binding.getOffset(),
565 binding.getStride());
566
567 mAppliedBindings[bindingIndex].setStride(binding.getStride());
568 mAppliedBindings[bindingIndex].setOffset(binding.getOffset());
569 mAppliedBindings[bindingIndex].setBuffer(context, binding.getBuffer().get());
570 }
571
updateBindingDivisor(size_t bindingIndex)572 void VertexArrayGL::updateBindingDivisor(size_t bindingIndex)
573 {
574 GLuint adjustedDivisor =
575 GetAdjustedDivisor(mAppliedNumViews, mState.getVertexBinding(bindingIndex).getDivisor());
576 if (mAppliedBindings[bindingIndex].getDivisor() == adjustedDivisor)
577 {
578 return;
579 }
580
581 if (supportVertexAttribBinding())
582 {
583 mFunctions->vertexBindingDivisor(static_cast<GLuint>(bindingIndex), adjustedDivisor);
584 }
585 else
586 {
587 // We can only use VertexAttribDivisor on platforms that don't support Vertex Attrib
588 // Binding.
589 mFunctions->vertexAttribDivisor(static_cast<GLuint>(bindingIndex), adjustedDivisor);
590 }
591
592 mAppliedBindings[bindingIndex].setDivisor(adjustedDivisor);
593 }
594
syncDirtyAttrib(const gl::Context * context,size_t attribIndex,const gl::VertexArray::DirtyAttribBits & dirtyAttribBits)595 void VertexArrayGL::syncDirtyAttrib(const gl::Context *context,
596 size_t attribIndex,
597 const gl::VertexArray::DirtyAttribBits &dirtyAttribBits)
598 {
599 ASSERT(dirtyAttribBits.any());
600
601 for (size_t dirtyBit : dirtyAttribBits)
602 {
603 switch (dirtyBit)
604 {
605 case VertexArray::DIRTY_ATTRIB_ENABLED:
606 updateAttribEnabled(attribIndex);
607 break;
608
609 case VertexArray::DIRTY_ATTRIB_POINTER_BUFFER:
610 case VertexArray::DIRTY_ATTRIB_POINTER:
611 updateAttribPointer(context, attribIndex);
612 break;
613
614 case VertexArray::DIRTY_ATTRIB_FORMAT:
615 ASSERT(supportVertexAttribBinding());
616 updateAttribFormat(attribIndex);
617 break;
618
619 case VertexArray::DIRTY_ATTRIB_BINDING:
620 ASSERT(supportVertexAttribBinding());
621 updateAttribBinding(attribIndex);
622 break;
623
624 default:
625 UNREACHABLE();
626 break;
627 }
628 }
629 }
630
syncDirtyBinding(const gl::Context * context,size_t bindingIndex,const gl::VertexArray::DirtyBindingBits & dirtyBindingBits)631 void VertexArrayGL::syncDirtyBinding(const gl::Context *context,
632 size_t bindingIndex,
633 const gl::VertexArray::DirtyBindingBits &dirtyBindingBits)
634 {
635 // Dependent state changes in buffers can trigger updates with no dirty bits set.
636
637 for (size_t dirtyBit : dirtyBindingBits)
638 {
639 switch (dirtyBit)
640 {
641 case VertexArray::DIRTY_BINDING_BUFFER:
642 ASSERT(supportVertexAttribBinding());
643 updateBindingBuffer(context, bindingIndex);
644 break;
645
646 case VertexArray::DIRTY_BINDING_DIVISOR:
647 updateBindingDivisor(bindingIndex);
648 break;
649
650 default:
651 UNREACHABLE();
652 break;
653 }
654 }
655 }
656
657 #define ANGLE_DIRTY_ATTRIB_FUNC(INDEX) \
658 case VertexArray::DIRTY_BIT_ATTRIB_0 + INDEX: \
659 syncDirtyAttrib(context, INDEX, (*attribBits)[INDEX]); \
660 (*attribBits)[INDEX].reset(); \
661 break;
662
663 #define ANGLE_DIRTY_BINDING_FUNC(INDEX) \
664 case VertexArray::DIRTY_BIT_BINDING_0 + INDEX: \
665 syncDirtyBinding(context, INDEX, (*bindingBits)[INDEX]); \
666 (*bindingBits)[INDEX].reset(); \
667 break;
668
669 #define ANGLE_DIRTY_BUFFER_DATA_FUNC(INDEX) \
670 case VertexArray::DIRTY_BIT_BUFFER_DATA_0 + INDEX: \
671 break;
672
syncState(const gl::Context * context,const gl::VertexArray::DirtyBits & dirtyBits,gl::VertexArray::DirtyAttribBitsArray * attribBits,gl::VertexArray::DirtyBindingBitsArray * bindingBits)673 angle::Result VertexArrayGL::syncState(const gl::Context *context,
674 const gl::VertexArray::DirtyBits &dirtyBits,
675 gl::VertexArray::DirtyAttribBitsArray *attribBits,
676 gl::VertexArray::DirtyBindingBitsArray *bindingBits)
677 {
678 mStateManager->bindVertexArray(mVertexArrayID, getAppliedElementArrayBufferID());
679
680 for (size_t dirtyBit : dirtyBits)
681 {
682 switch (dirtyBit)
683 {
684 case VertexArray::DIRTY_BIT_ELEMENT_ARRAY_BUFFER:
685 updateElementArrayBufferBinding(context);
686 break;
687
688 case VertexArray::DIRTY_BIT_ELEMENT_ARRAY_BUFFER_DATA:
689 break;
690
691 ANGLE_VERTEX_INDEX_CASES(ANGLE_DIRTY_ATTRIB_FUNC)
692 ANGLE_VERTEX_INDEX_CASES(ANGLE_DIRTY_BINDING_FUNC)
693 ANGLE_VERTEX_INDEX_CASES(ANGLE_DIRTY_BUFFER_DATA_FUNC)
694
695 default:
696 UNREACHABLE();
697 break;
698 }
699 }
700
701 return angle::Result::Continue;
702 }
703
applyNumViewsToDivisor(int numViews)704 void VertexArrayGL::applyNumViewsToDivisor(int numViews)
705 {
706 if (numViews != mAppliedNumViews)
707 {
708 mStateManager->bindVertexArray(mVertexArrayID, getAppliedElementArrayBufferID());
709 mAppliedNumViews = numViews;
710 for (size_t index = 0u; index < mAppliedBindings.size(); ++index)
711 {
712 updateBindingDivisor(index);
713 }
714 }
715 }
716
applyActiveAttribLocationsMask(const gl::AttributesMask & activeMask)717 void VertexArrayGL::applyActiveAttribLocationsMask(const gl::AttributesMask &activeMask)
718 {
719 gl::AttributesMask updateMask = mProgramActiveAttribLocationsMask ^ activeMask;
720 if (!updateMask.any())
721 {
722 return;
723 }
724 ASSERT(mVertexArrayID == mStateManager->getVertexArrayID());
725 mProgramActiveAttribLocationsMask = activeMask;
726
727 for (size_t attribIndex : updateMask)
728 {
729 updateAttribEnabled(attribIndex);
730 }
731 }
732
733 } // namespace rx
734