• 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 {
31 
GetNativeBufferID(const gl::Buffer * frontendBuffer)32 GLuint GetNativeBufferID(const gl::Buffer *frontendBuffer)
33 {
34     return frontendBuffer ? GetImplAs<BufferGL>(frontendBuffer)->getBufferID() : 0;
35 }
36 
SameVertexAttribFormat(const VertexAttributeGL & a,const VertexAttribute & b)37 bool SameVertexAttribFormat(const VertexAttributeGL &a, const VertexAttribute &b)
38 {
39     return a.format == b.format && a.relativeOffset == b.relativeOffset;
40 }
41 
SameVertexBuffer(const VertexBindingGL & a,const VertexBinding & b)42 bool SameVertexBuffer(const VertexBindingGL &a, const VertexBinding &b)
43 {
44     return a.stride == b.getStride() && a.offset == b.getOffset() &&
45            a.buffer == GetNativeBufferID(b.getBuffer().get());
46 }
47 
SameIndexBuffer(const VertexArrayStateGL * a,const gl::Buffer * frontendBuffer)48 bool SameIndexBuffer(const VertexArrayStateGL *a, const gl::Buffer *frontendBuffer)
49 {
50     return a->elementArrayBuffer == GetNativeBufferID(frontendBuffer);
51 }
52 
IsVertexAttribPointerSupported(size_t attribIndex,const VertexAttribute & attrib)53 bool IsVertexAttribPointerSupported(size_t attribIndex, const VertexAttribute &attrib)
54 {
55     return (attribIndex == attrib.bindingIndex && attrib.relativeOffset == 0);
56 }
57 
GetAdjustedDivisor(GLuint numViews,GLuint divisor)58 GLuint GetAdjustedDivisor(GLuint numViews, GLuint divisor)
59 {
60     return numViews * divisor;
61 }
62 
ValidateStateHelperGetIntegerv(const gl::Context * context,const GLuint localValue,const GLenum pname,const char * localName,const char * driverName)63 static angle::Result ValidateStateHelperGetIntegerv(const gl::Context *context,
64                                                     const GLuint localValue,
65                                                     const GLenum pname,
66                                                     const char *localName,
67                                                     const char *driverName)
68 {
69     const FunctionsGL *functions = GetFunctionsGL(context);
70 
71     GLint queryValue;
72     ANGLE_GL_TRY(context, functions->getIntegerv(pname, &queryValue));
73     if (localValue != static_cast<GLuint>(queryValue))
74     {
75         WARN() << localName << " (" << localValue << ") != " << driverName << " (" << queryValue
76                << ")";
77         // Re-add ASSERT: http://anglebug.com/3900
78         // ASSERT(false);
79     }
80 
81     return angle::Result::Continue;
82 }
83 
ValidateStateHelperGetVertexAttribiv(const gl::Context * context,const GLint index,const GLuint localValue,const GLenum pname,const char * localName,const char * driverName)84 static angle::Result ValidateStateHelperGetVertexAttribiv(const gl::Context *context,
85                                                           const GLint index,
86                                                           const GLuint localValue,
87                                                           const GLenum pname,
88                                                           const char *localName,
89                                                           const char *driverName)
90 {
91     const FunctionsGL *functions = GetFunctionsGL(context);
92 
93     GLint queryValue;
94     ANGLE_GL_TRY(context, functions->getVertexAttribiv(index, pname, &queryValue));
95     if (localValue != static_cast<GLuint>(queryValue))
96     {
97         WARN() << localName << "[" << index << "] (" << localValue << ") != " << driverName << "["
98                << index << "] (" << queryValue << ")";
99         // Re-add ASSERT: http://anglebug.com/3900
100         // ASSERT(false);
101     }
102 
103     return angle::Result::Continue;
104 }
105 }  // anonymous namespace
106 
VertexArrayGL(const VertexArrayState & state,GLuint id)107 VertexArrayGL::VertexArrayGL(const VertexArrayState &state, GLuint id)
108     : VertexArrayImpl(state),
109       mVertexArrayID(id),
110       mOwnsNativeState(true),
111       mNativeState(new VertexArrayStateGL(state.getMaxAttribs(), state.getMaxBindings()))
112 {
113     mForcedStreamingAttributesFirstOffsets.fill(0);
114 }
115 
VertexArrayGL(const gl::VertexArrayState & state,GLuint id,VertexArrayStateGL * sharedState)116 VertexArrayGL::VertexArrayGL(const gl::VertexArrayState &state,
117                              GLuint id,
118                              VertexArrayStateGL *sharedState)
119     : VertexArrayImpl(state), mVertexArrayID(id), mOwnsNativeState(false), mNativeState(sharedState)
120 {
121     ASSERT(mNativeState);
122     mForcedStreamingAttributesFirstOffsets.fill(0);
123 }
124 
~VertexArrayGL()125 VertexArrayGL::~VertexArrayGL() {}
126 
destroy(const gl::Context * context)127 void VertexArrayGL::destroy(const gl::Context *context)
128 {
129     StateManagerGL *stateManager = GetStateManagerGL(context);
130 
131     if (mOwnsNativeState)
132     {
133         stateManager->deleteVertexArray(mVertexArrayID);
134     }
135     mVertexArrayID   = 0;
136     mAppliedNumViews = 1;
137 
138     mElementArrayBuffer.set(context, nullptr);
139     for (gl::BindingPointer<gl::Buffer> &binding : mArrayBuffers)
140     {
141         binding.set(context, nullptr);
142     }
143 
144     stateManager->deleteBuffer(mStreamingElementArrayBuffer);
145     mStreamingElementArrayBufferSize = 0;
146     mStreamingElementArrayBuffer     = 0;
147 
148     stateManager->deleteBuffer(mStreamingArrayBuffer);
149     mStreamingArrayBufferSize = 0;
150     mStreamingArrayBuffer     = 0;
151 
152     if (mOwnsNativeState)
153     {
154         delete mNativeState;
155     }
156     mNativeState = nullptr;
157 }
158 
syncClientSideData(const gl::Context * context,const gl::AttributesMask & activeAttributesMask,GLint first,GLsizei count,GLsizei instanceCount) const159 angle::Result VertexArrayGL::syncClientSideData(const gl::Context *context,
160                                                 const gl::AttributesMask &activeAttributesMask,
161                                                 GLint first,
162                                                 GLsizei count,
163                                                 GLsizei instanceCount) const
164 {
165     return syncDrawState(context, activeAttributesMask, first, count,
166                          gl::DrawElementsType::InvalidEnum, nullptr, instanceCount, false, nullptr);
167 }
168 
updateElementArrayBufferBinding(const gl::Context * context) const169 angle::Result VertexArrayGL::updateElementArrayBufferBinding(const gl::Context *context) const
170 {
171     gl::Buffer *elementArrayBuffer = mState.getElementArrayBuffer();
172     if (!SameIndexBuffer(mNativeState, elementArrayBuffer))
173     {
174         GLuint elementArrayBufferId = GetNativeBufferID(elementArrayBuffer);
175 
176         StateManagerGL *stateManager = GetStateManagerGL(context);
177         stateManager->bindBuffer(gl::BufferBinding::ElementArray, elementArrayBufferId);
178         mElementArrayBuffer.set(context, elementArrayBuffer);
179         mNativeState->elementArrayBuffer = elementArrayBufferId;
180     }
181 
182     return angle::Result::Continue;
183 }
184 
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) const185 angle::Result VertexArrayGL::syncDrawState(const gl::Context *context,
186                                            const gl::AttributesMask &activeAttributesMask,
187                                            GLint first,
188                                            GLsizei count,
189                                            gl::DrawElementsType type,
190                                            const void *indices,
191                                            GLsizei instanceCount,
192                                            bool primitiveRestartEnabled,
193                                            const void **outIndices) const
194 {
195     // Check if any attributes need to be streamed, determines if the index range needs to be
196     // computed
197     const gl::AttributesMask &needsStreamingAttribs =
198         context->getStateCache().getActiveClientAttribsMask();
199 
200     // Determine if an index buffer needs to be streamed and the range of vertices that need to be
201     // copied
202     IndexRange indexRange;
203     const angle::FeaturesGL &features = GetFeaturesGL(context);
204     if (type != gl::DrawElementsType::InvalidEnum)
205     {
206         ANGLE_TRY(syncIndexData(context, count, type, indices, primitiveRestartEnabled,
207                                 needsStreamingAttribs.any(), &indexRange, outIndices));
208     }
209     else
210     {
211         // Not an indexed call, set the range to [first, first + count - 1]
212         indexRange.start = first;
213         indexRange.end   = first + count - 1;
214 
215         if (features.shiftInstancedArrayDataWithOffset.enabled && first > 0)
216         {
217             gl::AttributesMask updatedStreamingAttribsMask = needsStreamingAttribs;
218             auto candidateAttributesMask =
219                 mInstancedAttributesMask & mProgramActiveAttribLocationsMask;
220             for (auto attribIndex : candidateAttributesMask)
221             {
222 
223                 if (mForcedStreamingAttributesFirstOffsets[attribIndex] != first)
224                 {
225                     updatedStreamingAttribsMask.set(attribIndex);
226                     mForcedStreamingAttributesForDrawArraysInstancedMask.set(attribIndex);
227                     mForcedStreamingAttributesFirstOffsets[attribIndex] = first;
228                 }
229             }
230 
231             // We need to recover attributes whose divisor used to be > 0 but is reset to 0 now if
232             // any
233             auto forcedStreamingAttributesNeedRecoverMask =
234                 candidateAttributesMask ^ mForcedStreamingAttributesForDrawArraysInstancedMask;
235             if (forcedStreamingAttributesNeedRecoverMask.any())
236             {
237                 ANGLE_TRY(recoverForcedStreamingAttributesForDrawArraysInstanced(
238                     context, &forcedStreamingAttributesNeedRecoverMask));
239                 mForcedStreamingAttributesForDrawArraysInstancedMask = candidateAttributesMask;
240             }
241 
242             if (updatedStreamingAttribsMask.any())
243             {
244                 ANGLE_TRY(streamAttributes(context, updatedStreamingAttribsMask, instanceCount,
245                                            indexRange, true));
246             }
247             return angle::Result::Continue;
248         }
249     }
250 
251     if (needsStreamingAttribs.any())
252     {
253         ANGLE_TRY(
254             streamAttributes(context, needsStreamingAttribs, instanceCount, indexRange, false));
255     }
256 
257     return angle::Result::Continue;
258 }
259 
syncIndexData(const gl::Context * context,GLsizei count,gl::DrawElementsType type,const void * indices,bool primitiveRestartEnabled,bool attributesNeedStreaming,IndexRange * outIndexRange,const void ** outIndices) const260 angle::Result VertexArrayGL::syncIndexData(const gl::Context *context,
261                                            GLsizei count,
262                                            gl::DrawElementsType type,
263                                            const void *indices,
264                                            bool primitiveRestartEnabled,
265                                            bool attributesNeedStreaming,
266                                            IndexRange *outIndexRange,
267                                            const void **outIndices) const
268 {
269     ASSERT(outIndices);
270 
271     gl::Buffer *elementArrayBuffer = mState.getElementArrayBuffer();
272 
273     // Need to check the range of indices if attributes need to be streamed
274     if (elementArrayBuffer)
275     {
276         ASSERT(SameIndexBuffer(mNativeState, elementArrayBuffer));
277         // Only compute the index range if the attributes also need to be streamed
278         if (attributesNeedStreaming)
279         {
280             ptrdiff_t elementArrayBufferOffset = reinterpret_cast<ptrdiff_t>(indices);
281             ANGLE_TRY(mState.getElementArrayBuffer()->getIndexRange(
282                 context, type, elementArrayBufferOffset, count, primitiveRestartEnabled,
283                 outIndexRange));
284         }
285 
286         // Indices serves as an offset into the index buffer in this case, use the same value for
287         // the draw call
288         *outIndices = indices;
289     }
290     else
291     {
292         const FunctionsGL *functions = GetFunctionsGL(context);
293         StateManagerGL *stateManager = GetStateManagerGL(context);
294 
295         // Need to stream the index buffer
296         // TODO: if GLES, nothing needs to be streamed
297 
298         // Only compute the index range if the attributes also need to be streamed
299         if (attributesNeedStreaming)
300         {
301             *outIndexRange = ComputeIndexRange(type, indices, count, primitiveRestartEnabled);
302         }
303 
304         // Allocate the streaming element array buffer
305         if (mStreamingElementArrayBuffer == 0)
306         {
307             ANGLE_GL_TRY(context, functions->genBuffers(1, &mStreamingElementArrayBuffer));
308             mStreamingElementArrayBufferSize = 0;
309         }
310 
311         stateManager->bindVertexArray(mVertexArrayID, mNativeState);
312 
313         stateManager->bindBuffer(gl::BufferBinding::ElementArray, mStreamingElementArrayBuffer);
314         mElementArrayBuffer.set(context, nullptr);
315         mNativeState->elementArrayBuffer = mStreamingElementArrayBuffer;
316 
317         // Make sure the element array buffer is large enough
318         const GLuint indexTypeBytes        = gl::GetDrawElementsTypeSize(type);
319         size_t requiredStreamingBufferSize = indexTypeBytes * count;
320         if (requiredStreamingBufferSize > mStreamingElementArrayBufferSize)
321         {
322             // Copy the indices in while resizing the buffer
323             ANGLE_GL_TRY(context,
324                          functions->bufferData(GL_ELEMENT_ARRAY_BUFFER, requiredStreamingBufferSize,
325                                                indices, GL_DYNAMIC_DRAW));
326             mStreamingElementArrayBufferSize = requiredStreamingBufferSize;
327         }
328         else
329         {
330             // Put the indices at the beginning of the buffer
331             ANGLE_GL_TRY(context, functions->bufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0,
332                                                            requiredStreamingBufferSize, indices));
333         }
334 
335         // Set the index offset for the draw call to zero since the supplied index pointer is to
336         // client data
337         *outIndices = nullptr;
338     }
339 
340     return angle::Result::Continue;
341 }
342 
computeStreamingAttributeSizes(const gl::AttributesMask & attribsToStream,GLsizei instanceCount,const gl::IndexRange & indexRange,size_t * outStreamingDataSize,size_t * outMaxAttributeDataSize) const343 void VertexArrayGL::computeStreamingAttributeSizes(const gl::AttributesMask &attribsToStream,
344                                                    GLsizei instanceCount,
345                                                    const gl::IndexRange &indexRange,
346                                                    size_t *outStreamingDataSize,
347                                                    size_t *outMaxAttributeDataSize) const
348 {
349     *outStreamingDataSize    = 0;
350     *outMaxAttributeDataSize = 0;
351 
352     ASSERT(attribsToStream.any());
353 
354     const auto &attribs  = mState.getVertexAttributes();
355     const auto &bindings = mState.getVertexBindings();
356 
357     for (auto idx : attribsToStream)
358     {
359         const auto &attrib  = attribs[idx];
360         const auto &binding = bindings[attrib.bindingIndex];
361 
362         // If streaming is going to be required, compute the size of the required buffer
363         // and how much slack space at the beginning of the buffer will be required by determining
364         // the attribute with the largest data size.
365         size_t typeSize        = ComputeVertexAttributeTypeSize(attrib);
366         GLuint adjustedDivisor = GetAdjustedDivisor(mAppliedNumViews, binding.getDivisor());
367         *outStreamingDataSize +=
368             typeSize * ComputeVertexBindingElementCount(adjustedDivisor, indexRange.vertexCount(),
369                                                         instanceCount);
370         *outMaxAttributeDataSize = std::max(*outMaxAttributeDataSize, typeSize);
371     }
372 }
373 
streamAttributes(const gl::Context * context,const gl::AttributesMask & attribsToStream,GLsizei instanceCount,const gl::IndexRange & indexRange,bool applyExtraOffsetWorkaroundForInstancedAttributes) const374 angle::Result VertexArrayGL::streamAttributes(
375     const gl::Context *context,
376     const gl::AttributesMask &attribsToStream,
377     GLsizei instanceCount,
378     const gl::IndexRange &indexRange,
379     bool applyExtraOffsetWorkaroundForInstancedAttributes) const
380 {
381     const FunctionsGL *functions = GetFunctionsGL(context);
382     StateManagerGL *stateManager = GetStateManagerGL(context);
383 
384     // Sync the vertex attribute state and track what data needs to be streamed
385     size_t streamingDataSize    = 0;
386     size_t maxAttributeDataSize = 0;
387 
388     computeStreamingAttributeSizes(attribsToStream, instanceCount, indexRange, &streamingDataSize,
389                                    &maxAttributeDataSize);
390 
391     if (streamingDataSize == 0)
392     {
393         return angle::Result::Continue;
394     }
395 
396     if (mStreamingArrayBuffer == 0)
397     {
398         ANGLE_GL_TRY(context, functions->genBuffers(1, &mStreamingArrayBuffer));
399         mStreamingArrayBufferSize = 0;
400     }
401 
402     // If first is greater than zero, a slack space needs to be left at the beginning of the buffer
403     // for each attribute so that the same 'first' argument can be passed into the draw call.
404     const size_t bufferEmptySpace =
405         attribsToStream.count() * maxAttributeDataSize * indexRange.start;
406     const size_t requiredBufferSize = streamingDataSize + bufferEmptySpace;
407 
408     stateManager->bindBuffer(gl::BufferBinding::Array, mStreamingArrayBuffer);
409     if (requiredBufferSize > mStreamingArrayBufferSize)
410     {
411         ANGLE_GL_TRY(context, functions->bufferData(GL_ARRAY_BUFFER, requiredBufferSize, nullptr,
412                                                     GL_DYNAMIC_DRAW));
413         mStreamingArrayBufferSize = requiredBufferSize;
414     }
415 
416     stateManager->bindVertexArray(mVertexArrayID, mNativeState);
417 
418     // Unmapping a buffer can return GL_FALSE to indicate that the system has corrupted the data
419     // somehow (such as by a screen change), retry writing the data a few times and return
420     // OUT_OF_MEMORY if that fails.
421     GLboolean unmapResult     = GL_FALSE;
422     size_t unmapRetryAttempts = 5;
423     while (unmapResult != GL_TRUE && --unmapRetryAttempts > 0)
424     {
425         uint8_t *bufferPointer = MapBufferRangeWithFallback(functions, GL_ARRAY_BUFFER, 0,
426                                                             requiredBufferSize, GL_MAP_WRITE_BIT);
427         size_t curBufferOffset = maxAttributeDataSize * indexRange.start;
428 
429         const auto &attribs  = mState.getVertexAttributes();
430         const auto &bindings = mState.getVertexBindings();
431 
432         for (auto idx : attribsToStream)
433         {
434             const auto &attrib = attribs[idx];
435             ASSERT(IsVertexAttribPointerSupported(idx, attrib));
436 
437             const auto &binding = bindings[attrib.bindingIndex];
438 
439             GLuint adjustedDivisor = GetAdjustedDivisor(mAppliedNumViews, binding.getDivisor());
440             // streamedVertexCount is only going to be modified by
441             // shiftInstancedArrayDataWithOffset workaround, otherwise it's const
442             size_t streamedVertexCount = ComputeVertexBindingElementCount(
443                 adjustedDivisor, indexRange.vertexCount(), instanceCount);
444 
445             const size_t sourceStride = ComputeVertexAttributeStride(attrib, binding);
446             const size_t destStride   = ComputeVertexAttributeTypeSize(attrib);
447 
448             // Vertices do not apply the 'start' offset when the divisor is non-zero even when doing
449             // a non-instanced draw call
450             const size_t firstIndex =
451                 (adjustedDivisor == 0 || applyExtraOffsetWorkaroundForInstancedAttributes)
452                     ? indexRange.start
453                     : 0;
454 
455             // Attributes using client memory ignore the VERTEX_ATTRIB_BINDING state.
456             // https://www.opengl.org/registry/specs/ARB/vertex_attrib_binding.txt
457             const uint8_t *inputPointer = static_cast<const uint8_t *>(attrib.pointer);
458             // store batchMemcpySize since streamedVertexCount could be changed by workaround
459             const size_t batchMemcpySize = destStride * streamedVertexCount;
460 
461             size_t batchMemcpyInputOffset                    = sourceStride * firstIndex;
462             bool needsUnmapAndRebindStreamingAttributeBuffer = false;
463             size_t firstIndexForSeparateCopy                 = firstIndex;
464 
465             if (applyExtraOffsetWorkaroundForInstancedAttributes && adjustedDivisor > 0)
466             {
467                 const size_t originalStreamedVertexCount = streamedVertexCount;
468                 streamedVertexCount =
469                     (instanceCount + indexRange.start + adjustedDivisor - 1u) / adjustedDivisor;
470 
471                 const size_t copySize =
472                     sourceStride *
473                     originalStreamedVertexCount;  // the real data in the buffer we are streaming
474 
475                 const gl::Buffer *bindingBufferPointer = binding.getBuffer().get();
476                 if (!bindingBufferPointer)
477                 {
478                     if (!inputPointer)
479                     {
480                         continue;
481                     }
482                     inputPointer = static_cast<const uint8_t *>(attrib.pointer);
483                 }
484                 else
485                 {
486                     needsUnmapAndRebindStreamingAttributeBuffer = true;
487                     const auto buffer = GetImplAs<BufferGL>(bindingBufferPointer);
488                     stateManager->bindBuffer(gl::BufferBinding::Array, buffer->getBufferID());
489                     // The workaround is only for latest Mac Intel so glMapBufferRange should be
490                     // supported
491                     ASSERT(CanMapBufferForRead(functions));
492                     uint8_t *inputBufferPointer = MapBufferRangeWithFallback(
493                         functions, GL_ARRAY_BUFFER, binding.getOffset(), copySize, GL_MAP_READ_BIT);
494                     ASSERT(inputBufferPointer);
495                     inputPointer = inputBufferPointer;
496                 }
497 
498                 batchMemcpyInputOffset    = 0;
499                 firstIndexForSeparateCopy = 0;
500             }
501 
502             // Pack the data when copying it, user could have supplied a very large stride that
503             // would cause the buffer to be much larger than needed.
504             if (destStride == sourceStride)
505             {
506                 // Can copy in one go, the data is packed
507                 memcpy(bufferPointer + curBufferOffset, inputPointer + batchMemcpyInputOffset,
508                        batchMemcpySize);
509             }
510             else
511             {
512                 for (size_t vertexIdx = 0; vertexIdx < streamedVertexCount; vertexIdx++)
513                 {
514                     uint8_t *out = bufferPointer + curBufferOffset + (destStride * vertexIdx);
515                     const uint8_t *in =
516                         inputPointer + sourceStride * (vertexIdx + firstIndexForSeparateCopy);
517                     memcpy(out, in, destStride);
518                 }
519             }
520 
521             if (needsUnmapAndRebindStreamingAttributeBuffer)
522             {
523                 ANGLE_GL_TRY(context, functions->unmapBuffer(GL_ARRAY_BUFFER));
524                 stateManager->bindBuffer(gl::BufferBinding::Array, mStreamingArrayBuffer);
525             }
526 
527             // Compute where the 0-index vertex would be.
528             const size_t vertexStartOffset = curBufferOffset - (firstIndex * destStride);
529 
530             ANGLE_TRY(callVertexAttribPointer(context, static_cast<GLuint>(idx), attrib,
531                                               static_cast<GLsizei>(destStride),
532                                               static_cast<GLintptr>(vertexStartOffset)));
533 
534             // Update the state to track the streamed attribute
535             mNativeState->attributes[idx].format = attrib.format;
536 
537             mNativeState->attributes[idx].relativeOffset = 0;
538             mNativeState->attributes[idx].bindingIndex   = static_cast<GLuint>(idx);
539 
540             mNativeState->bindings[idx].stride = static_cast<GLsizei>(destStride);
541             mNativeState->bindings[idx].offset = static_cast<GLintptr>(vertexStartOffset);
542             mArrayBuffers[idx].set(context, nullptr);
543             mNativeState->bindings[idx].buffer = mStreamingArrayBuffer;
544 
545             // There's maxAttributeDataSize * indexRange.start of empty space allocated for each
546             // streaming attributes
547             curBufferOffset +=
548                 destStride * streamedVertexCount + maxAttributeDataSize * indexRange.start;
549         }
550 
551         unmapResult = ANGLE_GL_TRY(context, functions->unmapBuffer(GL_ARRAY_BUFFER));
552     }
553 
554     ANGLE_CHECK(GetImplAs<ContextGL>(context), unmapResult == GL_TRUE,
555                 "Failed to unmap the client data streaming buffer.", GL_OUT_OF_MEMORY);
556     return angle::Result::Continue;
557 }
558 
recoverForcedStreamingAttributesForDrawArraysInstanced(const gl::Context * context) const559 angle::Result VertexArrayGL::recoverForcedStreamingAttributesForDrawArraysInstanced(
560     const gl::Context *context) const
561 {
562     return recoverForcedStreamingAttributesForDrawArraysInstanced(
563         context, &mForcedStreamingAttributesForDrawArraysInstancedMask);
564 }
565 
recoverForcedStreamingAttributesForDrawArraysInstanced(const gl::Context * context,gl::AttributesMask * attributeMask) const566 angle::Result VertexArrayGL::recoverForcedStreamingAttributesForDrawArraysInstanced(
567     const gl::Context *context,
568     gl::AttributesMask *attributeMask) const
569 {
570     if (attributeMask->none())
571     {
572         return angle::Result::Continue;
573     }
574 
575     StateManagerGL *stateManager = GetStateManagerGL(context);
576 
577     stateManager->bindVertexArray(mVertexArrayID, mNativeState);
578 
579     const auto &attribs  = mState.getVertexAttributes();
580     const auto &bindings = mState.getVertexBindings();
581     for (auto idx : *attributeMask)
582     {
583         const auto &attrib = attribs[idx];
584         ASSERT(IsVertexAttribPointerSupported(idx, attrib));
585 
586         const auto &binding = bindings[attrib.bindingIndex];
587         const auto buffer   = GetImplAs<BufferGL>(binding.getBuffer().get());
588         stateManager->bindBuffer(gl::BufferBinding::Array, buffer->getBufferID());
589 
590         ANGLE_TRY(callVertexAttribPointer(context, static_cast<GLuint>(idx), attrib,
591                                           static_cast<GLsizei>(binding.getStride()),
592                                           static_cast<GLintptr>(binding.getOffset())));
593 
594         // Restore the state to track their original buffers
595         mNativeState->attributes[idx].format = attrib.format;
596 
597         mNativeState->attributes[idx].relativeOffset = 0;
598         mNativeState->attributes[idx].bindingIndex   = static_cast<GLuint>(attrib.bindingIndex);
599 
600         mNativeState->bindings[idx].stride = binding.getStride();
601         mNativeState->bindings[idx].offset = binding.getOffset();
602         mArrayBuffers[idx].set(context, binding.getBuffer().get());
603         mNativeState->bindings[idx].buffer = buffer->getBufferID();
604     }
605 
606     attributeMask->reset();
607     mForcedStreamingAttributesFirstOffsets.fill(0);
608 
609     return angle::Result::Continue;
610 }
611 
getVertexArrayID() const612 GLuint VertexArrayGL::getVertexArrayID() const
613 {
614     return mVertexArrayID;
615 }
616 
getNativeState() const617 rx::VertexArrayStateGL *VertexArrayGL::getNativeState() const
618 {
619     return mNativeState;
620 }
621 
updateAttribEnabled(const gl::Context * context,size_t attribIndex)622 angle::Result VertexArrayGL::updateAttribEnabled(const gl::Context *context, size_t attribIndex)
623 {
624     const bool enabled = mState.getVertexAttribute(attribIndex).enabled &&
625                          mProgramActiveAttribLocationsMask.test(attribIndex);
626     if (mNativeState->attributes[attribIndex].enabled == enabled)
627     {
628         return angle::Result::Continue;
629     }
630 
631     const FunctionsGL *functions = GetFunctionsGL(context);
632 
633     if (enabled)
634     {
635         ANGLE_GL_TRY(context, functions->enableVertexAttribArray(static_cast<GLuint>(attribIndex)));
636     }
637     else
638     {
639         ANGLE_GL_TRY(context,
640                      functions->disableVertexAttribArray(static_cast<GLuint>(attribIndex)));
641     }
642 
643     mNativeState->attributes[attribIndex].enabled = enabled;
644     return angle::Result::Continue;
645 }
646 
updateAttribPointer(const gl::Context * context,size_t attribIndex)647 angle::Result VertexArrayGL::updateAttribPointer(const gl::Context *context, size_t attribIndex)
648 {
649     const angle::FeaturesGL &features = GetFeaturesGL(context);
650 
651     const VertexAttribute &attrib = mState.getVertexAttribute(attribIndex);
652 
653     // According to SPEC, VertexAttribPointer should update the binding indexed attribIndex instead
654     // of the binding indexed attrib.bindingIndex (unless attribIndex == attrib.bindingIndex).
655     const VertexBinding &binding = mState.getVertexBinding(attribIndex);
656 
657     // Early return when the vertex attribute isn't using a buffer object:
658     // - If we need to stream, defer the attribPointer to the draw call.
659     // - Skip the attribute that is disabled and uses a client memory pointer.
660     // - Skip the attribute whose buffer is detached by BindVertexBuffer. Since it cannot have a
661     //   client memory pointer either, it must be disabled and shouldn't affect the draw.
662     const auto &bindingBuffer = binding.getBuffer();
663     gl::Buffer *arrayBuffer   = bindingBuffer.get();
664     if (arrayBuffer == nullptr)
665     {
666         // Mark the applied binding isn't using a buffer by setting its buffer to nullptr so that if
667         // it starts to use a buffer later, there is no chance that the caching will skip it.
668 
669         mArrayBuffers[attribIndex].set(context, nullptr);
670         mNativeState->bindings[attribIndex].buffer = 0;
671         return angle::Result::Continue;
672     }
673 
674     // We do not need to compare attrib.pointer because when we use a different client memory
675     // pointer, we don't need to update mAttributesNeedStreaming by binding.buffer and we won't
676     // update attribPointer in this function.
677     if ((SameVertexAttribFormat(mNativeState->attributes[attribIndex], attrib)) &&
678         (mNativeState->attributes[attribIndex].bindingIndex == attrib.bindingIndex) &&
679         (SameVertexBuffer(mNativeState->bindings[attribIndex], binding)))
680     {
681         return angle::Result::Continue;
682     }
683 
684     // Since ANGLE always uses a non-zero VAO, we cannot use a client memory pointer on it:
685     // [OpenGL ES 3.0.2] Section 2.8 page 24:
686     // An INVALID_OPERATION error is generated when a non-zero vertex array object is bound,
687     // zero is bound to the ARRAY_BUFFER buffer object binding point, and the pointer argument
688     // is not NULL.
689 
690     StateManagerGL *stateManager = GetStateManagerGL(context);
691     BufferGL *bufferGL           = GetImplAs<BufferGL>(arrayBuffer);
692     GLuint bufferId              = bufferGL->getBufferID();
693     stateManager->bindBuffer(gl::BufferBinding::Array, bufferId);
694     if (features.ensureNonEmptyBufferIsBoundForDraw.enabled && bufferGL->getBufferSize() == 0)
695     {
696         constexpr uint32_t data = 0;
697         ANGLE_TRY(bufferGL->setData(context, gl::BufferBinding::Array, &data, sizeof(data),
698                                     gl::BufferUsage::StaticDraw));
699         ASSERT(bufferGL->getBufferSize() > 0);
700     }
701     ANGLE_TRY(callVertexAttribPointer(context, static_cast<GLuint>(attribIndex), attrib,
702                                       binding.getStride(), binding.getOffset()));
703 
704     mNativeState->attributes[attribIndex].format = attrib.format;
705 
706     // After VertexAttribPointer, attrib.relativeOffset is set to 0 and attrib.bindingIndex is set
707     // to attribIndex in driver. If attrib.relativeOffset != 0 or attrib.bindingIndex !=
708     // attribIndex, they should be set in updateAttribFormat and updateAttribBinding. The cache
709     // should be consistent with driver so that we won't miss anything.
710     mNativeState->attributes[attribIndex].relativeOffset = 0;
711     mNativeState->attributes[attribIndex].bindingIndex   = static_cast<GLuint>(attribIndex);
712 
713     mNativeState->bindings[attribIndex].stride = binding.getStride();
714     mNativeState->bindings[attribIndex].offset = binding.getOffset();
715     mArrayBuffers[attribIndex].set(context, arrayBuffer);
716     mNativeState->bindings[attribIndex].buffer = bufferId;
717 
718     return angle::Result::Continue;
719 }
720 
callVertexAttribPointer(const gl::Context * context,GLuint attribIndex,const VertexAttribute & attrib,GLsizei stride,GLintptr offset) const721 angle::Result VertexArrayGL::callVertexAttribPointer(const gl::Context *context,
722                                                      GLuint attribIndex,
723                                                      const VertexAttribute &attrib,
724                                                      GLsizei stride,
725                                                      GLintptr offset) const
726 {
727     const FunctionsGL *functions = GetFunctionsGL(context);
728     const GLvoid *pointer        = reinterpret_cast<const GLvoid *>(offset);
729     const angle::Format &format  = *attrib.format;
730     if (format.isPureInt())
731     {
732         ASSERT(!format.isNorm());
733         ANGLE_GL_TRY(context, functions->vertexAttribIPointer(attribIndex, format.channelCount,
734                                                               gl::ToGLenum(format.vertexAttribType),
735                                                               stride, pointer));
736     }
737     else
738     {
739         ANGLE_GL_TRY(context, functions->vertexAttribPointer(attribIndex, format.channelCount,
740                                                              gl::ToGLenum(format.vertexAttribType),
741                                                              format.isNorm(), stride, pointer));
742     }
743 
744     return angle::Result::Continue;
745 }
746 
supportVertexAttribBinding(const gl::Context * context) const747 bool VertexArrayGL::supportVertexAttribBinding(const gl::Context *context) const
748 {
749     const FunctionsGL *functions      = GetFunctionsGL(context);
750     const angle::FeaturesGL &features = GetFeaturesGL(context);
751     ASSERT(functions);
752     // Vertex attrib bindings are not supported on the default VAO so if we're syncing to the
753     // default VAO due to the feature, disable bindings.
754     return (functions->vertexAttribBinding != nullptr) &&
755            !features.syncVertexArraysToDefault.enabled;
756 }
757 
updateAttribFormat(const gl::Context * context,size_t attribIndex)758 angle::Result VertexArrayGL::updateAttribFormat(const gl::Context *context, size_t attribIndex)
759 {
760     ASSERT(supportVertexAttribBinding(context));
761 
762     const VertexAttribute &attrib = mState.getVertexAttribute(attribIndex);
763     if (SameVertexAttribFormat(mNativeState->attributes[attribIndex], attrib))
764     {
765         return angle::Result::Continue;
766     }
767 
768     const FunctionsGL *functions = GetFunctionsGL(context);
769 
770     const angle::Format &format = *attrib.format;
771     if (format.isPureInt())
772     {
773         ASSERT(!format.isNorm());
774         ANGLE_GL_TRY(context, functions->vertexAttribIFormat(
775                                   static_cast<GLuint>(attribIndex), format.channelCount,
776                                   gl::ToGLenum(format.vertexAttribType), attrib.relativeOffset));
777     }
778     else
779     {
780         ANGLE_GL_TRY(context, functions->vertexAttribFormat(
781                                   static_cast<GLuint>(attribIndex), format.channelCount,
782                                   gl::ToGLenum(format.vertexAttribType), format.isNorm(),
783                                   attrib.relativeOffset));
784     }
785 
786     mNativeState->attributes[attribIndex].format         = attrib.format;
787     mNativeState->attributes[attribIndex].relativeOffset = attrib.relativeOffset;
788     return angle::Result::Continue;
789 }
790 
updateAttribBinding(const gl::Context * context,size_t attribIndex)791 angle::Result VertexArrayGL::updateAttribBinding(const gl::Context *context, size_t attribIndex)
792 {
793     ASSERT(supportVertexAttribBinding(context));
794 
795     GLuint bindingIndex = mState.getVertexAttribute(attribIndex).bindingIndex;
796     if (mNativeState->attributes[attribIndex].bindingIndex == bindingIndex)
797     {
798         return angle::Result::Continue;
799     }
800 
801     const FunctionsGL *functions = GetFunctionsGL(context);
802     ANGLE_GL_TRY(context,
803                  functions->vertexAttribBinding(static_cast<GLuint>(attribIndex), bindingIndex));
804 
805     mNativeState->attributes[attribIndex].bindingIndex = bindingIndex;
806 
807     return angle::Result::Continue;
808 }
809 
updateBindingBuffer(const gl::Context * context,size_t bindingIndex)810 angle::Result VertexArrayGL::updateBindingBuffer(const gl::Context *context, size_t bindingIndex)
811 {
812     ASSERT(supportVertexAttribBinding(context));
813 
814     const VertexBinding &binding = mState.getVertexBinding(bindingIndex);
815     if (SameVertexBuffer(mNativeState->bindings[bindingIndex], binding))
816     {
817         return angle::Result::Continue;
818     }
819 
820     gl::Buffer *arrayBuffer = binding.getBuffer().get();
821     GLuint bufferId         = GetNativeBufferID(arrayBuffer);
822 
823     const FunctionsGL *functions = GetFunctionsGL(context);
824     ANGLE_GL_TRY(context, functions->bindVertexBuffer(static_cast<GLuint>(bindingIndex), bufferId,
825                                                       binding.getOffset(), binding.getStride()));
826 
827     mNativeState->bindings[bindingIndex].stride = binding.getStride();
828     mNativeState->bindings[bindingIndex].offset = binding.getOffset();
829     mArrayBuffers[bindingIndex].set(context, arrayBuffer);
830     mNativeState->bindings[bindingIndex].buffer = bufferId;
831 
832     return angle::Result::Continue;
833 }
834 
updateBindingDivisor(const gl::Context * context,size_t bindingIndex)835 angle::Result VertexArrayGL::updateBindingDivisor(const gl::Context *context, size_t bindingIndex)
836 {
837     GLuint adjustedDivisor =
838         GetAdjustedDivisor(mAppliedNumViews, mState.getVertexBinding(bindingIndex).getDivisor());
839     if (mNativeState->bindings[bindingIndex].divisor == adjustedDivisor)
840     {
841         return angle::Result::Continue;
842     }
843 
844     const FunctionsGL *functions = GetFunctionsGL(context);
845     if (supportVertexAttribBinding(context))
846     {
847         ANGLE_GL_TRY(context, functions->vertexBindingDivisor(static_cast<GLuint>(bindingIndex),
848                                                               adjustedDivisor));
849     }
850     else
851     {
852         // We can only use VertexAttribDivisor on platforms that don't support Vertex Attrib
853         // Binding.
854         ANGLE_GL_TRY(context, functions->vertexAttribDivisor(static_cast<GLuint>(bindingIndex),
855                                                              adjustedDivisor));
856     }
857 
858     if (adjustedDivisor > 0)
859     {
860         mInstancedAttributesMask.set(bindingIndex);
861     }
862     else if (mInstancedAttributesMask.test(bindingIndex))
863     {
864         // divisor is reset to 0
865         mInstancedAttributesMask.reset(bindingIndex);
866     }
867 
868     mNativeState->bindings[bindingIndex].divisor = adjustedDivisor;
869 
870     return angle::Result::Continue;
871 }
872 
syncDirtyAttrib(const gl::Context * context,size_t attribIndex,const gl::VertexArray::DirtyAttribBits & dirtyAttribBits)873 angle::Result VertexArrayGL::syncDirtyAttrib(
874     const gl::Context *context,
875     size_t attribIndex,
876     const gl::VertexArray::DirtyAttribBits &dirtyAttribBits)
877 {
878     ASSERT(dirtyAttribBits.any());
879 
880     for (size_t dirtyBit : dirtyAttribBits)
881     {
882         switch (dirtyBit)
883         {
884             case VertexArray::DIRTY_ATTRIB_ENABLED:
885                 ANGLE_TRY(updateAttribEnabled(context, attribIndex));
886                 break;
887 
888             case VertexArray::DIRTY_ATTRIB_POINTER_BUFFER:
889             case VertexArray::DIRTY_ATTRIB_POINTER:
890                 ANGLE_TRY(updateAttribPointer(context, attribIndex));
891                 break;
892 
893             case VertexArray::DIRTY_ATTRIB_FORMAT:
894                 ASSERT(supportVertexAttribBinding(context));
895                 ANGLE_TRY(updateAttribFormat(context, attribIndex));
896                 break;
897 
898             case VertexArray::DIRTY_ATTRIB_BINDING:
899                 ASSERT(supportVertexAttribBinding(context));
900                 ANGLE_TRY(updateAttribBinding(context, attribIndex));
901                 break;
902 
903             default:
904                 UNREACHABLE();
905                 break;
906         }
907     }
908     return angle::Result::Continue;
909 }
910 
syncDirtyBinding(const gl::Context * context,size_t bindingIndex,const gl::VertexArray::DirtyBindingBits & dirtyBindingBits)911 angle::Result VertexArrayGL::syncDirtyBinding(
912     const gl::Context *context,
913     size_t bindingIndex,
914     const gl::VertexArray::DirtyBindingBits &dirtyBindingBits)
915 {
916     // Dependent state changes in buffers can trigger updates with no dirty bits set.
917 
918     for (auto iter = dirtyBindingBits.begin(), endIter = dirtyBindingBits.end(); iter != endIter;
919          ++iter)
920     {
921         size_t dirtyBit = *iter;
922         switch (dirtyBit)
923         {
924             case VertexArray::DIRTY_BINDING_BUFFER:
925             case VertexArray::DIRTY_BINDING_STRIDE:
926             case VertexArray::DIRTY_BINDING_OFFSET:
927                 ASSERT(supportVertexAttribBinding(context));
928                 ANGLE_TRY(updateBindingBuffer(context, bindingIndex));
929                 // Clear these bits to avoid repeated processing
930                 iter.resetLaterBits(gl::VertexArray::DirtyBindingBits{
931                     VertexArray::DIRTY_BINDING_BUFFER, VertexArray::DIRTY_BINDING_STRIDE,
932                     VertexArray::DIRTY_BINDING_OFFSET});
933                 break;
934 
935             case VertexArray::DIRTY_BINDING_DIVISOR:
936                 ANGLE_TRY(updateBindingDivisor(context, bindingIndex));
937                 break;
938 
939             default:
940                 UNREACHABLE();
941                 break;
942         }
943     }
944     return angle::Result::Continue;
945 }
946 
947 #define ANGLE_DIRTY_ATTRIB_FUNC(INDEX)                                    \
948     case VertexArray::DIRTY_BIT_ATTRIB_0 + INDEX:                         \
949         ANGLE_TRY(syncDirtyAttrib(context, INDEX, (*attribBits)[INDEX])); \
950         (*attribBits)[INDEX].reset();                                     \
951         break;
952 
953 #define ANGLE_DIRTY_BINDING_FUNC(INDEX)                                     \
954     case VertexArray::DIRTY_BIT_BINDING_0 + INDEX:                          \
955         ANGLE_TRY(syncDirtyBinding(context, INDEX, (*bindingBits)[INDEX])); \
956         (*bindingBits)[INDEX].reset();                                      \
957         break;
958 
959 #define ANGLE_DIRTY_BUFFER_DATA_FUNC(INDEX)            \
960     case VertexArray::DIRTY_BIT_BUFFER_DATA_0 + INDEX: \
961         break;
962 
syncState(const gl::Context * context,const gl::VertexArray::DirtyBits & dirtyBits,gl::VertexArray::DirtyAttribBitsArray * attribBits,gl::VertexArray::DirtyBindingBitsArray * bindingBits)963 angle::Result VertexArrayGL::syncState(const gl::Context *context,
964                                        const gl::VertexArray::DirtyBits &dirtyBits,
965                                        gl::VertexArray::DirtyAttribBitsArray *attribBits,
966                                        gl::VertexArray::DirtyBindingBitsArray *bindingBits)
967 {
968     StateManagerGL *stateManager = GetStateManagerGL(context);
969     stateManager->bindVertexArray(mVertexArrayID, mNativeState);
970 
971     for (auto iter = dirtyBits.begin(), endIter = dirtyBits.end(); iter != endIter; ++iter)
972     {
973         size_t dirtyBit = *iter;
974         switch (dirtyBit)
975         {
976             case gl::VertexArray::DIRTY_BIT_LOST_OBSERVATION:
977             {
978                 // If vertex array was not observing while unbound, we need to check buffer's
979                 // internal storage and take action if buffer has changed while not observing.
980                 // For now we just simply assume buffer storage has changed and always dirty all
981                 // binding points.
982                 iter.setLaterBits(
983                     gl::VertexArray::DirtyBits(mState.getBufferBindingMask().to_ulong()
984                                                << gl::VertexArray::DIRTY_BIT_BINDING_0));
985                 break;
986             }
987 
988             case VertexArray::DIRTY_BIT_ELEMENT_ARRAY_BUFFER:
989                 ANGLE_TRY(updateElementArrayBufferBinding(context));
990                 break;
991 
992             case VertexArray::DIRTY_BIT_ELEMENT_ARRAY_BUFFER_DATA:
993                 break;
994 
995                 ANGLE_VERTEX_INDEX_CASES(ANGLE_DIRTY_ATTRIB_FUNC)
996                 ANGLE_VERTEX_INDEX_CASES(ANGLE_DIRTY_BINDING_FUNC)
997                 ANGLE_VERTEX_INDEX_CASES(ANGLE_DIRTY_BUFFER_DATA_FUNC)
998 
999             default:
1000                 UNREACHABLE();
1001                 break;
1002         }
1003     }
1004 
1005     return angle::Result::Continue;
1006 }
1007 
applyNumViewsToDivisor(const gl::Context * context,int numViews)1008 angle::Result VertexArrayGL::applyNumViewsToDivisor(const gl::Context *context, int numViews)
1009 {
1010     if (numViews != mAppliedNumViews)
1011     {
1012         StateManagerGL *stateManager = GetStateManagerGL(context);
1013         stateManager->bindVertexArray(mVertexArrayID, mNativeState);
1014         mAppliedNumViews = numViews;
1015         for (size_t index = 0u; index < mNativeState->bindings.size(); ++index)
1016         {
1017             ANGLE_TRY(updateBindingDivisor(context, index));
1018         }
1019     }
1020 
1021     return angle::Result::Continue;
1022 }
1023 
applyActiveAttribLocationsMask(const gl::Context * context,const gl::AttributesMask & activeMask)1024 angle::Result VertexArrayGL::applyActiveAttribLocationsMask(const gl::Context *context,
1025                                                             const gl::AttributesMask &activeMask)
1026 {
1027     gl::AttributesMask updateMask = mProgramActiveAttribLocationsMask ^ activeMask;
1028     if (!updateMask.any())
1029     {
1030         return angle::Result::Continue;
1031     }
1032 
1033     ASSERT(mVertexArrayID == GetStateManagerGL(context)->getVertexArrayID());
1034     mProgramActiveAttribLocationsMask = activeMask;
1035 
1036     for (size_t attribIndex : updateMask)
1037     {
1038         ANGLE_TRY(updateAttribEnabled(context, attribIndex));
1039     }
1040 
1041     return angle::Result::Continue;
1042 }
1043 
validateState(const gl::Context * context) const1044 angle::Result VertexArrayGL::validateState(const gl::Context *context) const
1045 {
1046     const FunctionsGL *functions = GetFunctionsGL(context);
1047 
1048     // Ensure this vao is currently bound
1049     ANGLE_TRY(ValidateStateHelperGetIntegerv(context, mVertexArrayID, GL_VERTEX_ARRAY_BINDING,
1050                                              "mVertexArrayID", "GL_VERTEX_ARRAY_BINDING"));
1051 
1052     // Element array buffer
1053     ANGLE_TRY(ValidateStateHelperGetIntegerv(
1054         context, mNativeState->elementArrayBuffer, GL_ELEMENT_ARRAY_BUFFER_BINDING,
1055         "mNativeState->elementArrayBuffer", "GL_ELEMENT_ARRAY_BUFFER_BINDING"));
1056 
1057     // ValidateStateHelperGetIntegerv but with > comparison instead of !=
1058     GLint queryValue;
1059     ANGLE_GL_TRY(context, functions->getIntegerv(GL_MAX_VERTEX_ATTRIBS, &queryValue));
1060     if (mNativeState->attributes.size() > static_cast<GLuint>(queryValue))
1061     {
1062         WARN() << "mNativeState->attributes.size() (" << mNativeState->attributes.size()
1063                << ") > GL_MAX_VERTEX_ATTRIBS (" << queryValue << ")";
1064         // Re-add ASSERT: http://anglebug.com/3900
1065         // ASSERT(false);
1066     }
1067 
1068     // Check each applied attribute/binding
1069     for (GLuint index = 0; index < mNativeState->attributes.size(); index++)
1070     {
1071         VertexAttributeGL &attribute = mNativeState->attributes[index];
1072         ASSERT(attribute.bindingIndex < mNativeState->bindings.size());
1073         VertexBindingGL &binding = mNativeState->bindings[attribute.bindingIndex];
1074 
1075         ANGLE_TRY(ValidateStateHelperGetVertexAttribiv(
1076             context, index, attribute.enabled, GL_VERTEX_ATTRIB_ARRAY_ENABLED,
1077             "mNativeState->attributes.enabled", "GL_VERTEX_ATTRIB_ARRAY_ENABLED"));
1078 
1079         if (attribute.enabled)
1080         {
1081             // Applied attributes
1082             ASSERT(attribute.format);
1083             ANGLE_TRY(ValidateStateHelperGetVertexAttribiv(
1084                 context, index, ToGLenum(attribute.format->vertexAttribType),
1085                 GL_VERTEX_ATTRIB_ARRAY_TYPE, "mNativeState->attributes.format->vertexAttribType",
1086                 "GL_VERTEX_ATTRIB_ARRAY_TYPE"));
1087             ANGLE_TRY(ValidateStateHelperGetVertexAttribiv(
1088                 context, index, attribute.format->channelCount, GL_VERTEX_ATTRIB_ARRAY_SIZE,
1089                 "attribute.format->channelCount", "GL_VERTEX_ATTRIB_ARRAY_SIZE"));
1090             ANGLE_TRY(ValidateStateHelperGetVertexAttribiv(
1091                 context, index, attribute.format->isNorm(), GL_VERTEX_ATTRIB_ARRAY_NORMALIZED,
1092                 "attribute.format->isNorm()", "GL_VERTEX_ATTRIB_ARRAY_NORMALIZED"));
1093             ANGLE_TRY(ValidateStateHelperGetVertexAttribiv(
1094                 context, index, attribute.format->isPureInt(), GL_VERTEX_ATTRIB_ARRAY_INTEGER,
1095                 "attribute.format->isPureInt()", "GL_VERTEX_ATTRIB_ARRAY_INTEGER"));
1096             if (supportVertexAttribBinding(context))
1097             {
1098                 ANGLE_TRY(ValidateStateHelperGetVertexAttribiv(
1099                     context, index, attribute.relativeOffset, GL_VERTEX_ATTRIB_RELATIVE_OFFSET,
1100                     "attribute.relativeOffset", "GL_VERTEX_ATTRIB_RELATIVE_OFFSET"));
1101                 ANGLE_TRY(ValidateStateHelperGetVertexAttribiv(
1102                     context, index, attribute.bindingIndex, GL_VERTEX_ATTRIB_BINDING,
1103                     "attribute.bindingIndex", "GL_VERTEX_ATTRIB_BINDING"));
1104             }
1105 
1106             // Applied bindings
1107             ANGLE_TRY(ValidateStateHelperGetVertexAttribiv(
1108                 context, index, binding.buffer, GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING,
1109                 "binding.buffer", "GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING"));
1110             if (binding.buffer != 0)
1111             {
1112                 ANGLE_TRY(ValidateStateHelperGetVertexAttribiv(
1113                     context, index, binding.stride, GL_VERTEX_ATTRIB_ARRAY_STRIDE, "binding.stride",
1114                     "GL_VERTEX_ATTRIB_ARRAY_STRIDE"));
1115                 ANGLE_TRY(ValidateStateHelperGetVertexAttribiv(
1116                     context, index, binding.divisor, GL_VERTEX_ATTRIB_ARRAY_DIVISOR,
1117                     "binding.divisor", "GL_VERTEX_ATTRIB_ARRAY_DIVISOR"));
1118             }
1119         }
1120     }
1121     return angle::Result::Continue;
1122 }
1123 
1124 }  // namespace rx
1125