// // Copyright 2014 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 classes for mananging GLES 3.1 Vertex Array Objects. // #include "libANGLE/VertexAttribute.h" namespace gl { // [OpenGL ES 3.1] (November 3, 2016) Section 20 Page 361 // Table 20.2: Vertex Array Object State VertexBinding::VertexBinding() : VertexBinding(0) {} VertexBinding::VertexBinding(GLuint boundAttribute) : mStride(16u), mDivisor(0), mOffset(0) { mBoundAttributesMask.set(boundAttribute); } VertexBinding::VertexBinding(VertexBinding &&binding) { *this = std::move(binding); } VertexBinding::~VertexBinding() {} VertexBinding &VertexBinding::operator=(VertexBinding &&binding) { if (this != &binding) { mStride = binding.mStride; mDivisor = binding.mDivisor; mOffset = binding.mOffset; mBoundAttributesMask = binding.mBoundAttributesMask; std::swap(binding.mBuffer, mBuffer); } return *this; } void VertexBinding::onContainerBindingChanged(const Context *context, int incr) const { if (mBuffer.get()) mBuffer->onNonTFBindingChanged(incr); } VertexAttribute::VertexAttribute(GLuint bindingIndex) : enabled(false), format(&angle::Format::Get(angle::FormatID::R32G32B32A32_FLOAT)), pointer(nullptr), relativeOffset(0), vertexAttribArrayStride(0), bindingIndex(bindingIndex), mCachedElementLimit(0) {} VertexAttribute::VertexAttribute(VertexAttribute &&attrib) : enabled(attrib.enabled), format(attrib.format), pointer(attrib.pointer), relativeOffset(attrib.relativeOffset), vertexAttribArrayStride(attrib.vertexAttribArrayStride), bindingIndex(attrib.bindingIndex), mCachedElementLimit(attrib.mCachedElementLimit) {} VertexAttribute &VertexAttribute::operator=(VertexAttribute &&attrib) { if (this != &attrib) { enabled = attrib.enabled; format = attrib.format; pointer = attrib.pointer; relativeOffset = attrib.relativeOffset; vertexAttribArrayStride = attrib.vertexAttribArrayStride; bindingIndex = attrib.bindingIndex; mCachedElementLimit = attrib.mCachedElementLimit; } return *this; } void VertexAttribute::updateCachedElementLimit(const VertexBinding &binding) { Buffer *buffer = binding.getBuffer().get(); if (!buffer) { mCachedElementLimit = 0; return; } angle::CheckedNumeric bufferSize(buffer->getSize()); angle::CheckedNumeric bufferOffset(binding.getOffset()); angle::CheckedNumeric attribOffset(relativeOffset); angle::CheckedNumeric attribSize(ComputeVertexAttributeTypeSize(*this)); // (buffer.size - buffer.offset - attrib.relativeOffset - attrib.size) / binding.stride angle::CheckedNumeric elementLimit = (bufferSize - bufferOffset - attribOffset - attribSize); // Use the special integer overflow value if there was a math error. if (!elementLimit.IsValid()) { static_assert(kIntegerOverflow < 0, "Unexpected value"); mCachedElementLimit = kIntegerOverflow; return; } mCachedElementLimit = elementLimit.ValueOrDie(); if (mCachedElementLimit < 0) { return; } if (binding.getStride() == 0) { // Special case for a zero stride. If we can fit one vertex we can fit infinite vertices. mCachedElementLimit = std::numeric_limits::max(); return; } angle::CheckedNumeric bindingStride(binding.getStride()); elementLimit /= bindingStride; if (binding.getDivisor() > 0) { // For instanced draws, the element count is floor(instanceCount - 1) / binding.divisor. angle::CheckedNumeric bindingDivisor(binding.getDivisor()); elementLimit *= bindingDivisor; // We account for the floor() part rounding by adding a rounding constant. elementLimit += bindingDivisor - 1; } mCachedElementLimit = elementLimit.ValueOrDefault(kIntegerOverflow); } size_t ComputeVertexAttributeStride(const VertexAttribute &attrib, const VertexBinding &binding) { // In ES 3.1, VertexAttribPointer will store the type size in the binding stride. // Hence, rendering always uses the binding's stride. return attrib.enabled ? binding.getStride() : 16u; } // Warning: you should ensure binding really matches attrib.bindingIndex before using this function. GLintptr ComputeVertexAttributeOffset(const VertexAttribute &attrib, const VertexBinding &binding) { return attrib.relativeOffset + binding.getOffset(); } size_t ComputeVertexBindingElementCount(GLuint divisor, size_t drawCount, size_t instanceCount) { // For instanced rendering, we draw "instanceDrawCount" sets of "vertexDrawCount" vertices. // // A vertex attribute with a positive divisor loads one instanced vertex for every set of // non-instanced vertices, and the instanced vertex index advances once every "mDivisor" // instances. if (instanceCount > 0 && divisor > 0) { // When instanceDrawCount is not a multiple attrib.divisor, the division must round up. // For instance, with 5 non-instanced vertices and a divisor equal to 3, we need 2 instanced // vertices. return (instanceCount + divisor - 1u) / divisor; } return drawCount; } } // namespace gl