• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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