Commit 63805b47 by Jamie Madill

Make the Program's semantic index D3D-only.

This concept isn't strictly necessary for GL-side validation. Instead we can use a bitset to track active attribs, and determine is a particular location is active. BUG=angleproject:1123 Change-Id: If7a920a3071672116bafffb3368671f721723b65 Reviewed-on: https://chromium-review.googlesource.com/294570Reviewed-by: 's avatarGeoff Lang <geofflang@chromium.org> Tested-by: 's avatarJamie Madill <jmadill@chromium.org>
parent a53000b0
......@@ -355,6 +355,7 @@ void Program::unlink(bool destroy)
}
mData.mAttributes.clear();
mData.mActiveAttribLocationsMask.reset();
mData.mTransformFeedbackVaryingVars.clear();
mProgram->reset();
......@@ -403,11 +404,9 @@ Error Program::loadBinary(GLenum binaryFormat, const void *binary, GLsizei lengt
return Error(GL_NO_ERROR);
}
// TODO(jmadill): replace MAX_VERTEX_ATTRIBS
for (int i = 0; i < MAX_VERTEX_ATTRIBS; ++i)
{
stream.readInt(&mProgram->getSemanticIndexes()[i]);
}
static_assert(MAX_VERTEX_ATTRIBS <= sizeof(unsigned long) * 8,
"Too many vertex attribs for mask");
mData.mActiveAttribLocationsMask = stream.readInt<unsigned long>();
unsigned int attribCount = stream.readInt<unsigned int>();
ASSERT(mData.mAttributes.empty());
......@@ -450,11 +449,7 @@ Error Program::saveBinary(GLenum *binaryFormat, void *binary, GLsizei bufSize, G
stream.writeInt(ANGLE_MINOR_VERSION);
stream.writeBytes(reinterpret_cast<const unsigned char*>(ANGLE_COMMIT_HASH), ANGLE_COMMIT_HASH_SIZE);
// TODO(jmadill): replace MAX_VERTEX_ATTRIBS
for (unsigned int i = 0; i < MAX_VERTEX_ATTRIBS; ++i)
{
stream.writeInt(mProgram->getSemanticIndexes()[i]);
}
stream.writeInt(mData.mActiveAttribLocationsMask.to_ulong());
stream.writeInt(mData.mAttributes.size());
for (const sh::Attribute &attrib : mData.mAttributes)
......@@ -594,16 +589,11 @@ GLuint Program::getAttributeLocation(const std::string &name)
return static_cast<GLuint>(-1);
}
const int *Program::getSemanticIndexes() const
bool Program::isAttribLocationActive(size_t attribLocation) const
{
return mProgram->getSemanticIndexes();
}
int Program::getSemanticIndex(int attributeIndex) const
{
ASSERT(attributeIndex >= 0 && attributeIndex < MAX_VERTEX_ATTRIBS);
return mProgram->getSemanticIndexes()[attributeIndex];
ASSERT(attribLocation >= 0 &&
static_cast<size_t>(attribLocation) < mData.mActiveAttribLocationsMask.size());
return mData.mActiveAttribLocationsMask[attribLocation];
}
void Program::getActiveAttribute(GLuint index, GLsizei bufsize, GLsizei *length, GLint *size, GLenum *type, GLchar *name)
......@@ -1313,9 +1303,9 @@ bool Program::linkAttributes(const gl::Data &data,
if (attribute.location != -1)
{
// Location is set by glBindAttribLocation or by location layout qualifier
const int rows = VariableRegisterCount(attribute.type);
const int regs = VariableRegisterCount(attribute.type);
if (static_cast<GLuint>(rows + attribute.location) > maxAttribs)
if (static_cast<GLuint>(regs + attribute.location) > maxAttribs)
{
infoLog << "Active attribute (" << attribute.name << ") at location "
<< attribute.location << " is too big to fit";
......@@ -1323,10 +1313,10 @@ bool Program::linkAttributes(const gl::Data &data,
return false;
}
for (int row = 0; row < rows; row++)
for (int reg = 0; reg < regs; reg++)
{
const int rowLocation = attribute.location + row;
sh::ShaderVariable *linkedAttribute = usedAttribMap[rowLocation];
const int regLocation = attribute.location + reg;
sh::ShaderVariable *linkedAttribute = usedAttribMap[regLocation];
// In GLSL 3.00, attribute aliasing produces a link error
// In GLSL 1.00, attribute aliasing is allowed, but ANGLE currently has a bug
......@@ -1336,16 +1326,16 @@ bool Program::linkAttributes(const gl::Data &data,
// if (mProgram->getShaderVersion() >= 300)
{
infoLog << "Attribute '" << attribute.name << "' aliases attribute '"
<< linkedAttribute->name << "' at location " << rowLocation;
<< linkedAttribute->name << "' at location " << regLocation;
return false;
}
}
else
{
usedAttribMap[rowLocation] = &attribute;
usedAttribMap[regLocation] = &attribute;
}
usedLocations |= 1 << rowLocation;
usedLocations |= 1 << regLocation;
}
}
}
......@@ -1358,10 +1348,10 @@ bool Program::linkAttributes(const gl::Data &data,
// Not set by glBindAttribLocation or by location layout qualifier
if (attribute.location == -1)
{
int rows = VariableRegisterCount(attribute.type);
int availableIndex = AllocateFirstFreeBits(&usedLocations, rows, maxAttribs);
int regs = VariableRegisterCount(attribute.type);
int availableIndex = AllocateFirstFreeBits(&usedLocations, regs, maxAttribs);
if (availableIndex == -1 || static_cast<GLuint>(availableIndex + rows) > maxAttribs)
if (availableIndex == -1 || static_cast<GLuint>(availableIndex + regs) > maxAttribs)
{
infoLog << "Too many active attributes (" << attribute.name << ")";
return false;
......@@ -1371,18 +1361,15 @@ bool Program::linkAttributes(const gl::Data &data,
}
}
// TODO(jmadill): make semantic index D3D-only
for (const sh::Attribute &attribute : mData.mAttributes)
{
ASSERT(attribute.staticUse);
ASSERT(attribute.location != -1);
int regs = VariableRegisterCount(attribute.type);
unsigned int attributeIndex = attribute.location;
int index = vertexShader->getSemanticIndex(attribute.name);
int rows = VariableRegisterCount(attribute.type);
for (int r = 0; r < rows; r++)
for (int r = 0; r < regs; r++)
{
mProgram->getSemanticIndexes()[attributeIndex++] = index++;
mData.mActiveAttribLocationsMask.set(attribute.location + r);
}
}
......
......@@ -20,10 +20,10 @@
#include <GLES2/gl2.h>
#include <GLSLANG/ShaderLang.h>
#include <vector>
#include <set>
#include <sstream>
#include <string>
#include <set>
#include <vector>
namespace rx
{
......@@ -181,6 +181,10 @@ class Program : angle::NonCopyable
return mUniformBlockBindings[uniformBlockIndex];
}
const std::vector<sh::Attribute> &getAttributes() const { return mAttributes; }
const AttributesMask &getActiveAttribLocationsMask() const
{
return mActiveAttribLocationsMask;
}
private:
friend class Program;
......@@ -195,6 +199,7 @@ class Program : angle::NonCopyable
GLuint mUniformBlockBindings[IMPLEMENTATION_MAX_COMBINED_SHADER_UNIFORM_BUFFERS];
std::vector<sh::Attribute> mAttributes;
std::bitset<MAX_VERTEX_ATTRIBS> mActiveAttribLocationsMask;
// TODO(jmadill): move more state into Data.
};
......@@ -225,8 +230,7 @@ class Program : angle::NonCopyable
void getAttachedShaders(GLsizei maxCount, GLsizei *count, GLuint *shaders);
GLuint getAttributeLocation(const std::string &name);
int getSemanticIndex(int attributeIndex) const;
const int *getSemanticIndexes() const;
bool isAttribLocationActive(size_t attribLocation) const;
void getActiveAttribute(GLuint index, GLsizei bufsize, GLsizei *length, GLint *size, GLenum *type, GLchar *name);
GLint getActiveAttributeCount();
......@@ -302,6 +306,11 @@ class Program : angle::NonCopyable
bool validateSamplers(InfoLog *infoLog, const Caps &caps);
bool isValidated() const;
const AttributesMask &getActiveAttribLocationsMask() const
{
return mData.mActiveAttribLocationsMask;
}
private:
void unlink(bool destroy = false);
void resetUniformBlockBindings();
......
......@@ -30,9 +30,6 @@ namespace gl
{
class Buffer;
// Used in other places.
typedef std::bitset<MAX_VERTEX_ATTRIBS> AttributesMask;
class VertexArray
{
public:
......
......@@ -14,6 +14,8 @@
#include <stdint.h>
#include <bitset>
namespace gl
{
class Buffer;
......@@ -247,6 +249,8 @@ struct PixelPackState
{}
};
// Used in Program and VertexArray.
typedef std::bitset<MAX_VERTEX_ATTRIBS> AttributesMask;
}
namespace rx
......
......@@ -129,7 +129,6 @@ GLuint ProgramImpl::getUniformBlockIndex(const std::string &name) const
void ProgramImpl::reset()
{
std::fill(mSemanticIndex, mSemanticIndex + ArraySize(mSemanticIndex), -1);
SafeDeleteContainer(mUniforms);
mUniformIndex.clear();
SafeDeleteContainer(mUniformBlocks);
......
......@@ -31,8 +31,6 @@ struct LinkResult
class ProgramImpl : angle::NonCopyable
{
public:
typedef int SemanticIndexArray[gl::MAX_VERTEX_ATTRIBS];
ProgramImpl(const gl::Program::Data &data);
virtual ~ProgramImpl();
......@@ -83,12 +81,10 @@ class ProgramImpl : angle::NonCopyable
const std::vector<gl::LinkedUniform*> &getUniforms() const { return mUniforms; }
const std::map<GLuint, gl::VariableLocation> &getUniformIndices() const { return mUniformIndex; }
const std::vector<gl::UniformBlock*> &getUniformBlocks() const { return mUniformBlocks; }
const SemanticIndexArray &getSemanticIndexes() const { return mSemanticIndex; }
std::vector<gl::LinkedUniform*> &getUniforms() { return mUniforms; }
std::map<GLuint, gl::VariableLocation> &getUniformIndices() { return mUniformIndex; }
std::vector<gl::UniformBlock*> &getUniformBlocks() { return mUniformBlocks; }
SemanticIndexArray &getSemanticIndexes() { return mSemanticIndex; }
gl::LinkedUniform *getUniformByLocation(GLint location) const;
gl::LinkedUniform *getUniformByName(const std::string &name) const;
......@@ -109,8 +105,6 @@ class ProgramImpl : angle::NonCopyable
std::map<GLuint, gl::VariableLocation> mUniformIndex;
std::vector<gl::UniformBlock*> mUniformBlocks;
SemanticIndexArray mSemanticIndex;
};
}
......
......@@ -109,7 +109,7 @@ bool IsRowMajorLayout(const sh::ShaderVariable &var)
struct AttributeSorter
{
AttributeSorter(const ProgramImpl::SemanticIndexArray &semanticIndices)
AttributeSorter(const ProgramD3D::SemanticIndexArray &semanticIndices)
: originalIndices(&semanticIndices)
{
}
......@@ -124,7 +124,7 @@ struct AttributeSorter
return (indexA < indexB);
}
const ProgramImpl::SemanticIndexArray *originalIndices;
const ProgramD3D::SemanticIndexArray *originalIndices;
};
bool LinkVaryingRegisters(gl::InfoLog &infoLog,
......@@ -513,6 +513,12 @@ LinkResult ProgramD3D::load(gl::InfoLog &infoLog, gl::BinaryInputStream *stream)
stream->readInt(&mShaderVersion);
// TODO(jmadill): replace MAX_VERTEX_ATTRIBS
for (int i = 0; i < gl::MAX_VERTEX_ATTRIBS; ++i)
{
stream->readInt(&mSemanticIndexes[i]);
}
const unsigned int psSamplerCount = stream->readInt<unsigned int>();
for (unsigned int i = 0; i < psSamplerCount; ++i)
{
......@@ -763,6 +769,12 @@ gl::Error ProgramD3D::save(gl::BinaryOutputStream *stream)
stream->writeInt(mShaderVersion);
// TODO(jmadill): replace MAX_VERTEX_ATTRIBS
for (unsigned int i = 0; i < gl::MAX_VERTEX_ATTRIBS; ++i)
{
stream->writeInt(mSemanticIndexes[i]);
}
stream->writeInt(mSamplersPS.size());
for (unsigned int i = 0; i < mSamplersPS.size(); ++i)
{
......@@ -1148,7 +1160,7 @@ LinkResult ProgramD3D::link(const gl::Data &data,
mUsesPointSize = vertexShaderD3D->usesPointSize();
initAttributesByLayout();
initSemanticIndex();
if (!defineUniforms(infoLog, *data.caps))
{
......@@ -2065,6 +2077,7 @@ void ProgramD3D::reset()
mUsedPixelSamplerRange = 0;
mDirtySamplerMapping = true;
std::fill(mSemanticIndexes, mSemanticIndexes + ArraySize(mSemanticIndexes), -1);
std::fill(mAttributesByLayout, mAttributesByLayout + ArraySize(mAttributesByLayout), -1);
mTransformFeedbackLinkedVaryings.clear();
......@@ -2080,6 +2093,27 @@ unsigned int ProgramD3D::issueSerial()
return mCurrentSerial++;
}
void ProgramD3D::initSemanticIndex()
{
const gl::Shader *vertexShader = mData.getAttachedVertexShader();
ASSERT(vertexShader != nullptr);
// Init semantic index
for (const sh::Attribute &attribute : mData.getAttributes())
{
int attributeIndex = attribute.location;
int index = vertexShader->getSemanticIndex(attribute.name);
int regs = gl::VariableRegisterCount(attribute.type);
for (int reg = 0; reg < regs; ++reg)
{
mSemanticIndexes[attributeIndex + reg] = index + reg;
}
}
initAttributesByLayout();
}
void ProgramD3D::initAttributesByLayout()
{
for (int i = 0; i < gl::MAX_VERTEX_ATTRIBS; i++)
......@@ -2087,7 +2121,8 @@ void ProgramD3D::initAttributesByLayout()
mAttributesByLayout[i] = i;
}
std::sort(&mAttributesByLayout[0], &mAttributesByLayout[gl::MAX_VERTEX_ATTRIBS], AttributeSorter(mSemanticIndex));
std::sort(&mAttributesByLayout[0], &mAttributesByLayout[gl::MAX_VERTEX_ATTRIBS],
AttributeSorter(mSemanticIndexes));
}
void ProgramD3D::sortAttributesByLayout(const std::vector<TranslatedAttribute> &unsortedAttributes,
......@@ -2097,21 +2132,19 @@ void ProgramD3D::sortAttributesByLayout(const std::vector<TranslatedAttribute> &
for (size_t attribIndex = 0; attribIndex < unsortedAttributes.size(); ++attribIndex)
{
int oldIndex = mAttributesByLayout[attribIndex];
sortedSemanticIndicesOut[attribIndex] = mSemanticIndex[oldIndex];
sortedSemanticIndicesOut[attribIndex] = mSemanticIndexes[oldIndex];
sortedAttributesOut[attribIndex] = &unsortedAttributes[oldIndex];
}
}
void ProgramD3D::updateCachedInputLayout(const gl::Program *program, const gl::State &state)
void ProgramD3D::updateCachedInputLayout(const gl::State &state)
{
mCachedInputLayout.clear();
const int *semanticIndexes = program->getSemanticIndexes();
const auto &vertexAttributes = state.getVertexArray()->getVertexAttributes();
for (unsigned int attributeIndex = 0; attributeIndex < vertexAttributes.size(); attributeIndex++)
{
int semanticIndex = semanticIndexes[attributeIndex];
int semanticIndex = mSemanticIndexes[attributeIndex];
if (semanticIndex != -1)
{
......
......@@ -42,6 +42,8 @@ class ShaderExecutableD3D;
class ProgramD3D : public ProgramImpl
{
public:
typedef int SemanticIndexArray[gl::MAX_VERTEX_ATTRIBS];
ProgramD3D(const gl::Program::Data &data, RendererD3D *renderer);
virtual ~ProgramD3D();
......@@ -118,13 +120,13 @@ class ProgramD3D : public ProgramImpl
unsigned int getSerial() const;
void initAttributesByLayout();
void sortAttributesByLayout(const std::vector<TranslatedAttribute> &unsortedAttributes,
int sortedSemanticIndicesOut[gl::MAX_VERTEX_ATTRIBS],
const rx::TranslatedAttribute *sortedAttributesOut[gl::MAX_VERTEX_ATTRIBS]) const;
const SemanticIndexArray &getSemanticIndexes() const { return mSemanticIndexes; }
const SemanticIndexArray &getAttributesByLayout() const { return mAttributesByLayout; }
void updateCachedInputLayout(const gl::Program *program, const gl::State &state);
void updateCachedInputLayout(const gl::State &state);
const gl::InputLayout &getCachedInputLayout() const { return mCachedInputLayout; }
private:
......@@ -210,6 +212,9 @@ class ProgramD3D : public ProgramImpl
void gatherTransformFeedbackVaryings(const std::vector<gl::LinkedVarying> &varyings);
void initSemanticIndex();
void initAttributesByLayout();
RendererD3D *mRenderer;
DynamicHLSL *mDynamicHLSL;
......@@ -244,6 +249,7 @@ class ProgramD3D : public ProgramImpl
int mShaderVersion;
SemanticIndexArray mSemanticIndexes;
SemanticIndexArray mAttributesByLayout;
unsigned int mSerial;
......
......@@ -375,7 +375,7 @@ gl::Error RendererD3D::applyShaders(const gl::Data &data)
{
gl::Program *program = data.state->getProgram();
ProgramD3D *programD3D = GetImplAs<ProgramD3D>(program);
programD3D->updateCachedInputLayout(program, *data.state);
programD3D->updateCachedInputLayout(*data.state);
const gl::Framebuffer *fbo = data.state->getDrawFramebuffer();
......
......@@ -135,8 +135,8 @@ gl::Error VertexDataManager::prepareVertexData(const gl::State &state,
// Compute active enabled and active disable attributes, for speed.
// TODO(jmadill): don't recompute if there was no state change
const gl::VertexArray *vertexArray = state.getVertexArray();
const int *semanticIndexes = state.getProgram()->getSemanticIndexes();
const std::vector<gl::VertexAttribute> &vertexAttributes = vertexArray->getVertexAttributes();
const gl::Program *program = state.getProgram();
const auto &vertexAttributes = vertexArray->getVertexAttributes();
mActiveEnabledAttributes.clear();
mActiveDisabledAttributes.clear();
......@@ -144,7 +144,7 @@ gl::Error VertexDataManager::prepareVertexData(const gl::State &state,
for (size_t attribIndex = 0; attribIndex < vertexAttributes.size(); ++attribIndex)
{
if (semanticIndexes[attribIndex] != -1)
if (program->isAttribLocationActive(attribIndex))
{
// Resize automatically puts in empty attribs
translatedAttribs->resize(attribIndex + 1);
......
......@@ -8,9 +8,9 @@
#include "libANGLE/renderer/d3d/d3d9/VertexDeclarationCache.h"
#include "libANGLE/Program.h"
#include "libANGLE/VertexAttribute.h"
#include "libANGLE/formatutils.h"
#include "libANGLE/renderer/d3d/ProgramD3D.h"
#include "libANGLE/renderer/d3d/d3d9/VertexBuffer9.h"
#include "libANGLE/renderer/d3d/d3d9/formatutils9.h"
......@@ -101,6 +101,9 @@ gl::Error VertexDeclarationCache::applyDeclaration(IDirect3DDevice9 *device,
D3DVERTEXELEMENT9 elements[gl::MAX_VERTEX_ATTRIBS + 1];
D3DVERTEXELEMENT9 *element = &elements[0];
ProgramD3D *programD3D = GetImplAs<ProgramD3D>(program);
const auto &semanticIndexes = programD3D->getSemanticIndexes();
for (size_t i = 0; i < attributes.size(); i++)
{
if (attributes[i].active)
......@@ -164,7 +167,7 @@ gl::Error VertexDeclarationCache::applyDeclaration(IDirect3DDevice9 *device,
element->Type = static_cast<BYTE>(d3d9VertexInfo.nativeFormat);
element->Method = D3DDECLMETHOD_DEFAULT;
element->Usage = D3DDECLUSAGE_TEXCOORD;
element->UsageIndex = static_cast<BYTE>(program->getSemanticIndex(static_cast<int>(i)));
element->UsageIndex = static_cast<BYTE>(semanticIndexes[i]);
element++;
}
}
......
......@@ -83,12 +83,6 @@ LinkResult ProgramGL::link(const gl::Data &data,
}
mFunctions->bindAttribLocation(mProgramID, attribute.location, attribute.name.c_str());
int registerCount = gl::VariableRegisterCount(attribute.type);
for (int offset = 0; offset < registerCount; ++offset)
{
mActiveAttributesMask.set(attribute.location + offset);
}
}
// Link and verify
......@@ -358,7 +352,6 @@ void ProgramGL::reset()
mSamplerUniformMap.clear();
mSamplerBindings.clear();
mActiveAttributesMask.reset();
}
GLuint ProgramGL::getProgramID() const
......@@ -371,9 +364,4 @@ const std::vector<SamplerBindingGL> &ProgramGL::getAppliedSamplerUniforms() cons
return mSamplerBindings;
}
const gl::AttributesMask &ProgramGL::getActiveAttributesMask() const
{
return mActiveAttributesMask;
}
}
......@@ -73,16 +73,10 @@ class ProgramGL : public ProgramImpl
bool validateSamplers(gl::InfoLog *infoLog, const gl::Caps &caps) override;
void defineUniformBlock(gl::InfoLog &infoLog,
const gl::Shader &shader,
const sh::InterfaceBlock &interfaceBlock,
const gl::Caps &caps);
void reset() override;
GLuint getProgramID() const;
const std::vector<SamplerBindingGL> &getAppliedSamplerUniforms() const;
const gl::AttributesMask &getActiveAttributesMask() const;
private:
const FunctionsGL *mFunctions;
......@@ -99,9 +93,6 @@ class ProgramGL : public ProgramImpl
// An array of the samplers that are used by the program
std::vector<SamplerBindingGL> mSamplerBindings;
// Array of attribute locations used by this program
gl::AttributesMask mActiveAttributesMask;
GLuint mProgramID;
};
......
......@@ -406,13 +406,12 @@ gl::Error StateManagerGL::setDrawArraysState(const gl::Data &data, GLint first,
const gl::State &state = *data.state;
const gl::Program *program = state.getProgram();
const ProgramGL *programGL = GetImplAs<ProgramGL>(program);
const gl::VertexArray *vao = state.getVertexArray();
const VertexArrayGL *vaoGL = GetImplAs<VertexArrayGL>(vao);
gl::Error error =
vaoGL->syncDrawArraysState(programGL->getActiveAttributesMask(), first, count);
vaoGL->syncDrawArraysState(program->getActiveAttribLocationsMask(), first, count);
if (error.isError())
{
return error;
......@@ -429,12 +428,11 @@ gl::Error StateManagerGL::setDrawElementsState(const gl::Data &data, GLsizei cou
const gl::State &state = *data.state;
const gl::Program *program = state.getProgram();
const ProgramGL *programGL = GetImplAs<ProgramGL>(program);
const gl::VertexArray *vao = state.getVertexArray();
const VertexArrayGL *vaoGL = GetImplAs<VertexArrayGL>(vao);
gl::Error error = vaoGL->syncDrawElementsState(programGL->getActiveAttributesMask(), count,
gl::Error error = vaoGL->syncDrawElementsState(program->getActiveAttribLocationsMask(), count,
type, indices, outIndices);
if (error.isError())
{
......
......@@ -36,13 +36,11 @@ bool ValidateDrawAttribs(gl::Context *context, GLint primcount, GLint maxVertex)
const VertexArray *vao = state.getVertexArray();
const auto &vertexAttribs = vao->getVertexAttributes();
const int *semanticIndexes = program->getSemanticIndexes();
size_t maxEnabledAttrib = vao->getMaxEnabledAttribute();
for (size_t attributeIndex = 0; attributeIndex < maxEnabledAttrib; ++attributeIndex)
{
const VertexAttribute &attrib = vertexAttribs[attributeIndex];
bool attribActive = (semanticIndexes[attributeIndex] != -1);
if (attribActive && attrib.enabled)
if (program->isAttribLocationActive(attributeIndex) && attrib.enabled)
{
gl::Buffer *buffer = attrib.buffer.get();
......@@ -1564,11 +1562,10 @@ static bool ValidateDrawInstancedANGLE(Context *context)
gl::Program *program = state.getProgram();
const VertexArray *vao = state.getVertexArray();
for (int attributeIndex = 0; attributeIndex < MAX_VERTEX_ATTRIBS; attributeIndex++)
for (size_t attributeIndex = 0; attributeIndex < MAX_VERTEX_ATTRIBS; attributeIndex++)
{
const VertexAttribute &attrib = vao->getVertexAttribute(attributeIndex);
bool active = (program->getSemanticIndex(attributeIndex) != -1);
if (active && attrib.divisor == 0)
if (program->isAttribLocationActive(attributeIndex) && attrib.divisor == 0)
{
return true;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment