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