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