blob: 321e1c5b1eda938910982c28836581f2fe3d0932 [file] [log] [blame]
Geoff Langf9a6f082015-01-22 13:32:49 -05001//
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
Jamie Madill20e005b2017-04-07 14:19:22 -040011#include "common/bitset_utils.h"
Geoff Langf9a6f082015-01-22 13:32:49 -050012#include "common/debug.h"
Geoff Lang7c82bc42015-03-09 16:18:08 -040013#include "common/mathutil.h"
Geoff Lang831b1952015-05-05 11:02:27 -040014#include "common/utilities.h"
Geoff Lang6ae6efc2015-03-09 14:42:35 -040015#include "libANGLE/Buffer.h"
Jamie Madilldc358af2018-07-31 11:22:13 -040016#include "libANGLE/Context.h"
Geoff Langba4c4a82015-02-24 12:38:46 -050017#include "libANGLE/angletypes.h"
Geoff Lang7c82bc42015-03-09 16:18:08 -040018#include "libANGLE/formatutils.h"
Geoff Langba4c4a82015-02-24 12:38:46 -050019#include "libANGLE/renderer/gl/BufferGL.h"
20#include "libANGLE/renderer/gl/FunctionsGL.h"
21#include "libANGLE/renderer/gl/StateManagerGL.h"
Jamie Madill231c7f52017-04-26 13:45:37 -040022#include "libANGLE/renderer/gl/renderergl_utils.h"
Geoff Langf9a6f082015-01-22 13:32:49 -050023
Jamie Madill0b9e9032015-08-17 11:51:52 +000024using namespace gl;
25
Geoff Langf9a6f082015-01-22 13:32:49 -050026namespace rx
27{
Jamie Madill0b9e9032015-08-17 11:51:52 +000028namespace
29{
Shaodf682a82017-03-31 15:13:21 +080030bool SameVertexAttribFormat(const VertexAttribute &a, const VertexAttribute &b)
31{
32 return a.size == b.size && a.type == b.type && a.normalized == b.normalized &&
33 a.pureInteger == b.pureInteger && a.relativeOffset == b.relativeOffset;
34}
35
36bool SameVertexBuffer(const VertexBinding &a, const VertexBinding &b)
37{
Martin Radevdd5f27e2017-06-07 10:17:09 +030038 return a.getStride() == b.getStride() && a.getOffset() == b.getOffset() &&
39 a.getBuffer().get() == b.getBuffer().get();
Shaodf682a82017-03-31 15:13:21 +080040}
41
42bool IsVertexAttribPointerSupported(size_t attribIndex, const VertexAttribute &attrib)
43{
44 return (attribIndex == attrib.bindingIndex && attrib.relativeOffset == 0);
45}
Martin Radev553590a2017-07-31 16:40:39 +030046
47GLuint GetAdjustedDivisor(GLuint numViews, GLuint divisor)
48{
49 return numViews * divisor;
50}
51
Jamie Madill0b9e9032015-08-17 11:51:52 +000052} // anonymous namespace
53
Jamie Madill3f572682016-04-26 13:41:36 -040054VertexArrayGL::VertexArrayGL(const VertexArrayState &state,
Jamie Madill77a90c22015-08-11 16:33:17 -040055 const FunctionsGL *functions,
56 StateManagerGL *stateManager)
Jamie Madill3f572682016-04-26 13:41:36 -040057 : VertexArrayImpl(state),
Geoff Langba4c4a82015-02-24 12:38:46 -050058 mFunctions(functions),
59 mStateManager(stateManager),
60 mVertexArrayID(0),
Martin Radev553590a2017-07-31 16:40:39 +030061 mAppliedNumViews(1),
Jamie Madill77a90c22015-08-11 16:33:17 -040062 mAppliedElementArrayBuffer(),
Jiawei-Shao2597fb62016-12-09 16:38:02 +080063 mAppliedBindings(state.getMaxBindings()),
Geoff Lang7c82bc42015-03-09 16:18:08 -040064 mStreamingElementArrayBufferSize(0),
65 mStreamingElementArrayBuffer(0),
66 mStreamingArrayBufferSize(0),
67 mStreamingArrayBuffer(0)
Geoff Langba4c4a82015-02-24 12:38:46 -050068{
69 ASSERT(mFunctions);
70 ASSERT(mStateManager);
71 mFunctions->genVertexArrays(1, &mVertexArrayID);
72
Jiawei-Shao2597fb62016-12-09 16:38:02 +080073 // Set the cached vertex attribute array and vertex attribute binding array size
74 GLuint maxVertexAttribs = static_cast<GLuint>(state.getMaxAttribs());
75 for (GLuint i = 0; i < maxVertexAttribs; i++)
76 {
77 mAppliedAttributes.emplace_back(i);
78 }
Geoff Langba4c4a82015-02-24 12:38:46 -050079}
Geoff Langf9a6f082015-01-22 13:32:49 -050080
Jamie Madillacf2f3a2017-11-21 19:22:44 -050081VertexArrayGL::~VertexArrayGL()
82{
83}
84
Jamie Madill4928b7c2017-06-20 12:57:39 -040085void VertexArrayGL::destroy(const gl::Context *context)
Geoff Langba4c4a82015-02-24 12:38:46 -050086{
Geoff Lang1eb708e2015-05-04 14:58:23 -040087 mStateManager->deleteVertexArray(mVertexArrayID);
88 mVertexArrayID = 0;
Martin Radev553590a2017-07-31 16:40:39 +030089 mAppliedNumViews = 1;
Geoff Langba4c4a82015-02-24 12:38:46 -050090
Geoff Lang1eb708e2015-05-04 14:58:23 -040091 mStateManager->deleteBuffer(mStreamingElementArrayBuffer);
92 mStreamingElementArrayBufferSize = 0;
Shao80957d92017-02-20 21:25:59 +080093 mStreamingElementArrayBuffer = 0;
Geoff Lang7c82bc42015-03-09 16:18:08 -040094
Geoff Lang1eb708e2015-05-04 14:58:23 -040095 mStateManager->deleteBuffer(mStreamingArrayBuffer);
96 mStreamingArrayBufferSize = 0;
Shao80957d92017-02-20 21:25:59 +080097 mStreamingArrayBuffer = 0;
Geoff Lang7c82bc42015-03-09 16:18:08 -040098
Jamie Madill4928b7c2017-06-20 12:57:39 -040099 mAppliedElementArrayBuffer.set(context, nullptr);
Jiawei-Shao2597fb62016-12-09 16:38:02 +0800100 for (auto &binding : mAppliedBindings)
Geoff Langba4c4a82015-02-24 12:38:46 -0500101 {
James Darpiniane8a93c62018-01-04 18:02:24 -0800102 binding.setBuffer(context, nullptr, false);
Geoff Langba4c4a82015-02-24 12:38:46 -0500103 }
104}
Geoff Langf9a6f082015-01-22 13:32:49 -0500105
Jamie Madill4928b7c2017-06-20 12:57:39 -0400106gl::Error VertexArrayGL::syncDrawArraysState(const gl::Context *context,
107 const gl::AttributesMask &activeAttributesMask,
Jamie Madill0b9e9032015-08-17 11:51:52 +0000108 GLint first,
Geoff Lang3cf12ce2015-08-27 14:40:48 -0400109 GLsizei count,
110 GLsizei instanceCount) const
Geoff Lang7c82bc42015-03-09 16:18:08 -0400111{
Jamie Madill4928b7c2017-06-20 12:57:39 -0400112 return syncDrawState(context, activeAttributesMask, first, count, GL_NONE, nullptr,
113 instanceCount, false, nullptr);
Geoff Lang7c82bc42015-03-09 16:18:08 -0400114}
115
Jamie Madill4928b7c2017-06-20 12:57:39 -0400116gl::Error VertexArrayGL::syncDrawElementsState(const gl::Context *context,
117 const gl::AttributesMask &activeAttributesMask,
Jamie Madill0b9e9032015-08-17 11:51:52 +0000118 GLsizei count,
119 GLenum type,
Jamie Madill876429b2017-04-20 15:46:24 -0400120 const void *indices,
Geoff Lang3cf12ce2015-08-27 14:40:48 -0400121 GLsizei instanceCount,
Geoff Lang3edfe032015-09-04 16:38:24 -0400122 bool primitiveRestartEnabled,
Jamie Madill876429b2017-04-20 15:46:24 -0400123 const void **outIndices) const
Geoff Lang7c82bc42015-03-09 16:18:08 -0400124{
Jamie Madill4928b7c2017-06-20 12:57:39 -0400125 return syncDrawState(context, activeAttributesMask, 0, count, type, indices, instanceCount,
Geoff Lang3edfe032015-09-04 16:38:24 -0400126 primitiveRestartEnabled, outIndices);
Geoff Lang7c82bc42015-03-09 16:18:08 -0400127}
128
Jiajia Qin47474142017-12-29 13:41:00 +0800129void VertexArrayGL::updateElementArrayBufferBinding(const gl::Context *context) const
Jiajia Qind9671222016-11-29 16:30:31 +0800130{
Jamie Madill492f58e2017-10-09 19:41:33 -0400131 gl::Buffer *elementArrayBuffer = mState.getElementArrayBuffer().get();
Jiajia Qin47474142017-12-29 13:41:00 +0800132 if (elementArrayBuffer != nullptr && elementArrayBuffer != mAppliedElementArrayBuffer.get())
Jiajia Qind9671222016-11-29 16:30:31 +0800133 {
134 const BufferGL *bufferGL = GetImplAs<BufferGL>(elementArrayBuffer);
Corentin Wallez336129f2017-10-17 15:55:40 -0400135 mStateManager->bindBuffer(gl::BufferBinding::ElementArray, bufferGL->getBufferID());
Jamie Madill4928b7c2017-06-20 12:57:39 -0400136 mAppliedElementArrayBuffer.set(context, elementArrayBuffer);
Jiajia Qind9671222016-11-29 16:30:31 +0800137 }
Jiajia Qind9671222016-11-29 16:30:31 +0800138}
139
Jamie Madill4928b7c2017-06-20 12:57:39 -0400140gl::Error VertexArrayGL::syncDrawState(const gl::Context *context,
141 const gl::AttributesMask &activeAttributesMask,
Jamie Madill0b9e9032015-08-17 11:51:52 +0000142 GLint first,
143 GLsizei count,
144 GLenum type,
Jamie Madill876429b2017-04-20 15:46:24 -0400145 const void *indices,
Geoff Lang3cf12ce2015-08-27 14:40:48 -0400146 GLsizei instanceCount,
Geoff Lang3edfe032015-09-04 16:38:24 -0400147 bool primitiveRestartEnabled,
Jamie Madill876429b2017-04-20 15:46:24 -0400148 const void **outIndices) const
Geoff Lang6ae6efc2015-03-09 14:42:35 -0400149{
Shao80957d92017-02-20 21:25:59 +0800150 // Check if any attributes need to be streamed, determines if the index range needs to be
151 // computed
Jamie Madillbcef3222018-04-13 15:19:11 -0400152 const gl::AttributesMask &needsStreamingAttribs =
Jamie Madilldc358af2018-07-31 11:22:13 -0400153 context->getStateCache().getActiveClientAttribsMask();
Geoff Lang7c82bc42015-03-09 16:18:08 -0400154
Shao80957d92017-02-20 21:25:59 +0800155 // Determine if an index buffer needs to be streamed and the range of vertices that need to be
156 // copied
Geoff Lang3edfe032015-09-04 16:38:24 -0400157 IndexRange indexRange;
Geoff Lang7c82bc42015-03-09 16:18:08 -0400158 if (type != GL_NONE)
Geoff Langba4c4a82015-02-24 12:38:46 -0500159 {
Jamie Madill4928b7c2017-06-20 12:57:39 -0400160 ANGLE_TRY(syncIndexData(context, count, type, indices, primitiveRestartEnabled,
Jamie Madillbcef3222018-04-13 15:19:11 -0400161 needsStreamingAttribs.any(), &indexRange, outIndices));
Geoff Lang7c82bc42015-03-09 16:18:08 -0400162 }
163 else
164 {
Corentin Wallez2c34a4b2015-08-25 16:26:02 -0400165 // Not an indexed call, set the range to [first, first + count - 1]
Geoff Lang7c82bc42015-03-09 16:18:08 -0400166 indexRange.start = first;
Shao80957d92017-02-20 21:25:59 +0800167 indexRange.end = first + count - 1;
Geoff Langba4c4a82015-02-24 12:38:46 -0500168 }
169
Jamie Madillbcef3222018-04-13 15:19:11 -0400170 if (needsStreamingAttribs.any())
Geoff Langba4c4a82015-02-24 12:38:46 -0500171 {
Jamie Madillbcef3222018-04-13 15:19:11 -0400172 ANGLE_TRY(streamAttributes(needsStreamingAttribs, instanceCount, indexRange));
Geoff Lang7c82bc42015-03-09 16:18:08 -0400173 }
174
Yuly Novikovc4d18aa2017-03-09 18:45:02 -0500175 return gl::NoError();
Geoff Lang7c82bc42015-03-09 16:18:08 -0400176}
177
Jamie Madill4928b7c2017-06-20 12:57:39 -0400178gl::Error VertexArrayGL::syncIndexData(const gl::Context *context,
179 GLsizei count,
Jiajia Qind9671222016-11-29 16:30:31 +0800180 GLenum type,
Jamie Madill876429b2017-04-20 15:46:24 -0400181 const void *indices,
Jiajia Qind9671222016-11-29 16:30:31 +0800182 bool primitiveRestartEnabled,
183 bool attributesNeedStreaming,
184 IndexRange *outIndexRange,
Jamie Madill876429b2017-04-20 15:46:24 -0400185 const void **outIndices) const
Geoff Lang7c82bc42015-03-09 16:18:08 -0400186{
187 ASSERT(outIndices);
188
Jamie Madill492f58e2017-10-09 19:41:33 -0400189 gl::Buffer *elementArrayBuffer = mState.getElementArrayBuffer().get();
Jamie Madill8e344942015-07-09 14:22:07 -0400190
Geoff Lang7c82bc42015-03-09 16:18:08 -0400191 // Need to check the range of indices if attributes need to be streamed
Jamie Madill8e344942015-07-09 14:22:07 -0400192 if (elementArrayBuffer != nullptr)
Geoff Lang7c82bc42015-03-09 16:18:08 -0400193 {
Jiajia Qin47474142017-12-29 13:41:00 +0800194 ASSERT(elementArrayBuffer == mAppliedElementArrayBuffer.get());
Geoff Lang7c82bc42015-03-09 16:18:08 -0400195 // Only compute the index range if the attributes also need to be streamed
196 if (attributesNeedStreaming)
197 {
198 ptrdiff_t elementArrayBufferOffset = reinterpret_cast<ptrdiff_t>(indices);
Jamie Madill492f58e2017-10-09 19:41:33 -0400199 Error error = mState.getElementArrayBuffer()->getIndexRange(
Jamie Madill33510102017-09-20 10:39:18 -0400200 context, type, elementArrayBufferOffset, count, primitiveRestartEnabled,
201 outIndexRange);
Geoff Lang520c4ae2015-05-05 13:12:36 -0400202 if (error.isError())
Geoff Lang7c82bc42015-03-09 16:18:08 -0400203 {
Geoff Lang520c4ae2015-05-05 13:12:36 -0400204 return error;
Geoff Lang7c82bc42015-03-09 16:18:08 -0400205 }
206 }
207
Shao80957d92017-02-20 21:25:59 +0800208 // Indices serves as an offset into the index buffer in this case, use the same value for
209 // the draw call
Geoff Lang7c82bc42015-03-09 16:18:08 -0400210 *outIndices = indices;
211 }
212 else
213 {
214 // Need to stream the index buffer
215 // TODO: if GLES, nothing needs to be streamed
216
217 // Only compute the index range if the attributes also need to be streamed
218 if (attributesNeedStreaming)
219 {
Geoff Lang3edfe032015-09-04 16:38:24 -0400220 *outIndexRange = ComputeIndexRange(type, indices, count, primitiveRestartEnabled);
Geoff Lang7c82bc42015-03-09 16:18:08 -0400221 }
222
223 // Allocate the streaming element array buffer
224 if (mStreamingElementArrayBuffer == 0)
225 {
226 mFunctions->genBuffers(1, &mStreamingElementArrayBuffer);
227 mStreamingElementArrayBufferSize = 0;
228 }
229
Geoff Lang5f3047c2018-02-14 14:39:12 -0500230 mStateManager->bindVertexArray(mVertexArrayID, getAppliedElementArrayBufferID());
231
Corentin Wallez336129f2017-10-17 15:55:40 -0400232 mStateManager->bindBuffer(gl::BufferBinding::ElementArray, mStreamingElementArrayBuffer);
Jamie Madill4928b7c2017-06-20 12:57:39 -0400233 mAppliedElementArrayBuffer.set(context, nullptr);
Geoff Lang7c82bc42015-03-09 16:18:08 -0400234
235 // Make sure the element array buffer is large enough
Jamie Madill0b9e9032015-08-17 11:51:52 +0000236 const Type &indexTypeInfo = GetTypeInfo(type);
Geoff Lang7c82bc42015-03-09 16:18:08 -0400237 size_t requiredStreamingBufferSize = indexTypeInfo.bytes * count;
238 if (requiredStreamingBufferSize > mStreamingElementArrayBufferSize)
239 {
240 // Copy the indices in while resizing the buffer
Shao80957d92017-02-20 21:25:59 +0800241 mFunctions->bufferData(GL_ELEMENT_ARRAY_BUFFER, requiredStreamingBufferSize, indices,
242 GL_DYNAMIC_DRAW);
Geoff Lang7c82bc42015-03-09 16:18:08 -0400243 mStreamingElementArrayBufferSize = requiredStreamingBufferSize;
244 }
245 else
246 {
247 // Put the indices at the beginning of the buffer
Shao80957d92017-02-20 21:25:59 +0800248 mFunctions->bufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, requiredStreamingBufferSize,
249 indices);
Geoff Lang7c82bc42015-03-09 16:18:08 -0400250 }
251
Shao80957d92017-02-20 21:25:59 +0800252 // Set the index offset for the draw call to zero since the supplied index pointer is to
253 // client data
Geoff Lang7c82bc42015-03-09 16:18:08 -0400254 *outIndices = nullptr;
255 }
256
Yuly Novikovc4d18aa2017-03-09 18:45:02 -0500257 return gl::NoError();
Geoff Lang7c82bc42015-03-09 16:18:08 -0400258}
259
Jamie Madillbcef3222018-04-13 15:19:11 -0400260void VertexArrayGL::computeStreamingAttributeSizes(const gl::AttributesMask &attribsToStream,
Geoff Lang3cf12ce2015-08-27 14:40:48 -0400261 GLsizei instanceCount,
Geoff Lang3edfe032015-09-04 16:38:24 -0400262 const gl::IndexRange &indexRange,
Jamie Madill0b9e9032015-08-17 11:51:52 +0000263 size_t *outStreamingDataSize,
264 size_t *outMaxAttributeDataSize) const
Geoff Lang7c82bc42015-03-09 16:18:08 -0400265{
Jamie Madill0b9e9032015-08-17 11:51:52 +0000266 *outStreamingDataSize = 0;
267 *outMaxAttributeDataSize = 0;
268
Jamie Madillbcef3222018-04-13 15:19:11 -0400269 ASSERT(attribsToStream.any());
Jamie Madill0b9e9032015-08-17 11:51:52 +0000270
Jamie Madill492f58e2017-10-09 19:41:33 -0400271 const auto &attribs = mState.getVertexAttributes();
272 const auto &bindings = mState.getVertexBindings();
Jamie Madill6de51852017-04-12 09:53:01 -0400273
Jamie Madill6de51852017-04-12 09:53:01 -0400274 for (auto idx : attribsToStream)
Jamie Madill0b9e9032015-08-17 11:51:52 +0000275 {
Shao80957d92017-02-20 21:25:59 +0800276 const auto &attrib = attribs[idx];
Jiawei-Shao2597fb62016-12-09 16:38:02 +0800277 const auto &binding = bindings[attrib.bindingIndex];
Jamie Madill0b9e9032015-08-17 11:51:52 +0000278
Jamie Madill0b9e9032015-08-17 11:51:52 +0000279 // If streaming is going to be required, compute the size of the required buffer
280 // and how much slack space at the beginning of the buffer will be required by determining
281 // the attribute with the largest data size.
282 size_t typeSize = ComputeVertexAttributeTypeSize(attrib);
Martin Radev553590a2017-07-31 16:40:39 +0300283 GLuint adjustedDivisor = GetAdjustedDivisor(mAppliedNumViews, binding.getDivisor());
284 *outStreamingDataSize +=
285 typeSize * ComputeVertexBindingElementCount(adjustedDivisor, indexRange.vertexCount(),
286 instanceCount);
Jamie Madill0b9e9032015-08-17 11:51:52 +0000287 *outMaxAttributeDataSize = std::max(*outMaxAttributeDataSize, typeSize);
288 }
289}
290
Jamie Madillbcef3222018-04-13 15:19:11 -0400291gl::Error VertexArrayGL::streamAttributes(const gl::AttributesMask &attribsToStream,
Geoff Lang3cf12ce2015-08-27 14:40:48 -0400292 GLsizei instanceCount,
Geoff Lang3edfe032015-09-04 16:38:24 -0400293 const gl::IndexRange &indexRange) const
Jamie Madill0b9e9032015-08-17 11:51:52 +0000294{
295 // Sync the vertex attribute state and track what data needs to be streamed
296 size_t streamingDataSize = 0;
297 size_t maxAttributeDataSize = 0;
298
Jamie Madillbcef3222018-04-13 15:19:11 -0400299 computeStreamingAttributeSizes(attribsToStream, instanceCount, indexRange, &streamingDataSize,
300 &maxAttributeDataSize);
Jamie Madill0b9e9032015-08-17 11:51:52 +0000301
302 if (streamingDataSize == 0)
303 {
He Yunchaoacd18982017-01-04 10:46:42 +0800304 return gl::NoError();
Jamie Madill0b9e9032015-08-17 11:51:52 +0000305 }
306
Geoff Lang7c82bc42015-03-09 16:18:08 -0400307 if (mStreamingArrayBuffer == 0)
308 {
309 mFunctions->genBuffers(1, &mStreamingArrayBuffer);
310 mStreamingArrayBufferSize = 0;
311 }
312
Shao80957d92017-02-20 21:25:59 +0800313 // If first is greater than zero, a slack space needs to be left at the beginning of the buffer
314 // so that the same 'first' argument can be passed into the draw call.
315 const size_t bufferEmptySpace = maxAttributeDataSize * indexRange.start;
Geoff Lang7c82bc42015-03-09 16:18:08 -0400316 const size_t requiredBufferSize = streamingDataSize + bufferEmptySpace;
317
Corentin Wallez336129f2017-10-17 15:55:40 -0400318 mStateManager->bindBuffer(gl::BufferBinding::Array, mStreamingArrayBuffer);
Geoff Lang7c82bc42015-03-09 16:18:08 -0400319 if (requiredBufferSize > mStreamingArrayBufferSize)
320 {
321 mFunctions->bufferData(GL_ARRAY_BUFFER, requiredBufferSize, nullptr, GL_DYNAMIC_DRAW);
322 mStreamingArrayBufferSize = requiredBufferSize;
323 }
324
Geoff Lang5f3047c2018-02-14 14:39:12 -0500325 mStateManager->bindVertexArray(mVertexArrayID, getAppliedElementArrayBufferID());
326
Geoff Lang7c82bc42015-03-09 16:18:08 -0400327 // Unmapping a buffer can return GL_FALSE to indicate that the system has corrupted the data
Shao80957d92017-02-20 21:25:59 +0800328 // somehow (such as by a screen change), retry writing the data a few times and return
329 // OUT_OF_MEMORY if that fails.
330 GLboolean unmapResult = GL_FALSE;
Geoff Lang7c82bc42015-03-09 16:18:08 -0400331 size_t unmapRetryAttempts = 5;
332 while (unmapResult != GL_TRUE && --unmapRetryAttempts > 0)
333 {
Geoff Langb2ebb5e2016-06-08 18:17:55 +0000334 uint8_t *bufferPointer = MapBufferRangeWithFallback(mFunctions, GL_ARRAY_BUFFER, 0,
335 requiredBufferSize, GL_MAP_WRITE_BIT);
Geoff Lang7c82bc42015-03-09 16:18:08 -0400336 size_t curBufferOffset = bufferEmptySpace;
337
Jamie Madill492f58e2017-10-09 19:41:33 -0400338 const auto &attribs = mState.getVertexAttributes();
339 const auto &bindings = mState.getVertexBindings();
Jamie Madill6de51852017-04-12 09:53:01 -0400340
Jamie Madill6de51852017-04-12 09:53:01 -0400341 for (auto idx : attribsToStream)
Geoff Lang7c82bc42015-03-09 16:18:08 -0400342 {
Shao80957d92017-02-20 21:25:59 +0800343 const auto &attrib = attribs[idx];
Shaodde78e82017-05-22 14:13:27 +0800344 ASSERT(IsVertexAttribPointerSupported(idx, attrib));
345
Jiawei-Shao2597fb62016-12-09 16:38:02 +0800346 const auto &binding = bindings[attrib.bindingIndex];
Geoff Lang7c82bc42015-03-09 16:18:08 -0400347
Martin Radev553590a2017-07-31 16:40:39 +0300348 GLuint adjustedDivisor = GetAdjustedDivisor(mAppliedNumViews, binding.getDivisor());
349 const size_t streamedVertexCount = ComputeVertexBindingElementCount(
350 adjustedDivisor, indexRange.vertexCount(), instanceCount);
Geoff Lang3cf12ce2015-08-27 14:40:48 -0400351
Jiawei-Shao2597fb62016-12-09 16:38:02 +0800352 const size_t sourceStride = ComputeVertexAttributeStride(attrib, binding);
Jamie Madill0b9e9032015-08-17 11:51:52 +0000353 const size_t destStride = ComputeVertexAttributeTypeSize(attrib);
354
Geoff Lang38a24a92017-02-15 13:53:06 -0500355 // Vertices do not apply the 'start' offset when the divisor is non-zero even when doing
356 // a non-instanced draw call
Martin Radev553590a2017-07-31 16:40:39 +0300357 const size_t firstIndex = adjustedDivisor == 0 ? indexRange.start : 0;
Geoff Lang38a24a92017-02-15 13:53:06 -0500358
Jiawei-Shao2597fb62016-12-09 16:38:02 +0800359 // Attributes using client memory ignore the VERTEX_ATTRIB_BINDING state.
360 // https://www.opengl.org/registry/specs/ARB/vertex_attrib_binding.txt
Rafael Cintron05a449a2018-06-20 18:08:04 -0700361 const uint8_t *inputPointer = static_cast<const uint8_t *>(attrib.pointer);
Jamie Madill0b9e9032015-08-17 11:51:52 +0000362
363 // Pack the data when copying it, user could have supplied a very large stride that
364 // would cause the buffer to be much larger than needed.
365 if (destStride == sourceStride)
Jamie Madill8e344942015-07-09 14:22:07 -0400366 {
Jamie Madill0b9e9032015-08-17 11:51:52 +0000367 // Can copy in one go, the data is packed
Geoff Lang38a24a92017-02-15 13:53:06 -0500368 memcpy(bufferPointer + curBufferOffset, inputPointer + (sourceStride * firstIndex),
Jamie Madill0b9e9032015-08-17 11:51:52 +0000369 destStride * streamedVertexCount);
Jamie Madill6d51c702015-08-14 10:38:10 -0400370 }
Jamie Madill0b9e9032015-08-17 11:51:52 +0000371 else
372 {
373 // Copy each vertex individually
Geoff Lang6b10ddb2015-09-02 15:55:10 -0400374 for (size_t vertexIdx = 0; vertexIdx < streamedVertexCount; vertexIdx++)
Jamie Madill0b9e9032015-08-17 11:51:52 +0000375 {
Shao80957d92017-02-20 21:25:59 +0800376 uint8_t *out = bufferPointer + curBufferOffset + (destStride * vertexIdx);
Geoff Lang38a24a92017-02-15 13:53:06 -0500377 const uint8_t *in = inputPointer + sourceStride * (vertexIdx + firstIndex);
Corentin Wallez4d5362d2015-08-25 11:25:14 -0400378 memcpy(out, in, destStride);
Jamie Madill0b9e9032015-08-17 11:51:52 +0000379 }
380 }
381
382 // Compute where the 0-index vertex would be.
Geoff Lang38a24a92017-02-15 13:53:06 -0500383 const size_t vertexStartOffset = curBufferOffset - (firstIndex * destStride);
Jamie Madill0b9e9032015-08-17 11:51:52 +0000384
Shaodf682a82017-03-31 15:13:21 +0800385 callVertexAttribPointer(static_cast<GLuint>(idx), attrib,
386 static_cast<GLsizei>(destStride),
387 static_cast<GLintptr>(vertexStartOffset));
Jamie Madill0b9e9032015-08-17 11:51:52 +0000388
389 curBufferOffset += destStride * streamedVertexCount;
Geoff Lang7c82bc42015-03-09 16:18:08 -0400390 }
391
392 unmapResult = mFunctions->unmapBuffer(GL_ARRAY_BUFFER);
393 }
394
395 if (unmapResult != GL_TRUE)
396 {
Yuly Novikovc4d18aa2017-03-09 18:45:02 -0500397 return gl::OutOfMemory() << "Failed to unmap the client data streaming buffer.";
Geoff Lang7c82bc42015-03-09 16:18:08 -0400398 }
399
Yuly Novikovc4d18aa2017-03-09 18:45:02 -0500400 return gl::NoError();
Geoff Langba4c4a82015-02-24 12:38:46 -0500401}
402
403GLuint VertexArrayGL::getVertexArrayID() const
404{
405 return mVertexArrayID;
Geoff Langf9a6f082015-01-22 13:32:49 -0500406}
407
Geoff Lang294cad92015-05-26 15:11:23 -0400408GLuint VertexArrayGL::getAppliedElementArrayBufferID() const
409{
Jamie Madill77a90c22015-08-11 16:33:17 -0400410 if (mAppliedElementArrayBuffer.get() == nullptr)
411 {
412 return mStreamingElementArrayBuffer;
413 }
414
415 return GetImplAs<BufferGL>(mAppliedElementArrayBuffer.get())->getBufferID();
Geoff Lang294cad92015-05-26 15:11:23 -0400416}
417
Jamie Madill0b9e9032015-08-17 11:51:52 +0000418void VertexArrayGL::updateAttribEnabled(size_t attribIndex)
419{
Shahbaz Youssefi337bd692018-08-22 16:16:38 -0400420 const bool enabled = mState.getVertexAttribute(attribIndex).enabled &
421 mProgramActiveAttribLocationsMask.test(attribIndex);
Shaodf682a82017-03-31 15:13:21 +0800422 if (mAppliedAttributes[attribIndex].enabled == enabled)
Jamie Madill0b9e9032015-08-17 11:51:52 +0000423 {
424 return;
425 }
426
Shaodf682a82017-03-31 15:13:21 +0800427 if (enabled)
Jamie Madill0b9e9032015-08-17 11:51:52 +0000428 {
429 mFunctions->enableVertexAttribArray(static_cast<GLuint>(attribIndex));
430 }
431 else
432 {
433 mFunctions->disableVertexAttribArray(static_cast<GLuint>(attribIndex));
434 }
Shaodf682a82017-03-31 15:13:21 +0800435
436 mAppliedAttributes[attribIndex].enabled = enabled;
Jamie Madill0b9e9032015-08-17 11:51:52 +0000437}
438
Jamie Madill4928b7c2017-06-20 12:57:39 -0400439void VertexArrayGL::updateAttribPointer(const gl::Context *context, size_t attribIndex)
Jamie Madill0b9e9032015-08-17 11:51:52 +0000440{
Jamie Madill492f58e2017-10-09 19:41:33 -0400441 const VertexAttribute &attrib = mState.getVertexAttribute(attribIndex);
Jiawei-Shao2597fb62016-12-09 16:38:02 +0800442
Shaodde78e82017-05-22 14:13:27 +0800443 // According to SPEC, VertexAttribPointer should update the binding indexed attribIndex instead
444 // of the binding indexed attrib.bindingIndex (unless attribIndex == attrib.bindingIndex).
Jamie Madill492f58e2017-10-09 19:41:33 -0400445 const VertexBinding &binding = mState.getVertexBinding(attribIndex);
Shaodf682a82017-03-31 15:13:21 +0800446
Shaodde78e82017-05-22 14:13:27 +0800447 // Early return when the vertex attribute isn't using a buffer object:
448 // - If we need to stream, defer the attribPointer to the draw call.
449 // - Skip the attribute that is disabled and uses a client memory pointer.
450 // - Skip the attribute whose buffer is detached by BindVertexBuffer. Since it cannot have a
451 // client memory pointer either, it must be disabled and shouldn't affect the draw.
Jamie Madillbcef3222018-04-13 15:19:11 -0400452 const auto &bindingBuffer = binding.getBuffer();
Shaodde78e82017-05-22 14:13:27 +0800453 const Buffer *arrayBuffer = bindingBuffer.get();
454 if (arrayBuffer == nullptr)
455 {
456 // Mark the applied binding isn't using a buffer by setting its buffer to nullptr so that if
457 // it starts to use a buffer later, there is no chance that the caching will skip it.
James Darpiniane8a93c62018-01-04 18:02:24 -0800458 mAppliedBindings[attribIndex].setBuffer(context, nullptr, false);
Shaodde78e82017-05-22 14:13:27 +0800459 return;
460 }
Jiawei-Shao2597fb62016-12-09 16:38:02 +0800461
Shaodf682a82017-03-31 15:13:21 +0800462 // We do not need to compare attrib.pointer because when we use a different client memory
463 // pointer, we don't need to update mAttributesNeedStreaming by binding.buffer and we won't
464 // update attribPointer in this function.
Shaodde78e82017-05-22 14:13:27 +0800465 if ((SameVertexAttribFormat(mAppliedAttributes[attribIndex], attrib)) &&
466 (mAppliedAttributes[attribIndex].bindingIndex == attrib.bindingIndex) &&
467 (SameVertexBuffer(mAppliedBindings[attribIndex], binding)))
Jamie Madill0b9e9032015-08-17 11:51:52 +0000468 {
469 return;
470 }
471
Shao4181bc92017-03-17 14:55:25 +0800472 // Since ANGLE always uses a non-zero VAO, we cannot use a client memory pointer on it:
473 // [OpenGL ES 3.0.2] Section 2.8 page 24:
474 // An INVALID_OPERATION error is generated when a non-zero vertex array object is bound,
475 // zero is bound to the ARRAY_BUFFER buffer object binding point, and the pointer argument
476 // is not NULL.
Shaodf682a82017-03-31 15:13:21 +0800477
Shao4181bc92017-03-17 14:55:25 +0800478 const BufferGL *arrayBufferGL = GetImplAs<BufferGL>(arrayBuffer);
Corentin Wallez336129f2017-10-17 15:55:40 -0400479 mStateManager->bindBuffer(gl::BufferBinding::Array, arrayBufferGL->getBufferID());
Martin Radevdd5f27e2017-06-07 10:17:09 +0300480 callVertexAttribPointer(static_cast<GLuint>(attribIndex), attrib, binding.getStride(),
481 binding.getOffset());
Shaodf682a82017-03-31 15:13:21 +0800482
Shaodde78e82017-05-22 14:13:27 +0800483 mAppliedAttributes[attribIndex].size = attrib.size;
484 mAppliedAttributes[attribIndex].type = attrib.type;
485 mAppliedAttributes[attribIndex].normalized = attrib.normalized;
486 mAppliedAttributes[attribIndex].pureInteger = attrib.pureInteger;
Shaodf682a82017-03-31 15:13:21 +0800487
Shaodde78e82017-05-22 14:13:27 +0800488 // After VertexAttribPointer, attrib.relativeOffset is set to 0 and attrib.bindingIndex is set
489 // to attribIndex in driver. If attrib.relativeOffset != 0 or attrib.bindingIndex !=
490 // attribIndex, they should be set in updateAttribFormat and updateAttribBinding. The cache
491 // should be consistent with driver so that we won't miss anything.
492 mAppliedAttributes[attribIndex].relativeOffset = 0;
493 mAppliedAttributes[attribIndex].bindingIndex = static_cast<GLuint>(attribIndex);
Jiawei-Shao2597fb62016-12-09 16:38:02 +0800494
Shaodde78e82017-05-22 14:13:27 +0800495 mAppliedBindings[attribIndex].setStride(binding.getStride());
496 mAppliedBindings[attribIndex].setOffset(binding.getOffset());
James Darpiniane8a93c62018-01-04 18:02:24 -0800497 mAppliedBindings[attribIndex].setBuffer(context, binding.getBuffer().get(), false);
Jiawei-Shao2597fb62016-12-09 16:38:02 +0800498}
499
Shaodf682a82017-03-31 15:13:21 +0800500void VertexArrayGL::callVertexAttribPointer(GLuint attribIndex,
501 const VertexAttribute &attrib,
502 GLsizei stride,
503 GLintptr offset) const
504{
505 const GLvoid *pointer = reinterpret_cast<const GLvoid *>(offset);
506 if (attrib.pureInteger)
507 {
508 ASSERT(!attrib.normalized);
509 mFunctions->vertexAttribIPointer(attribIndex, attrib.size, attrib.type, stride, pointer);
510 }
511 else
512 {
513 mFunctions->vertexAttribPointer(attribIndex, attrib.size, attrib.type, attrib.normalized,
514 stride, pointer);
515 }
516}
517
Shaodde78e82017-05-22 14:13:27 +0800518bool VertexArrayGL::supportVertexAttribBinding() const
Jiawei-Shao2597fb62016-12-09 16:38:02 +0800519{
Shaodde78e82017-05-22 14:13:27 +0800520 ASSERT(mFunctions);
521 return (mFunctions->vertexAttribBinding != nullptr);
522}
Jiawei-Shao2597fb62016-12-09 16:38:02 +0800523
Shaodde78e82017-05-22 14:13:27 +0800524void VertexArrayGL::updateAttribFormat(size_t attribIndex)
525{
526 ASSERT(supportVertexAttribBinding());
527
Jamie Madill492f58e2017-10-09 19:41:33 -0400528 const VertexAttribute &attrib = mState.getVertexAttribute(attribIndex);
Shaodde78e82017-05-22 14:13:27 +0800529 if (SameVertexAttribFormat(mAppliedAttributes[attribIndex], attrib))
Jiawei-Shao2597fb62016-12-09 16:38:02 +0800530 {
Shaodf682a82017-03-31 15:13:21 +0800531 return;
Jiawei-Shao2597fb62016-12-09 16:38:02 +0800532 }
Shaodf682a82017-03-31 15:13:21 +0800533
Shaodde78e82017-05-22 14:13:27 +0800534 if (attrib.pureInteger)
535 {
536 ASSERT(!attrib.normalized);
537 mFunctions->vertexAttribIFormat(static_cast<GLuint>(attribIndex), attrib.size, attrib.type,
538 attrib.relativeOffset);
539 }
540 else
541 {
542 mFunctions->vertexAttribFormat(static_cast<GLuint>(attribIndex), attrib.size, attrib.type,
543 attrib.normalized, attrib.relativeOffset);
544 }
545
546 mAppliedAttributes[attribIndex].size = attrib.size;
547 mAppliedAttributes[attribIndex].type = attrib.type;
548 mAppliedAttributes[attribIndex].normalized = attrib.normalized;
549 mAppliedAttributes[attribIndex].pureInteger = attrib.pureInteger;
550 mAppliedAttributes[attribIndex].relativeOffset = attrib.relativeOffset;
551}
552
553void VertexArrayGL::updateAttribBinding(size_t attribIndex)
554{
555 ASSERT(supportVertexAttribBinding());
556
Jamie Madill492f58e2017-10-09 19:41:33 -0400557 GLuint bindingIndex = mState.getVertexAttribute(attribIndex).bindingIndex;
Shaodde78e82017-05-22 14:13:27 +0800558 if (mAppliedAttributes[attribIndex].bindingIndex == bindingIndex)
559 {
560 return;
561 }
562
563 mFunctions->vertexAttribBinding(static_cast<GLuint>(attribIndex), bindingIndex);
Shaodf682a82017-03-31 15:13:21 +0800564
565 mAppliedAttributes[attribIndex].bindingIndex = bindingIndex;
Shaodde78e82017-05-22 14:13:27 +0800566}
567
568void VertexArrayGL::updateBindingBuffer(const gl::Context *context, size_t bindingIndex)
569{
570 ASSERT(supportVertexAttribBinding());
571
Jamie Madill492f58e2017-10-09 19:41:33 -0400572 const VertexBinding &binding = mState.getVertexBinding(bindingIndex);
Shaodde78e82017-05-22 14:13:27 +0800573 if (SameVertexBuffer(mAppliedBindings[bindingIndex], binding))
574 {
575 return;
576 }
577
578 const Buffer *arrayBuffer = binding.getBuffer().get();
579 GLuint bufferId = 0;
580 if (arrayBuffer != nullptr)
581 {
582 bufferId = GetImplAs<BufferGL>(arrayBuffer)->getBufferID();
583 }
584
585 mFunctions->bindVertexBuffer(static_cast<GLuint>(bindingIndex), bufferId, binding.getOffset(),
586 binding.getStride());
587
588 mAppliedBindings[bindingIndex].setStride(binding.getStride());
589 mAppliedBindings[bindingIndex].setOffset(binding.getOffset());
James Darpiniane8a93c62018-01-04 18:02:24 -0800590 mAppliedBindings[bindingIndex].setBuffer(context, binding.getBuffer().get(), false);
Shaodde78e82017-05-22 14:13:27 +0800591}
592
593void VertexArrayGL::updateBindingDivisor(size_t bindingIndex)
594{
Martin Radev553590a2017-07-31 16:40:39 +0300595 GLuint adjustedDivisor =
Jamie Madill492f58e2017-10-09 19:41:33 -0400596 GetAdjustedDivisor(mAppliedNumViews, mState.getVertexBinding(bindingIndex).getDivisor());
Martin Radev553590a2017-07-31 16:40:39 +0300597 if (mAppliedBindings[bindingIndex].getDivisor() == adjustedDivisor)
Shaodde78e82017-05-22 14:13:27 +0800598 {
599 return;
600 }
601
602 if (supportVertexAttribBinding())
603 {
Martin Radev553590a2017-07-31 16:40:39 +0300604 mFunctions->vertexBindingDivisor(static_cast<GLuint>(bindingIndex), adjustedDivisor);
Shaodde78e82017-05-22 14:13:27 +0800605 }
606 else
607 {
608 // We can only use VertexAttribDivisor on platforms that don't support Vertex Attrib
609 // Binding.
Martin Radev553590a2017-07-31 16:40:39 +0300610 mFunctions->vertexAttribDivisor(static_cast<GLuint>(bindingIndex), adjustedDivisor);
Shaodde78e82017-05-22 14:13:27 +0800611 }
612
Martin Radev553590a2017-07-31 16:40:39 +0300613 mAppliedBindings[bindingIndex].setDivisor(adjustedDivisor);
Jamie Madill0b9e9032015-08-17 11:51:52 +0000614}
615
Jamie Madille858cb12018-03-27 09:44:32 -0400616void VertexArrayGL::syncDirtyAttrib(const gl::Context *context,
617 size_t attribIndex,
618 const gl::VertexArray::DirtyAttribBits &dirtyAttribBits)
619{
620 ASSERT(dirtyAttribBits.any());
621
622 for (size_t dirtyBit : dirtyAttribBits)
623 {
624 switch (dirtyBit)
625 {
626 case VertexArray::DIRTY_ATTRIB_ENABLED:
627 updateAttribEnabled(attribIndex);
628 break;
629
630 case VertexArray::DIRTY_ATTRIB_POINTER:
631 updateAttribPointer(context, attribIndex);
632 break;
633
634 case VertexArray::DIRTY_ATTRIB_FORMAT:
635 ASSERT(supportVertexAttribBinding());
636 updateAttribFormat(attribIndex);
637 break;
638
639 case VertexArray::DIRTY_ATTRIB_BINDING:
640 ASSERT(supportVertexAttribBinding());
641 updateAttribBinding(attribIndex);
642 break;
643
644 default:
645 UNREACHABLE();
646 break;
647 }
648 }
649}
650
651void VertexArrayGL::syncDirtyBinding(const gl::Context *context,
652 size_t bindingIndex,
653 const gl::VertexArray::DirtyBindingBits &dirtyBindingBits)
654{
Jamie Madill02c9c042018-04-17 13:43:48 -0400655 // Dependent state changes in buffers can trigger updates with no dirty bits set.
Jamie Madille858cb12018-03-27 09:44:32 -0400656
657 for (size_t dirtyBit : dirtyBindingBits)
658 {
659 switch (dirtyBit)
660 {
661 case VertexArray::DIRTY_BINDING_BUFFER:
662 ASSERT(supportVertexAttribBinding());
663 updateBindingBuffer(context, bindingIndex);
664 break;
665
666 case VertexArray::DIRTY_BINDING_DIVISOR:
667 updateBindingDivisor(bindingIndex);
668 break;
669
670 default:
671 UNREACHABLE();
672 break;
673 }
674 }
675}
676
Jamie Madilla56467e2018-04-11 16:19:41 -0400677#define ANGLE_DIRTY_ATTRIB_FUNC(INDEX) \
678 case VertexArray::DIRTY_BIT_ATTRIB_0 + INDEX: \
679 syncDirtyAttrib(context, INDEX, attribBits[INDEX]); \
680 break;
681
682#define ANGLE_DIRTY_BINDING_FUNC(INDEX) \
683 case VertexArray::DIRTY_BIT_BINDING_0 + INDEX: \
684 syncDirtyBinding(context, INDEX, bindingBits[INDEX]); \
685 break;
686
687#define ANGLE_DIRTY_BUFFER_DATA_FUNC(INDEX) \
688 case VertexArray::DIRTY_BIT_BUFFER_DATA_0 + INDEX: \
689 break;
690
Frank Henigman0af5b862018-03-27 20:19:33 -0400691gl::Error VertexArrayGL::syncState(const gl::Context *context,
692 const VertexArray::DirtyBits &dirtyBits,
693 const gl::VertexArray::DirtyAttribBitsArray &attribBits,
694 const gl::VertexArray::DirtyBindingBitsArray &bindingBits)
Jamie Madill0b9e9032015-08-17 11:51:52 +0000695{
Shaodf682a82017-03-31 15:13:21 +0800696 mStateManager->bindVertexArray(mVertexArrayID, getAppliedElementArrayBufferID());
697
Jamie Madill6de51852017-04-12 09:53:01 -0400698 for (size_t dirtyBit : dirtyBits)
Jamie Madill0b9e9032015-08-17 11:51:52 +0000699 {
Jamie Madille858cb12018-03-27 09:44:32 -0400700 switch (dirtyBit)
Jamie Madill0b9e9032015-08-17 11:51:52 +0000701 {
Jamie Madille858cb12018-03-27 09:44:32 -0400702 case VertexArray::DIRTY_BIT_ELEMENT_ARRAY_BUFFER:
703 updateElementArrayBufferBinding(context);
704 break;
Jiawei-Shao2597fb62016-12-09 16:38:02 +0800705
Jamie Madill09463932018-04-04 05:26:59 -0400706 case VertexArray::DIRTY_BIT_ELEMENT_ARRAY_BUFFER_DATA:
707 break;
708
Jamie Madilla56467e2018-04-11 16:19:41 -0400709 ANGLE_VERTEX_INDEX_CASES(ANGLE_DIRTY_ATTRIB_FUNC);
710 ANGLE_VERTEX_INDEX_CASES(ANGLE_DIRTY_BINDING_FUNC);
711 ANGLE_VERTEX_INDEX_CASES(ANGLE_DIRTY_BUFFER_DATA_FUNC);
712
Jamie Madille858cb12018-03-27 09:44:32 -0400713 default:
Jamie Madilla56467e2018-04-11 16:19:41 -0400714 UNREACHABLE();
Jamie Madille858cb12018-03-27 09:44:32 -0400715 break;
Jamie Madill0b9e9032015-08-17 11:51:52 +0000716 }
Jamie Madill0b9e9032015-08-17 11:51:52 +0000717 }
Frank Henigman0af5b862018-03-27 20:19:33 -0400718
719 return gl::NoError();
Jamie Madill0b9e9032015-08-17 11:51:52 +0000720}
721
Martin Radev553590a2017-07-31 16:40:39 +0300722void VertexArrayGL::applyNumViewsToDivisor(int numViews)
723{
724 if (numViews != mAppliedNumViews)
725 {
726 mStateManager->bindVertexArray(mVertexArrayID, getAppliedElementArrayBufferID());
727 mAppliedNumViews = numViews;
728 for (size_t index = 0u; index < mAppliedBindings.size(); ++index)
729 {
730 updateBindingDivisor(index);
731 }
732 }
733}
734
Shahbaz Youssefi337bd692018-08-22 16:16:38 -0400735void VertexArrayGL::applyActiveAttribLocationsMask(const gl::AttributesMask &activeMask)
736{
737 gl::AttributesMask updateMask = mProgramActiveAttribLocationsMask ^ activeMask;
738 mProgramActiveAttribLocationsMask = activeMask;
739
740 for (size_t attribIndex : updateMask)
741 {
742 updateAttribEnabled(attribIndex);
743 }
744}
745
Jamie Madill09463932018-04-04 05:26:59 -0400746} // namespace rx