Commit d860552f by Geoff Lang Committed by Commit Bot

Implement support for CHROMIUM_bind_uniform_location.

BUG=angleproject:1353 Change-Id: Ia219ff973de0de2f8e112c276b3ab6319f7d3884 Reviewed-on: https://chromium-review.googlesource.com/334252Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Commit-Queue: Geoff Lang <geofflang@chromium.org>
parent 5ed23982
Name
CHROMIUM_bind_uniform_location
Name Strings
GL_CHROMIUM_bind_uniform_location
Version
Last Modifed Date: September 8, 2015
Dependencies
OpenGL ES 2.0 is required.
Overview
This extension is simlar to glBindAttribLocation but instead
lets you choose a location for a uniform. This allows you
to not have to query the locations of uniforms.
This allows the client program to know the locations of uniforms
without having to wait for shaders to compile or GLSL programs to
link to query the locations and therefore have no blocking calls
when initializing programs.
Issues
If a uniform is an array you can only call glBindUniformLocation
for the location of the first element. Other elements' locations
must be queried if you need them. Often this is not an issue
because you can set all the elements with a single gl call from
the first location.
Good Example:
--shader--
uniform float u_someArray[4];
--C--
GLint location = 123;
glBindUniformLocation(program, location, "u_someArray");
glLinkProgram(program);
glUseProgram(program);
// set all 4 floats in u_someArray
float values[] = { 0.1f, 0.2f, 0.3f, 0.4f, };
glUniform1fv(location, 4, values);
Bad example 1:
GLint location = 123;
glBindUniformLocation(program, location, "u_someArray");
glLinkProgram(program);
glUseProgram(program);
// set floats in u_someArray one at a time
glUniform1f(location, 0.1f);
glUniform1f(location + 1, 0.2f); // ERROR! math not allowed on locations
Bad example 2:
GLint location0 = 123;
GLint location1 = 124;
glBindUniformLocation(program, location0, "u_someArray[0]");
glBindUniformLocation(program, location1, "u_someArray[1]"); // ERROR!
// not allowed to assign locations to array elements
If you need to set individual elements of a uniform array you must query the
location of the each element you wish to set.
New Tokens
None
New Procedures and Functions
void BindUniformLocationCHROMIUM (GLuint program, GLint location,
const GLhchar* name);
specifes that the uniform variable named <name> in program <program>
should be bound to uniform location <location> when the program is next
linked. If <name> was bound previously, its assigned binding is replaced
with <location>. <name> must be a null terminated string. The error
INVALID_VALUE is generated if <location> is equal or greater than
(MAX_VERTEX_UNIFORM_VECTORS + MAX_FRAGMENT_UNIFORM_VECTORS) * 4
or less than 0. BindUniformLocation has no effect until the program is
linked. In particular, it doesn't modify the bindings of active uniforms
variables in a program that has already been linked.
The error INVALID_OPERATION is generated if name starts with the reserved
"gl_" prefix. The error INVALID_VALUE is generated if name ends with
an array element expression other than "[0]".
When a program is linked, any active uniforms without a binding specified
through BindUniformLocation will be automatically be bound to locations by
the GL. Such bindings can be queried using the command
GetUniformLocation.
BindUniformLocation may be issued before any shader objects are attached
to a program object. Hence it is allowed to bind any name (except a name
starting with "gl_") to an index, including a name that is never used as a
uniform in any shader object. Assigned bindings for uniform variables
that do not exist or are not active are ignored. Using such bindings
behaves as if passed location was -1.
It is possible for an application to bind more than one uniform name to
the same location. This is referred to as aliasing. This will only work
if only one of the aliased uniforms is active in the executable program,
or if no path through the shader consumes more than one uniform of a set
of uniforms aliased to the same location. If two statically used uniforms
in a program are bound to the name location, link must fail.
Errors
None.
New State
None.
Revision History
7/20/2012 Documented the extension
9/8/2015 Require program link to fail if two statically used uniforms
are bound to the same location.
11/6/2015 Require inactive and non-existing, bound uniform locations
to behave like location -1.
......@@ -158,6 +158,7 @@ Extensions::Extensions()
maxLabelLength(0),
noError(false),
lossyETCDecode(false),
bindUniformLocation(false),
colorBufferFloat(false)
{
}
......@@ -230,6 +231,7 @@ std::vector<std::string> Extensions::getStrings() const
//InsertExtensionString("GL_KHR_no_error", noError, &extensionStrings);
InsertExtensionString("GL_ANGLE_lossy_etc_decode", lossyETCDecode, &extensionStrings);
InsertExtensionString("GL_CHROMIUM_bind_uniform_location", bindUniformLocation, &extensionStrings);
// clang-format on
return extensionStrings;
......
......@@ -281,6 +281,9 @@ struct Extensions
// GL_ANGLE_lossy_etc_decode
bool lossyETCDecode;
// GL_CHROMIUM_bind_uniform_location
bool bindUniformLocation;
// ES3 Extension support
// GL_EXT_color_buffer_float
......
......@@ -1634,6 +1634,14 @@ void Context::popGroupMarker()
mRenderer->popGroupMarker();
}
void Context::bindUniformLocation(GLuint program, GLint location, const GLchar *name)
{
Program *programObject = getProgram(program);
ASSERT(programObject);
programObject->bindUniformLocation(location, name);
}
void Context::recordError(const Error &error)
{
if (error.isError())
......
......@@ -386,6 +386,8 @@ class Context final : public ValidationContext
void pushGroupMarker(GLsizei length, const char *marker);
void popGroupMarker();
void bindUniformLocation(GLuint program, GLint location, const GLchar *name);
void recordError(const Error &error) override;
GLenum getError();
......
......@@ -138,14 +138,6 @@ bool UniformInList(const std::vector<LinkedUniform> &list, const std::string &na
const char *const g_fakepath = "C:\\fakepath";
AttributeBindings::AttributeBindings()
{
}
AttributeBindings::~AttributeBindings()
{
}
InfoLog::InfoLog()
{
}
......@@ -208,18 +200,36 @@ void InfoLog::reset()
{
}
VariableLocation::VariableLocation()
: name(),
element(0),
index(0)
VariableLocation::VariableLocation() : name(), element(0), index(0), used(false), ignored(false)
{
}
VariableLocation::VariableLocation(const std::string &name,
unsigned int element,
unsigned int index)
: name(name), element(element), index(index), used(true), ignored(false)
{
}
void Program::Bindings::bindLocation(GLuint index, const std::string &name)
{
mBindings[name] = index;
}
int Program::Bindings::getBinding(const std::string &name) const
{
auto iter = mBindings.find(name);
return (iter != mBindings.end()) ? iter->second : -1;
}
VariableLocation::VariableLocation(const std::string &name, unsigned int element, unsigned int index)
: name(name),
element(element),
index(index)
Program::Bindings::const_iterator Program::Bindings::begin() const
{
return mBindings.begin();
}
Program::Bindings::const_iterator Program::Bindings::end() const
{
return mBindings.end();
}
Program::Data::Data()
......@@ -270,14 +280,29 @@ GLint Program::Data::getUniformLocation(const std::string &name) const
for (size_t location = 0; location < mUniformLocations.size(); ++location)
{
const VariableLocation &uniformLocation = mUniformLocations[location];
const LinkedUniform &uniform = mUniforms[uniformLocation.index];
if (!uniformLocation.used)
{
continue;
}
const LinkedUniform &uniform = mUniforms[uniformLocation.index];
if (uniform.name == baseName)
{
if ((uniform.isArray() && uniformLocation.element == subscript) ||
(subscript == GL_INVALID_INDEX))
if (uniform.isArray())
{
return static_cast<GLint>(location);
if (uniformLocation.element == subscript ||
(uniformLocation.element == 0 && subscript == GL_INVALID_INDEX))
{
return static_cast<GLint>(location);
}
}
else
{
if (subscript == GL_INVALID_INDEX)
{
return static_cast<GLint>(location);
}
}
}
}
......@@ -403,22 +428,15 @@ int Program::getAttachedShadersCount() const
return (mData.mAttachedVertexShader ? 1 : 0) + (mData.mAttachedFragmentShader ? 1 : 0);
}
void AttributeBindings::bindAttributeLocation(GLuint index, const char *name)
void Program::bindAttributeLocation(GLuint index, const char *name)
{
if (index < MAX_VERTEX_ATTRIBS)
{
for (int i = 0; i < MAX_VERTEX_ATTRIBS; i++)
{
mAttributeBinding[i].erase(name);
}
mAttributeBinding[index].insert(name);
}
mAttributeBindings.bindLocation(index, name);
}
void Program::bindAttributeLocation(GLuint index, const char *name)
void Program::bindUniformLocation(GLuint index, const char *name)
{
mAttributeBindings.bindAttributeLocation(index, name);
// Bind the base uniform name only since array indices other than 0 cannot be bound
mUniformBindings.bindLocation(index, ParseUniformName(name, nullptr));
}
// Links the HLSL code of the vertex and pixel shader by matching up their varyings,
......@@ -453,7 +471,7 @@ Error Program::link(const gl::Data &data)
return Error(GL_NO_ERROR);
}
if (!linkUniforms(mInfoLog, *data.caps))
if (!linkUniforms(mInfoLog, *data.caps, mUniformBindings))
{
return Error(GL_NO_ERROR);
}
......@@ -485,19 +503,6 @@ Error Program::link(const gl::Data &data)
return gl::Error(GL_NO_ERROR);
}
int AttributeBindings::getAttributeBinding(const std::string &name) const
{
for (int location = 0; location < MAX_VERTEX_ATTRIBS; location++)
{
if (mAttributeBinding[location].find(name) != mAttributeBinding[location].end())
{
return location;
}
}
return -1;
}
// Returns the program object to an unlinked state, before re-linking, or at destruction
void Program::unlink(bool destroy)
{
......@@ -605,6 +610,8 @@ Error Program::loadBinary(GLenum binaryFormat, const void *binary, GLsizei lengt
stream.readString(&variable.name);
stream.readInt(&variable.element);
stream.readInt(&variable.index);
stream.readBool(&variable.used);
stream.readBool(&variable.ignored);
mData.mUniformLocations.push_back(variable);
}
......@@ -714,6 +721,8 @@ Error Program::saveBinary(GLenum *binaryFormat, void *binary, GLsizei bufSize, G
stream.writeString(variable.name);
stream.writeInt(variable.element);
stream.writeInt(variable.index);
stream.writeInt(variable.used);
stream.writeInt(variable.ignored);
}
stream.writeInt(mData.mUniformBlocks.size());
......@@ -1113,7 +1122,17 @@ GLint Program::getActiveUniformi(GLuint index, GLenum pname) const
bool Program::isValidUniformLocation(GLint location) const
{
ASSERT(rx::IsIntegerCastSafe<GLint>(mData.mUniformLocations.size()));
return (location >= 0 && static_cast<size_t>(location) < mData.mUniformLocations.size());
return (location >= 0 && static_cast<size_t>(location) < mData.mUniformLocations.size() &&
mData.mUniformLocations[static_cast<size_t>(location)].used);
}
bool Program::isIgnoredUniformLocation(GLint location) const
{
// Location is ignored if it is -1 or it was bound but non-existant in the shader or optimized
// out
return location == -1 ||
(location >= 0 && static_cast<size_t>(location) < mData.mUniformLocations.size() &&
mData.mUniformLocations[static_cast<size_t>(location)].ignored);
}
const LinkedUniform &Program::getUniformByLocation(GLint location) const
......@@ -1640,7 +1659,9 @@ bool Program::linkVaryings(InfoLog &infoLog,
return true;
}
bool Program::linkUniforms(gl::InfoLog &infoLog, const gl::Caps &caps)
bool Program::linkUniforms(gl::InfoLog &infoLog,
const gl::Caps &caps,
const Bindings &uniformBindings)
{
const std::vector<sh::Uniform> &vertexUniforms = mData.mAttachedVertexShader->getUniforms();
const std::vector<sh::Uniform> &fragmentUniforms = mData.mAttachedFragmentShader->getUniforms();
......@@ -1674,27 +1695,105 @@ bool Program::linkUniforms(gl::InfoLog &infoLog, const gl::Caps &caps)
return false;
}
indexUniforms();
if (!indexUniforms(infoLog, caps, uniformBindings))
{
return false;
}
return true;
}
void Program::indexUniforms()
bool Program::indexUniforms(gl::InfoLog &infoLog,
const gl::Caps &caps,
const Bindings &uniformBindings)
{
// Uniforms awaiting a location
std::vector<VariableLocation> unboundUniforms;
std::map<GLuint, VariableLocation> boundUniforms;
int maxUniformLocation = -1;
// Gather bound and unbound uniforms
for (size_t uniformIndex = 0; uniformIndex < mData.mUniforms.size(); uniformIndex++)
{
const gl::LinkedUniform &uniform = mData.mUniforms[uniformIndex];
if (uniform.isBuiltIn())
{
continue;
}
int bindingLocation = uniformBindings.getBinding(uniform.name);
// Verify that this location isn't bound twice
if (bindingLocation != -1 && boundUniforms.find(bindingLocation) != boundUniforms.end())
{
infoLog << "Multiple uniforms bound to location " << bindingLocation << ".";
return false;
}
for (unsigned int arrayIndex = 0; arrayIndex < uniform.elementCount(); arrayIndex++)
{
if (!uniform.isBuiltIn())
VariableLocation location(uniform.name, arrayIndex,
static_cast<unsigned int>(uniformIndex));
if (arrayIndex == 0 && bindingLocation != -1)
{
// Assign in-order uniform locations
mData.mUniformLocations.push_back(gl::VariableLocation(
uniform.name, arrayIndex, static_cast<unsigned int>(uniformIndex)));
boundUniforms[bindingLocation] = location;
maxUniformLocation = std::max(maxUniformLocation, bindingLocation);
}
else
{
unboundUniforms.push_back(location);
}
}
}
// Gather the reserved bindings, ones that are bound but not referenced. Other uniforms should
// not be assigned to those locations.
std::set<GLuint> reservedLocations;
for (const auto &binding : uniformBindings)
{
GLuint location = binding.second;
if (boundUniforms.find(location) == boundUniforms.end())
{
reservedLocations.insert(location);
maxUniformLocation = std::max(maxUniformLocation, static_cast<int>(location));
}
}
// Make enough space for all uniforms, bound and unbound
mData.mUniformLocations.resize(
std::max(unboundUniforms.size() + boundUniforms.size() + reservedLocations.size(),
static_cast<size_t>(maxUniformLocation + 1)));
// Assign bound uniforms
for (const auto &boundUniform : boundUniforms)
{
mData.mUniformLocations[boundUniform.first] = boundUniform.second;
}
// Assign reserved uniforms
for (const auto &reservedLocation : reservedLocations)
{
mData.mUniformLocations[reservedLocation].ignored = true;
}
// Assign unbound uniforms
size_t nextUniformLocation = 0;
for (const auto &unboundUniform : unboundUniforms)
{
while (mData.mUniformLocations[nextUniformLocation].used ||
mData.mUniformLocations[nextUniformLocation].ignored)
{
nextUniformLocation++;
}
ASSERT(nextUniformLocation < mData.mUniformLocations.size());
mData.mUniformLocations[nextUniformLocation] = unboundUniform;
nextUniformLocation++;
}
return true;
}
bool Program::linkValidateInterfaceBlockFields(InfoLog &infoLog, const std::string &uniformName, const sh::InterfaceBlockField &vertexUniform, const sh::InterfaceBlockField &fragmentUniform)
......@@ -1717,7 +1816,7 @@ bool Program::linkValidateInterfaceBlockFields(InfoLog &infoLog, const std::stri
// Determines the mapping between GL attributes and Direct3D 9 vertex stream usage indices
bool Program::linkAttributes(const gl::Data &data,
InfoLog &infoLog,
const AttributeBindings &attributeBindings,
const Bindings &attributeBindings,
const Shader *vertexShader)
{
unsigned int usedLocations = 0;
......@@ -1739,7 +1838,7 @@ bool Program::linkAttributes(const gl::Data &data,
// TODO(jmadill): do staticUse filtering step here, or not at all
ASSERT(attribute.staticUse);
int bindingLocation = attributeBindings.getAttributeBinding(attribute.name);
int bindingLocation = attributeBindings.getBinding(attribute.name);
if (attribute.location == -1 && bindingLocation != -1)
{
attribute.location = bindingLocation;
......
......@@ -42,7 +42,6 @@ struct Data;
class ResourceManager;
class Shader;
class InfoLog;
class AttributeBindings;
class Buffer;
class Framebuffer;
struct UniformBlock;
......@@ -50,19 +49,6 @@ struct LinkedUniform;
extern const char * const g_fakepath;
class AttributeBindings
{
public:
AttributeBindings();
~AttributeBindings();
void bindAttributeLocation(GLuint index, const char *name);
int getAttributeBinding(const std::string &name) const;
private:
std::set<std::string> mAttributeBinding[MAX_VERTEX_ATTRIBS];
};
class InfoLog : angle::NonCopyable
{
public:
......@@ -142,6 +128,13 @@ struct VariableLocation
std::string name;
unsigned int element;
unsigned int index;
// If this is a valid uniform location
bool used;
// If this location was bound to an unreferenced uniform. Setting data on this uniform is a
// no-op.
bool ignored;
};
class Program final : angle::NonCopyable, public LabeledObject
......@@ -240,6 +233,7 @@ class Program final : angle::NonCopyable, public LabeledObject
int getAttachedShadersCount() const;
void bindAttributeLocation(GLuint index, const char *name);
void bindUniformLocation(GLuint index, const char *name);
Error link(const gl::Data &data);
bool isLinked() const;
......@@ -274,6 +268,7 @@ class Program final : angle::NonCopyable, public LabeledObject
GLint getActiveUniformMaxLength() const;
GLint getActiveUniformi(GLuint index, GLenum pname) const;
bool isValidUniformLocation(GLint location) const;
bool isIgnoredUniformLocation(GLint location) const;
const LinkedUniform &getUniformByLocation(GLint location) const;
GLint getUniformLocation(const std::string &name) const;
......@@ -341,19 +336,33 @@ class Program final : angle::NonCopyable, public LabeledObject
}
private:
class Bindings final : angle::NonCopyable
{
public:
void bindLocation(GLuint index, const std::string &name);
int getBinding(const std::string &name) const;
typedef std::unordered_map<std::string, GLuint>::const_iterator const_iterator;
const_iterator begin() const;
const_iterator end() const;
private:
std::unordered_map<std::string, GLuint> mBindings;
};
void unlink(bool destroy = false);
void resetUniformBlockBindings();
bool linkAttributes(const gl::Data &data,
InfoLog &infoLog,
const AttributeBindings &attributeBindings,
const Bindings &attributeBindings,
const Shader *vertexShader);
bool linkUniformBlocks(InfoLog &infoLog, const Caps &caps);
static bool linkVaryings(InfoLog &infoLog,
const Shader *vertexShader,
const Shader *fragmentShader);
bool linkUniforms(gl::InfoLog &infoLog, const gl::Caps &caps);
void indexUniforms();
bool linkUniforms(gl::InfoLog &infoLog, const gl::Caps &caps, const Bindings &uniformBindings);
bool indexUniforms(gl::InfoLog &infoLog, const gl::Caps &caps, const Bindings &uniformBindings);
bool areMatchingInterfaceBlocks(gl::InfoLog &infoLog, const sh::InterfaceBlock &vertexInterfaceBlock,
const sh::InterfaceBlock &fragmentInterfaceBlock);
......@@ -420,7 +429,8 @@ class Program final : angle::NonCopyable, public LabeledObject
bool mValidated;
AttributeBindings mAttributeBindings;
Bindings mAttributeBindings;
Bindings mUniformBindings;
bool mLinked;
bool mDeleteStatus; // Flag to indicate that the program can be deleted when no longer in use
......
......@@ -1234,6 +1234,7 @@ void GenerateCaps(ID3D11Device *device, ID3D11DeviceContext *deviceContext, cons
extensions->vertexArrayObject = true;
extensions->noError = true;
extensions->lossyETCDecode = true;
extensions->bindUniformLocation = true;
// D3D11 Feature Level 10_0+ uses SV_IsFrontFace in HLSL to emulate gl_FrontFacing.
// D3D11 Feature Level 9_3 doesn't support SV_IsFrontFace, and has no equivalent, so can't support gl_FrontFacing.
......
......@@ -579,6 +579,7 @@ void GenerateCaps(IDirect3D9 *d3d9,
extensions->packSubimage = true;
extensions->vertexArrayObject = true;
extensions->noError = true;
extensions->bindUniformLocation = true;
// D3D9 has no concept of separate masks and refs for front and back faces in the depth stencil
// state.
......
......@@ -157,9 +157,17 @@ LinkResult ProgramGL::link(const gl::Data &data, gl::InfoLog &infoLog)
// Query the uniform information
ASSERT(mUniformRealLocationMap.empty());
const auto &uniformLocations = mData.getUniformLocations();
const auto &uniforms = mData.getUniforms();
for (const gl::VariableLocation &entry : mData.getUniformLocations())
mUniformRealLocationMap.resize(uniformLocations.size(), GL_INVALID_INDEX);
for (size_t uniformLocation = 0; uniformLocation < uniformLocations.size(); uniformLocation++)
{
const auto &entry = uniformLocations[uniformLocation];
if (!entry.used)
{
continue;
}
// From the spec:
// "Locations for sequential array indices are not required to be sequential."
const gl::LinkedUniform &uniform = uniforms[entry.index];
......@@ -172,7 +180,7 @@ LinkResult ProgramGL::link(const gl::Data &data, gl::InfoLog &infoLog)
const std::string &fullName = fullNameStr.str();
GLint realLocation = mFunctions->getUniformLocation(mProgramID, fullName.c_str());
mUniformRealLocationMap.push_back(realLocation);
mUniformRealLocationMap[uniformLocation] = realLocation;
}
mUniformIndexToSamplerIndex.resize(mData.getUniforms().size(), GL_INVALID_INDEX);
......
......@@ -642,6 +642,8 @@ void GenerateCaps(const FunctionsGL *functions, gl::Caps *caps, gl::TextureCapsM
extensions->vertexArrayObject = true;
extensions->noError = true;
extensions->bindUniformLocation = true;
}
void GenerateWorkarounds(const FunctionsGL *functions, WorkaroundsGL *workarounds)
......
......@@ -1391,7 +1391,7 @@ static bool ValidateUniformCommonBase(gl::Context *context,
return false;
}
if (location == -1)
if (program->isIgnoredUniformLocation(location))
{
// Silently ignore the uniform command
return false;
......
......@@ -15,6 +15,7 @@
#include "libANGLE/Renderbuffer.h"
#include "libANGLE/formatutils.h"
#include "libANGLE/FramebufferAttachment.h"
#include "libANGLE/Uniform.h"
#include "common/mathutil.h"
#include "common/utilities.h"
......@@ -2011,4 +2012,48 @@ bool ValidateBindTexture(Context *context, GLenum target, GLuint texture)
return true;
}
bool ValidateBindUniformLocationCHROMIUM(Context *context,
GLuint program,
GLint location,
const GLchar *name)
{
if (!context->getExtensions().bindUniformLocation)
{
context->recordError(
Error(GL_INVALID_OPERATION, "GL_CHROMIUM_bind_uniform_location is not available."));
return false;
}
Program *programObject = GetValidProgram(context, program);
if (!programObject)
{
return false;
}
if (location < 0)
{
context->recordError(Error(GL_INVALID_VALUE, "Location cannot be less than 0."));
return false;
}
const Caps &caps = context->getCaps();
if (static_cast<size_t>(location) >=
(caps.maxVertexUniformVectors + caps.maxFragmentUniformVectors) * 4)
{
context->recordError(Error(GL_INVALID_VALUE,
"Location must be less than (MAX_VERTEX_UNIFORM_VECTORS + "
"MAX_FRAGMENT_UNIFORM_VECTORS) * 4"));
return false;
}
if (strncmp(name, "gl_", 3) == 0)
{
context->recordError(
Error(GL_INVALID_OPERATION, "Name cannot start with the reserved \"gl_\" prefix."));
return false;
}
return true;
}
} // namespace gl
......@@ -183,6 +183,11 @@ bool ValidateFlushMappedBufferRangeEXT(Context *context,
GLintptr offset,
GLsizeiptr length);
bool ValidateBindUniformLocationCHROMIUM(Context *context,
GLuint program,
GLint location,
const GLchar *name);
} // namespace gl
#endif // LIBANGLE_VALIDATION_ES2_H_
......@@ -1460,6 +1460,9 @@ __eglMustCastToProperFunctionPointerType EGLAPIENTRY GetProcAddress(const char *
INSERT_PROC_ADDRESS(gl, GetObjectPtrLabelKHR);
INSERT_PROC_ADDRESS(gl, GetPointervKHR);
// GL_CHROMIUM_bind_uniform_location
INSERT_PROC_ADDRESS(gl, BindUniformLocationCHROMIUM);
// GLES3 core
INSERT_PROC_ADDRESS(gl, ReadBuffer);
INSERT_PROC_ADDRESS(gl, DrawRangeElements);
......
......@@ -1369,4 +1369,23 @@ void GL_APIENTRY GetPointervKHR(GLenum pname, void **params)
context->getPointerv(pname, params);
}
}
ANGLE_EXPORT void GL_APIENTRY BindUniformLocationCHROMIUM(GLuint program,
GLint location,
const GLchar *name)
{
EVENT("(GLuint program = %u, GLint location = %d, const GLchar *name = 0x%0.8p)", program,
location, name);
Context *context = GetValidGlobalContext();
if (context)
{
if (!ValidateBindUniformLocationCHROMIUM(context, program, location, name))
{
return;
}
context->bindUniformLocation(program, location, name);
}
}
}
......@@ -140,6 +140,11 @@ ANGLE_EXPORT void GL_APIENTRY GetObjectPtrLabelKHR(const void *ptr,
GLsizei *length,
GLchar *label);
ANGLE_EXPORT void GL_APIENTRY GetPointervKHR(GLenum pname, void **params);
// GL_CHROMIUM_bind_uniform_location
ANGLE_EXPORT void GL_APIENTRY BindUniformLocationCHROMIUM(GLuint program,
GLint location,
const GLchar *name);
}
#endif // LIBGLESV2_ENTRYPOINTGLES20EXT_H_
......@@ -1562,4 +1562,9 @@ void GL_APIENTRY glGetPointervKHR(GLenum pname, void **params)
{
return gl::GetPointervKHR(pname, params);
}
void GL_APIENTRY glBindUniformLocationCHROMIUM(GLuint program, GLint location, const GLchar *name)
{
return gl::BindUniformLocationCHROMIUM(program, location, name);
}
}
......@@ -202,6 +202,7 @@ EXPORTS
glGetQueryObjectivEXT @315
glGetQueryObjecti64vEXT @316
glGetQueryObjectui64vEXT @317
glBindUniformLocationCHROMIUM @318
; GLES 3.0 Functions
glReadBuffer @180
......
......@@ -15,6 +15,7 @@
{
'angle_end2end_tests_sources':
[
'<(angle_path)/src/tests/gl_tests/BindUniformLocationTest.cpp',
'<(angle_path)/src/tests/gl_tests/BlendMinMaxTest.cpp',
'<(angle_path)/src/tests/gl_tests/BlitFramebufferANGLETest.cpp',
'<(angle_path)/src/tests/gl_tests/BufferDataTest.cpp',
......
//
// 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.
//
// BindUniformLocationTest.cpp : Tests of the GL_CHROMIUM_bind_uniform_location extension.
#include "test_utils/ANGLETest.h"
#include <cmath>
using namespace angle;
namespace
{
class BindUniformLocationTest : public ANGLETest
{
protected:
BindUniformLocationTest()
{
setWindowWidth(128);
setWindowHeight(128);
setConfigRedBits(8);
setConfigGreenBits(8);
setConfigBlueBits(8);
setConfigAlphaBits(8);
}
void SetUp() override
{
ANGLETest::SetUp();
mBindUniformLocation = reinterpret_cast<PFNGLBINDUNIFORMLOCATIONCHROMIUMPROC>(
eglGetProcAddress("glBindUniformLocationCHROMIUM"));
}
void TearDown() override
{
if (mProgram != 0)
{
glDeleteProgram(mProgram);
}
ANGLETest::TearDown();
}
typedef void(GL_APIENTRYP PFNGLBINDUNIFORMLOCATIONCHROMIUMPROC)(GLuint mProgram,
GLint location,
const GLchar *name);
PFNGLBINDUNIFORMLOCATIONCHROMIUMPROC mBindUniformLocation = nullptr;
GLuint mProgram = 0;
};
// Test basic functionality of GL_CHROMIUM_bind_uniform_location
TEST_P(BindUniformLocationTest, Basic)
{
if (!extensionEnabled("GL_CHROMIUM_bind_uniform_location"))
{
std::cout << "Test skipped because GL_CHROMIUM_bind_uniform_location is not available."
<< std::endl;
return;
}
ASSERT_NE(mBindUniformLocation, nullptr);
// clang-format off
const std::string vsSource = SHADER_SOURCE
(
attribute vec4 a_position;
void main()
{
gl_Position = a_position;
}
);
const std::string fsSource = SHADER_SOURCE
(
precision mediump float;
uniform vec4 u_colorC;
uniform vec4 u_colorB[2];
uniform vec4 u_colorA;
void main()
{
gl_FragColor = u_colorA + u_colorB[0] + u_colorB[1] + u_colorC;
}
);
// clang-format on
GLint colorALocation = 3;
GLint colorBLocation = 10;
GLint colorCLocation = 5;
GLuint vs = CompileShader(GL_VERTEX_SHADER, vsSource);
GLuint fs = CompileShader(GL_FRAGMENT_SHADER, fsSource);
mProgram = glCreateProgram();
mBindUniformLocation(mProgram, colorALocation, "u_colorA");
mBindUniformLocation(mProgram, colorBLocation, "u_colorB[0]");
mBindUniformLocation(mProgram, colorCLocation, "u_colorC");
glAttachShader(mProgram, vs);
glDeleteShader(vs);
glAttachShader(mProgram, fs);
glDeleteShader(fs);
// Link the mProgram
glLinkProgram(mProgram);
// Check the link status
GLint linked = 0;
glGetProgramiv(mProgram, GL_LINK_STATUS, &linked);
ASSERT_EQ(1, linked);
glUseProgram(mProgram);
static const float colorB[] = {
0.0f, 0.50f, 0.0f, 0.0f, 0.0f, 0.0f, 0.75f, 0.0f,
};
glUniform4f(colorALocation, 0.25f, 0.0f, 0.0f, 0.0f);
glUniform4fv(colorBLocation, 2, colorB);
glUniform4f(colorCLocation, 0.0f, 0.0f, 0.0f, 1.0f);
drawQuad(mProgram, "a_position", 0.5f);
EXPECT_GL_NO_ERROR();
EXPECT_PIXEL_NEAR(0, 0, 64, 128, 192, 255, 1.0);
}
// Test that conflicts are detected when two uniforms are bound to the same location
TEST_P(BindUniformLocationTest, ConflictsDetection)
{
if (!extensionEnabled("GL_CHROMIUM_bind_uniform_location"))
{
std::cout << "Test skipped because GL_CHROMIUM_bind_uniform_location is not available."
<< std::endl;
return;
}
ASSERT_NE(nullptr, mBindUniformLocation);
// clang-format off
const std::string vsSource = SHADER_SOURCE
(
attribute vec4 a_position;
void main()
{
gl_Position = a_position;
}
);
const std::string fsSource = SHADER_SOURCE
(
precision mediump float;
uniform vec4 u_colorA;
uniform vec4 u_colorB;
void main()
{
gl_FragColor = u_colorA + u_colorB;
}
);
// clang-format on
GLint colorALocation = 3;
GLint colorBLocation = 4;
GLuint vs = CompileShader(GL_VERTEX_SHADER, vsSource);
GLuint fs = CompileShader(GL_FRAGMENT_SHADER, fsSource);
mProgram = glCreateProgram();
glAttachShader(mProgram, vs);
glDeleteShader(vs);
glAttachShader(mProgram, fs);
glDeleteShader(fs);
mBindUniformLocation(mProgram, colorALocation, "u_colorA");
// Bind u_colorB to location a, causing conflicts, link should fail.
mBindUniformLocation(mProgram, colorALocation, "u_colorB");
glLinkProgram(mProgram);
GLint linked = 0;
glGetProgramiv(mProgram, GL_LINK_STATUS, &linked);
ASSERT_EQ(0, linked);
// Bind u_colorB to location b, no conflicts, link should succeed.
mBindUniformLocation(mProgram, colorBLocation, "u_colorB");
glLinkProgram(mProgram);
linked = 0;
glGetProgramiv(mProgram, GL_LINK_STATUS, &linked);
EXPECT_EQ(1, linked);
}
// Test a use case of the chromium compositor
TEST_P(BindUniformLocationTest, Compositor)
{
if (!extensionEnabled("GL_CHROMIUM_bind_uniform_location"))
{
std::cout << "Test skipped because GL_CHROMIUM_bind_uniform_location is not available."
<< std::endl;
return;
}
ASSERT_NE(nullptr, mBindUniformLocation);
// clang-format off
const std::string vsSource = SHADER_SOURCE
(
attribute vec4 a_position;
attribute vec2 a_texCoord;
uniform mat4 matrix;
uniform vec2 color_a[4];
uniform vec4 color_b;
varying vec4 v_color;
void main()
{
v_color.xy = color_a[0] + color_a[1];
v_color.zw = color_a[2] + color_a[3];
v_color += color_b;
gl_Position = matrix * a_position;
}
);
const std::string fsSource = SHADER_SOURCE
(
precision mediump float;
varying vec4 v_color;
uniform float alpha;
uniform vec4 multiplier;
uniform vec3 color_c[8];
void main()
{
vec4 color_c_sum = vec4(0.0);
color_c_sum.xyz += color_c[0];
color_c_sum.xyz += color_c[1];
color_c_sum.xyz += color_c[2];
color_c_sum.xyz += color_c[3];
color_c_sum.xyz += color_c[4];
color_c_sum.xyz += color_c[5];
color_c_sum.xyz += color_c[6];
color_c_sum.xyz += color_c[7];
color_c_sum.w = alpha;
color_c_sum *= multiplier;
gl_FragColor = v_color + color_c_sum;
}
);
// clang-format on
int counter = 6;
int matrixLocation = counter++;
int colorALocation = counter++;
int colorBLocation = counter++;
int alphaLocation = counter++;
int multiplierLocation = counter++;
int colorCLocation = counter++;
GLuint vs = CompileShader(GL_VERTEX_SHADER, vsSource);
GLuint fs = CompileShader(GL_FRAGMENT_SHADER, fsSource);
mProgram = glCreateProgram();
mBindUniformLocation(mProgram, matrixLocation, "matrix");
mBindUniformLocation(mProgram, colorALocation, "color_a");
mBindUniformLocation(mProgram, colorBLocation, "color_b");
mBindUniformLocation(mProgram, alphaLocation, "alpha");
mBindUniformLocation(mProgram, multiplierLocation, "multiplier");
mBindUniformLocation(mProgram, colorCLocation, "color_c");
glAttachShader(mProgram, vs);
glDeleteShader(vs);
glAttachShader(mProgram, fs);
glDeleteShader(fs);
// Link the mProgram
glLinkProgram(mProgram);
// Check the link status
GLint linked = 0;
glGetProgramiv(mProgram, GL_LINK_STATUS, &linked);
ASSERT_EQ(1, linked);
glUseProgram(mProgram);
static const float colorA[] = {
0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f,
};
static const float colorC[] = {
0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f,
0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f,
};
static const float identity[] = {
1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1,
};
glUniformMatrix4fv(matrixLocation, 1, false, identity);
glUniform2fv(colorALocation, 4, colorA);
glUniform4f(colorBLocation, 0.2f, 0.2f, 0.2f, 0.2f);
glUniform1f(alphaLocation, 0.8f);
glUniform4f(multiplierLocation, 0.5f, 0.5f, 0.5f, 0.5f);
glUniform3fv(colorCLocation, 8, colorC);
glDrawArrays(GL_TRIANGLES, 0, 6);
drawQuad(mProgram, "a_position", 0.5f);
EXPECT_PIXEL_EQ(0, 0, 204, 204, 204, 204);
}
// Test that unused uniforms don't conflict when bound to the same location
TEST_P(BindUniformLocationTest, UnusedUniformUpdate)
{
if (!extensionEnabled("GL_CHROMIUM_bind_uniform_location"))
{
std::cout << "Test skipped because GL_CHROMIUM_bind_uniform_location is not available."
<< std::endl;
return;
}
ASSERT_NE(nullptr, mBindUniformLocation);
// clang-format off
const std::string vsSource = SHADER_SOURCE
(
attribute vec4 a_position;
void main()
{
gl_Position = a_position;
}
);
const std::string fsSource = SHADER_SOURCE
(
precision mediump float;
uniform vec4 u_colorA;
uniform float u_colorU;
uniform vec4 u_colorC;
void main()
{
gl_FragColor = u_colorA + u_colorC;
}
);
// clang-format on
const GLint colorULocation = 1;
const GLint nonexistingLocation = 5;
const GLint unboundLocation = 6;
GLuint vs = CompileShader(GL_VERTEX_SHADER, vsSource);
GLuint fs = CompileShader(GL_FRAGMENT_SHADER, fsSource);
mProgram = glCreateProgram();
mBindUniformLocation(mProgram, colorULocation, "u_colorU");
// The non-existing uniform should behave like existing, but optimized away
// uniform.
mBindUniformLocation(mProgram, nonexistingLocation, "nonexisting");
// Let A and C be assigned automatic locations.
glAttachShader(mProgram, vs);
glDeleteShader(vs);
glAttachShader(mProgram, fs);
glDeleteShader(fs);
glLinkProgram(mProgram);
GLint linked = 0;
glGetProgramiv(mProgram, GL_LINK_STATUS, &linked);
ASSERT_EQ(1, linked);
glUseProgram(mProgram);
// No errors on bound locations, since caller does not know
// if the driver optimizes them away or not.
glUniform1f(colorULocation, 0.25f);
EXPECT_GL_NO_ERROR();
// No errors on bound locations of names that do not exist
// in the shader. Otherwise it would be inconsistent wrt the
// optimization case.
glUniform1f(nonexistingLocation, 0.25f);
EXPECT_GL_NO_ERROR();
// The above are equal to updating -1.
glUniform1f(-1, 0.25f);
EXPECT_GL_NO_ERROR();
// No errors when updating with other type either.
// The type can not be known with the non-existing case.
glUniform2f(colorULocation, 0.25f, 0.25f);
EXPECT_GL_NO_ERROR();
glUniform2f(nonexistingLocation, 0.25f, 0.25f);
EXPECT_GL_NO_ERROR();
glUniform2f(-1, 0.25f, 0.25f);
EXPECT_GL_NO_ERROR();
// Ensure that driver or ANGLE has optimized the variable
// away and the test tests what it is supposed to.
EXPECT_EQ(-1, glGetUniformLocation(mProgram, "u_colorU"));
// The bound location gets marked as used and the driver
// does not allocate other variables to that location.
EXPECT_NE(colorULocation, glGetUniformLocation(mProgram, "u_colorA"));
EXPECT_NE(colorULocation, glGetUniformLocation(mProgram, "u_colorC"));
EXPECT_NE(nonexistingLocation, glGetUniformLocation(mProgram, "u_colorA"));
EXPECT_NE(nonexistingLocation, glGetUniformLocation(mProgram, "u_colorC"));
// Unintuitive: while specifying value works, getting the value does not.
GLfloat getResult = 0.0f;
glGetUniformfv(mProgram, colorULocation, &getResult);
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
glGetUniformfv(mProgram, nonexistingLocation, &getResult);
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
glGetUniformfv(mProgram, -1, &getResult);
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
// Updating an unbound, non-existing location still causes
// an error.
glUniform1f(unboundLocation, 0.25f);
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
}
// Test for a bug where using a sampler caused GL error if the mProgram had
// uniforms that were optimized away by the driver. This was only a problem with
// glBindUniformLocationCHROMIUM implementation. This could be reproed by
// binding the sampler to a location higher than the amount of active uniforms.
TEST_P(BindUniformLocationTest, UseSamplerWhenUnusedUniforms)
{
if (!extensionEnabled("GL_CHROMIUM_bind_uniform_location"))
{
std::cout << "Test skipped because GL_CHROMIUM_bind_uniform_location is not available."
<< std::endl;
return;
}
ASSERT_NE(nullptr, mBindUniformLocation);
// clang-format off
const std::string vsSource = SHADER_SOURCE
(
void main()
{
gl_Position = vec4(0);
}
);
const std::string fsSource = SHADER_SOURCE
(
uniform sampler2D tex;
void main()
{
gl_FragColor = texture2D(tex, vec2(1));
}
);
// clang-format on
const GLuint texLocation = 54;
GLuint vs = CompileShader(GL_VERTEX_SHADER, vsSource);
GLuint fs = CompileShader(GL_FRAGMENT_SHADER, fsSource);
mProgram = glCreateProgram();
mBindUniformLocation(mProgram, texLocation, "tex");
glAttachShader(mProgram, vs);
glDeleteShader(vs);
glAttachShader(mProgram, fs);
glDeleteShader(fs);
glLinkProgram(mProgram);
GLint linked = 0;
glGetProgramiv(mProgram, GL_LINK_STATUS, &linked);
EXPECT_NE(0, linked);
glUseProgram(mProgram);
glUniform1i(texLocation, 0);
EXPECT_GL_NO_ERROR();
}
// Use this to select which configurations (e.g. which renderer, which GLES major version) these
// tests should be run against.
ANGLE_INSTANTIATE_TEST(BindUniformLocationTest,
ES2_D3D9(),
ES2_D3D11(),
ES2_D3D11_FL9_3(),
ES2_OPENGL(),
ES2_OPENGLES());
} // namespace
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