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.shiftInstancedArrayDataWithExtraOffset.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 // shiftInstancedArrayDataWithExtraOffset 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
650 const VertexAttribute &attrib = mState.getVertexAttribute(attribIndex);
651
652 // According to SPEC, VertexAttribPointer should update the binding indexed attribIndex instead
653 // of the binding indexed attrib.bindingIndex (unless attribIndex == attrib.bindingIndex).
654 const VertexBinding &binding = mState.getVertexBinding(attribIndex);
655
656 // Early return when the vertex attribute isn't using a buffer object:
657 // - If we need to stream, defer the attribPointer to the draw call.
658 // - Skip the attribute that is disabled and uses a client memory pointer.
659 // - Skip the attribute whose buffer is detached by BindVertexBuffer. Since it cannot have a
660 // client memory pointer either, it must be disabled and shouldn't affect the draw.
661 const auto &bindingBuffer = binding.getBuffer();
662 gl::Buffer *arrayBuffer = bindingBuffer.get();
663 if (arrayBuffer == nullptr)
664 {
665 // Mark the applied binding isn't using a buffer by setting its buffer to nullptr so that if
666 // it starts to use a buffer later, there is no chance that the caching will skip it.
667
668 mArrayBuffers[attribIndex].set(context, nullptr);
669 mNativeState->bindings[attribIndex].buffer = 0;
670 return angle::Result::Continue;
671 }
672
673 // We do not need to compare attrib.pointer because when we use a different client memory
674 // pointer, we don't need to update mAttributesNeedStreaming by binding.buffer and we won't
675 // update attribPointer in this function.
676 if ((SameVertexAttribFormat(mNativeState->attributes[attribIndex], attrib)) &&
677 (mNativeState->attributes[attribIndex].bindingIndex == attrib.bindingIndex) &&
678 (SameVertexBuffer(mNativeState->bindings[attribIndex], binding)))
679 {
680 return angle::Result::Continue;
681 }
682
683 // Since ANGLE always uses a non-zero VAO, we cannot use a client memory pointer on it:
684 // [OpenGL ES 3.0.2] Section 2.8 page 24:
685 // An INVALID_OPERATION error is generated when a non-zero vertex array object is bound,
686 // zero is bound to the ARRAY_BUFFER buffer object binding point, and the pointer argument
687 // is not NULL.
688
689 StateManagerGL *stateManager = GetStateManagerGL(context);
690 GLuint bufferId = GetNativeBufferID(arrayBuffer);
691 stateManager->bindBuffer(gl::BufferBinding::Array, bufferId);
692 ANGLE_TRY(callVertexAttribPointer(context, static_cast<GLuint>(attribIndex), attrib,
693 binding.getStride(), binding.getOffset()));
694
695 mNativeState->attributes[attribIndex].format = attrib.format;
696
697 // After VertexAttribPointer, attrib.relativeOffset is set to 0 and attrib.bindingIndex is set
698 // to attribIndex in driver. If attrib.relativeOffset != 0 or attrib.bindingIndex !=
699 // attribIndex, they should be set in updateAttribFormat and updateAttribBinding. The cache
700 // should be consistent with driver so that we won't miss anything.
701 mNativeState->attributes[attribIndex].relativeOffset = 0;
702 mNativeState->attributes[attribIndex].bindingIndex = static_cast<GLuint>(attribIndex);
703
704 mNativeState->bindings[attribIndex].stride = binding.getStride();
705 mNativeState->bindings[attribIndex].offset = binding.getOffset();
706 mArrayBuffers[attribIndex].set(context, arrayBuffer);
707 mNativeState->bindings[attribIndex].buffer = bufferId;
708
709 return angle::Result::Continue;
710 }
711
callVertexAttribPointer(const gl::Context * context,GLuint attribIndex,const VertexAttribute & attrib,GLsizei stride,GLintptr offset) const712 angle::Result VertexArrayGL::callVertexAttribPointer(const gl::Context *context,
713 GLuint attribIndex,
714 const VertexAttribute &attrib,
715 GLsizei stride,
716 GLintptr offset) const
717 {
718 const FunctionsGL *functions = GetFunctionsGL(context);
719 const GLvoid *pointer = reinterpret_cast<const GLvoid *>(offset);
720 const angle::Format &format = *attrib.format;
721 if (format.isPureInt())
722 {
723 ASSERT(!format.isNorm());
724 ANGLE_GL_TRY(context, functions->vertexAttribIPointer(attribIndex, format.channelCount,
725 gl::ToGLenum(format.vertexAttribType),
726 stride, pointer));
727 }
728 else
729 {
730 ANGLE_GL_TRY(context, functions->vertexAttribPointer(attribIndex, format.channelCount,
731 gl::ToGLenum(format.vertexAttribType),
732 format.isNorm(), stride, pointer));
733 }
734
735 return angle::Result::Continue;
736 }
737
supportVertexAttribBinding(const gl::Context * context) const738 bool VertexArrayGL::supportVertexAttribBinding(const gl::Context *context) const
739 {
740 const FunctionsGL *functions = GetFunctionsGL(context);
741 const angle::FeaturesGL &features = GetFeaturesGL(context);
742 ASSERT(functions);
743 // Vertex attrib bindings are not supported on the default VAO so if we're syncing to the
744 // default VAO due to the feature, disable bindings.
745 return (functions->vertexAttribBinding != nullptr) &&
746 !features.syncVertexArraysToDefault.enabled;
747 }
748
updateAttribFormat(const gl::Context * context,size_t attribIndex)749 angle::Result VertexArrayGL::updateAttribFormat(const gl::Context *context, size_t attribIndex)
750 {
751 ASSERT(supportVertexAttribBinding(context));
752
753 const VertexAttribute &attrib = mState.getVertexAttribute(attribIndex);
754 if (SameVertexAttribFormat(mNativeState->attributes[attribIndex], attrib))
755 {
756 return angle::Result::Continue;
757 }
758
759 const FunctionsGL *functions = GetFunctionsGL(context);
760
761 const angle::Format &format = *attrib.format;
762 if (format.isPureInt())
763 {
764 ASSERT(!format.isNorm());
765 ANGLE_GL_TRY(context, functions->vertexAttribIFormat(
766 static_cast<GLuint>(attribIndex), format.channelCount,
767 gl::ToGLenum(format.vertexAttribType), attrib.relativeOffset));
768 }
769 else
770 {
771 ANGLE_GL_TRY(context, functions->vertexAttribFormat(
772 static_cast<GLuint>(attribIndex), format.channelCount,
773 gl::ToGLenum(format.vertexAttribType), format.isNorm(),
774 attrib.relativeOffset));
775 }
776
777 mNativeState->attributes[attribIndex].format = attrib.format;
778 mNativeState->attributes[attribIndex].relativeOffset = attrib.relativeOffset;
779 return angle::Result::Continue;
780 }
781
updateAttribBinding(const gl::Context * context,size_t attribIndex)782 angle::Result VertexArrayGL::updateAttribBinding(const gl::Context *context, size_t attribIndex)
783 {
784 ASSERT(supportVertexAttribBinding(context));
785
786 GLuint bindingIndex = mState.getVertexAttribute(attribIndex).bindingIndex;
787 if (mNativeState->attributes[attribIndex].bindingIndex == bindingIndex)
788 {
789 return angle::Result::Continue;
790 }
791
792 const FunctionsGL *functions = GetFunctionsGL(context);
793 ANGLE_GL_TRY(context,
794 functions->vertexAttribBinding(static_cast<GLuint>(attribIndex), bindingIndex));
795
796 mNativeState->attributes[attribIndex].bindingIndex = bindingIndex;
797
798 return angle::Result::Continue;
799 }
800
updateBindingBuffer(const gl::Context * context,size_t bindingIndex)801 angle::Result VertexArrayGL::updateBindingBuffer(const gl::Context *context, size_t bindingIndex)
802 {
803 ASSERT(supportVertexAttribBinding(context));
804
805 const VertexBinding &binding = mState.getVertexBinding(bindingIndex);
806 if (SameVertexBuffer(mNativeState->bindings[bindingIndex], binding))
807 {
808 return angle::Result::Continue;
809 }
810
811 gl::Buffer *arrayBuffer = binding.getBuffer().get();
812 GLuint bufferId = GetNativeBufferID(arrayBuffer);
813
814 const FunctionsGL *functions = GetFunctionsGL(context);
815 ANGLE_GL_TRY(context, functions->bindVertexBuffer(static_cast<GLuint>(bindingIndex), bufferId,
816 binding.getOffset(), binding.getStride()));
817
818 mNativeState->bindings[bindingIndex].stride = binding.getStride();
819 mNativeState->bindings[bindingIndex].offset = binding.getOffset();
820 mArrayBuffers[bindingIndex].set(context, arrayBuffer);
821 mNativeState->bindings[bindingIndex].buffer = bufferId;
822
823 return angle::Result::Continue;
824 }
825
updateBindingDivisor(const gl::Context * context,size_t bindingIndex)826 angle::Result VertexArrayGL::updateBindingDivisor(const gl::Context *context, size_t bindingIndex)
827 {
828 GLuint adjustedDivisor =
829 GetAdjustedDivisor(mAppliedNumViews, mState.getVertexBinding(bindingIndex).getDivisor());
830 if (mNativeState->bindings[bindingIndex].divisor == adjustedDivisor)
831 {
832 return angle::Result::Continue;
833 }
834
835 const FunctionsGL *functions = GetFunctionsGL(context);
836 if (supportVertexAttribBinding(context))
837 {
838 ANGLE_GL_TRY(context, functions->vertexBindingDivisor(static_cast<GLuint>(bindingIndex),
839 adjustedDivisor));
840 }
841 else
842 {
843 // We can only use VertexAttribDivisor on platforms that don't support Vertex Attrib
844 // Binding.
845 ANGLE_GL_TRY(context, functions->vertexAttribDivisor(static_cast<GLuint>(bindingIndex),
846 adjustedDivisor));
847 }
848
849 if (adjustedDivisor > 0)
850 {
851 mInstancedAttributesMask.set(bindingIndex);
852 }
853 else if (mInstancedAttributesMask.test(bindingIndex))
854 {
855 // divisor is reset to 0
856 mInstancedAttributesMask.reset(bindingIndex);
857 }
858
859 mNativeState->bindings[bindingIndex].divisor = adjustedDivisor;
860
861 return angle::Result::Continue;
862 }
863
syncDirtyAttrib(const gl::Context * context,size_t attribIndex,const gl::VertexArray::DirtyAttribBits & dirtyAttribBits)864 angle::Result VertexArrayGL::syncDirtyAttrib(
865 const gl::Context *context,
866 size_t attribIndex,
867 const gl::VertexArray::DirtyAttribBits &dirtyAttribBits)
868 {
869 ASSERT(dirtyAttribBits.any());
870
871 for (size_t dirtyBit : dirtyAttribBits)
872 {
873 switch (dirtyBit)
874 {
875 case VertexArray::DIRTY_ATTRIB_ENABLED:
876 ANGLE_TRY(updateAttribEnabled(context, attribIndex));
877 break;
878
879 case VertexArray::DIRTY_ATTRIB_POINTER_BUFFER:
880 case VertexArray::DIRTY_ATTRIB_POINTER:
881 ANGLE_TRY(updateAttribPointer(context, attribIndex));
882 break;
883
884 case VertexArray::DIRTY_ATTRIB_FORMAT:
885 ASSERT(supportVertexAttribBinding(context));
886 ANGLE_TRY(updateAttribFormat(context, attribIndex));
887 break;
888
889 case VertexArray::DIRTY_ATTRIB_BINDING:
890 ASSERT(supportVertexAttribBinding(context));
891 ANGLE_TRY(updateAttribBinding(context, attribIndex));
892 break;
893
894 default:
895 UNREACHABLE();
896 break;
897 }
898 }
899 return angle::Result::Continue;
900 }
901
syncDirtyBinding(const gl::Context * context,size_t bindingIndex,const gl::VertexArray::DirtyBindingBits & dirtyBindingBits)902 angle::Result VertexArrayGL::syncDirtyBinding(
903 const gl::Context *context,
904 size_t bindingIndex,
905 const gl::VertexArray::DirtyBindingBits &dirtyBindingBits)
906 {
907 // Dependent state changes in buffers can trigger updates with no dirty bits set.
908
909 for (size_t dirtyBit : dirtyBindingBits)
910 {
911 switch (dirtyBit)
912 {
913 case VertexArray::DIRTY_BINDING_BUFFER:
914 ASSERT(supportVertexAttribBinding(context));
915 ANGLE_TRY(updateBindingBuffer(context, bindingIndex));
916 break;
917
918 case VertexArray::DIRTY_BINDING_DIVISOR:
919 ANGLE_TRY(updateBindingDivisor(context, bindingIndex));
920 break;
921
922 default:
923 UNREACHABLE();
924 break;
925 }
926 }
927 return angle::Result::Continue;
928 }
929
930 #define ANGLE_DIRTY_ATTRIB_FUNC(INDEX) \
931 case VertexArray::DIRTY_BIT_ATTRIB_0 + INDEX: \
932 ANGLE_TRY(syncDirtyAttrib(context, INDEX, (*attribBits)[INDEX])); \
933 (*attribBits)[INDEX].reset(); \
934 break;
935
936 #define ANGLE_DIRTY_BINDING_FUNC(INDEX) \
937 case VertexArray::DIRTY_BIT_BINDING_0 + INDEX: \
938 ANGLE_TRY(syncDirtyBinding(context, INDEX, (*bindingBits)[INDEX])); \
939 (*bindingBits)[INDEX].reset(); \
940 break;
941
942 #define ANGLE_DIRTY_BUFFER_DATA_FUNC(INDEX) \
943 case VertexArray::DIRTY_BIT_BUFFER_DATA_0 + INDEX: \
944 break;
945
syncState(const gl::Context * context,const gl::VertexArray::DirtyBits & dirtyBits,gl::VertexArray::DirtyAttribBitsArray * attribBits,gl::VertexArray::DirtyBindingBitsArray * bindingBits)946 angle::Result VertexArrayGL::syncState(const gl::Context *context,
947 const gl::VertexArray::DirtyBits &dirtyBits,
948 gl::VertexArray::DirtyAttribBitsArray *attribBits,
949 gl::VertexArray::DirtyBindingBitsArray *bindingBits)
950 {
951 StateManagerGL *stateManager = GetStateManagerGL(context);
952 stateManager->bindVertexArray(mVertexArrayID, mNativeState);
953
954 for (size_t dirtyBit : dirtyBits)
955 {
956 switch (dirtyBit)
957 {
958 case VertexArray::DIRTY_BIT_ELEMENT_ARRAY_BUFFER:
959 ANGLE_TRY(updateElementArrayBufferBinding(context));
960 break;
961
962 case VertexArray::DIRTY_BIT_ELEMENT_ARRAY_BUFFER_DATA:
963 break;
964
965 ANGLE_VERTEX_INDEX_CASES(ANGLE_DIRTY_ATTRIB_FUNC)
966 ANGLE_VERTEX_INDEX_CASES(ANGLE_DIRTY_BINDING_FUNC)
967 ANGLE_VERTEX_INDEX_CASES(ANGLE_DIRTY_BUFFER_DATA_FUNC)
968
969 default:
970 UNREACHABLE();
971 break;
972 }
973 }
974
975 return angle::Result::Continue;
976 }
977
applyNumViewsToDivisor(const gl::Context * context,int numViews)978 angle::Result VertexArrayGL::applyNumViewsToDivisor(const gl::Context *context, int numViews)
979 {
980 if (numViews != mAppliedNumViews)
981 {
982 StateManagerGL *stateManager = GetStateManagerGL(context);
983 stateManager->bindVertexArray(mVertexArrayID, mNativeState);
984 mAppliedNumViews = numViews;
985 for (size_t index = 0u; index < mNativeState->bindings.size(); ++index)
986 {
987 ANGLE_TRY(updateBindingDivisor(context, index));
988 }
989 }
990
991 return angle::Result::Continue;
992 }
993
applyActiveAttribLocationsMask(const gl::Context * context,const gl::AttributesMask & activeMask)994 angle::Result VertexArrayGL::applyActiveAttribLocationsMask(const gl::Context *context,
995 const gl::AttributesMask &activeMask)
996 {
997 gl::AttributesMask updateMask = mProgramActiveAttribLocationsMask ^ activeMask;
998 if (!updateMask.any())
999 {
1000 return angle::Result::Continue;
1001 }
1002
1003 ASSERT(mVertexArrayID == GetStateManagerGL(context)->getVertexArrayID());
1004 mProgramActiveAttribLocationsMask = activeMask;
1005
1006 for (size_t attribIndex : updateMask)
1007 {
1008 ANGLE_TRY(updateAttribEnabled(context, attribIndex));
1009 }
1010
1011 return angle::Result::Continue;
1012 }
1013
validateState(const gl::Context * context) const1014 angle::Result VertexArrayGL::validateState(const gl::Context *context) const
1015 {
1016 const FunctionsGL *functions = GetFunctionsGL(context);
1017
1018 // Ensure this vao is currently bound
1019 ANGLE_TRY(ValidateStateHelperGetIntegerv(context, mVertexArrayID, GL_VERTEX_ARRAY_BINDING,
1020 "mVertexArrayID", "GL_VERTEX_ARRAY_BINDING"));
1021
1022 // Element array buffer
1023 ANGLE_TRY(ValidateStateHelperGetIntegerv(
1024 context, mNativeState->elementArrayBuffer, GL_ELEMENT_ARRAY_BUFFER_BINDING,
1025 "mNativeState->elementArrayBuffer", "GL_ELEMENT_ARRAY_BUFFER_BINDING"));
1026
1027 // ValidateStateHelperGetIntegerv but with > comparison instead of !=
1028 GLint queryValue;
1029 ANGLE_GL_TRY(context, functions->getIntegerv(GL_MAX_VERTEX_ATTRIBS, &queryValue));
1030 if (mNativeState->attributes.size() > static_cast<GLuint>(queryValue))
1031 {
1032 WARN() << "mNativeState->attributes.size() (" << mNativeState->attributes.size()
1033 << ") > GL_MAX_VERTEX_ATTRIBS (" << queryValue << ")";
1034 // Re-add ASSERT: http://anglebug.com/3900
1035 // ASSERT(false);
1036 }
1037
1038 // Check each applied attribute/binding
1039 for (GLuint index = 0; index < mNativeState->attributes.size(); index++)
1040 {
1041 VertexAttributeGL &attribute = mNativeState->attributes[index];
1042 ASSERT(attribute.bindingIndex < mNativeState->bindings.size());
1043 VertexBindingGL &binding = mNativeState->bindings[attribute.bindingIndex];
1044
1045 ANGLE_TRY(ValidateStateHelperGetVertexAttribiv(
1046 context, index, attribute.enabled, GL_VERTEX_ATTRIB_ARRAY_ENABLED,
1047 "mNativeState->attributes.enabled", "GL_VERTEX_ATTRIB_ARRAY_ENABLED"));
1048
1049 if (attribute.enabled)
1050 {
1051 // Applied attributes
1052 ASSERT(attribute.format);
1053 ANGLE_TRY(ValidateStateHelperGetVertexAttribiv(
1054 context, index, ToGLenum(attribute.format->vertexAttribType),
1055 GL_VERTEX_ATTRIB_ARRAY_TYPE, "mNativeState->attributes.format->vertexAttribType",
1056 "GL_VERTEX_ATTRIB_ARRAY_TYPE"));
1057 ANGLE_TRY(ValidateStateHelperGetVertexAttribiv(
1058 context, index, attribute.format->channelCount, GL_VERTEX_ATTRIB_ARRAY_SIZE,
1059 "attribute.format->channelCount", "GL_VERTEX_ATTRIB_ARRAY_SIZE"));
1060 ANGLE_TRY(ValidateStateHelperGetVertexAttribiv(
1061 context, index, attribute.format->isNorm(), GL_VERTEX_ATTRIB_ARRAY_NORMALIZED,
1062 "attribute.format->isNorm()", "GL_VERTEX_ATTRIB_ARRAY_NORMALIZED"));
1063 ANGLE_TRY(ValidateStateHelperGetVertexAttribiv(
1064 context, index, attribute.format->isPureInt(), GL_VERTEX_ATTRIB_ARRAY_INTEGER,
1065 "attribute.format->isPureInt()", "GL_VERTEX_ATTRIB_ARRAY_INTEGER"));
1066 if (supportVertexAttribBinding(context))
1067 {
1068 ANGLE_TRY(ValidateStateHelperGetVertexAttribiv(
1069 context, index, attribute.relativeOffset, GL_VERTEX_ATTRIB_RELATIVE_OFFSET,
1070 "attribute.relativeOffset", "GL_VERTEX_ATTRIB_RELATIVE_OFFSET"));
1071 ANGLE_TRY(ValidateStateHelperGetVertexAttribiv(
1072 context, index, attribute.bindingIndex, GL_VERTEX_ATTRIB_BINDING,
1073 "attribute.bindingIndex", "GL_VERTEX_ATTRIB_BINDING"));
1074 }
1075
1076 // Applied bindings
1077 ANGLE_TRY(ValidateStateHelperGetVertexAttribiv(
1078 context, index, binding.buffer, GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING,
1079 "binding.buffer", "GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING"));
1080 if (binding.buffer != 0)
1081 {
1082 ANGLE_TRY(ValidateStateHelperGetVertexAttribiv(
1083 context, index, binding.stride, GL_VERTEX_ATTRIB_ARRAY_STRIDE, "binding.stride",
1084 "GL_VERTEX_ATTRIB_ARRAY_STRIDE"));
1085 ANGLE_TRY(ValidateStateHelperGetVertexAttribiv(
1086 context, index, binding.divisor, GL_VERTEX_ATTRIB_ARRAY_DIVISOR,
1087 "binding.divisor", "GL_VERTEX_ATTRIB_ARRAY_DIVISOR"));
1088 }
1089 }
1090 }
1091 return angle::Result::Continue;
1092 }
1093
1094 } // namespace rx
1095