// // Copyright 2015 The ANGLE Project Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // // VertexArrayGL.cpp: Implements the class methods for VertexArrayGL. #include "libANGLE/renderer/gl/VertexArrayGL.h" #include "common/bitset_utils.h" #include "common/debug.h" #include "common/mathutil.h" #include "common/utilities.h" #include "libANGLE/Buffer.h" #include "libANGLE/Context.h" #include "libANGLE/angletypes.h" #include "libANGLE/formatutils.h" #include "libANGLE/renderer/gl/BufferGL.h" #include "libANGLE/renderer/gl/ContextGL.h" #include "libANGLE/renderer/gl/FunctionsGL.h" #include "libANGLE/renderer/gl/StateManagerGL.h" #include "libANGLE/renderer/gl/renderergl_utils.h" using namespace gl; namespace rx { namespace { bool SameVertexAttribFormat(const VertexAttribute &a, const VertexAttribute &b) { return a.format == b.format && a.relativeOffset == b.relativeOffset; } bool SameVertexBuffer(const VertexBinding &a, const VertexBinding &b) { return a.getStride() == b.getStride() && a.getOffset() == b.getOffset() && a.getBuffer().get() == b.getBuffer().get(); } bool IsVertexAttribPointerSupported(size_t attribIndex, const VertexAttribute &attrib) { return (attribIndex == attrib.bindingIndex && attrib.relativeOffset == 0); } GLuint GetAdjustedDivisor(GLuint numViews, GLuint divisor) { return numViews * divisor; } static void ValidateStateHelperGetIntegerv(const FunctionsGL *functions, const GLuint localValue, const GLenum pname, const char *localName, const char *driverName) { GLint queryValue; functions->getIntegerv(pname, &queryValue); if (localValue != static_cast(queryValue)) { WARN() << localName << " (" << localValue << ") != " << driverName << " (" << queryValue << ")"; // Re-add ASSERT: http://anglebug.com/3900 // ASSERT(false); } } static void ValidateStateHelperGetVertexAttribiv(const FunctionsGL *functions, const GLint index, const GLuint localValue, const GLenum pname, const char *localName, const char *driverName) { GLint queryValue; functions->getVertexAttribiv(index, pname, &queryValue); if (localValue != static_cast(queryValue)) { WARN() << localName << "[" << index << "] (" << localValue << ") != " << driverName << "[" << index << "] (" << queryValue << ")"; // Re-add ASSERT: http://anglebug.com/3900 // ASSERT(false); } } } // anonymous namespace VertexArrayGL::VertexArrayGL(const VertexArrayState &state, const FunctionsGL *functions, StateManagerGL *stateManager) : VertexArrayImpl(state), mFunctions(functions), mStateManager(stateManager), mVertexArrayID(0), mAppliedNumViews(1), mAppliedElementArrayBuffer(), mAppliedBindings(state.getMaxBindings()), mStreamingElementArrayBufferSize(0), mStreamingElementArrayBuffer(0), mStreamingArrayBufferSize(0), mStreamingArrayBuffer(0) { ASSERT(mFunctions); ASSERT(mStateManager); mFunctions->genVertexArrays(1, &mVertexArrayID); // Set the cached vertex attribute array and vertex attribute binding array size GLuint maxVertexAttribs = static_cast(state.getMaxAttribs()); for (GLuint i = 0; i < maxVertexAttribs; i++) { mAppliedAttributes.emplace_back(i); } } VertexArrayGL::~VertexArrayGL() {} void VertexArrayGL::destroy(const gl::Context *context) { mStateManager->deleteVertexArray(mVertexArrayID); mVertexArrayID = 0; mAppliedNumViews = 1; mStateManager->deleteBuffer(mStreamingElementArrayBuffer); mStreamingElementArrayBufferSize = 0; mStreamingElementArrayBuffer = 0; mStateManager->deleteBuffer(mStreamingArrayBuffer); mStreamingArrayBufferSize = 0; mStreamingArrayBuffer = 0; mAppliedElementArrayBuffer.set(context, nullptr); for (auto &binding : mAppliedBindings) { binding.setBuffer(context, nullptr); } } angle::Result VertexArrayGL::syncClientSideData(const gl::Context *context, const gl::AttributesMask &activeAttributesMask, GLint first, GLsizei count, GLsizei instanceCount) const { return syncDrawState(context, activeAttributesMask, first, count, gl::DrawElementsType::InvalidEnum, nullptr, instanceCount, false, nullptr); } void VertexArrayGL::updateElementArrayBufferBinding(const gl::Context *context) const { gl::Buffer *elementArrayBuffer = mState.getElementArrayBuffer(); if (elementArrayBuffer != nullptr && elementArrayBuffer != mAppliedElementArrayBuffer.get()) { const BufferGL *bufferGL = GetImplAs(elementArrayBuffer); mStateManager->bindBuffer(gl::BufferBinding::ElementArray, bufferGL->getBufferID()); mAppliedElementArrayBuffer.set(context, elementArrayBuffer); } } angle::Result VertexArrayGL::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) const { // Check if any attributes need to be streamed, determines if the index range needs to be // computed const gl::AttributesMask &needsStreamingAttribs = context->getStateCache().getActiveClientAttribsMask(); // Determine if an index buffer needs to be streamed and the range of vertices that need to be // copied IndexRange indexRange; if (type != gl::DrawElementsType::InvalidEnum) { ANGLE_TRY(syncIndexData(context, count, type, indices, primitiveRestartEnabled, needsStreamingAttribs.any(), &indexRange, outIndices)); } else { // Not an indexed call, set the range to [first, first + count - 1] indexRange.start = first; indexRange.end = first + count - 1; } if (needsStreamingAttribs.any()) { ANGLE_TRY(streamAttributes(context, needsStreamingAttribs, instanceCount, indexRange)); } return angle::Result::Continue; } angle::Result VertexArrayGL::syncIndexData(const gl::Context *context, GLsizei count, gl::DrawElementsType type, const void *indices, bool primitiveRestartEnabled, bool attributesNeedStreaming, IndexRange *outIndexRange, const void **outIndices) const { ASSERT(outIndices); gl::Buffer *elementArrayBuffer = mState.getElementArrayBuffer(); // Need to check the range of indices if attributes need to be streamed if (elementArrayBuffer != nullptr) { ASSERT(elementArrayBuffer == mAppliedElementArrayBuffer.get()); // Only compute the index range if the attributes also need to be streamed if (attributesNeedStreaming) { ptrdiff_t elementArrayBufferOffset = reinterpret_cast(indices); ANGLE_TRY(mState.getElementArrayBuffer()->getIndexRange( context, type, elementArrayBufferOffset, count, primitiveRestartEnabled, outIndexRange)); } // Indices serves as an offset into the index buffer in this case, use the same value for // the draw call *outIndices = indices; } else { // Need to stream the index buffer // TODO: if GLES, nothing needs to be streamed // Only compute the index range if the attributes also need to be streamed if (attributesNeedStreaming) { *outIndexRange = ComputeIndexRange(type, indices, count, primitiveRestartEnabled); } // Allocate the streaming element array buffer if (mStreamingElementArrayBuffer == 0) { mFunctions->genBuffers(1, &mStreamingElementArrayBuffer); mStreamingElementArrayBufferSize = 0; } mStateManager->bindVertexArray(mVertexArrayID, getAppliedElementArrayBufferID()); mStateManager->bindBuffer(gl::BufferBinding::ElementArray, mStreamingElementArrayBuffer); mAppliedElementArrayBuffer.set(context, nullptr); // Make sure the element array buffer is large enough const GLuint indexTypeBytes = gl::GetDrawElementsTypeSize(type); size_t requiredStreamingBufferSize = indexTypeBytes * count; if (requiredStreamingBufferSize > mStreamingElementArrayBufferSize) { // Copy the indices in while resizing the buffer mFunctions->bufferData(GL_ELEMENT_ARRAY_BUFFER, requiredStreamingBufferSize, indices, GL_DYNAMIC_DRAW); mStreamingElementArrayBufferSize = requiredStreamingBufferSize; } else { // Put the indices at the beginning of the buffer mFunctions->bufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, requiredStreamingBufferSize, indices); } // Set the index offset for the draw call to zero since the supplied index pointer is to // client data *outIndices = nullptr; } return angle::Result::Continue; } void VertexArrayGL::computeStreamingAttributeSizes(const gl::AttributesMask &attribsToStream, GLsizei instanceCount, const gl::IndexRange &indexRange, size_t *outStreamingDataSize, size_t *outMaxAttributeDataSize) const { *outStreamingDataSize = 0; *outMaxAttributeDataSize = 0; ASSERT(attribsToStream.any()); const auto &attribs = mState.getVertexAttributes(); const auto &bindings = mState.getVertexBindings(); for (auto idx : attribsToStream) { const auto &attrib = attribs[idx]; const auto &binding = bindings[attrib.bindingIndex]; // If streaming is going to be required, compute the size of the required buffer // and how much slack space at the beginning of the buffer will be required by determining // the attribute with the largest data size. size_t typeSize = ComputeVertexAttributeTypeSize(attrib); GLuint adjustedDivisor = GetAdjustedDivisor(mAppliedNumViews, binding.getDivisor()); *outStreamingDataSize += typeSize * ComputeVertexBindingElementCount(adjustedDivisor, indexRange.vertexCount(), instanceCount); *outMaxAttributeDataSize = std::max(*outMaxAttributeDataSize, typeSize); } } angle::Result VertexArrayGL::streamAttributes(const gl::Context *context, const gl::AttributesMask &attribsToStream, GLsizei instanceCount, const gl::IndexRange &indexRange) const { // Sync the vertex attribute state and track what data needs to be streamed size_t streamingDataSize = 0; size_t maxAttributeDataSize = 0; computeStreamingAttributeSizes(attribsToStream, instanceCount, indexRange, &streamingDataSize, &maxAttributeDataSize); if (streamingDataSize == 0) { return angle::Result::Continue; } if (mStreamingArrayBuffer == 0) { mFunctions->genBuffers(1, &mStreamingArrayBuffer); mStreamingArrayBufferSize = 0; } // If first is greater than zero, a slack space needs to be left at the beginning of the buffer // so that the same 'first' argument can be passed into the draw call. const size_t bufferEmptySpace = maxAttributeDataSize * indexRange.start; const size_t requiredBufferSize = streamingDataSize + bufferEmptySpace; mStateManager->bindBuffer(gl::BufferBinding::Array, mStreamingArrayBuffer); if (requiredBufferSize > mStreamingArrayBufferSize) { mFunctions->bufferData(GL_ARRAY_BUFFER, requiredBufferSize, nullptr, GL_DYNAMIC_DRAW); mStreamingArrayBufferSize = requiredBufferSize; } mStateManager->bindVertexArray(mVertexArrayID, getAppliedElementArrayBufferID()); // Unmapping a buffer can return GL_FALSE to indicate that the system has corrupted the data // somehow (such as by a screen change), retry writing the data a few times and return // OUT_OF_MEMORY if that fails. GLboolean unmapResult = GL_FALSE; size_t unmapRetryAttempts = 5; while (unmapResult != GL_TRUE && --unmapRetryAttempts > 0) { uint8_t *bufferPointer = MapBufferRangeWithFallback(mFunctions, GL_ARRAY_BUFFER, 0, requiredBufferSize, GL_MAP_WRITE_BIT); size_t curBufferOffset = bufferEmptySpace; const auto &attribs = mState.getVertexAttributes(); const auto &bindings = mState.getVertexBindings(); for (auto idx : attribsToStream) { const auto &attrib = attribs[idx]; ASSERT(IsVertexAttribPointerSupported(idx, attrib)); const auto &binding = bindings[attrib.bindingIndex]; GLuint adjustedDivisor = GetAdjustedDivisor(mAppliedNumViews, binding.getDivisor()); const size_t streamedVertexCount = ComputeVertexBindingElementCount( adjustedDivisor, indexRange.vertexCount(), instanceCount); const size_t sourceStride = ComputeVertexAttributeStride(attrib, binding); const size_t destStride = ComputeVertexAttributeTypeSize(attrib); // Vertices do not apply the 'start' offset when the divisor is non-zero even when doing // a non-instanced draw call const size_t firstIndex = adjustedDivisor == 0 ? indexRange.start : 0; // Attributes using client memory ignore the VERTEX_ATTRIB_BINDING state. // https://www.opengl.org/registry/specs/ARB/vertex_attrib_binding.txt const uint8_t *inputPointer = static_cast(attrib.pointer); // Pack the data when copying it, user could have supplied a very large stride that // would cause the buffer to be much larger than needed. if (destStride == sourceStride) { // Can copy in one go, the data is packed memcpy(bufferPointer + curBufferOffset, inputPointer + (sourceStride * firstIndex), destStride * streamedVertexCount); } else { // Copy each vertex individually for (size_t vertexIdx = 0; vertexIdx < streamedVertexCount; vertexIdx++) { uint8_t *out = bufferPointer + curBufferOffset + (destStride * vertexIdx); const uint8_t *in = inputPointer + sourceStride * (vertexIdx + firstIndex); memcpy(out, in, destStride); } } // Compute where the 0-index vertex would be. const size_t vertexStartOffset = curBufferOffset - (firstIndex * destStride); callVertexAttribPointer(static_cast(idx), attrib, static_cast(destStride), static_cast(vertexStartOffset)); // Update the state to track the streamed attribute mAppliedAttributes[idx].format = attrib.format; mAppliedAttributes[idx].relativeOffset = 0; mAppliedAttributes[idx].bindingIndex = static_cast(idx); mAppliedBindings[idx].setStride(static_cast(destStride)); mAppliedBindings[idx].setOffset(static_cast(vertexStartOffset)); mAppliedBindings[idx].setBuffer(context, nullptr); curBufferOffset += destStride * streamedVertexCount; } unmapResult = mFunctions->unmapBuffer(GL_ARRAY_BUFFER); } ANGLE_CHECK(GetImplAs(context), unmapResult == GL_TRUE, "Failed to unmap the client data streaming buffer.", GL_OUT_OF_MEMORY); return angle::Result::Continue; } GLuint VertexArrayGL::getVertexArrayID() const { return mVertexArrayID; } GLuint VertexArrayGL::getAppliedElementArrayBufferID() const { if (mAppliedElementArrayBuffer.get() == nullptr) { return mStreamingElementArrayBuffer; } return GetImplAs(mAppliedElementArrayBuffer.get())->getBufferID(); } void VertexArrayGL::updateAttribEnabled(size_t attribIndex) { const bool enabled = mState.getVertexAttribute(attribIndex).enabled & mProgramActiveAttribLocationsMask.test(attribIndex); if (mAppliedAttributes[attribIndex].enabled == enabled) { return; } if (enabled) { mFunctions->enableVertexAttribArray(static_cast(attribIndex)); } else { mFunctions->disableVertexAttribArray(static_cast(attribIndex)); } mAppliedAttributes[attribIndex].enabled = enabled; } void VertexArrayGL::updateAttribPointer(const gl::Context *context, size_t attribIndex) { const VertexAttribute &attrib = mState.getVertexAttribute(attribIndex); // According to SPEC, VertexAttribPointer should update the binding indexed attribIndex instead // of the binding indexed attrib.bindingIndex (unless attribIndex == attrib.bindingIndex). const VertexBinding &binding = mState.getVertexBinding(attribIndex); // Early return when the vertex attribute isn't using a buffer object: // - If we need to stream, defer the attribPointer to the draw call. // - Skip the attribute that is disabled and uses a client memory pointer. // - Skip the attribute whose buffer is detached by BindVertexBuffer. Since it cannot have a // client memory pointer either, it must be disabled and shouldn't affect the draw. const auto &bindingBuffer = binding.getBuffer(); const Buffer *arrayBuffer = bindingBuffer.get(); if (arrayBuffer == nullptr) { // Mark the applied binding isn't using a buffer by setting its buffer to nullptr so that if // it starts to use a buffer later, there is no chance that the caching will skip it. mAppliedBindings[attribIndex].setBuffer(context, nullptr); return; } // We do not need to compare attrib.pointer because when we use a different client memory // pointer, we don't need to update mAttributesNeedStreaming by binding.buffer and we won't // update attribPointer in this function. if ((SameVertexAttribFormat(mAppliedAttributes[attribIndex], attrib)) && (mAppliedAttributes[attribIndex].bindingIndex == attrib.bindingIndex) && (SameVertexBuffer(mAppliedBindings[attribIndex], binding))) { return; } // Since ANGLE always uses a non-zero VAO, we cannot use a client memory pointer on it: // [OpenGL ES 3.0.2] Section 2.8 page 24: // An INVALID_OPERATION error is generated when a non-zero vertex array object is bound, // zero is bound to the ARRAY_BUFFER buffer object binding point, and the pointer argument // is not NULL. const BufferGL *arrayBufferGL = GetImplAs(arrayBuffer); mStateManager->bindBuffer(gl::BufferBinding::Array, arrayBufferGL->getBufferID()); callVertexAttribPointer(static_cast(attribIndex), attrib, binding.getStride(), binding.getOffset()); mAppliedAttributes[attribIndex].format = attrib.format; // After VertexAttribPointer, attrib.relativeOffset is set to 0 and attrib.bindingIndex is set // to attribIndex in driver. If attrib.relativeOffset != 0 or attrib.bindingIndex != // attribIndex, they should be set in updateAttribFormat and updateAttribBinding. The cache // should be consistent with driver so that we won't miss anything. mAppliedAttributes[attribIndex].relativeOffset = 0; mAppliedAttributes[attribIndex].bindingIndex = static_cast(attribIndex); mAppliedBindings[attribIndex].setStride(binding.getStride()); mAppliedBindings[attribIndex].setOffset(binding.getOffset()); mAppliedBindings[attribIndex].setBuffer(context, binding.getBuffer().get()); } void VertexArrayGL::callVertexAttribPointer(GLuint attribIndex, const VertexAttribute &attrib, GLsizei stride, GLintptr offset) const { const GLvoid *pointer = reinterpret_cast(offset); const angle::Format &format = *attrib.format; if (format.isPureInt()) { ASSERT(!format.isNorm()); mFunctions->vertexAttribIPointer(attribIndex, format.channelCount, gl::ToGLenum(format.vertexAttribType), stride, pointer); } else { mFunctions->vertexAttribPointer(attribIndex, format.channelCount, gl::ToGLenum(format.vertexAttribType), format.isNorm(), stride, pointer); } } bool VertexArrayGL::supportVertexAttribBinding() const { ASSERT(mFunctions); return (mFunctions->vertexAttribBinding != nullptr); } void VertexArrayGL::updateAttribFormat(size_t attribIndex) { ASSERT(supportVertexAttribBinding()); const VertexAttribute &attrib = mState.getVertexAttribute(attribIndex); if (SameVertexAttribFormat(mAppliedAttributes[attribIndex], attrib)) { return; } const angle::Format &format = *attrib.format; if (format.isPureInt()) { ASSERT(!format.isNorm()); mFunctions->vertexAttribIFormat(static_cast(attribIndex), format.channelCount, gl::ToGLenum(format.vertexAttribType), attrib.relativeOffset); } else { mFunctions->vertexAttribFormat(static_cast(attribIndex), format.channelCount, gl::ToGLenum(format.vertexAttribType), format.isNorm(), attrib.relativeOffset); } mAppliedAttributes[attribIndex].format = attrib.format; mAppliedAttributes[attribIndex].relativeOffset = attrib.relativeOffset; } void VertexArrayGL::updateAttribBinding(size_t attribIndex) { ASSERT(supportVertexAttribBinding()); GLuint bindingIndex = mState.getVertexAttribute(attribIndex).bindingIndex; if (mAppliedAttributes[attribIndex].bindingIndex == bindingIndex) { return; } mFunctions->vertexAttribBinding(static_cast(attribIndex), bindingIndex); mAppliedAttributes[attribIndex].bindingIndex = bindingIndex; } void VertexArrayGL::updateBindingBuffer(const gl::Context *context, size_t bindingIndex) { ASSERT(supportVertexAttribBinding()); const VertexBinding &binding = mState.getVertexBinding(bindingIndex); if (SameVertexBuffer(mAppliedBindings[bindingIndex], binding)) { return; } const Buffer *arrayBuffer = binding.getBuffer().get(); GLuint bufferId = 0; if (arrayBuffer != nullptr) { bufferId = GetImplAs(arrayBuffer)->getBufferID(); } mFunctions->bindVertexBuffer(static_cast(bindingIndex), bufferId, binding.getOffset(), binding.getStride()); mAppliedBindings[bindingIndex].setStride(binding.getStride()); mAppliedBindings[bindingIndex].setOffset(binding.getOffset()); mAppliedBindings[bindingIndex].setBuffer(context, binding.getBuffer().get()); } void VertexArrayGL::updateBindingDivisor(size_t bindingIndex) { GLuint adjustedDivisor = GetAdjustedDivisor(mAppliedNumViews, mState.getVertexBinding(bindingIndex).getDivisor()); if (mAppliedBindings[bindingIndex].getDivisor() == adjustedDivisor) { return; } if (supportVertexAttribBinding()) { mFunctions->vertexBindingDivisor(static_cast(bindingIndex), adjustedDivisor); } else { // We can only use VertexAttribDivisor on platforms that don't support Vertex Attrib // Binding. mFunctions->vertexAttribDivisor(static_cast(bindingIndex), adjustedDivisor); } mAppliedBindings[bindingIndex].setDivisor(adjustedDivisor); } void VertexArrayGL::syncDirtyAttrib(const gl::Context *context, size_t attribIndex, const gl::VertexArray::DirtyAttribBits &dirtyAttribBits) { ASSERT(dirtyAttribBits.any()); for (size_t dirtyBit : dirtyAttribBits) { switch (dirtyBit) { case VertexArray::DIRTY_ATTRIB_ENABLED: updateAttribEnabled(attribIndex); break; case VertexArray::DIRTY_ATTRIB_POINTER_BUFFER: case VertexArray::DIRTY_ATTRIB_POINTER: updateAttribPointer(context, attribIndex); break; case VertexArray::DIRTY_ATTRIB_FORMAT: ASSERT(supportVertexAttribBinding()); updateAttribFormat(attribIndex); break; case VertexArray::DIRTY_ATTRIB_BINDING: ASSERT(supportVertexAttribBinding()); updateAttribBinding(attribIndex); break; default: UNREACHABLE(); break; } } } void VertexArrayGL::syncDirtyBinding(const gl::Context *context, size_t bindingIndex, const gl::VertexArray::DirtyBindingBits &dirtyBindingBits) { // Dependent state changes in buffers can trigger updates with no dirty bits set. for (size_t dirtyBit : dirtyBindingBits) { switch (dirtyBit) { case VertexArray::DIRTY_BINDING_BUFFER: ASSERT(supportVertexAttribBinding()); updateBindingBuffer(context, bindingIndex); break; case VertexArray::DIRTY_BINDING_DIVISOR: updateBindingDivisor(bindingIndex); break; default: UNREACHABLE(); break; } } } #define ANGLE_DIRTY_ATTRIB_FUNC(INDEX) \ case VertexArray::DIRTY_BIT_ATTRIB_0 + INDEX: \ syncDirtyAttrib(context, INDEX, (*attribBits)[INDEX]); \ (*attribBits)[INDEX].reset(); \ break; #define ANGLE_DIRTY_BINDING_FUNC(INDEX) \ case VertexArray::DIRTY_BIT_BINDING_0 + INDEX: \ syncDirtyBinding(context, INDEX, (*bindingBits)[INDEX]); \ (*bindingBits)[INDEX].reset(); \ break; #define ANGLE_DIRTY_BUFFER_DATA_FUNC(INDEX) \ case VertexArray::DIRTY_BIT_BUFFER_DATA_0 + INDEX: \ break; angle::Result VertexArrayGL::syncState(const gl::Context *context, const gl::VertexArray::DirtyBits &dirtyBits, gl::VertexArray::DirtyAttribBitsArray *attribBits, gl::VertexArray::DirtyBindingBitsArray *bindingBits) { mStateManager->bindVertexArray(mVertexArrayID, getAppliedElementArrayBufferID()); for (size_t dirtyBit : dirtyBits) { switch (dirtyBit) { case VertexArray::DIRTY_BIT_ELEMENT_ARRAY_BUFFER: updateElementArrayBufferBinding(context); break; case VertexArray::DIRTY_BIT_ELEMENT_ARRAY_BUFFER_DATA: break; ANGLE_VERTEX_INDEX_CASES(ANGLE_DIRTY_ATTRIB_FUNC) ANGLE_VERTEX_INDEX_CASES(ANGLE_DIRTY_BINDING_FUNC) ANGLE_VERTEX_INDEX_CASES(ANGLE_DIRTY_BUFFER_DATA_FUNC) default: UNREACHABLE(); break; } } return angle::Result::Continue; } void VertexArrayGL::applyNumViewsToDivisor(int numViews) { if (numViews != mAppliedNumViews) { mStateManager->bindVertexArray(mVertexArrayID, getAppliedElementArrayBufferID()); mAppliedNumViews = numViews; for (size_t index = 0u; index < mAppliedBindings.size(); ++index) { updateBindingDivisor(index); } } } void VertexArrayGL::applyActiveAttribLocationsMask(const gl::AttributesMask &activeMask) { gl::AttributesMask updateMask = mProgramActiveAttribLocationsMask ^ activeMask; if (!updateMask.any()) { return; } ASSERT(mVertexArrayID == mStateManager->getVertexArrayID()); mProgramActiveAttribLocationsMask = activeMask; for (size_t attribIndex : updateMask) { updateAttribEnabled(attribIndex); } } void VertexArrayGL::validateState() const { // Ensure this vao is currently bound ValidateStateHelperGetIntegerv(mFunctions, mVertexArrayID, GL_VERTEX_ARRAY_BINDING, "mVertexArrayID", "GL_VERTEX_ARRAY_BINDING"); // Element array buffer if (mAppliedElementArrayBuffer.get() == nullptr) { ValidateStateHelperGetIntegerv( mFunctions, mStreamingElementArrayBuffer, GL_ELEMENT_ARRAY_BUFFER_BINDING, "mAppliedElementArrayBuffer", "GL_ELEMENT_ARRAY_BUFFER_BINDING"); } else { const BufferGL *bufferGL = GetImplAs(mAppliedElementArrayBuffer.get()); ValidateStateHelperGetIntegerv( mFunctions, bufferGL->getBufferID(), GL_ELEMENT_ARRAY_BUFFER_BINDING, "mAppliedElementArrayBuffer", "GL_ELEMENT_ARRAY_BUFFER_BINDING"); } // ValidateStateHelperGetIntegerv but with > comparison instead of != GLint queryValue; mFunctions->getIntegerv(GL_MAX_VERTEX_ATTRIBS, &queryValue); if (mAppliedAttributes.size() > static_cast(queryValue)) { WARN() << "mAppliedAttributes.size() (" << mAppliedAttributes.size() << ") > GL_MAX_VERTEX_ATTRIBS (" << queryValue << ")"; // Re-add ASSERT: http://anglebug.com/3900 // ASSERT(false); } // Check each applied attribute/binding for (GLuint index = 0; index < mAppliedAttributes.size(); index++) { VertexAttribute &attribute = mAppliedAttributes[index]; ASSERT(attribute.bindingIndex < mAppliedBindings.size()); VertexBinding &binding = mAppliedBindings[attribute.bindingIndex]; ValidateStateHelperGetVertexAttribiv( mFunctions, index, attribute.enabled, GL_VERTEX_ATTRIB_ARRAY_ENABLED, "mAppliedAttributes.enabled", "GL_VERTEX_ATTRIB_ARRAY_ENABLED"); if (attribute.enabled) { // Applied attributes ASSERT(attribute.format); ValidateStateHelperGetVertexAttribiv( mFunctions, index, ToGLenum(attribute.format->vertexAttribType), GL_VERTEX_ATTRIB_ARRAY_TYPE, "mAppliedAttributes.format->vertexAttribType", "GL_VERTEX_ATTRIB_ARRAY_TYPE"); ValidateStateHelperGetVertexAttribiv( mFunctions, index, attribute.format->channelCount, GL_VERTEX_ATTRIB_ARRAY_SIZE, "attribute.format->channelCount", "GL_VERTEX_ATTRIB_ARRAY_SIZE"); ValidateStateHelperGetVertexAttribiv( mFunctions, index, attribute.format->isNorm(), GL_VERTEX_ATTRIB_ARRAY_NORMALIZED, "attribute.format->isNorm()", "GL_VERTEX_ATTRIB_ARRAY_NORMALIZED"); ValidateStateHelperGetVertexAttribiv( mFunctions, index, attribute.format->isPureInt(), GL_VERTEX_ATTRIB_ARRAY_INTEGER, "attribute.format->isPureInt()", "GL_VERTEX_ATTRIB_ARRAY_INTEGER"); if (supportVertexAttribBinding()) { ValidateStateHelperGetVertexAttribiv( mFunctions, index, attribute.relativeOffset, GL_VERTEX_ATTRIB_RELATIVE_OFFSET, "attribute.relativeOffset", "GL_VERTEX_ATTRIB_RELATIVE_OFFSET"); ValidateStateHelperGetVertexAttribiv( mFunctions, index, attribute.bindingIndex, GL_VERTEX_ATTRIB_BINDING, "attribute.bindingIndex", "GL_VERTEX_ATTRIB_BINDING"); } // Applied bindings if (binding.getBuffer().get() == nullptr) { ValidateStateHelperGetVertexAttribiv( mFunctions, index, mStreamingArrayBuffer, GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, "mAppliedBindings.bufferID", "GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING"); } else { const BufferGL *arrayBufferGL = GetImplAs(binding.getBuffer().get()); ASSERT(arrayBufferGL); ValidateStateHelperGetVertexAttribiv( mFunctions, index, arrayBufferGL->getBufferID(), GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, "mAppliedBindings.bufferID", "GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING"); ValidateStateHelperGetVertexAttribiv( mFunctions, index, binding.getStride(), GL_VERTEX_ATTRIB_ARRAY_STRIDE, "binding.getStride()", "GL_VERTEX_ATTRIB_ARRAY_STRIDE"); ValidateStateHelperGetVertexAttribiv( mFunctions, index, binding.getDivisor(), GL_VERTEX_ATTRIB_ARRAY_DIVISOR, "binding.getDivisor()", "GL_VERTEX_ATTRIB_ARRAY_DIVISOR"); } } } } } // namespace rx