blob: 84722a4a8ac0790c29799d7163d0a0cbcc591d67 [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
11#include "common/debug.h"
Geoff Lang7c82bc42015-03-09 16:18:08 -040012#include "common/mathutil.h"
Geoff Lang831b1952015-05-05 11:02:27 -040013#include "common/utilities.h"
Geoff Lang6ae6efc2015-03-09 14:42:35 -040014#include "libANGLE/Buffer.h"
Geoff Langba4c4a82015-02-24 12:38:46 -050015#include "libANGLE/angletypes.h"
Geoff Lang7c82bc42015-03-09 16:18:08 -040016#include "libANGLE/formatutils.h"
Geoff Langba4c4a82015-02-24 12:38:46 -050017#include "libANGLE/renderer/gl/BufferGL.h"
18#include "libANGLE/renderer/gl/FunctionsGL.h"
19#include "libANGLE/renderer/gl/StateManagerGL.h"
Geoff Langf9a6f082015-01-22 13:32:49 -050020
21namespace rx
22{
23
Jamie Madill8e344942015-07-09 14:22:07 -040024VertexArrayGL::VertexArrayGL(const gl::VertexArray::Data &data, const FunctionsGL *functions, StateManagerGL *stateManager)
25 : VertexArrayImpl(data),
Geoff Langba4c4a82015-02-24 12:38:46 -050026 mFunctions(functions),
27 mStateManager(stateManager),
28 mVertexArrayID(0),
Jamie Madill0018c852015-07-30 10:57:46 -040029 mAppliedElementArrayBuffer(),
Geoff Lang7c82bc42015-03-09 16:18:08 -040030 mStreamingElementArrayBufferSize(0),
31 mStreamingElementArrayBuffer(0),
32 mStreamingArrayBufferSize(0),
33 mStreamingArrayBuffer(0)
Geoff Langba4c4a82015-02-24 12:38:46 -050034{
35 ASSERT(mFunctions);
36 ASSERT(mStateManager);
37 mFunctions->genVertexArrays(1, &mVertexArrayID);
38
39 // Set the cached vertex attribute array size
40 GLint maxVertexAttribs;
41 mFunctions->getIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxVertexAttribs);
42 mAppliedAttributes.resize(maxVertexAttribs);
43}
Geoff Langf9a6f082015-01-22 13:32:49 -050044
45VertexArrayGL::~VertexArrayGL()
Geoff Langba4c4a82015-02-24 12:38:46 -050046{
Geoff Lang1eb708e2015-05-04 14:58:23 -040047 mStateManager->deleteVertexArray(mVertexArrayID);
48 mVertexArrayID = 0;
Geoff Langba4c4a82015-02-24 12:38:46 -050049
Geoff Lang1eb708e2015-05-04 14:58:23 -040050 mStateManager->deleteBuffer(mStreamingElementArrayBuffer);
51 mStreamingElementArrayBufferSize = 0;
52 mStreamingElementArrayBuffer = 0;
Geoff Lang7c82bc42015-03-09 16:18:08 -040053
Geoff Lang1eb708e2015-05-04 14:58:23 -040054 mStateManager->deleteBuffer(mStreamingArrayBuffer);
55 mStreamingArrayBufferSize = 0;
56 mStreamingArrayBuffer = 0;
Geoff Lang7c82bc42015-03-09 16:18:08 -040057
Jamie Madill0018c852015-07-30 10:57:46 -040058 mAppliedElementArrayBuffer.set(nullptr);
Geoff Langba4c4a82015-02-24 12:38:46 -050059 for (size_t idx = 0; idx < mAppliedAttributes.size(); idx++)
60 {
Jamie Madill8e344942015-07-09 14:22:07 -040061 mAppliedAttributes[idx].buffer.set(nullptr);
Geoff Langba4c4a82015-02-24 12:38:46 -050062 }
63}
Geoff Langf9a6f082015-01-22 13:32:49 -050064
Geoff Langb61e1732015-06-05 11:49:55 -040065gl::Error VertexArrayGL::syncDrawArraysState(const std::vector<GLuint> &activeAttribLocations, GLint first, GLsizei count) const
Geoff Lang7c82bc42015-03-09 16:18:08 -040066{
Geoff Langb61e1732015-06-05 11:49:55 -040067 return syncDrawState(activeAttribLocations, first, count, GL_NONE, nullptr, nullptr);
Geoff Lang7c82bc42015-03-09 16:18:08 -040068}
69
Geoff Langb61e1732015-06-05 11:49:55 -040070gl::Error VertexArrayGL::syncDrawElementsState(const std::vector<GLuint> &activeAttribLocations, GLsizei count,
71 GLenum type, const GLvoid *indices, const GLvoid **outIndices) const
Geoff Lang7c82bc42015-03-09 16:18:08 -040072{
Geoff Langb61e1732015-06-05 11:49:55 -040073 return syncDrawState(activeAttribLocations, 0, count, type, indices, outIndices);
Geoff Lang7c82bc42015-03-09 16:18:08 -040074}
75
Geoff Langb61e1732015-06-05 11:49:55 -040076gl::Error VertexArrayGL::syncDrawState(const std::vector<GLuint> &activeAttribLocations, GLint first, GLsizei count, GLenum type, const GLvoid *indices, const GLvoid **outIndices) const
Geoff Lang6ae6efc2015-03-09 14:42:35 -040077{
Jamie Madill0018c852015-07-30 10:57:46 -040078 mStateManager->bindVertexArray(mVertexArrayID, getAppliedElementArrayBufferID());
Geoff Lang6ae6efc2015-03-09 14:42:35 -040079
Geoff Lang7c82bc42015-03-09 16:18:08 -040080 // Check if any attributes need to be streamed, determines if the index range needs to be computed
Geoff Langb61e1732015-06-05 11:49:55 -040081 bool attributesNeedStreaming = doAttributesNeedStreaming(activeAttribLocations);
Geoff Lang7c82bc42015-03-09 16:18:08 -040082
83 // Determine if an index buffer needs to be streamed and the range of vertices that need to be copied
Geoff Lang831b1952015-05-05 11:02:27 -040084 gl::RangeUI indexRange(0, 0);
Geoff Lang7c82bc42015-03-09 16:18:08 -040085 if (type != GL_NONE)
Geoff Langba4c4a82015-02-24 12:38:46 -050086 {
Geoff Lang7c82bc42015-03-09 16:18:08 -040087 gl::Error error = syncIndexData(count, type, indices, attributesNeedStreaming, &indexRange, outIndices);
88 if (error.isError())
89 {
90 return error;
91 }
92 }
93 else
94 {
95 // Not an indexed call, set the range to [first, first + count)
96 indexRange.start = first;
97 indexRange.end = first + count;
Geoff Langba4c4a82015-02-24 12:38:46 -050098 }
99
Geoff Lang7c82bc42015-03-09 16:18:08 -0400100 // Sync the vertex attribute state and track what data needs to be streamed
101 size_t streamingDataSize = 0;
102 size_t maxAttributeDataSize = 0;
Geoff Langb61e1732015-06-05 11:49:55 -0400103 gl::Error error = syncAttributeState(activeAttribLocations, attributesNeedStreaming, indexRange,
104 &streamingDataSize, &maxAttributeDataSize);
Geoff Lang7c82bc42015-03-09 16:18:08 -0400105 if (error.isError())
Geoff Langba4c4a82015-02-24 12:38:46 -0500106 {
Geoff Lang7c82bc42015-03-09 16:18:08 -0400107 return error;
Geoff Langba4c4a82015-02-24 12:38:46 -0500108 }
Geoff Langf9a6f082015-01-22 13:32:49 -0500109
Geoff Lang7c82bc42015-03-09 16:18:08 -0400110 if (streamingDataSize > 0)
111 {
112 ASSERT(attributesNeedStreaming);
113
Geoff Langb61e1732015-06-05 11:49:55 -0400114 gl::Error error = streamAttributes(activeAttribLocations, streamingDataSize, maxAttributeDataSize,
115 indexRange);
Geoff Lang7c82bc42015-03-09 16:18:08 -0400116 if (error.isError())
117 {
118 return error;
119 }
120 }
121
122 return gl::Error(GL_NO_ERROR);
123}
124
Geoff Langb61e1732015-06-05 11:49:55 -0400125bool VertexArrayGL::doAttributesNeedStreaming(const std::vector<GLuint> &activeAttribLocations) const
Geoff Lang7c82bc42015-03-09 16:18:08 -0400126{
127 // TODO: if GLES, nothing needs to be streamed
Jamie Madill8e344942015-07-09 14:22:07 -0400128 const auto &attribs = mData.getVertexAttributes();
Geoff Langb61e1732015-06-05 11:49:55 -0400129 for (size_t activeAttrib = 0; activeAttrib < activeAttribLocations.size(); activeAttrib++)
Geoff Lang7c82bc42015-03-09 16:18:08 -0400130 {
Geoff Langb61e1732015-06-05 11:49:55 -0400131 GLuint idx = activeAttribLocations[activeAttrib];
Jamie Madill8e344942015-07-09 14:22:07 -0400132 if (attribs[idx].enabled && attribs[idx].buffer.get() == nullptr)
Geoff Lang7c82bc42015-03-09 16:18:08 -0400133 {
134 return true;
135 }
136 }
137
138 return false;
139}
140
Geoff Langb61e1732015-06-05 11:49:55 -0400141gl::Error VertexArrayGL::syncAttributeState(const std::vector<GLuint> &activeAttribLocations, bool attributesNeedStreaming,
142 const gl::RangeUI &indexRange, size_t *outStreamingDataSize, size_t *outMaxAttributeDataSize) const
Geoff Lang7c82bc42015-03-09 16:18:08 -0400143{
144 *outStreamingDataSize = 0;
145 *outMaxAttributeDataSize = 0;
146
Jamie Madill8e344942015-07-09 14:22:07 -0400147 const auto &attribs = mData.getVertexAttributes();
Geoff Langb61e1732015-06-05 11:49:55 -0400148 for (size_t activeAttrib = 0; activeAttrib < activeAttribLocations.size(); activeAttrib++)
Geoff Langba4c4a82015-02-24 12:38:46 -0500149 {
Geoff Langb61e1732015-06-05 11:49:55 -0400150 GLuint idx = activeAttribLocations[activeAttrib];
Jamie Madill8e344942015-07-09 14:22:07 -0400151 const auto &attrib = attribs[idx];
152
Geoff Lang7c82bc42015-03-09 16:18:08 -0400153 // Always sync the enabled and divisor state, they are required for both streaming and buffered
154 // attributes
Jamie Madill8e344942015-07-09 14:22:07 -0400155 if (mAppliedAttributes[idx].enabled != attrib.enabled)
Geoff Langba4c4a82015-02-24 12:38:46 -0500156 {
Jamie Madill8e344942015-07-09 14:22:07 -0400157 if (attrib.enabled)
Geoff Lang6ae6efc2015-03-09 14:42:35 -0400158 {
159 mFunctions->enableVertexAttribArray(idx);
160 }
161 else
162 {
163 mFunctions->disableVertexAttribArray(idx);
164 }
Jamie Madill8e344942015-07-09 14:22:07 -0400165 mAppliedAttributes[idx].enabled = attrib.enabled;
Geoff Lang7c82bc42015-03-09 16:18:08 -0400166 }
Jamie Madill8e344942015-07-09 14:22:07 -0400167 if (mAppliedAttributes[idx].divisor != attrib.divisor)
Geoff Lang7c82bc42015-03-09 16:18:08 -0400168 {
Jamie Madill8e344942015-07-09 14:22:07 -0400169 mFunctions->vertexAttribDivisor(idx, attrib.divisor);
170 mAppliedAttributes[idx].divisor = attrib.divisor;
Geoff Lang7c82bc42015-03-09 16:18:08 -0400171 }
Geoff Lang6ae6efc2015-03-09 14:42:35 -0400172
Jamie Madill8e344942015-07-09 14:22:07 -0400173 if (attribs[idx].enabled && attrib.buffer.get() == nullptr)
Geoff Lang7c82bc42015-03-09 16:18:08 -0400174 {
175 ASSERT(attributesNeedStreaming);
176
177 const size_t streamedVertexCount = indexRange.end - indexRange.start + 1;
178
179 // If streaming is going to be required, compute the size of the required buffer
180 // and how much slack space at the beginning of the buffer will be required by determining
181 // the attribute with the largest data size.
Jamie Madill8e344942015-07-09 14:22:07 -0400182 size_t typeSize = ComputeVertexAttributeTypeSize(attrib);
Geoff Lang7c82bc42015-03-09 16:18:08 -0400183 *outStreamingDataSize += typeSize * streamedVertexCount;
184 *outMaxAttributeDataSize = std::max(*outMaxAttributeDataSize, typeSize);
185 }
186 else
187 {
188 // Sync the attribute with no translation
Jamie Madill8e344942015-07-09 14:22:07 -0400189 if (mAppliedAttributes[idx] != attrib)
Geoff Lang6ae6efc2015-03-09 14:42:35 -0400190 {
Jamie Madill8e344942015-07-09 14:22:07 -0400191 const gl::Buffer *arrayBuffer = attrib.buffer.get();
Geoff Lang851cd582015-05-26 16:47:23 -0400192 if (arrayBuffer != nullptr)
193 {
194 const BufferGL *arrayBufferGL = GetImplAs<BufferGL>(arrayBuffer);
195 mStateManager->bindBuffer(GL_ARRAY_BUFFER, arrayBufferGL->getBufferID());
196 }
197 else
198 {
199 mStateManager->bindBuffer(GL_ARRAY_BUFFER, 0);
200 }
Geoff Lang6ae6efc2015-03-09 14:42:35 -0400201
Jamie Madill8e344942015-07-09 14:22:07 -0400202 if (attrib.pureInteger)
Geoff Lang7c82bc42015-03-09 16:18:08 -0400203 {
Jamie Madill8e344942015-07-09 14:22:07 -0400204 mFunctions->vertexAttribIPointer(idx, attrib.size, attrib.type,
205 attrib.stride, attrib.pointer);
Geoff Lang7c82bc42015-03-09 16:18:08 -0400206 }
207 else
208 {
Jamie Madill8e344942015-07-09 14:22:07 -0400209 mFunctions->vertexAttribPointer(idx, attrib.size, attrib.type,
210 attrib.normalized, attrib.stride,
211 attrib.pointer);
Geoff Lang7c82bc42015-03-09 16:18:08 -0400212 }
Geoff Lang6ae6efc2015-03-09 14:42:35 -0400213
Jamie Madill8e344942015-07-09 14:22:07 -0400214 mAppliedAttributes[idx] = attrib;
Geoff Lang7c82bc42015-03-09 16:18:08 -0400215 }
Geoff Langba4c4a82015-02-24 12:38:46 -0500216 }
Geoff Langba4c4a82015-02-24 12:38:46 -0500217 }
Geoff Lang7c82bc42015-03-09 16:18:08 -0400218
219 return gl::Error(GL_NO_ERROR);
220}
221
222gl::Error VertexArrayGL::syncIndexData(GLsizei count, GLenum type, const GLvoid *indices, bool attributesNeedStreaming,
Geoff Lang831b1952015-05-05 11:02:27 -0400223 gl::RangeUI *outIndexRange, const GLvoid **outIndices) const
Geoff Lang7c82bc42015-03-09 16:18:08 -0400224{
225 ASSERT(outIndices);
226
Jamie Madill8e344942015-07-09 14:22:07 -0400227 gl::Buffer *elementArrayBuffer = mData.getElementArrayBuffer().get();
228
Geoff Lang7c82bc42015-03-09 16:18:08 -0400229 // Need to check the range of indices if attributes need to be streamed
Jamie Madill8e344942015-07-09 14:22:07 -0400230 if (elementArrayBuffer != nullptr)
Geoff Lang7c82bc42015-03-09 16:18:08 -0400231 {
Jamie Madill0018c852015-07-30 10:57:46 -0400232 if (elementArrayBuffer != mAppliedElementArrayBuffer.get())
Geoff Lang7c82bc42015-03-09 16:18:08 -0400233 {
Jamie Madill0018c852015-07-30 10:57:46 -0400234 const BufferGL *bufferGL = GetImplAs<BufferGL>(elementArrayBuffer);
235 mStateManager->bindBuffer(GL_ELEMENT_ARRAY_BUFFER, bufferGL->getBufferID());
236 mAppliedElementArrayBuffer.set(elementArrayBuffer);
Geoff Lang7c82bc42015-03-09 16:18:08 -0400237 }
238
239 // Only compute the index range if the attributes also need to be streamed
240 if (attributesNeedStreaming)
241 {
242 ptrdiff_t elementArrayBufferOffset = reinterpret_cast<ptrdiff_t>(indices);
Jamie Madill8e344942015-07-09 14:22:07 -0400243 gl::Error error = mData.getElementArrayBuffer()->getIndexRange(type, static_cast<size_t>(elementArrayBufferOffset), count, outIndexRange);
Geoff Lang520c4ae2015-05-05 13:12:36 -0400244 if (error.isError())
Geoff Lang7c82bc42015-03-09 16:18:08 -0400245 {
Geoff Lang520c4ae2015-05-05 13:12:36 -0400246 return error;
Geoff Lang7c82bc42015-03-09 16:18:08 -0400247 }
248 }
249
250 // Indices serves as an offset into the index buffer in this case, use the same value for the draw call
251 *outIndices = indices;
252 }
253 else
254 {
255 // Need to stream the index buffer
256 // TODO: if GLES, nothing needs to be streamed
257
258 // Only compute the index range if the attributes also need to be streamed
259 if (attributesNeedStreaming)
260 {
Geoff Lang831b1952015-05-05 11:02:27 -0400261 *outIndexRange = gl::ComputeIndexRange(type, indices, count);
Geoff Lang7c82bc42015-03-09 16:18:08 -0400262 }
263
264 // Allocate the streaming element array buffer
265 if (mStreamingElementArrayBuffer == 0)
266 {
267 mFunctions->genBuffers(1, &mStreamingElementArrayBuffer);
268 mStreamingElementArrayBufferSize = 0;
269 }
270
271 mStateManager->bindBuffer(GL_ELEMENT_ARRAY_BUFFER, mStreamingElementArrayBuffer);
Jamie Madill0018c852015-07-30 10:57:46 -0400272 mAppliedElementArrayBuffer.set(nullptr);
Geoff Lang7c82bc42015-03-09 16:18:08 -0400273
274 // Make sure the element array buffer is large enough
275 const gl::Type &indexTypeInfo = gl::GetTypeInfo(type);
276 size_t requiredStreamingBufferSize = indexTypeInfo.bytes * count;
277 if (requiredStreamingBufferSize > mStreamingElementArrayBufferSize)
278 {
279 // Copy the indices in while resizing the buffer
280 mFunctions->bufferData(GL_ELEMENT_ARRAY_BUFFER, requiredStreamingBufferSize, indices, GL_DYNAMIC_DRAW);
281 mStreamingElementArrayBufferSize = requiredStreamingBufferSize;
282 }
283 else
284 {
285 // Put the indices at the beginning of the buffer
286 mFunctions->bufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, requiredStreamingBufferSize, indices);
287 }
288
289 // Set the index offset for the draw call to zero since the supplied index pointer is to client data
290 *outIndices = nullptr;
291 }
292
293 return gl::Error(GL_NO_ERROR);
294}
295
Geoff Langb61e1732015-06-05 11:49:55 -0400296gl::Error VertexArrayGL::streamAttributes(const std::vector<GLuint> &activeAttribLocations, size_t streamingDataSize,
297 size_t maxAttributeDataSize, const gl::RangeUI &indexRange) const
Geoff Lang7c82bc42015-03-09 16:18:08 -0400298{
299 if (mStreamingArrayBuffer == 0)
300 {
301 mFunctions->genBuffers(1, &mStreamingArrayBuffer);
302 mStreamingArrayBufferSize = 0;
303 }
304
305 // If first is greater than zero, a slack space needs to be left at the beginning of the buffer so that
306 // the same 'first' argument can be passed into the draw call.
307 const size_t bufferEmptySpace = maxAttributeDataSize * indexRange.start;
308 const size_t requiredBufferSize = streamingDataSize + bufferEmptySpace;
309
310 mStateManager->bindBuffer(GL_ARRAY_BUFFER, mStreamingArrayBuffer);
311 if (requiredBufferSize > mStreamingArrayBufferSize)
312 {
313 mFunctions->bufferData(GL_ARRAY_BUFFER, requiredBufferSize, nullptr, GL_DYNAMIC_DRAW);
314 mStreamingArrayBufferSize = requiredBufferSize;
315 }
316
317 // Unmapping a buffer can return GL_FALSE to indicate that the system has corrupted the data
318 // somehow (such as by a screen change), retry writing the data a few times and return OUT_OF_MEMORY
319 // if that fails.
320 GLboolean unmapResult = GL_FALSE;
321 size_t unmapRetryAttempts = 5;
322 while (unmapResult != GL_TRUE && --unmapRetryAttempts > 0)
323 {
324 uint8_t *bufferPointer = reinterpret_cast<uint8_t*>(mFunctions->mapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY));
325 size_t curBufferOffset = bufferEmptySpace;
326
327 const size_t streamedVertexCount = indexRange.end - indexRange.start + 1;
328
Jamie Madill8e344942015-07-09 14:22:07 -0400329 const auto &attribs = mData.getVertexAttributes();
Geoff Langb61e1732015-06-05 11:49:55 -0400330 for (size_t activeAttrib = 0; activeAttrib < activeAttribLocations.size(); activeAttrib++)
Geoff Lang7c82bc42015-03-09 16:18:08 -0400331 {
Geoff Langb61e1732015-06-05 11:49:55 -0400332 GLuint idx = activeAttribLocations[activeAttrib];
Jamie Madill8e344942015-07-09 14:22:07 -0400333 const auto &attrib = attribs[idx];
Geoff Lang7c82bc42015-03-09 16:18:08 -0400334
Jamie Madill8e344942015-07-09 14:22:07 -0400335 if (attrib.enabled && attrib.buffer.get() == nullptr)
336 {
337 const size_t sourceStride = ComputeVertexAttributeStride(attrib);
338 const size_t destStride = ComputeVertexAttributeTypeSize(attrib);
339
340 const uint8_t *inputPointer = reinterpret_cast<const uint8_t*>(attrib.pointer);
Geoff Lang7c82bc42015-03-09 16:18:08 -0400341
342 // Pack the data when copying it, user could have supplied a very large stride that would
343 // cause the buffer to be much larger than needed.
344 if (destStride == sourceStride)
345 {
346 // Can copy in one go, the data is packed
347 memcpy(bufferPointer + curBufferOffset,
348 inputPointer + (sourceStride * indexRange.start),
349 destStride * streamedVertexCount);
350 }
351 else
352 {
353 // Copy each vertex individually
354 for (size_t vertexIdx = indexRange.start; vertexIdx <= indexRange.end; vertexIdx++)
355 {
356 memcpy(bufferPointer + curBufferOffset + (destStride * vertexIdx),
357 inputPointer + (sourceStride * vertexIdx),
358 destStride);
359 }
360 }
361
362 // Compute where the 0-index vertex would be.
363 const size_t vertexStartOffset = curBufferOffset - (indexRange.start * destStride);
364
Jamie Madill8e344942015-07-09 14:22:07 -0400365 mFunctions->vertexAttribPointer(idx, attrib.size, attrib.type,
366 attrib.normalized, destStride,
Geoff Lang7c82bc42015-03-09 16:18:08 -0400367 reinterpret_cast<const GLvoid*>(vertexStartOffset));
368
369 curBufferOffset += destStride * streamedVertexCount;
370
371 // Mark the applied attribute as dirty by setting an invalid size so that if it doesn't
372 // need to be streamed later, there is no chance that the caching will skip it.
373 mAppliedAttributes[idx].size = static_cast<GLuint>(-1);
374 }
375 }
376
377 unmapResult = mFunctions->unmapBuffer(GL_ARRAY_BUFFER);
378 }
379
380 if (unmapResult != GL_TRUE)
381 {
382 return gl::Error(GL_OUT_OF_MEMORY, "Failed to unmap the client data streaming buffer.");
383 }
384
385 return gl::Error(GL_NO_ERROR);
Geoff Langba4c4a82015-02-24 12:38:46 -0500386}
387
388GLuint VertexArrayGL::getVertexArrayID() const
389{
390 return mVertexArrayID;
Geoff Langf9a6f082015-01-22 13:32:49 -0500391}
392
Geoff Lang294cad92015-05-26 15:11:23 -0400393GLuint VertexArrayGL::getAppliedElementArrayBufferID() const
394{
Jamie Madill0018c852015-07-30 10:57:46 -0400395 return mAppliedElementArrayBuffer.get() == nullptr ? mStreamingElementArrayBuffer
396 : mAppliedElementArrayBuffer.id();
Geoff Lang294cad92015-05-26 15:11:23 -0400397}
398
Geoff Langf9a6f082015-01-22 13:32:49 -0500399}