Commit 05b6b7fc by Zhenyao Mo

Add an SH_GLSL_CORE_OUTPUT profile.

So we could generate shaders for Apple using core GL profile. By switching to core profile, we still pass most WebGL conformance tests 1.0.2 on Linux, but not all, so apparently more work is needed. However, I think it's OK to check this CL in because this output profile will be only used behind a chromium switch. BUG=angleproject:933 TEST=webgl conformance tests Change-Id: Iad70e1aebf82349d3fc5f4116c1d6bc4448193fd Reviewed-on: https://chromium-review.googlesource.com/255282Tested-by: 's avatarZhenyao Mo <zmo@chromium.org> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org>
parent 7749005a
...@@ -48,7 +48,7 @@ typedef unsigned int GLenum; ...@@ -48,7 +48,7 @@ typedef unsigned int GLenum;
// Version number for shader translation API. // Version number for shader translation API.
// It is incremented every time the API changes. // It is incremented every time the API changes.
#define ANGLE_SH_VERSION 133 #define ANGLE_SH_VERSION 134
typedef enum { typedef enum {
SH_GLES2_SPEC = 0x8B40, SH_GLES2_SPEC = 0x8B40,
...@@ -81,13 +81,16 @@ typedef enum { ...@@ -81,13 +81,16 @@ typedef enum {
} ShShaderSpec; } ShShaderSpec;
typedef enum { typedef enum {
SH_ESSL_OUTPUT = 0x8B45, SH_ESSL_OUTPUT = 0x8B45,
SH_GLSL_OUTPUT = 0x8B46, // SH_GLSL_OUTPUT is deprecated. This is to not break the build.
SH_GLSL_OUTPUT = 0x8B46,
SH_GLSL_COMPATIBILITY_OUTPUT = 0x8B46,
SH_GLSL_CORE_OUTPUT = 0x8B47,
// HLSL output only supported in some configurations. // HLSL output only supported in some configurations.
SH_HLSL_OUTPUT = 0x8B47, SH_HLSL_OUTPUT = 0x8B48,
SH_HLSL9_OUTPUT = 0x8B47, SH_HLSL9_OUTPUT = 0x8B48,
SH_HLSL11_OUTPUT = 0x8B48 SH_HLSL11_OUTPUT = 0x8B49
} ShShaderOutput; } ShShaderOutput;
// Compile options. // Compile options.
......
...@@ -19,12 +19,13 @@ TCompiler* ConstructCompiler( ...@@ -19,12 +19,13 @@ TCompiler* ConstructCompiler(
sh::GLenum type, ShShaderSpec spec, ShShaderOutput output) sh::GLenum type, ShShaderSpec spec, ShShaderOutput output)
{ {
switch (output) { switch (output) {
case SH_ESSL_OUTPUT: case SH_ESSL_OUTPUT:
return new TranslatorESSL(type, spec); return new TranslatorESSL(type, spec);
case SH_GLSL_OUTPUT: case SH_GLSL_CORE_OUTPUT:
return new TranslatorGLSL(type, spec); case SH_GLSL_COMPATIBILITY_OUTPUT:
case SH_HLSL9_OUTPUT: return new TranslatorGLSL(type, spec, output);
case SH_HLSL11_OUTPUT: case SH_HLSL9_OUTPUT:
case SH_HLSL11_OUTPUT:
#ifdef ANGLE_ENABLE_HLSL #ifdef ANGLE_ENABLE_HLSL
return new TranslatorHLSL(type, spec, output); return new TranslatorHLSL(type, spec, output);
#else #else
...@@ -32,7 +33,7 @@ TCompiler* ConstructCompiler( ...@@ -32,7 +33,7 @@ TCompiler* ConstructCompiler(
// configuration. Return NULL per the ShConstructCompiler API. // configuration. Return NULL per the ShConstructCompiler API.
return NULL; return NULL;
#endif // ANGLE_ENABLE_HLSL #endif // ANGLE_ENABLE_HLSL
default: default:
// Unknown format. Return NULL per the ShConstructCompiler API. // Unknown format. Return NULL per the ShConstructCompiler API.
return NULL; return NULL;
} }
......
...@@ -510,7 +510,9 @@ bool EmulatePrecision::visitUnary(Visit visit, TIntermUnary *node) ...@@ -510,7 +510,9 @@ bool EmulatePrecision::visitUnary(Visit visit, TIntermUnary *node)
void EmulatePrecision::writeEmulationHelpers(TInfoSinkBase& sink, ShShaderOutput outputLanguage) void EmulatePrecision::writeEmulationHelpers(TInfoSinkBase& sink, ShShaderOutput outputLanguage)
{ {
// Other languages not yet supported // Other languages not yet supported
ASSERT(outputLanguage == SH_GLSL_OUTPUT || outputLanguage == SH_ESSL_OUTPUT); ASSERT(outputLanguage == SH_GLSL_COMPATIBILITY_OUTPUT ||
outputLanguage == SH_GLSL_CORE_OUTPUT ||
outputLanguage == SH_ESSL_OUTPUT);
writeCommonPrecisionEmulationHelpers(sink, outputLanguage); writeCommonPrecisionEmulationHelpers(sink, outputLanguage);
EmulationSet::const_iterator it; EmulationSet::const_iterator it;
......
...@@ -6,14 +6,20 @@ ...@@ -6,14 +6,20 @@
#include "compiler/translator/OutputESSL.h" #include "compiler/translator/OutputESSL.h"
TOutputESSL::TOutputESSL(TInfoSinkBase& objSink, TOutputESSL::TOutputESSL(TInfoSinkBase &objSink,
ShArrayIndexClampingStrategy clampingStrategy, ShArrayIndexClampingStrategy clampingStrategy,
ShHashFunction64 hashFunction, ShHashFunction64 hashFunction,
NameMap& nameMap, NameMap &nameMap,
TSymbolTable& symbolTable, TSymbolTable &symbolTable,
int shaderVersion, int shaderVersion,
bool forceHighp) bool forceHighp)
: TOutputGLSLBase(objSink, clampingStrategy, hashFunction, nameMap, symbolTable, shaderVersion), : TOutputGLSLBase(objSink,
clampingStrategy,
hashFunction,
nameMap,
symbolTable,
shaderVersion,
SH_ESSL_OUTPUT),
mForceHighp(forceHighp) mForceHighp(forceHighp)
{ {
} }
......
...@@ -11,8 +11,15 @@ TOutputGLSL::TOutputGLSL(TInfoSinkBase& objSink, ...@@ -11,8 +11,15 @@ TOutputGLSL::TOutputGLSL(TInfoSinkBase& objSink,
ShHashFunction64 hashFunction, ShHashFunction64 hashFunction,
NameMap& nameMap, NameMap& nameMap,
TSymbolTable& symbolTable, TSymbolTable& symbolTable,
int shaderVersion) int shaderVersion,
: TOutputGLSLBase(objSink, clampingStrategy, hashFunction, nameMap, symbolTable, shaderVersion) ShShaderOutput output)
: TOutputGLSLBase(objSink,
clampingStrategy,
hashFunction,
nameMap,
symbolTable,
shaderVersion,
output)
{ {
} }
...@@ -21,21 +28,30 @@ bool TOutputGLSL::writeVariablePrecision(TPrecision) ...@@ -21,21 +28,30 @@ bool TOutputGLSL::writeVariablePrecision(TPrecision)
return false; return false;
} }
void TOutputGLSL::visitSymbol(TIntermSymbol* node) void TOutputGLSL::visitSymbol(TIntermSymbol *node)
{ {
TInfoSinkBase& out = objSink(); TInfoSinkBase& out = objSink();
if (node->getSymbol() == "gl_FragDepthEXT") const TString &symbol = node->getSymbol();
if (symbol == "gl_FragDepthEXT")
{ {
out << "gl_FragDepth"; out << "gl_FragDepth";
} }
else if (symbol == "gl_FragColor" && getShaderOutput() == SH_GLSL_CORE_OUTPUT)
{
out << "webgl_FragColor";
}
else if (symbol == "gl_FragData" && getShaderOutput() == SH_GLSL_CORE_OUTPUT)
{
out << "webgl_FragData";
}
else else
{ {
TOutputGLSLBase::visitSymbol(node); TOutputGLSLBase::visitSymbol(node);
} }
} }
TString TOutputGLSL::translateTextureFunction(TString& name) TString TOutputGLSL::translateTextureFunction(TString &name)
{ {
static const char *simpleRename[] = { static const char *simpleRename[] = {
"texture2DLodEXT", "texture2DLod", "texture2DLodEXT", "texture2DLod",
...@@ -46,10 +62,30 @@ TString TOutputGLSL::translateTextureFunction(TString& name) ...@@ -46,10 +62,30 @@ TString TOutputGLSL::translateTextureFunction(TString& name)
"textureCubeGradEXT", "textureCubeGradARB", "textureCubeGradEXT", "textureCubeGradARB",
NULL, NULL NULL, NULL
}; };
static const char *legacyToCoreRename[] = {
"texture2D", "texture",
"texture2DProj", "textureProj",
"texture2DLod", "textureLod",
"texture2DProjLod", "textureProjLod",
"textureCube", "texture",
"textureCubeLod", "textureLod",
// Extensions
"texture2DLodEXT", "textureLod",
"texture2DProjLodEXT", "textureProjLod",
"textureCubeLodEXT", "textureLod",
"texture2DGradEXT", "textureGrad",
"texture2DProjGradEXT", "textureProjGrad",
"textureCubeGradEXT", "textureGrad",
NULL, NULL
};
const char **mapping = (getShaderOutput() == SH_GLSL_CORE_OUTPUT) ?
legacyToCoreRename : simpleRename;
for (int i = 0; simpleRename[i] != NULL; i += 2) { for (int i = 0; mapping[i] != NULL; i += 2)
if (name == simpleRename[i]) { {
return simpleRename[i+1]; if (name == mapping[i])
{
return mapping[i+1];
} }
} }
......
...@@ -11,15 +11,16 @@ ...@@ -11,15 +11,16 @@
class TOutputGLSL : public TOutputGLSLBase class TOutputGLSL : public TOutputGLSLBase
{ {
public: public:
TOutputGLSL(TInfoSinkBase& objSink, TOutputGLSL(TInfoSinkBase& objSink,
ShArrayIndexClampingStrategy clampingStrategy, ShArrayIndexClampingStrategy clampingStrategy,
ShHashFunction64 hashFunction, ShHashFunction64 hashFunction,
NameMap& nameMap, NameMap& nameMap,
TSymbolTable& symbolTable, TSymbolTable& symbolTable,
int shaderVersion); int shaderVersion,
ShShaderOutput output);
protected: protected:
virtual bool writeVariablePrecision(TPrecision); virtual bool writeVariablePrecision(TPrecision);
virtual void visitSymbol(TIntermSymbol* node); virtual void visitSymbol(TIntermSymbol* node);
virtual TString translateTextureFunction(TString& name); virtual TString translateTextureFunction(TString& name);
......
...@@ -46,6 +46,7 @@ bool isSingleStatement(TIntermNode *node) ...@@ -46,6 +46,7 @@ bool isSingleStatement(TIntermNode *node)
} }
return true; return true;
} }
} // namespace } // namespace
TOutputGLSLBase::TOutputGLSLBase(TInfoSinkBase &objSink, TOutputGLSLBase::TOutputGLSLBase(TInfoSinkBase &objSink,
...@@ -53,7 +54,8 @@ TOutputGLSLBase::TOutputGLSLBase(TInfoSinkBase &objSink, ...@@ -53,7 +54,8 @@ TOutputGLSLBase::TOutputGLSLBase(TInfoSinkBase &objSink,
ShHashFunction64 hashFunction, ShHashFunction64 hashFunction,
NameMap &nameMap, NameMap &nameMap,
TSymbolTable &symbolTable, TSymbolTable &symbolTable,
int shaderVersion) int shaderVersion,
ShShaderOutput output)
: TIntermTraverser(true, true, true), : TIntermTraverser(true, true, true),
mObjSink(objSink), mObjSink(objSink),
mDeclaringVariables(false), mDeclaringVariables(false),
...@@ -61,7 +63,8 @@ TOutputGLSLBase::TOutputGLSLBase(TInfoSinkBase &objSink, ...@@ -61,7 +63,8 @@ TOutputGLSLBase::TOutputGLSLBase(TInfoSinkBase &objSink,
mHashFunction(hashFunction), mHashFunction(hashFunction),
mNameMap(nameMap), mNameMap(nameMap),
mSymbolTable(symbolTable), mSymbolTable(symbolTable),
mShaderVersion(shaderVersion) mShaderVersion(shaderVersion),
mOutput(output)
{ {
} }
...@@ -91,7 +94,34 @@ void TOutputGLSLBase::writeVariableType(const TType &type) ...@@ -91,7 +94,34 @@ void TOutputGLSLBase::writeVariableType(const TType &type)
TQualifier qualifier = type.getQualifier(); TQualifier qualifier = type.getQualifier();
if (qualifier != EvqTemporary && qualifier != EvqGlobal) if (qualifier != EvqTemporary && qualifier != EvqGlobal)
{ {
out << type.getQualifierString() << " "; if (mOutput == SH_GLSL_CORE_OUTPUT)
{
switch (qualifier)
{
case EvqAttribute:
out << "in" << " ";
break;
case EvqVaryingIn:
out << "in" << " ";
break;
case EvqVaryingOut:
out << "out" << " ";
break;
case EvqInvariantVaryingIn:
out << "invariant in" << " ";
break;
case EvqInvariantVaryingOut:
out << "invariant out" << " ";
break;
default:
out << type.getQualifierString() << " ";
break;
}
}
else
{
out << type.getQualifierString() << " ";
}
} }
// Declare the struct if we have not done so already. // Declare the struct if we have not done so already.
if (type.getBasicType() == EbtStruct && !structDeclared(type.getStruct())) if (type.getBasicType() == EbtStruct && !structDeclared(type.getStruct()))
......
...@@ -21,7 +21,13 @@ class TOutputGLSLBase : public TIntermTraverser ...@@ -21,7 +21,13 @@ class TOutputGLSLBase : public TIntermTraverser
ShHashFunction64 hashFunction, ShHashFunction64 hashFunction,
NameMap &nameMap, NameMap &nameMap,
TSymbolTable& symbolTable, TSymbolTable& symbolTable,
int shaderVersion); int shaderVersion,
ShShaderOutput output);
ShShaderOutput getShaderOutput() const
{
return mOutput;
}
protected: protected:
TInfoSinkBase &objSink() { return mObjSink; } TInfoSinkBase &objSink() { return mObjSink; }
...@@ -80,6 +86,8 @@ class TOutputGLSLBase : public TIntermTraverser ...@@ -80,6 +86,8 @@ class TOutputGLSLBase : public TIntermTraverser
TSymbolTable &mSymbolTable; TSymbolTable &mSymbolTable;
const int mShaderVersion; const int mShaderVersion;
ShShaderOutput mOutput;
}; };
#endif // COMPILER_TRANSLATOR_OUTPUTGLSLBASE_H_ #endif // COMPILER_TRANSLATOR_OUTPUTGLSLBASE_H_
...@@ -11,7 +11,8 @@ ...@@ -11,7 +11,8 @@
#include "angle_gl.h" #include "angle_gl.h"
TranslatorESSL::TranslatorESSL(sh::GLenum type, ShShaderSpec spec) TranslatorESSL::TranslatorESSL(sh::GLenum type, ShShaderSpec spec)
: TCompiler(type, spec, SH_ESSL_OUTPUT) { : TCompiler(type, spec, SH_ESSL_OUTPUT)
{
} }
void TranslatorESSL::translate(TIntermNode *root, int) { void TranslatorESSL::translate(TIntermNode *root, int) {
...@@ -40,7 +41,13 @@ void TranslatorESSL::translate(TIntermNode *root, int) { ...@@ -40,7 +41,13 @@ void TranslatorESSL::translate(TIntermNode *root, int) {
getArrayBoundsClamper().OutputClampingFunctionDefinition(sink); getArrayBoundsClamper().OutputClampingFunctionDefinition(sink);
// Write translated shader. // Write translated shader.
TOutputESSL outputESSL(sink, getArrayIndexClampingStrategy(), getHashFunction(), getNameMap(), getSymbolTable(), getShaderVersion(), precisionEmulation); TOutputESSL outputESSL(sink,
getArrayIndexClampingStrategy(),
getHashFunction(),
getNameMap(),
getSymbolTable(),
getShaderVersion(),
precisionEmulation);
root->traverse(&outputESSL); root->traverse(&outputESSL);
} }
......
...@@ -9,14 +9,15 @@ ...@@ -9,14 +9,15 @@
#include "compiler/translator/Compiler.h" #include "compiler/translator/Compiler.h"
class TranslatorESSL : public TCompiler { class TranslatorESSL : public TCompiler
public: {
public:
TranslatorESSL(sh::GLenum type, ShShaderSpec spec); TranslatorESSL(sh::GLenum type, ShShaderSpec spec);
protected: protected:
virtual void translate(TIntermNode *root, int compileOptions); virtual void translate(TIntermNode *root, int compileOptions);
private: private:
void writeExtensionBehavior(); void writeExtensionBehavior();
}; };
......
...@@ -6,13 +6,59 @@ ...@@ -6,13 +6,59 @@
#include "compiler/translator/TranslatorGLSL.h" #include "compiler/translator/TranslatorGLSL.h"
#include "angle_gl.h"
#include "compiler/translator/EmulatePrecision.h" #include "compiler/translator/EmulatePrecision.h"
#include "compiler/translator/OutputGLSL.h" #include "compiler/translator/OutputGLSL.h"
#include "compiler/translator/VersionGLSL.h" #include "compiler/translator/VersionGLSL.h"
namespace
{
// To search for what output variables are used in a fragment shader.
// We handle gl_FragColor and gl_FragData at the moment.
class TFragmentOutSearcher : public TIntermTraverser
{
public:
TFragmentOutSearcher()
: mUsesGlFragColor(false),
mUsesGlFragData(false)
{
}
bool usesGlFragColor() const
{
return mUsesGlFragColor;
}
bool usesGlFragData() const
{
return mUsesGlFragData;
}
TranslatorGLSL::TranslatorGLSL(sh::GLenum type, ShShaderSpec spec) protected:
: TCompiler(type, spec, SH_GLSL_OUTPUT) { virtual void visitSymbol(TIntermSymbol *node) override
{
if (node->getSymbol() == "gl_FragColor")
{
mUsesGlFragColor = true;
}
else if (node->getSymbol() == "gl_FragData")
{
mUsesGlFragData = true;
}
}
private:
bool mUsesGlFragColor;
bool mUsesGlFragData;
};
} // namespace anonymous
TranslatorGLSL::TranslatorGLSL(sh::GLenum type,
ShShaderSpec spec,
ShShaderOutput output)
: TCompiler(type, spec, output) {
} }
void TranslatorGLSL::translate(TIntermNode *root, int) { void TranslatorGLSL::translate(TIntermNode *root, int) {
...@@ -33,7 +79,7 @@ void TranslatorGLSL::translate(TIntermNode *root, int) { ...@@ -33,7 +79,7 @@ void TranslatorGLSL::translate(TIntermNode *root, int) {
EmulatePrecision emulatePrecision; EmulatePrecision emulatePrecision;
root->traverse(&emulatePrecision); root->traverse(&emulatePrecision);
emulatePrecision.updateTree(); emulatePrecision.updateTree();
emulatePrecision.writeEmulationHelpers(sink, SH_GLSL_OUTPUT); emulatePrecision.writeEmulationHelpers(sink, getOutputType());
} }
// Write emulated built-in functions if needed. // Write emulated built-in functions if needed.
...@@ -43,14 +89,38 @@ void TranslatorGLSL::translate(TIntermNode *root, int) { ...@@ -43,14 +89,38 @@ void TranslatorGLSL::translate(TIntermNode *root, int) {
// Write array bounds clamping emulation if needed. // Write array bounds clamping emulation if needed.
getArrayBoundsClamper().OutputClampingFunctionDefinition(sink); getArrayBoundsClamper().OutputClampingFunctionDefinition(sink);
// Declare gl_FragColor and glFragData as webgl_FragColor and webgl_FragData
// if it's core profile shaders and they are used.
if (getShaderType() == GL_FRAGMENT_SHADER &&
getOutputType() == SH_GLSL_CORE_OUTPUT)
{
TFragmentOutSearcher searcher;
root->traverse(&searcher);
ASSERT(!(searcher.usesGlFragData() && searcher.usesGlFragColor()));
if (searcher.usesGlFragColor())
{
sink << "out vec4 webgl_FragColor;\n";
}
if (searcher.usesGlFragData())
{
sink << "out vec4 webgl_FragData[gl_MaxDrawBuffers];\n";
}
}
// Write translated shader. // Write translated shader.
TOutputGLSL outputGLSL(sink, getArrayIndexClampingStrategy(), getHashFunction(), getNameMap(), getSymbolTable(), getShaderVersion()); TOutputGLSL outputGLSL(sink,
getArrayIndexClampingStrategy(),
getHashFunction(),
getNameMap(),
getSymbolTable(),
getShaderVersion(),
getOutputType());
root->traverse(&outputGLSL); root->traverse(&outputGLSL);
} }
void TranslatorGLSL::writeVersion(TIntermNode *root) void TranslatorGLSL::writeVersion(TIntermNode *root)
{ {
TVersionGLSL versionGLSL(getShaderType(), getPragma()); TVersionGLSL versionGLSL(getShaderType(), getPragma(), getOutputType());
root->traverse(&versionGLSL); root->traverse(&versionGLSL);
int version = versionGLSL.getVersion(); int version = versionGLSL.getVersion();
// We need to write version directive only if it is greater than 110. // We need to write version directive only if it is greater than 110.
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
class TranslatorGLSL : public TCompiler class TranslatorGLSL : public TCompiler
{ {
public: public:
TranslatorGLSL(sh::GLenum type, ShShaderSpec spec); TranslatorGLSL(sh::GLenum type, ShShaderSpec spec, ShShaderOutput output);
protected: protected:
virtual void translate(TIntermNode *root, int compileOptions); virtual void translate(TIntermNode *root, int compileOptions);
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
static const int GLSL_VERSION_110 = 110; static const int GLSL_VERSION_110 = 110;
static const int GLSL_VERSION_120 = 120; static const int GLSL_VERSION_120 = 120;
static const int GLSL_VERSION_150 = 150;
// We need to scan for the following: // We need to scan for the following:
// 1. "invariant" keyword: This can occur in both - vertex and fragment shaders // 1. "invariant" keyword: This can occur in both - vertex and fragment shaders
...@@ -26,12 +27,22 @@ static const int GLSL_VERSION_120 = 120; ...@@ -26,12 +27,22 @@ static const int GLSL_VERSION_120 = 120;
// GLSL 1.2 relaxed the restriction on arrays, section 5.8: "Variables that // GLSL 1.2 relaxed the restriction on arrays, section 5.8: "Variables that
// are built-in types, entire structures or arrays... are all l-values." // are built-in types, entire structures or arrays... are all l-values."
// //
TVersionGLSL::TVersionGLSL(sh::GLenum type, const TPragma &pragma) TVersionGLSL::TVersionGLSL(sh::GLenum type,
const TPragma &pragma,
ShShaderOutput output)
{ {
if (pragma.stdgl.invariantAll) if (output == SH_GLSL_CORE_OUTPUT)
mVersion = GLSL_VERSION_120; {
mVersion = GLSL_VERSION_150;
}
else else
mVersion = GLSL_VERSION_110; {
ASSERT(output == SH_GLSL_COMPATIBILITY_OUTPUT);
if (pragma.stdgl.invariantAll)
mVersion = GLSL_VERSION_120;
else
mVersion = GLSL_VERSION_110;
}
} }
void TVersionGLSL::visitSymbol(TIntermSymbol *node) void TVersionGLSL::visitSymbol(TIntermSymbol *node)
......
...@@ -29,14 +29,16 @@ ...@@ -29,14 +29,16 @@
class TVersionGLSL : public TIntermTraverser class TVersionGLSL : public TIntermTraverser
{ {
public: public:
TVersionGLSL(sh::GLenum type, const TPragma &pragma); TVersionGLSL(sh::GLenum type, const TPragma &pragma, ShShaderOutput output);
// Returns 120 if the following is used the shader: // If output is core profile, returns 150.
// - "invariant", // If output is legacy profile,
// - "gl_PointCoord", // Returns 120 if the following is used the shader:
// - matrix/matrix constructors // - "invariant",
// - array "out" parameters // - "gl_PointCoord",
// Else 110 is returned. // - matrix/matrix constructors
// - array "out" parameters
// Else 110 is returned.
int getVersion() { return mVersion; } int getVersion() { return mVersion; }
virtual void visitSymbol(TIntermSymbol *); virtual void visitSymbol(TIntermSymbol *);
......
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