blob: 0f4345e3bdae5f5339da93b7ae0d480cb49d97f1 [file] [log] [blame]
//
// 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<GLuint>(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<GLuint>(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<GLuint>(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<BufferGL>(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<ptrdiff_t>(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<const uint8_t *>(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<GLuint>(idx), attrib,
static_cast<GLsizei>(destStride),
static_cast<GLintptr>(vertexStartOffset));
// Update the state to track the streamed attribute
mAppliedAttributes[idx].format = attrib.format;
mAppliedAttributes[idx].relativeOffset = 0;
mAppliedAttributes[idx].bindingIndex = static_cast<GLuint>(idx);
mAppliedBindings[idx].setStride(static_cast<GLsizei>(destStride));
mAppliedBindings[idx].setOffset(static_cast<GLintptr>(vertexStartOffset));
mAppliedBindings[idx].setBuffer(context, nullptr);
curBufferOffset += destStride * streamedVertexCount;
}
unmapResult = mFunctions->unmapBuffer(GL_ARRAY_BUFFER);
}
ANGLE_CHECK(GetImplAs<ContextGL>(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<BufferGL>(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<GLuint>(attribIndex));
}
else
{
mFunctions->disableVertexAttribArray(static_cast<GLuint>(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<BufferGL>(arrayBuffer);
mStateManager->bindBuffer(gl::BufferBinding::Array, arrayBufferGL->getBufferID());
callVertexAttribPointer(static_cast<GLuint>(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<GLuint>(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<const GLvoid *>(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<GLuint>(attribIndex), format.channelCount,
gl::ToGLenum(format.vertexAttribType),
attrib.relativeOffset);
}
else
{
mFunctions->vertexAttribFormat(static_cast<GLuint>(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<GLuint>(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<BufferGL>(arrayBuffer)->getBufferID();
}
mFunctions->bindVertexBuffer(static_cast<GLuint>(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<GLuint>(bindingIndex), adjustedDivisor);
}
else
{
// We can only use VertexAttribDivisor on platforms that don't support Vertex Attrib
// Binding.
mFunctions->vertexAttribDivisor(static_cast<GLuint>(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<BufferGL>(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<GLuint>(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<BufferGL>(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