Commit 46eaa946 by Sami Väisänen Committed by Commit Bot

Support CHROMIUM_path_rendering fragment operations

This brings two new APIs, BindFragmentInputLocation and ProgramPathFragmentInputGen that together dictate how the fragment shader varyings are used. BUG=angleproject:1382 Change-Id: I4b52fd8a3555235a73aecd4f3dba2d500789cbb0 Reviewed-on: https://chromium-review.googlesource.com/357071Reviewed-by: 's avatarGeoff Lang <geofflang@chromium.org> Reviewed-by: 's avatarSami Väisänen <svaisanen@nvidia.com> Commit-Queue: Sami Väisänen <svaisanen@nvidia.com>
parent 5b130489
......@@ -971,6 +971,15 @@ typedef void(GL_APIENTRYP PFNGLSTENCILTHENCOVERSTROKEPATHINSTANCEDCHROMIUMPROC)(
GLenum coverMode,
GLenum transformType,
const GLfloat *transformValues);
typedef void(GL_APIENTRY PFNGLBINDFRAGMENTINPUTLOCATIONCHROMIUMPROC)(GLuint program,
GLint location,
const GLchar *name);
typedef void(GL_APIENTRYP PFNGLPROGRAMPATHFRAGMENTINPUTGENCHROMIUMPROC)(GLuint program,
GLint location,
GLenum genMode,
GLint components,
const GLfloat *coeffs);
#ifdef GL_GLEXT_PROTOTYPES
GL_APICALL void GL_APIENTRY glMatrixLoadfCHROMIUM(GLenum matrixMode, const GLfloat *m);
GL_APICALL void GL_APIENTRY glMatrixLoadIdentityCHROMIUM(GLenum matrixMode);
......@@ -1052,6 +1061,15 @@ glStencilThenCoverStrokePathInstancedCHROMIUM(GLsizei numPaths,
GLenum transformType,
const GLfloat *transformValues);
GL_APICALL void GL_APIENTRY glBindFragmentInputLocationCHROMIUM(GLuint program,
GLint location,
const GLchar *name);
GL_APICALL void GL_APIENTRY glProgramPathFragmentInputGenCHROMIUM(GLuint program,
GLint location,
GLenum genMode,
GLint components,
const GLfloat *coeffs);
#endif
#endif /* GL_CHROMIUM_path_rendering */
......
......@@ -34,10 +34,10 @@ class NonCopyable
};
extern const uintptr_t DirtyPointer;
}
} // namespace angle
template <typename T, size_t N>
inline size_t ArraySize(T(&)[N])
constexpr inline size_t ArraySize(T (&)[N])
{
return N;
}
......
......@@ -9,8 +9,9 @@
#include "string_utils.h"
#include <algorithm>
#include <stdlib.h>
#include <string.h>
#include <fstream>
#include <sstream>
......@@ -157,4 +158,25 @@ Optional<std::vector<wchar_t>> WidenString(size_t length, const char *cString)
return Optional<std::vector<wchar_t>>(wcstring);
}
bool BeginsWith(const std::string &str, const char *prefix)
{
return strncmp(str.c_str(), prefix, strlen(prefix)) == 0;
}
bool BeginsWith(const char *str, const char *prefix)
{
return strncmp(str, prefix, strlen(prefix)) == 0;
}
bool EndsWith(const std::string &str, const char *suffix)
{
const auto len = strlen(suffix);
if (len > str.size())
return false;
const char *end = str.c_str() + str.size() - len;
return memcmp(end, suffix, len) == 0;
}
} // namespace angle
......@@ -47,6 +47,21 @@ bool HexStringToUInt(const std::string &input, unsigned int *uintOut);
bool ReadFileToString(const std::string &path, std::string *stringOut);
Optional<std::vector<wchar_t>> WidenString(size_t length, const char *cString);
// Check if the string str begins with the given prefix.
// Prefix may not be NULL and needs to be NULL terminated.
// The comparison is case sensitive.
bool BeginsWith(const std::string &str, const char *prefix);
// Check if the string str begins with the given prefix.
// str and prefix may not be NULL and need to be NULL terminated.
// The comparison is case sensitive.
bool BeginsWith(const char *str, const char *prefix);
// Check if the string str ends with the given suffix.
// Suffix may not be NUL and needs to be NULL terminated.
// The comparison is case sensitive.
bool EndsWith(const std::string& str, const char* suffix);
}
#endif // LIBANGLE_STRING_UTILS_H_
......@@ -138,4 +138,26 @@ TEST(StringUtilsTest, HexStringToUIntBasic)
// Note: ReadFileToString is harder to test
TEST(StringUtilsTest, BeginsEndsWith)
{
ASSERT_FALSE(BeginsWith("foo", "bar"));
ASSERT_FALSE(BeginsWith("", "foo"));
ASSERT_FALSE(BeginsWith("foo", "foobar"));
ASSERT_TRUE(BeginsWith("foobar", "foo"));
ASSERT_TRUE(BeginsWith("foobar", ""));
ASSERT_TRUE(BeginsWith("foo", "foo"));
ASSERT_TRUE(BeginsWith("", ""));
ASSERT_FALSE(EndsWith("foo", "bar"));
ASSERT_FALSE(EndsWith("", "bar"));
ASSERT_FALSE(EndsWith("foo", "foobar"));
ASSERT_TRUE(EndsWith("foobar", "bar"));
ASSERT_TRUE(EndsWith("foobar", ""));
ASSERT_TRUE(EndsWith("bar", "bar"));
ASSERT_TRUE(EndsWith("", ""));
}
}
\ No newline at end of file
......@@ -1540,6 +1540,7 @@ void Context::coverFillPathInstanced(GLsizei numPaths,
mImplementation->coverFillPathInstanced(pathObjects, coverMode, transformType, transformValues);
}
void Context::coverStrokePathInstanced(GLsizei numPaths,
GLenum pathNameType,
const void *paths,
......@@ -1557,6 +1558,7 @@ void Context::coverStrokePathInstanced(GLsizei numPaths,
mImplementation->coverStrokePathInstanced(pathObjects, coverMode, transformType,
transformValues);
}
void Context::stencilFillPathInstanced(GLsizei numPaths,
GLenum pathNameType,
const void *paths,
......@@ -1575,6 +1577,7 @@ void Context::stencilFillPathInstanced(GLsizei numPaths,
mImplementation->stencilFillPathInstanced(pathObjects, fillMode, mask, transformType,
transformValues);
}
void Context::stencilStrokePathInstanced(GLsizei numPaths,
GLenum pathNameType,
const void *paths,
......@@ -1593,6 +1596,7 @@ void Context::stencilStrokePathInstanced(GLsizei numPaths,
mImplementation->stencilStrokePathInstanced(pathObjects, reference, mask, transformType,
transformValues);
}
void Context::stencilThenCoverFillPathInstanced(GLsizei numPaths,
GLenum pathNameType,
const void *paths,
......@@ -1612,6 +1616,7 @@ void Context::stencilThenCoverFillPathInstanced(GLsizei numPaths,
mImplementation->stencilThenCoverFillPathInstanced(pathObjects, coverMode, fillMode, mask,
transformType, transformValues);
}
void Context::stencilThenCoverStrokePathInstanced(GLsizei numPaths,
GLenum pathNameType,
const void *paths,
......@@ -1632,6 +1637,24 @@ void Context::stencilThenCoverStrokePathInstanced(GLsizei numPaths,
transformType, transformValues);
}
void Context::bindFragmentInputLocation(GLuint program, GLint location, const GLchar *name)
{
auto *programObject = getProgram(program);
programObject->bindFragmentInputLocation(location, name);
}
void Context::programPathFragmentInputGen(GLuint program,
GLint location,
GLenum genMode,
GLint components,
const GLfloat *coeffs)
{
auto *programObject = getProgram(program);
programObject->pathFragmentInputGen(location, genMode, components, coeffs);
}
void Context::handleError(const Error &error)
{
if (error.isError())
......
......@@ -539,6 +539,12 @@ class Context final : public ValidationContext
GLenum coverMode,
GLenum transformType,
const GLfloat *transformValues);
void bindFragmentInputLocation(GLuint program, GLint location, const GLchar *name);
void programPathFragmentInputGen(GLuint program,
GLint location,
GLenum genMode,
GLint components,
const GLfloat *coeffs);
void handleError(const Error &error) override;
......
......@@ -177,7 +177,7 @@ void InfoLog::getLog(GLsizei bufSize, GLsizei *length, char *infoLog) const
}
// append a santized message to the program info log.
// The D3D compiler includes a fake file path in some of the warning or error
// The D3D compiler includes a fake file path in some of the warning or error
// messages, so lets remove all occurrences of this fake file path from the log.
void InfoLog::appendSanitized(const char *message)
{
......@@ -440,6 +440,82 @@ void Program::bindUniformLocation(GLuint index, const char *name)
mUniformBindings.bindLocation(index, ParseUniformName(name, nullptr));
}
void Program::bindFragmentInputLocation(GLint index, const char *name)
{
mFragmentInputBindings.bindLocation(index, name);
}
BindingInfo Program::getFragmentInputBindingInfo(GLint index) const
{
BindingInfo ret;
ret.type = GL_NONE;
ret.valid = false;
const Shader *fragmentShader = mState.getAttachedFragmentShader();
ASSERT(fragmentShader);
// Find the actual fragment shader varying we're interested in
const std::vector<sh::Varying> &inputs = fragmentShader->getVaryings();
for (const auto &binding : mFragmentInputBindings)
{
if (binding.second != static_cast<GLuint>(index))
continue;
ret.valid = true;
std::string originalName = binding.first;
unsigned int index = ParseAndStripArrayIndex(&originalName);
for (const auto &in : inputs)
{
if (in.name == originalName)
{
if (in.isArray())
{
// The client wants to bind either "name" or "name[0]".
// GL ES 3.1 spec refers to active array names with language such as:
// "if the string identifies the base name of an active array, where the
// string would exactly match the name of the variable if the suffix "[0]"
// were appended to the string".
if (index == GL_INVALID_INDEX)
index = 0;
ret.name = in.mappedName + "[" + std::to_string(index) + "]";
}
else
{
ret.name = in.mappedName;
}
ret.type = in.type;
return ret;
}
}
}
return ret;
}
void Program::pathFragmentInputGen(GLint index,
GLenum genMode,
GLint components,
const GLfloat *coeffs)
{
// If the location is -1 then the command is silently ignored
if (index == -1)
return;
const auto &binding = getFragmentInputBindingInfo(index);
// If the input doesn't exist then then the command is silently ignored
// This could happen through optimization for example, the shader translator
// decides that a variable is not actually being used and optimizes it away.
if (binding.name.empty())
return;
mProgram->setPathFragmentInputGen(binding.name, genMode, components, coeffs);
}
// Links the HLSL code of the vertex and pixel shader by matching up their varyings,
// compiling them into binaries, determining the attribute mappings, and collecting
// a list of uniforms
......@@ -1623,16 +1699,17 @@ GLenum Program::getTransformFeedbackBufferMode() const
return mState.mTransformFeedbackBufferMode;
}
// static
bool Program::linkVaryings(InfoLog &infoLog,
const Shader *vertexShader,
const Shader *fragmentShader)
const Shader *fragmentShader) const
{
ASSERT(vertexShader->getShaderVersion() == fragmentShader->getShaderVersion());
const std::vector<sh::Varying> &vertexVaryings = vertexShader->getVaryings();
const std::vector<sh::Varying> &fragmentVaryings = fragmentShader->getVaryings();
std::map<GLuint, std::string> staticFragmentInputLocations;
for (const sh::Varying &output : fragmentVaryings)
{
bool matched = false;
......@@ -1665,6 +1742,29 @@ bool Program::linkVaryings(InfoLog &infoLog,
infoLog << "Fragment varying " << output.name << " does not match any vertex varying";
return false;
}
// Check for aliased path rendering input bindings (if any).
// If more than one binding refer statically to the same
// location the link must fail.
if (!output.staticUse)
continue;
const auto inputBinding = mFragmentInputBindings.getBinding(output.name);
if (inputBinding == -1)
continue;
const auto it = staticFragmentInputLocations.find(inputBinding);
if (it == std::end(staticFragmentInputLocations))
{
staticFragmentInputLocations.insert(std::make_pair(inputBinding, output.name));
}
else
{
infoLog << "Binding for fragment input " << output.name << " conflicts with "
<< it->second;
return false;
}
}
// TODO(jmadill): verify no unmatched vertex varyings?
......
......@@ -137,6 +137,24 @@ struct VariableLocation
bool ignored;
};
// Information about a variable binding.
// Currently used by CHROMIUM_path_rendering
struct BindingInfo
{
// The type of binding, for example GL_FLOAT_VEC3.
// This can be GL_NONE if the variable is optimized away.
GLenum type;
// This is the name of the variable in
// the translated shader program. Note that
// this can be empty in the case where the
// variable has been optimized away.
std::string name;
// True if the binding is valid, otherwise false.
bool valid;
};
class ProgramState final : angle::NonCopyable
{
public:
......@@ -229,6 +247,14 @@ class Program final : angle::NonCopyable, public LabeledObject
void bindAttributeLocation(GLuint index, const char *name);
void bindUniformLocation(GLuint index, const char *name);
// CHROMIUM_path_rendering
BindingInfo getFragmentInputBindingInfo(GLint index) const;
void bindFragmentInputLocation(GLint index, const char *name);
void pathFragmentInputGen(GLint index,
GLenum genMode,
GLint components,
const GLfloat *coeffs);
Error link(const ContextState &data);
bool isLinked() const;
......@@ -352,9 +378,7 @@ class Program final : angle::NonCopyable, public LabeledObject
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 linkVaryings(InfoLog &infoLog, const Shader *vertexShader, const Shader *fragmentShader) const;
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,
......@@ -430,6 +454,9 @@ class Program final : angle::NonCopyable, public LabeledObject
Bindings mAttributeBindings;
Bindings mUniformBindings;
// CHROMIUM_path_rendering
Bindings mFragmentInputBindings;
bool mLinked;
bool mDeleteStatus; // Flag to indicate that the program can be deleted when no longer in use
......
......@@ -79,6 +79,12 @@ class ProgramImpl : angle::NonCopyable
// Returns false for inactive members.
virtual bool getUniformBlockMemberInfo(const std::string &memberUniformName,
sh::BlockMemberInfo *memberInfoOut) const = 0;
// CHROMIUM_path_rendering
// Set parameters to control fragment shader input variable interpolation
virtual void setPathFragmentInputGen(const std::string &inputName,
GLenum genMode,
GLint components,
const GLfloat *coeffs) = 0;
protected:
const gl::ProgramState &mState;
......
......@@ -56,6 +56,8 @@ class MockProgramImpl : public rx::ProgramImpl
MOCK_METHOD2(setUniformBlockBinding, void(GLuint, GLuint));
MOCK_CONST_METHOD2(getUniformBlockSize, bool(const std::string &, size_t *));
MOCK_CONST_METHOD2(getUniformBlockMemberInfo, bool(const std::string &, sh::BlockMemberInfo *));
MOCK_METHOD4(setPathFragmentInputGen,
void(const std::string &, GLenum, GLint, const GLfloat *));
MOCK_METHOD0(destroy, void());
};
......
......@@ -2309,4 +2309,13 @@ bool ProgramD3D::getUniformBlockMemberInfo(const std::string &memberUniformName,
*memberInfoOut = infoIter->second;
return true;
}
void ProgramD3D::setPathFragmentInputGen(const std::string &inputName,
GLenum genMode,
GLint components,
const GLfloat *coeffs)
{
UNREACHABLE();
}
} // namespace rx
......@@ -174,6 +174,10 @@ class ProgramD3D : public ProgramImpl
bool getUniformBlockSize(const std::string &blockName, size_t *sizeOut) const override;
bool getUniformBlockMemberInfo(const std::string &memberUniformName,
sh::BlockMemberInfo *memberInfoOut) const override;
void setPathFragmentInputGen(const std::string &inputName,
GLenum genMode,
GLint components,
const GLfloat *coeffs) override;
void initializeUniformStorage();
gl::Error applyUniforms(GLenum drawMode);
......
......@@ -56,7 +56,8 @@ ShaderImpl *ContextGL::createShader(const gl::ShaderState &data)
ProgramImpl *ContextGL::createProgram(const gl::ProgramState &data)
{
return new ProgramGL(data, getFunctions(), getWorkaroundsGL(), getStateManager());
return new ProgramGL(data, getFunctions(), getWorkaroundsGL(), getStateManager(),
getExtensions().pathRendering);
}
FramebufferImpl *ContextGL::createFramebuffer(const gl::FramebufferState &data)
......
......@@ -439,6 +439,7 @@ FunctionsGL::FunctionsGL()
stencilStrokePathInstancedNV(nullptr),
stencilThenCoverFillPathInstancedNV(nullptr),
stencilThenCoverStrokePathInstancedNV(nullptr),
programPathFragmentInputGenNV(nullptr),
bindFragDataLocationIndexed(nullptr),
bindSampler(nullptr),
......@@ -846,10 +847,13 @@ void FunctionsGL::initializeProcsDesktopGL()
// Even though extensions are written against specific versions of GL, many drivers expose the extensions
// in even older versions. Always try loading the extensions regardless of GL version.
// EXT_direct_state_access (loading only functions relevant to GL_NV_path_rendering here)
AssignGLExtensionEntryPoint(extensions, "GL_EXT_direct_state_access", loadProcAddress("glMatrixLoadfEXT"), &matrixLoadEXT);
// GL_ARB_program_interface_query (loading only functions relevant to GL_NV_path_rendering here)
AssignGLExtensionEntryPoint(extensions, "GL_ARB_program_interface_query", loadProcAddress("glGetProgramInterfaceiv"), &getProgramInterfaceiv);
AssignGLExtensionEntryPoint(extensions, "GL_ARB_program_interface_query", loadProcAddress("glGetProgramResourceName"), &getProgramResourceName);
AssignGLExtensionEntryPoint(extensions, "GL_ARB_program_interface_query", loadProcAddress("glGetProgramResourceiv"), &getProgramResourceiv);
// GL_NV_path_rendering
AssignGLExtensionEntryPoint(extensions, "GL_NV_path_rendering", loadProcAddress("glMatrixLoadfEXT"), &matrixLoadEXT);
AssignGLExtensionEntryPoint(extensions, "GL_NV_path_rendering", loadProcAddress("glGenPathsNV"), &genPathsNV);
AssignGLExtensionEntryPoint(extensions, "GL_NV_path_rendering", loadProcAddress("glDeletePathsNV"), &delPathsNV);
AssignGLExtensionEntryPoint(extensions, "GL_NV_path_rendering", loadProcAddress("glPathCommandsNV"), &pathCommandsNV);
......@@ -871,7 +875,7 @@ void FunctionsGL::initializeProcsDesktopGL()
AssignGLExtensionEntryPoint(extensions, "GL_NV_path_rendering", loadProcAddress("glStencilStrokePathInstancedNV"), &stencilStrokePathInstancedNV);
AssignGLExtensionEntryPoint(extensions, "GL_NV_path_rendering", loadProcAddress("glStencilThenCoverFillPathInstancedNV"), &stencilThenCoverFillPathInstancedNV);
AssignGLExtensionEntryPoint(extensions, "GL_NV_path_rendering", loadProcAddress("glStencilThenCoverStrokePathInstancedNV"), &stencilThenCoverStrokePathInstancedNV);
AssignGLExtensionEntryPoint(extensions, "GL_NV_path_rendering", loadProcAddress("glProgramPathFragmentInputGenNV"), &programPathFragmentInputGenNV);
// GL_NV_framebuffer_mixed_samples
AssignGLExtensionEntryPoint(extensions, "GL_NV_framebuffer_mixed_samples", loadProcAddress("glCoverageModulationNV"), &coverageModulationNV);
......@@ -1771,10 +1775,9 @@ void FunctionsGL::initializeProcsGLES()
profile = 0;
// clang-format off
// EXT_direct_state_access (loading only functions relevant to GL_NV_path_rendering here)
AssignGLExtensionEntryPoint(extensions, "GL_EXT_direct_state_access", loadProcAddress("glMatrixLoadfEXT"), &matrixLoadEXT);
// GL_NV_path_rendering
AssignGLExtensionEntryPoint(extensions, "GL_NV_path_rendering", loadProcAddress("glMatrixLoadfEXT"), &matrixLoadEXT);
AssignGLExtensionEntryPoint(extensions, "GL_NV_path_rendering", loadProcAddress("glGenPathsNV"), &genPathsNV);
AssignGLExtensionEntryPoint(extensions, "GL_NV_path_rendering", loadProcAddress("glDeletePathsNV"), &delPathsNV);
AssignGLExtensionEntryPoint(extensions, "GL_NV_path_rendering", loadProcAddress("glPathCommandsNV"), &pathCommandsNV);
......@@ -1796,7 +1799,7 @@ void FunctionsGL::initializeProcsGLES()
AssignGLExtensionEntryPoint(extensions, "GL_NV_path_rendering", loadProcAddress("glStencilStrokePathInstancedNV"), &stencilStrokePathInstancedNV);
AssignGLExtensionEntryPoint(extensions, "GL_NV_path_rendering", loadProcAddress("glStencilThenCoverFillPathInstancedNV"), &stencilThenCoverFillPathInstancedNV);
AssignGLExtensionEntryPoint(extensions, "GL_NV_path_rendering", loadProcAddress("glStencilThenCoverStrokePathInstancedNV"), &stencilThenCoverStrokePathInstancedNV);
AssignGLExtensionEntryPoint(extensions, "GL_NV_path_rendering", loadProcAddress("glProgramPathFragmentInputGenNV"), &programPathFragmentInputGenNV);
// GL_OES_texture_3D
AssignGLExtensionEntryPoint(extensions, "GL_OES_texture_3D", loadProcAddress("glTexImage3DOES"), &texImage3D);
......
......@@ -392,10 +392,8 @@ class FunctionsGL
PFNGLTEXIMAGE3DMULTISAMPLEPROC texImage3DMultisample;
PFNGLWAITSYNCPROC waitSync;
// EXT_direct_state_access. Needed by NV_path_rendering.
PFNGLMATRIXLOADFEXTPROC matrixLoadEXT;
// NV_path_rendering (originally written against 3.2 compatibility profile)
PFNGLMATRIXLOADFEXTPROC matrixLoadEXT;
PFNGLGENPATHSNVPROC genPathsNV;
PFNGLDELETEPATHSNVPROC delPathsNV;
PFNGLPATHCOMMANDSNVPROC pathCommandsNV;
......@@ -405,20 +403,19 @@ class FunctionsGL
PFNGLGETPATHPARAMETERFVNVPROC getPathParameterfNV;
PFNGLGETPATHPARAMETERIVNVPROC getPathParameteriNV;
PFNGLPATHSTENCILFUNCNVPROC pathStencilFuncNV;
PFNGLSTENCILFILLPATHNVPROC stencilFillPathNV;
PFNGLSTENCILSTROKEPATHNVPROC stencilStrokePathNV;
PFNGLCOVERFILLPATHNVPROC coverFillPathNV;
PFNGLCOVERSTROKEPATHNVPROC coverStrokePathNV;
PFNGLSTENCILTHENCOVERFILLPATHNVPROC stencilThenCoverFillPathNV;
PFNGLSTENCILTHENCOVERSTROKEPATHNVPROC stencilThenCoverStrokePathNV;
PFNGLCOVERFILLPATHINSTANCEDNVPROC coverFillPathInstancedNV;
PFNGLCOVERSTROKEPATHINSTANCEDNVPROC coverStrokePathInstancedNV;
PFNGLSTENCILFILLPATHINSTANCEDNVPROC stencilFillPathInstancedNV;
PFNGLSTENCILSTROKEPATHINSTANCEDNVPROC stencilStrokePathInstancedNV;
PFNGLSTENCILTHENCOVERFILLPATHINSTANCEDNVPROC stencilThenCoverFillPathInstancedNV;
PFNGLSTENCILTHENCOVERSTROKEPATHINSTANCEDNVPROC stencilThenCoverStrokePathInstancedNV;
PFNGLPROGRAMPATHFRAGMENTINPUTGENNVPROC programPathFragmentInputGenNV;
// 3.3
PFNGLBINDFRAGDATALOCATIONINDEXEDPROC bindFragDataLocationIndexed;
......
......@@ -8,7 +8,9 @@
#include "libANGLE/renderer/gl/ProgramGL.h"
#include "common/angleutils.h"
#include "common/debug.h"
#include "common/string_utils.h"
#include "common/utilities.h"
#include "libANGLE/renderer/gl/FunctionsGL.h"
#include "libANGLE/renderer/gl/ShaderGL.h"
......@@ -23,11 +25,13 @@ namespace rx
ProgramGL::ProgramGL(const gl::ProgramState &data,
const FunctionsGL *functions,
const WorkaroundsGL &workarounds,
StateManagerGL *stateManager)
StateManagerGL *stateManager,
bool enablePathRendering)
: ProgramImpl(data),
mFunctions(functions),
mWorkarounds(workarounds),
mStateManager(stateManager),
mEnablePathRendering(enablePathRendering),
mProgramID(0)
{
ASSERT(mFunctions);
......@@ -382,6 +386,26 @@ bool ProgramGL::getUniformBlockMemberInfo(const std::string &memberUniformName,
return true;
}
void ProgramGL::setPathFragmentInputGen(const std::string &inputName,
GLenum genMode,
GLint components,
const GLfloat *coeffs)
{
ASSERT(mEnablePathRendering);
for (const auto &input : mPathRenderingFragmentInputs)
{
if (input.name == inputName)
{
mFunctions->programPathFragmentInputGenNV(mProgramID, input.location, genMode,
components, coeffs);
ASSERT(mFunctions->getError() == GL_NO_ERROR);
return;
}
}
}
void ProgramGL::preLink()
{
// Reset the program state
......@@ -389,6 +413,7 @@ void ProgramGL::preLink()
mUniformBlockRealLocationMap.clear();
mSamplerBindings.clear();
mUniformIndexToSamplerIndex.clear();
mPathRenderingFragmentInputs.clear();
}
bool ProgramGL::checkLinkStatus(gl::InfoLog &infoLog)
......@@ -478,6 +503,70 @@ void ProgramGL::postLink()
samplerBinding.boundTextureUnits.resize(linkedUniform.elementCount(), 0);
mSamplerBindings.push_back(samplerBinding);
}
// Discover CHROMIUM_path_rendering fragment inputs if enabled.
if (!mEnablePathRendering)
return;
GLint numFragmentInputs = 0;
mFunctions->getProgramInterfaceiv(mProgramID, GL_FRAGMENT_INPUT_NV, GL_ACTIVE_RESOURCES,
&numFragmentInputs);
if (numFragmentInputs <= 0)
return;
GLint maxNameLength = 0;
mFunctions->getProgramInterfaceiv(mProgramID, GL_FRAGMENT_INPUT_NV, GL_MAX_NAME_LENGTH,
&maxNameLength);
ASSERT(maxNameLength);
for (GLint i = 0; i < numFragmentInputs; ++i)
{
std::string name;
name.resize(maxNameLength);
GLsizei nameLen = 0;
mFunctions->getProgramResourceName(mProgramID, GL_FRAGMENT_INPUT_NV, i, maxNameLength,
&nameLen, &name[0]);
name.resize(nameLen);
// Ignore built-ins
if (angle::BeginsWith(name, "gl_"))
continue;
const GLenum kQueryProperties[] = {GL_LOCATION, GL_ARRAY_SIZE};
GLint queryResults[ArraySize(kQueryProperties)];
GLsizei queryLength = 0;
mFunctions->getProgramResourceiv(mProgramID, GL_FRAGMENT_INPUT_NV, i,
ArraySize(kQueryProperties), kQueryProperties,
ArraySize(queryResults), &queryLength, queryResults);
ASSERT(queryLength == ArraySize(kQueryProperties));
PathRenderingFragmentInput input;
input.name = name;
input.location = queryResults[0];
mPathRenderingFragmentInputs.push_back(std::move(input));
// If the input is an array it's denoted by [0] suffix on the variable
// name. We'll then create an entry per each array index where index > 0
if (angle::EndsWith(name, "[0]"))
{
// drop the suffix
name.resize(name.size() - 3);
const auto arraySize = queryResults[1];
const auto baseLocation = queryResults[0];
for (GLint i = 1; i < arraySize; ++i)
{
PathRenderingFragmentInput input;
input.name = name + "[" + std::to_string(i) + "]";
input.location = baseLocation + i;
mPathRenderingFragmentInputs.push_back(std::move(input));
}
}
}
}
} // namespace rx
......@@ -9,6 +9,9 @@
#ifndef LIBANGLE_RENDERER_GL_PROGRAMGL_H_
#define LIBANGLE_RENDERER_GL_PROGRAMGL_H_
#include <string>
#include <vector>
#include "libANGLE/renderer/gl/WorkaroundsGL.h"
#include "libANGLE/renderer/ProgramImpl.h"
......@@ -30,7 +33,8 @@ class ProgramGL : public ProgramImpl
ProgramGL(const gl::ProgramState &data,
const FunctionsGL *functions,
const WorkaroundsGL &workarounds,
StateManagerGL *stateManager);
StateManagerGL *stateManager,
bool enablePathRendering);
~ProgramGL() override;
LinkResult load(gl::InfoLog &infoLog, gl::BinaryInputStream *stream) override;
......@@ -68,6 +72,11 @@ class ProgramGL : public ProgramImpl
bool getUniformBlockMemberInfo(const std::string &memberUniformName,
sh::BlockMemberInfo *memberInfoOut) const override;
void setPathFragmentInputGen(const std::string &inputName,
GLenum genMode,
GLint components,
const GLfloat *coeffs) override;
GLuint getProgramID() const;
const std::vector<SamplerBindingGL> &getAppliedSamplerUniforms() const;
......@@ -92,6 +101,15 @@ class ProgramGL : public ProgramImpl
// A map from a mData.getUniforms() index to a mSamplerBindings index.
std::vector<size_t> mUniformIndexToSamplerIndex;
struct PathRenderingFragmentInput
{
std::string name;
GLint location;
};
std::vector<PathRenderingFragmentInput> mPathRenderingFragmentInputs;
bool mEnablePathRendering;
GLuint mProgramID;
};
......
......@@ -682,13 +682,20 @@ void GenerateCaps(const FunctionsGL *functions, gl::Caps *caps, gl::TextureCapsM
functions->hasGLExtension("GL_NV_framebuffer_mixed_samples") ||
functions->hasGLESExtension("GL_NV_framebuffer_mixed_samples");
// if NV_path_rendering is to be supported then EXT_direct_state_access
// must also be available. NV_path_rendering needs some of the matrix loads
// from this extension.
extensions->pathRendering = (functions->hasGLExtension("GL_NV_path_rendering") &&
functions->hasGLExtension("GL_EXT_direct_state_access")) ||
(functions->hasGLESExtension("GL_NV_path_rendering") &&
functions->hasGLESExtension("GL_EXT_direct_state_access"));
// NV_path_rendering
// We also need interface query which is available in
// >= 4.3 core or ARB_interface_query or >= GLES 3.1
const bool canEnableGLPathRendering =
functions->hasGLExtension("GL_NV_path_rendering") &&
(functions->hasGLExtension("GL_ARB_program_interface_query") ||
functions->isAtLeastGL(gl::Version(4, 3)));
const bool canEnableESPathRendering =
functions->hasGLESExtension("GL_NV_path_rendering") &&
functions->isAtLeastGLES(gl::Version(3, 1));
extensions->pathRendering = canEnableGLPathRendering || canEnableESPathRendering;
}
void GenerateWorkarounds(const FunctionsGL *functions, WorkaroundsGL *workarounds)
......
......@@ -201,4 +201,12 @@ bool ProgramVk::getUniformBlockMemberInfo(const std::string &memberUniformName,
return bool();
}
void ProgramVk::setPathFragmentInputGen(const std::string &inputName,
GLenum genMode,
GLint components,
const GLfloat *coeffs)
{
UNIMPLEMENTED();
}
} // namespace rx
......@@ -88,6 +88,11 @@ class ProgramVk : public ProgramImpl
// Returns false for inactive members.
bool getUniformBlockMemberInfo(const std::string &memberUniformName,
sh::BlockMemberInfo *memberInfoOut) const override;
void setPathFragmentInputGen(const std::string &inputName,
GLenum genMode,
GLint components,
const GLfloat *coeffs) override;
};
} // namespace rx
......
......@@ -21,6 +21,7 @@
#include "libANGLE/Uniform.h"
#include "common/mathutil.h"
#include "common/string_utils.h"
#include "common/utilities.h"
namespace gl
......@@ -2874,4 +2875,146 @@ bool ValidateStencilThenCoverStrokePathInstanced(Context *context,
return true;
}
bool ValidateBindFragmentInputLocation(Context *context,
GLuint program,
GLint location,
const GLchar *name)
{
if (!context->getExtensions().pathRendering)
{
context->handleError(
Error(GL_INVALID_OPERATION, "GL_CHROMIUM_path_rendering is not available."));
return false;
}
const GLint MaxLocation = context->getCaps().maxVaryingVectors * 4;
if (location >= MaxLocation)
{
context->handleError(Error(GL_INVALID_VALUE, "Location exceeds max varying."));
return false;
}
const auto *programObject = context->getProgram(program);
if (!programObject)
{
context->handleError(Error(GL_INVALID_OPERATION, "No such program."));
return false;
}
if (!name)
{
context->handleError(Error(GL_INVALID_VALUE, "No name given."));
return false;
}
if (angle::BeginsWith(name, "gl_"))
{
context->handleError(Error(GL_INVALID_OPERATION, "Cannot bind a built-in variable."));
return false;
}
return true;
}
bool ValidateProgramPathFragmentInputGen(Context *context,
GLuint program,
GLint location,
GLenum genMode,
GLint components,
const GLfloat *coeffs)
{
if (!context->getExtensions().pathRendering)
{
context->handleError(
Error(GL_INVALID_OPERATION, "GL_CHROMIUM_path_rendering is not available."));
return false;
}
const auto *programObject = context->getProgram(program);
if (!programObject || programObject->isFlaggedForDeletion())
{
context->handleError(Error(GL_INVALID_OPERATION, "No such program."));
return false;
}
if (!programObject->isLinked())
{
context->handleError(Error(GL_INVALID_OPERATION, "Program is not linked."));
return false;
}
switch (genMode)
{
case GL_NONE:
if (components != 0)
{
context->handleError(Error(GL_INVALID_VALUE, "Invalid components."));
return false;
}
break;
case GL_OBJECT_LINEAR_CHROMIUM:
case GL_EYE_LINEAR_CHROMIUM:
case GL_CONSTANT_CHROMIUM:
if (components < 1 || components > 4)
{
context->handleError(Error(GL_INVALID_VALUE, "Invalid components."));
return false;
}
if (!coeffs)
{
context->handleError(Error(GL_INVALID_VALUE, "No coefficients array given."));
return false;
}
break;
default:
context->handleError(Error(GL_INVALID_ENUM, "Invalid gen mode."));
return false;
}
// If the location is -1 then the command is silently ignored
// and no further validation is needed.
if (location == -1)
return true;
const auto &binding = programObject->getFragmentInputBindingInfo(location);
if (!binding.valid)
{
context->handleError(Error(GL_INVALID_OPERATION, "No such binding."));
return false;
}
if (binding.type != GL_NONE)
{
GLint expectedComponents = 0;
switch (binding.type)
{
case GL_FLOAT:
expectedComponents = 1;
break;
case GL_FLOAT_VEC2:
expectedComponents = 2;
break;
case GL_FLOAT_VEC3:
expectedComponents = 3;
break;
case GL_FLOAT_VEC4:
expectedComponents = 4;
break;
default:
context->handleError(Error(GL_INVALID_OPERATION,
"Fragment input type is not a floating point scalar or vector."));
return false;
}
if (expectedComponents != components && genMode != GL_NONE)
{
context->handleError(Error(GL_INVALID_OPERATION, "Unexpected number of components"));
return false;
}
}
return true;
}
} // namespace gl
......@@ -273,6 +273,16 @@ bool ValidateStencilThenCoverStrokePathInstanced(Context *context,
GLenum coverMode,
GLenum transformType,
const GLfloat *transformValues);
bool ValidateBindFragmentInputLocation(Context *context,
GLuint program,
GLint location,
const GLchar *name);
bool ValidateProgramPathFragmentInputGen(Context *context,
GLuint program,
GLint location,
GLenum genMode,
GLint components,
const GLfloat *coeffs);
} // namespace gl
......
......@@ -1847,4 +1847,47 @@ StencilThenCoverStrokePathInstancedCHROMIUM(GLsizei numPaths,
}
}
ANGLE_EXPORT void GL_APIENTRY BindFragmentInputLocationCHROMIUM(GLuint program,
GLint location,
const GLchar *name)
{
EVENT("(GLuint program = %u, GLint location = %d, const GLchar *name = %p)", program, location,
name);
Context *context = GetValidGlobalContext();
if (context)
{
if (!context->skipValidation() &&
!ValidateBindFragmentInputLocation(context, program, location, name))
{
return;
}
context->bindFragmentInputLocation(program, location, name);
}
}
ANGLE_EXPORT void GL_APIENTRY ProgramPathFragmentInputGenCHROMIUM(GLuint program,
GLint location,
GLenum genMode,
GLint components,
const GLfloat *coeffs)
{
EVENT(
"(GLuint program = %u, GLint location %d, GLenum genMode = %u, GLint components = %d, "
"const GLfloat * coeffs = %p)",
program, location, genMode, components, coeffs);
Context *context = GetValidGlobalContext();
if (context)
{
if (!context->skipValidation() &&
!ValidateProgramPathFragmentInputGen(context, program, location, genMode, components,
coeffs))
{
return;
}
context->programPathFragmentInputGen(program, location, genMode, components, coeffs);
}
}
} // gl
......@@ -229,6 +229,14 @@ StencilThenCoverStrokePathInstancedCHROMIUM(GLsizei numPaths,
GLenum coverMode,
GLenum transformType,
const GLfloat *transformValues);
ANGLE_EXPORT void GL_APIENTRY BindFragmentInputLocationCHROMIUM(GLuint program,
GLint location,
const GLchar *name);
ANGLE_EXPORT void GL_APIENTRY ProgramPathFragmentInputGenCHROMIUM(GLuint program,
GLint location,
GLenum genMode,
GLint components,
const GLfloat *coeffs);
} // namespace gl
......
......@@ -1748,4 +1748,20 @@ void GL_APIENTRY glStencilThenCoverStrokePathInstancedCHROMIUM(GLsizei numPaths,
gl::StencilThenCoverStrokePathInstancedCHROMIUM(numPaths, pathNameType, paths, pathBase,
reference, mask, coverMode, transformType,
transformValues);
}
void GL_APIENTRY glBindFragmentInputLocationCHROMIUM(GLuint program,
GLint location,
const GLchar *name)
{
gl::BindFragmentInputLocationCHROMIUM(program, location, name);
}
void GL_APIENTRY glProgramPathFragmentInputGenCHROMIUM(GLuint program,
GLint location,
GLenum genMode,
GLint components,
const GLfloat *coeffs)
{
gl::ProgramPathFragmentInputGenCHROMIUM(program, location, genMode, components, coeffs);
}
\ No newline at end of file
......@@ -228,6 +228,8 @@ EXPORTS
glStencilFillPathInstancedCHROMIUM @340
glStencilThenCoverFillPathInstancedCHROMIUM @341
glStencilThenCoverStrokePathInstancedCHROMIUM @342
glBindFragmentInputLocationCHROMIUM @343
glProgramPathFragmentInputGenCHROMIUM @344
; GLES 3.0 Functions
glReadBuffer @180
......
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