| // |
| // Copyright (c) 2013 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. |
| // |
| // Implementation of the state class for mananging GLES 3 Vertex Array Objects. |
| // |
| |
| #include "libANGLE/VertexArray.h" |
| #include "libANGLE/Buffer.h" |
| #include "libANGLE/Context.h" |
| #include "libANGLE/renderer/GLImplFactory.h" |
| #include "libANGLE/renderer/VertexArrayImpl.h" |
| |
| namespace gl |
| { |
| |
| VertexArrayState::VertexArrayState(size_t maxAttribs, size_t maxAttribBindings) |
| : mLabel(), mVertexBindings(maxAttribBindings), mMaxEnabledAttribute(0) |
| { |
| ASSERT(maxAttribs <= maxAttribBindings); |
| |
| for (size_t i = 0; i < maxAttribs; i++) |
| { |
| mVertexAttributes.emplace_back(static_cast<GLuint>(i)); |
| } |
| } |
| |
| VertexArrayState::~VertexArrayState() |
| { |
| } |
| |
| VertexArray::VertexArray(rx::GLImplFactory *factory, |
| GLuint id, |
| size_t maxAttribs, |
| size_t maxAttribBindings) |
| : mId(id), |
| mState(maxAttribs, maxAttribBindings), |
| mVertexArray(factory->createVertexArray(mState)) |
| { |
| } |
| |
| void VertexArray::onDestroy(const Context *context) |
| { |
| for (auto &binding : mState.mVertexBindings) |
| { |
| binding.setBuffer(context, nullptr); |
| } |
| mState.mElementArrayBuffer.set(context, nullptr); |
| mVertexArray->destroy(context); |
| SafeDelete(mVertexArray); |
| delete this; |
| } |
| |
| VertexArray::~VertexArray() |
| { |
| ASSERT(!mVertexArray); |
| } |
| |
| GLuint VertexArray::id() const |
| { |
| return mId; |
| } |
| |
| void VertexArray::setLabel(const std::string &label) |
| { |
| mState.mLabel = label; |
| } |
| |
| const std::string &VertexArray::getLabel() const |
| { |
| return mState.mLabel; |
| } |
| |
| void VertexArray::detachBuffer(const Context *context, GLuint bufferName) |
| { |
| for (auto &binding : mState.mVertexBindings) |
| { |
| if (binding.getBuffer().id() == bufferName) |
| { |
| binding.setBuffer(context, nullptr); |
| } |
| } |
| |
| if (mState.mElementArrayBuffer.id() == bufferName) |
| { |
| mState.mElementArrayBuffer.set(context, nullptr); |
| } |
| } |
| |
| const VertexAttribute &VertexArray::getVertexAttribute(size_t attribIndex) const |
| { |
| ASSERT(attribIndex < getMaxAttribs()); |
| return mState.mVertexAttributes[attribIndex]; |
| } |
| |
| const VertexBinding &VertexArray::getVertexBinding(size_t bindingIndex) const |
| { |
| ASSERT(bindingIndex < getMaxBindings()); |
| return mState.mVertexBindings[bindingIndex]; |
| } |
| |
| size_t VertexArray::GetVertexIndexFromDirtyBit(size_t dirtyBit) |
| { |
| static_assert(gl::MAX_VERTEX_ATTRIBS == gl::MAX_VERTEX_ATTRIB_BINDINGS, |
| "The stride of vertex attributes should equal to that of vertex bindings."); |
| ASSERT(dirtyBit > DIRTY_BIT_ELEMENT_ARRAY_BUFFER); |
| return (dirtyBit - DIRTY_BIT_ATTRIB_0_ENABLED) % gl::MAX_VERTEX_ATTRIBS; |
| } |
| |
| void VertexArray::bindVertexBufferImpl(const Context *context, |
| size_t bindingIndex, |
| Buffer *boundBuffer, |
| GLintptr offset, |
| GLsizei stride) |
| { |
| ASSERT(bindingIndex < getMaxBindings()); |
| |
| VertexBinding *binding = &mState.mVertexBindings[bindingIndex]; |
| |
| binding->setBuffer(context, boundBuffer); |
| binding->setOffset(offset); |
| binding->setStride(stride); |
| } |
| |
| void VertexArray::bindVertexBuffer(const Context *context, |
| size_t bindingIndex, |
| Buffer *boundBuffer, |
| GLintptr offset, |
| GLsizei stride) |
| { |
| bindVertexBufferImpl(context, bindingIndex, boundBuffer, offset, stride); |
| |
| mDirtyBits.set(DIRTY_BIT_BINDING_0_BUFFER + bindingIndex); |
| } |
| |
| void VertexArray::setVertexAttribBinding(const Context *context, |
| size_t attribIndex, |
| GLuint bindingIndex) |
| { |
| ASSERT(attribIndex < getMaxAttribs() && bindingIndex < getMaxBindings()); |
| |
| if (mState.mVertexAttributes[attribIndex].bindingIndex != bindingIndex) |
| { |
| // In ES 3.0 contexts, the binding cannot change, hence the code below is unreachable. |
| ASSERT(context->getClientVersion() >= ES_3_1); |
| mState.mVertexAttributes[attribIndex].bindingIndex = bindingIndex; |
| |
| mDirtyBits.set(DIRTY_BIT_ATTRIB_0_BINDING + attribIndex); |
| } |
| } |
| |
| void VertexArray::setVertexBindingDivisor(size_t bindingIndex, GLuint divisor) |
| { |
| ASSERT(bindingIndex < getMaxBindings()); |
| |
| mState.mVertexBindings[bindingIndex].setDivisor(divisor); |
| |
| mDirtyBits.set(DIRTY_BIT_BINDING_0_DIVISOR + bindingIndex); |
| } |
| |
| void VertexArray::setVertexAttribFormatImpl(size_t attribIndex, |
| GLint size, |
| GLenum type, |
| bool normalized, |
| bool pureInteger, |
| GLuint relativeOffset) |
| { |
| ASSERT(attribIndex < getMaxAttribs()); |
| |
| VertexAttribute *attrib = &mState.mVertexAttributes[attribIndex]; |
| |
| attrib->size = size; |
| attrib->type = type; |
| attrib->normalized = normalized; |
| attrib->pureInteger = pureInteger; |
| attrib->relativeOffset = relativeOffset; |
| } |
| |
| void VertexArray::setVertexAttribFormat(size_t attribIndex, |
| GLint size, |
| GLenum type, |
| bool normalized, |
| bool pureInteger, |
| GLuint relativeOffset) |
| { |
| setVertexAttribFormatImpl(attribIndex, size, type, normalized, pureInteger, relativeOffset); |
| |
| mDirtyBits.set(DIRTY_BIT_ATTRIB_0_FORMAT + attribIndex); |
| } |
| |
| void VertexArray::setVertexAttribDivisor(const Context *context, size_t attribIndex, GLuint divisor) |
| { |
| ASSERT(attribIndex < getMaxAttribs()); |
| |
| setVertexAttribBinding(context, attribIndex, static_cast<GLuint>(attribIndex)); |
| setVertexBindingDivisor(attribIndex, divisor); |
| } |
| |
| void VertexArray::enableAttribute(size_t attribIndex, bool enabledState) |
| { |
| ASSERT(attribIndex < getMaxAttribs()); |
| |
| mState.mVertexAttributes[attribIndex].enabled = enabledState; |
| |
| mDirtyBits.set(DIRTY_BIT_ATTRIB_0_ENABLED + attribIndex); |
| |
| // Update state cache |
| if (enabledState) |
| { |
| mState.mMaxEnabledAttribute = std::max(attribIndex + 1, mState.mMaxEnabledAttribute); |
| } |
| else if (mState.mMaxEnabledAttribute == attribIndex + 1) |
| { |
| while (mState.mMaxEnabledAttribute > 0 && |
| !mState.mVertexAttributes[mState.mMaxEnabledAttribute - 1].enabled) |
| { |
| --mState.mMaxEnabledAttribute; |
| } |
| } |
| } |
| |
| void VertexArray::setVertexAttribPointer(const Context *context, |
| size_t attribIndex, |
| gl::Buffer *boundBuffer, |
| GLint size, |
| GLenum type, |
| bool normalized, |
| bool pureInteger, |
| GLsizei stride, |
| const void *pointer) |
| { |
| ASSERT(attribIndex < getMaxAttribs()); |
| |
| GLintptr offset = boundBuffer ? reinterpret_cast<GLintptr>(pointer) : 0; |
| |
| setVertexAttribFormatImpl(attribIndex, size, type, normalized, pureInteger, 0); |
| setVertexAttribBinding(context, attribIndex, static_cast<GLuint>(attribIndex)); |
| |
| VertexAttribute &attrib = mState.mVertexAttributes[attribIndex]; |
| |
| GLsizei effectiveStride = |
| stride != 0 ? stride : static_cast<GLsizei>(ComputeVertexAttributeTypeSize(attrib)); |
| attrib.pointer = pointer; |
| attrib.vertexAttribArrayStride = stride; |
| |
| bindVertexBufferImpl(context, attribIndex, boundBuffer, offset, effectiveStride); |
| |
| mDirtyBits.set(DIRTY_BIT_ATTRIB_0_POINTER + attribIndex); |
| } |
| |
| void VertexArray::setElementArrayBuffer(const Context *context, Buffer *buffer) |
| { |
| mState.mElementArrayBuffer.set(context, buffer); |
| mDirtyBits.set(DIRTY_BIT_ELEMENT_ARRAY_BUFFER); |
| } |
| |
| void VertexArray::syncState(const Context *context) |
| { |
| if (mDirtyBits.any()) |
| { |
| mVertexArray->syncState(context, mDirtyBits); |
| mDirtyBits.reset(); |
| } |
| } |
| |
| } // namespace gl |