Commit 15015f7f by jchen10 Committed by Commit Bot

ES31: Add glGetProgramResourceIndex API

Add API entry and validation checks(GLES 3.1 section 7.3). Add the first 2 interfaces(PROGRAM_INPUT and PROGRAM_OUTPUT) implementation. BUG=angleproject:1920 Change-Id: Ib2dedded9fd79b315e9f38de7c27a5e4ec4c6066 Reviewed-on: https://chromium-review.googlesource.com/453085 Commit-Queue: Jamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarCorentin Wallez <cwallez@chromium.org>
parent f651c773
......@@ -697,7 +697,7 @@ int VariableSortOrder(GLenum type)
}
}
std::string ParseUniformName(const std::string &name, size_t *outSubscript)
std::string ParseResourceName(const std::string &name, size_t *outSubscript)
{
// Strip any trailing array operator and retrieve the subscript
size_t open = name.find_last_of('[');
......
......@@ -48,9 +48,10 @@ bool IsCubeMapTextureTarget(GLenum target);
size_t CubeMapTextureTargetToLayerIndex(GLenum target);
GLenum LayerIndexToCubeMapTextureTarget(size_t index);
// Parse the base uniform name and array index. Returns the base name of the uniform. outSubscript is
// set to GL_INVALID_INDEX if the provided name is not an array or the array index is invalid.
std::string ParseUniformName(const std::string &name, size_t *outSubscript);
// Parse the base resource name and array index. Returns the base name of the resource.
// outSubscript is set to GL_INVALID_INDEX if the provided name is not an array or the array index
// is invalid.
std::string ParseResourceName(const std::string &name, size_t *outSubscript);
// Find the range of index values in the provided indices pointer. Primitive restart indices are
// only counted in the range if primitive restart is disabled.
......
......@@ -13,42 +13,42 @@
namespace
{
TEST(ParseUniformName, ArrayIndex)
TEST(ParseResourceName, ArrayIndex)
{
size_t index;
EXPECT_EQ("foo", gl::ParseUniformName("foo[123]", &index));
EXPECT_EQ("foo", gl::ParseResourceName("foo[123]", &index));
EXPECT_EQ(123u, index);
EXPECT_EQ("bar", gl::ParseUniformName("bar[0]", &index));
EXPECT_EQ("bar", gl::ParseResourceName("bar[0]", &index));
EXPECT_EQ(0u, index);
}
TEST(ParseUniformName, NegativeArrayIndex)
TEST(ParseResourceName, NegativeArrayIndex)
{
size_t index;
EXPECT_EQ("foo", gl::ParseUniformName("foo[-1]", &index));
EXPECT_EQ("foo", gl::ParseResourceName("foo[-1]", &index));
EXPECT_EQ(GL_INVALID_INDEX, index);
}
TEST(ParseUniformName, NoArrayIndex)
TEST(ParseResourceName, NoArrayIndex)
{
size_t index;
EXPECT_EQ("foo", gl::ParseUniformName("foo", &index));
EXPECT_EQ("foo", gl::ParseResourceName("foo", &index));
EXPECT_EQ(GL_INVALID_INDEX, index);
}
TEST(ParseUniformName, NULLArrayIndex)
TEST(ParseResourceName, NULLArrayIndex)
{
EXPECT_EQ("foo", gl::ParseUniformName("foo[10]", nullptr));
EXPECT_EQ("foo", gl::ParseResourceName("foo[10]", nullptr));
}
TEST(ParseUniformName, TrailingWhitespace)
TEST(ParseResourceName, TrailingWhitespace)
{
size_t index;
EXPECT_EQ("foo ", gl::ParseUniformName("foo ", &index));
EXPECT_EQ("foo ", gl::ParseResourceName("foo ", &index));
EXPECT_EQ(GL_INVALID_INDEX, index);
EXPECT_EQ("foo[10] ", gl::ParseUniformName("foo[10] ", &index));
EXPECT_EQ("foo[10] ", gl::ParseResourceName("foo[10] ", &index));
EXPECT_EQ(GL_INVALID_INDEX, index);
}
......
......@@ -2038,6 +2038,12 @@ void Context::programPathFragmentInputGen(GLuint program,
programObject->pathFragmentInputGen(location, genMode, components, coeffs);
}
GLuint Context::getProgramResourceIndex(GLuint program, GLenum programInterface, const GLchar *name)
{
gl::Program *programObject = getProgram(program);
return QueryProgramResourceIndex(programObject, programInterface, name);
}
void Context::handleError(const Error &error)
{
if (error.isError())
......
......@@ -188,6 +188,8 @@ class Context final : public ValidationContext
void programParameteri(GLuint program, GLenum pname, GLint value);
GLuint getProgramResourceIndex(GLuint program, GLenum programInterface, const GLchar *name);
Buffer *getBuffer(GLuint handle) const;
FenceNV *getFenceNV(GLuint handle);
FenceSync *getFenceSync(GLsync handle) const;
......
......@@ -132,6 +132,33 @@ bool ComparePackedVarying(const PackedVarying &x, const PackedVarying &y)
return gl::CompareShaderVar(*x.varying, *y.varying);
}
template <typename VarT>
GLuint GetResourceIndexFromName(const std::vector<VarT> &list, const std::string &name)
{
size_t subscript = GL_INVALID_INDEX;
std::string baseName = ParseResourceName(name, &subscript);
// The app is not allowed to specify array indices other than 0 for arrays of basic types
if (subscript != 0 && subscript != GL_INVALID_INDEX)
{
return GL_INVALID_INDEX;
}
for (size_t index = 0; index < list.size(); index++)
{
const VarT &resource = list[index];
if (resource.name == baseName)
{
if (resource.isArray() || subscript == GL_INVALID_INDEX)
{
return static_cast<GLuint>(index);
}
}
}
return GL_INVALID_INDEX;
}
} // anonymous namespace
const char *const g_fakepath = "C:\\fakepath";
......@@ -255,7 +282,7 @@ const std::string &ProgramState::getLabel()
GLint ProgramState::getUniformLocation(const std::string &name) const
{
size_t subscript = GL_INVALID_INDEX;
std::string baseName = ParseUniformName(name, &subscript);
std::string baseName = ParseResourceName(name, &subscript);
for (size_t location = 0; location < mUniformLocations.size(); ++location)
{
......@@ -292,28 +319,7 @@ GLint ProgramState::getUniformLocation(const std::string &name) const
GLuint ProgramState::getUniformIndexFromName(const std::string &name) const
{
size_t subscript = GL_INVALID_INDEX;
std::string baseName = ParseUniformName(name, &subscript);
// The app is not allowed to specify array indices other than 0 for arrays of basic types
if (subscript != 0 && subscript != GL_INVALID_INDEX)
{
return GL_INVALID_INDEX;
}
for (size_t index = 0; index < mUniforms.size(); index++)
{
const LinkedUniform &uniform = mUniforms[index];
if (uniform.name == baseName)
{
if (uniform.isArray() || subscript == GL_INVALID_INDEX)
{
return static_cast<GLuint>(index);
}
}
}
return GL_INVALID_INDEX;
return GetResourceIndexFromName(mUniforms, name);
}
GLuint ProgramState::getUniformIndexFromLocation(GLint location) const
......@@ -487,7 +493,7 @@ void Program::bindAttributeLocation(GLuint index, const char *name)
void Program::bindUniformLocation(GLuint index, const char *name)
{
// Bind the base uniform name only since array indices other than 0 cannot be bound
mUniformLocationBindings.bindLocation(index, ParseUniformName(name, nullptr));
mUniformLocationBindings.bindLocation(index, ParseResourceName(name, nullptr));
}
void Program::bindFragmentInputLocation(GLint index, const char *name)
......@@ -720,6 +726,7 @@ void Program::unlink()
mState.mUniformLocations.clear();
mState.mUniformBlocks.clear();
mState.mOutputVariables.clear();
mState.mOutputLocations.clear();
mState.mComputeShaderLocalSize.fill(1);
mState.mSamplerBindings.clear();
......@@ -865,6 +872,16 @@ Error Program::loadBinary(const Context *context,
stream.readInt(&mState.mTransformFeedbackBufferMode);
unsigned int outputCount = stream.readInt<unsigned int>();
ASSERT(mState.mOutputVariables.empty());
for (unsigned int outputIndex = 0; outputIndex < outputCount; ++outputIndex)
{
sh::OutputVariable output;
LoadShaderVar(&stream, &output);
output.location = stream.readInt<int>();
mState.mOutputVariables.push_back(output);
}
unsigned int outputVarCount = stream.readInt<unsigned int>();
for (unsigned int outputIndex = 0; outputIndex < outputVarCount; ++outputIndex)
{
......@@ -873,7 +890,7 @@ Error Program::loadBinary(const Context *context,
stream.readInt(&locationData.element);
stream.readInt(&locationData.index);
stream.readString(&locationData.name);
mState.mOutputVariables[locationIndex] = locationData;
mState.mOutputLocations[locationIndex] = locationData;
}
stream.readInt(&mState.mSamplerUniformRange.start);
......@@ -991,7 +1008,14 @@ Error Program::saveBinary(const Context *context,
stream.writeInt(mState.mTransformFeedbackBufferMode);
stream.writeInt(mState.mOutputVariables.size());
for (const auto &outputPair : mState.mOutputVariables)
for (const sh::OutputVariable &output : mState.mOutputVariables)
{
WriteShaderVar(&stream, output);
stream.writeInt(output.location);
}
stream.writeInt(mState.mOutputLocations.size());
for (const auto &outputPair : mState.mOutputLocations)
{
stream.writeInt(outputPair.first);
stream.writeIntOrNegOne(outputPair.second.element);
......@@ -1222,11 +1246,29 @@ GLint Program::getActiveAttributeMaxLength() const
return static_cast<GLint>(maxLength);
}
GLuint Program::getInputResourceIndex(const GLchar *name) const
{
for (GLuint attributeIndex = 0; attributeIndex < mState.mAttributes.size(); ++attributeIndex)
{
const sh::Attribute &attribute = mState.mAttributes[attributeIndex];
if (attribute.name == name)
{
return attributeIndex;
}
}
return GL_INVALID_INDEX;
}
GLuint Program::getOutputResourceIndex(const GLchar *name) const
{
return GetResourceIndexFromName(mState.mOutputVariables, std::string(name));
}
GLint Program::getFragDataLocation(const std::string &name) const
{
std::string baseName(name);
unsigned int arrayIndex = ParseAndStripArrayIndex(&baseName);
for (auto outputPair : mState.mOutputVariables)
for (auto outputPair : mState.mOutputLocations)
{
const VariableLocation &outputVariable = outputPair.second;
if (outputVariable.name == baseName && (arrayIndex == GL_INVALID_INDEX || arrayIndex == outputVariable.element))
......@@ -1679,7 +1721,7 @@ GLint Program::getActiveUniformBlockMaxLength() const
GLuint Program::getUniformBlockIndex(const std::string &name) const
{
size_t subscript = GL_INVALID_INDEX;
std::string baseName = ParseUniformName(name, &subscript);
std::string baseName = ParseResourceName(name, &subscript);
unsigned int numUniformBlocks = static_cast<unsigned int>(mState.mUniformBlocks.size());
for (unsigned int blockIndex = 0; blockIndex < numUniformBlocks; blockIndex++)
......@@ -2505,14 +2547,13 @@ void Program::linkOutputVariables()
if (fragmentShader->getShaderVersion() == 100)
return;
const auto &shaderOutputVars = fragmentShader->getActiveOutputVariables();
mState.mOutputVariables = fragmentShader->getActiveOutputVariables();
// TODO(jmadill): any caps validation here?
for (unsigned int outputVariableIndex = 0; outputVariableIndex < shaderOutputVars.size();
for (unsigned int outputVariableIndex = 0; outputVariableIndex < mState.mOutputVariables.size();
outputVariableIndex++)
{
const sh::OutputVariable &outputVariable = shaderOutputVars[outputVariableIndex];
const sh::OutputVariable &outputVariable = mState.mOutputVariables[outputVariableIndex];
// Don't store outputs for gl_FragDepth, gl_FragColor, etc.
if (outputVariable.isBuiltIn())
......@@ -2525,9 +2566,9 @@ void Program::linkOutputVariables()
elementIndex++)
{
const int location = baseLocation + elementIndex;
ASSERT(mState.mOutputVariables.count(location) == 0);
ASSERT(mState.mOutputLocations.count(location) == 0);
unsigned int element = outputVariable.isArray() ? elementIndex : GL_INVALID_INDEX;
mState.mOutputVariables[location] =
mState.mOutputLocations[location] =
VariableLocation(outputVariable.name, element, outputVariableIndex);
}
}
......
......@@ -205,7 +205,7 @@ class ProgramState final : angle::NonCopyable
{
return mActiveAttribLocationsMask;
}
const std::map<int, VariableLocation> &getOutputVariables() const { return mOutputVariables; }
const std::map<int, VariableLocation> &getOutputLocations() const { return mOutputLocations; }
const std::vector<LinkedUniform> &getUniforms() const { return mUniforms; }
const std::vector<VariableLocation> &getUniformLocations() const { return mUniformLocations; }
const std::vector<UniformBlock> &getUniformBlocks() const { return mUniformBlocks; }
......@@ -252,8 +252,9 @@ class ProgramState final : angle::NonCopyable
// An array of the samplers that are used by the program
std::vector<gl::SamplerBinding> mSamplerBindings;
std::vector<sh::OutputVariable> mOutputVariables;
// TODO(jmadill): use unordered/hash map when available
std::map<int, VariableLocation> mOutputVariables;
std::map<int, VariableLocation> mOutputLocations;
bool mBinaryRetrieveableHint;
};
......@@ -411,6 +412,9 @@ class Program final : angle::NonCopyable, public LabeledObject
const sh::ShaderVariable &fragmentVariable,
bool validatePrecision);
GLuint getInputResourceIndex(const GLchar *name) const;
GLuint getOutputResourceIndex(const GLchar *name) const;
class Bindings final : angle::NonCopyable
{
public:
......
......@@ -939,6 +939,33 @@ void SetFramebufferParameteri(Framebuffer *framebuffer, GLenum pname, GLint para
}
}
GLuint QueryProgramResourceIndex(const Program *program,
GLenum programInterface,
const GLchar *name)
{
switch (programInterface)
{
case GL_PROGRAM_INPUT:
return program->getInputResourceIndex(name);
case GL_PROGRAM_OUTPUT:
return program->getOutputResourceIndex(name);
// TODO(Jie): more interfaces.
case GL_UNIFORM:
case GL_UNIFORM_BLOCK:
case GL_TRANSFORM_FEEDBACK_VARYING:
case GL_BUFFER_VARIABLE:
case GL_SHADER_STORAGE_BLOCK:
UNIMPLEMENTED();
return GL_INVALID_INDEX;
default:
UNREACHABLE();
return GL_INVALID_INDEX;
}
}
} // namespace gl
namespace egl
......
......@@ -107,6 +107,10 @@ void SetSamplerParameteriv(Sampler *sampler, GLenum pname, const GLint *params);
void SetFramebufferParameteri(Framebuffer *framebuffer, GLenum pname, GLint param);
GLuint QueryProgramResourceIndex(const Program *program,
GLenum programInterface,
const GLchar *name);
} // namespace gl
namespace egl
......
......@@ -1130,7 +1130,7 @@ void DynamicHLSL::getPixelShaderOutputKey(const gl::ContextState &data,
const auto &shaderOutputVars =
metadata.getFragmentShader()->getData().getActiveOutputVariables();
for (auto outputPair : programData.getOutputVariables())
for (auto outputPair : programData.getOutputLocations())
{
const VariableLocation &outputLocation = outputPair.second;
const sh::ShaderVariable &outputVariable = shaderOutputVars[outputLocation.index];
......
......@@ -21,6 +21,28 @@ using namespace angle;
namespace gl
{
namespace
{
bool ValidateNamedProgramInterface(GLenum programInterface)
{
switch (programInterface)
{
case GL_UNIFORM:
case GL_UNIFORM_BLOCK:
case GL_PROGRAM_INPUT:
case GL_PROGRAM_OUTPUT:
case GL_TRANSFORM_FEEDBACK_VARYING:
case GL_BUFFER_VARIABLE:
case GL_SHADER_STORAGE_BLOCK:
return true;
default:
return false;
}
}
} // anonymous namespace
bool ValidateGetBooleani_v(Context *context, GLenum target, GLuint index, GLboolean *data)
{
if (context->getClientVersion() < ES_3_1)
......@@ -505,4 +527,30 @@ bool ValidationGetFramebufferParameteri(Context *context,
return true;
}
bool ValidateGetProgramResourceIndex(Context *context,
GLuint program,
GLenum programInterface,
const GLchar *name)
{
if (context->getClientVersion() < ES_3_1)
{
context->handleError(Error(GL_INVALID_OPERATION, "Context does not support GLES 3.1."));
return false;
}
Program *programObject = GetValidProgram(context, program);
if (programObject == nullptr)
{
return false;
}
if (!ValidateNamedProgramInterface(programInterface))
{
context->handleError(
Error(GL_INVALID_ENUM, "Invalid program interface: 0x%X", programInterface));
return false;
}
return true;
}
} // namespace gl
......@@ -55,6 +55,12 @@ bool ValidationGetFramebufferParameteri(Context *context,
GLenum target,
GLenum pname,
GLint *params);
bool ValidateGetProgramResourceIndex(Context *context,
GLuint program,
GLenum programInterface,
const GLchar *name);
} // namespace gl
#endif // LIBANGLE_VALIDATION_ES31_H_
......@@ -145,11 +145,12 @@ GLuint GL_APIENTRY GetProgramResourceIndex(GLuint program,
Context *context = GetValidGlobalContext();
if (context)
{
if (!context->skipValidation())
if (!context->skipValidation() &&
!ValidateGetProgramResourceIndex(context, program, programInterface, name))
{
context->handleError(Error(GL_INVALID_OPERATION, "Entry point not implemented"));
return GL_INVALID_INDEX;
}
UNIMPLEMENTED();
return context->getProgramResourceIndex(program, programInterface, name);
}
return 0u;
}
......
......@@ -64,6 +64,7 @@
'<(angle_path)/src/tests/gl_tests/ObjectAllocationTest.cpp',
'<(angle_path)/src/tests/gl_tests/OcclusionQueriesTest.cpp',
'<(angle_path)/src/tests/gl_tests/ProgramBinaryTest.cpp',
'<(angle_path)/src/tests/gl_tests/ProgramInterfaceTest.cpp',
'<(angle_path)/src/tests/gl_tests/ReadPixelsTest.cpp',
'<(angle_path)/src/tests/gl_tests/RendererTest.cpp',
'<(angle_path)/src/tests/gl_tests/RobustClientMemoryTest.cpp',
......
//
// Copyright 2017 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.
//
// ProgramInterfaceTest: Tests of program interfaces.
#include "test_utils/ANGLETest.h"
#include "test_utils/gl_raii.h"
using namespace angle;
namespace
{
class ProgramInterfaceTestES31 : public ANGLETest
{
protected:
ProgramInterfaceTestES31()
{
setWindowWidth(64);
setWindowHeight(64);
setConfigRedBits(8);
setConfigGreenBits(8);
setConfigBlueBits(8);
setConfigAlphaBits(8);
}
};
// Tests glGetProgramResourceIndex.
TEST_P(ProgramInterfaceTestES31, GetResourceIndex)
{
const std::string &vertexShaderSource =
"#version 310 es\n"
"precision highp float;\n"
"in highp vec4 position;\n"
"void main()\n"
"{\n"
" gl_Position = position;\n"
"}";
const std::string fragmentShaderSource =
"#version 310 es\n"
"precision highp float;\n"
"uniform vec4 color;\n"
"out vec4 oColor;\n"
"void main()\n"
"{\n"
" oColor = color;\n"
"}";
ANGLE_GL_PROGRAM(program, vertexShaderSource, fragmentShaderSource);
GLuint index = glGetProgramResourceIndex(program, GL_PROGRAM_INPUT, "position");
EXPECT_GL_NO_ERROR();
EXPECT_NE(GL_INVALID_INDEX, index);
index = glGetProgramResourceIndex(program, GL_PROGRAM_INPUT, "missing");
EXPECT_GL_NO_ERROR();
EXPECT_EQ(GL_INVALID_INDEX, index);
index = glGetProgramResourceIndex(program, GL_PROGRAM_OUTPUT, "oColor");
EXPECT_GL_NO_ERROR();
EXPECT_NE(GL_INVALID_INDEX, index);
index = glGetProgramResourceIndex(program, GL_PROGRAM_OUTPUT, "missing");
EXPECT_GL_NO_ERROR();
EXPECT_EQ(GL_INVALID_INDEX, index);
index = glGetProgramResourceIndex(program, GL_ATOMIC_COUNTER_BUFFER, "missing");
EXPECT_GL_ERROR(GL_INVALID_ENUM);
}
ANGLE_INSTANTIATE_TEST(ProgramInterfaceTestES31, ES31_OPENGL(), ES31_D3D11(), ES31_OPENGLES());
} // anonymous 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