Commit e1a94c67 by Olli Etuaho

Check that texture offset is constant and valid

Offset passed to textureOffset and similar functions must be constant. See ESSL 3.00 spec section 8.8. It must also be in the valid range between MIN_PROGRAM_TEXEL_OFFSET and MAX_PROGRAM_TEXEL_OFFSET. Using values outside the valid range makes the results of the texture lookup undefined, as specified in GLES 3.0.4 section 3.8.10. We generate a compiler error if an offset is outside the valid range. BUG=angleproject:1215 TEST=angle_unittests Change-Id: Ida28361444d2f4050d516160f1491674c31868a1 Reviewed-on: https://chromium-review.googlesource.com/312223Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Tested-by: 's avatarOlli Etuaho <oetuaho@nvidia.com>
parent 8ca1351a
...@@ -213,11 +213,9 @@ TIntermNode *TCompiler::compileTreeImpl(const char *const shaderStrings[], ...@@ -213,11 +213,9 @@ TIntermNode *TCompiler::compileTreeImpl(const char *const shaderStrings[],
++firstSource; ++firstSource;
} }
bool debugShaderPrecision = getResources().WEBGL_debug_shader_precision == 1;
TIntermediate intermediate(infoSink); TIntermediate intermediate(infoSink);
TParseContext parseContext(symbolTable, extensionBehavior, intermediate, TParseContext parseContext(symbolTable, extensionBehavior, intermediate, shaderType, shaderSpec,
shaderType, shaderSpec, compileOptions, true, compileOptions, true, infoSink, getResources());
infoSink, debugShaderPrecision);
parseContext.setFragmentPrecisionHighOnESSL1(fragmentPrecisionHigh); parseContext.setFragmentPrecisionHighOnESSL1(fragmentPrecisionHigh);
SetGlobalParseContext(&parseContext); SetGlobalParseContext(&parseContext);
......
...@@ -3767,6 +3767,59 @@ TIntermBranch *TParseContext::addBranch(TOperator op, ...@@ -3767,6 +3767,59 @@ TIntermBranch *TParseContext::addBranch(TOperator op,
return intermediate.addBranch(op, returnValue, loc); return intermediate.addBranch(op, returnValue, loc);
} }
void TParseContext::checkTextureOffsetConst(TIntermAggregate *functionCall)
{
ASSERT(!functionCall->isUserDefined());
const TString &name = functionCall->getName();
TIntermNode *offset = nullptr;
TIntermSequence *arguments = functionCall->getSequence();
if (name.compare(0, 16, "texelFetchOffset") == 0 ||
name.compare(0, 16, "textureLodOffset") == 0 ||
name.compare(0, 20, "textureProjLodOffset") == 0 ||
name.compare(0, 17, "textureGradOffset") == 0 ||
name.compare(0, 21, "textureProjGradOffset") == 0)
{
offset = arguments->back();
}
else if (name.compare(0, 13, "textureOffset") == 0 ||
name.compare(0, 17, "textureProjOffset") == 0)
{
// A bias parameter might follow the offset parameter.
ASSERT(arguments->size() >= 3);
offset = (*arguments)[2];
}
if (offset != nullptr)
{
TIntermConstantUnion *offsetConstantUnion = offset->getAsConstantUnion();
if (offset->getAsTyped()->getQualifier() != EvqConst || !offsetConstantUnion)
{
TString unmangledName = TFunction::unmangleName(name);
error(functionCall->getLine(), "Texture offset must be a constant expression",
unmangledName.c_str());
recover();
}
else
{
ASSERT(offsetConstantUnion->getBasicType() == EbtInt);
size_t size = offsetConstantUnion->getType().getObjectSize();
const TConstantUnion *values = offsetConstantUnion->getUnionArrayPointer();
for (size_t i = 0u; i < size; ++i)
{
int offsetValue = values[i].getIConst();
if (offsetValue > mMaxProgramTexelOffset || offsetValue < mMinProgramTexelOffset)
{
std::stringstream tokenStream;
tokenStream << offsetValue;
std::string token = tokenStream.str();
error(offset->getLine(), "Texture offset value out of valid range",
token.c_str());
recover();
}
}
}
}
}
TIntermTyped *TParseContext::addFunctionCallOrMethod(TFunction *fnCall, TIntermTyped *TParseContext::addFunctionCallOrMethod(TFunction *fnCall,
TIntermNode *paramNode, TIntermNode *paramNode,
TIntermNode *thisNode, TIntermNode *thisNode,
...@@ -3935,8 +3988,12 @@ TIntermTyped *TParseContext::addFunctionCallOrMethod(TFunction *fnCall, ...@@ -3935,8 +3988,12 @@ TIntermTyped *TParseContext::addFunctionCallOrMethod(TFunction *fnCall,
// This needs to happen after the name is set // This needs to happen after the name is set
if (builtIn) if (builtIn)
{
aggregate->setBuiltInFunctionPrecision(); aggregate->setBuiltInFunctionPrecision();
checkTextureOffsetConst(aggregate);
}
callNode = aggregate; callNode = aggregate;
functionCallLValueErrorCheck(fnCandidate, aggregate); functionCallLValueErrorCheck(fnCandidate, aggregate);
......
...@@ -36,7 +36,7 @@ class TParseContext : angle::NonCopyable ...@@ -36,7 +36,7 @@ class TParseContext : angle::NonCopyable
int options, int options,
bool checksPrecErrors, bool checksPrecErrors,
TInfoSink &is, TInfoSink &is,
bool debugShaderPrecisionSupported) const ShBuiltInResources &resources)
: intermediate(interm), : intermediate(interm),
symbolTable(symt), symbolTable(symt),
mDeferredSingleDeclarationErrorCheck(false), mDeferredSingleDeclarationErrorCheck(false),
...@@ -54,12 +54,17 @@ class TParseContext : angle::NonCopyable ...@@ -54,12 +54,17 @@ class TParseContext : angle::NonCopyable
mDefaultMatrixPacking(EmpColumnMajor), mDefaultMatrixPacking(EmpColumnMajor),
mDefaultBlockStorage(EbsShared), mDefaultBlockStorage(EbsShared),
mDiagnostics(is), mDiagnostics(is),
mDirectiveHandler(ext, mDiagnostics, mShaderVersion, debugShaderPrecisionSupported), mDirectiveHandler(ext,
mDiagnostics,
mShaderVersion,
resources.WEBGL_debug_shader_precision == 1),
mPreprocessor(&mDiagnostics, &mDirectiveHandler), mPreprocessor(&mDiagnostics, &mDirectiveHandler),
mScanner(nullptr), mScanner(nullptr),
mUsesFragData(false), mUsesFragData(false),
mUsesFragColor(false), mUsesFragColor(false),
mUsesSecondaryOutputs(false) mUsesSecondaryOutputs(false),
mMinProgramTexelOffset(resources.MinProgramTexelOffset),
mMaxProgramTexelOffset(resources.MaxProgramTexelOffset)
{ {
} }
...@@ -324,6 +329,7 @@ class TParseContext : angle::NonCopyable ...@@ -324,6 +329,7 @@ class TParseContext : angle::NonCopyable
TIntermBranch *addBranch(TOperator op, const TSourceLoc &loc); TIntermBranch *addBranch(TOperator op, const TSourceLoc &loc);
TIntermBranch *addBranch(TOperator op, TIntermTyped *returnValue, const TSourceLoc &loc); TIntermBranch *addBranch(TOperator op, TIntermTyped *returnValue, const TSourceLoc &loc);
void checkTextureOffsetConst(TIntermAggregate *functionCall);
TIntermTyped *addFunctionCallOrMethod(TFunction *fnCall, TIntermTyped *addFunctionCallOrMethod(TFunction *fnCall,
TIntermNode *paramNode, TIntermNode *paramNode,
TIntermNode *thisNode, TIntermNode *thisNode,
...@@ -381,6 +387,8 @@ class TParseContext : angle::NonCopyable ...@@ -381,6 +387,8 @@ class TParseContext : angle::NonCopyable
bool mUsesFragColor; bool mUsesFragColor;
bool mUsesSecondaryOutputs; // Track if we are using either gl_SecondaryFragData or bool mUsesSecondaryOutputs; // Track if we are using either gl_SecondaryFragData or
// gl_Secondary FragColor or both. // gl_Secondary FragColor or both.
int mMinProgramTexelOffset;
int mMaxProgramTexelOffset;
}; };
int PaParseStrings( int PaParseStrings(
......
...@@ -1253,3 +1253,65 @@ TEST_F(MalformedWebGL2ShaderTest, IndexFragDataWithNonConstant) ...@@ -1253,3 +1253,65 @@ TEST_F(MalformedWebGL2ShaderTest, IndexFragDataWithNonConstant)
FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog;
} }
} }
// Test that a non-constant texture offset is not accepted for textureOffset.
// ESSL 3.00 section 8.8
TEST_F(MalformedShaderTest, TextureOffsetNonConst)
{
const std::string &shaderString =
"#version 300 es\n"
"precision mediump float;\n"
"out vec4 my_FragColor;\n"
"uniform vec3 u_texCoord;\n"
"uniform mediump sampler3D u_sampler;\n"
"uniform int x;\n"
"void main()\n"
"{\n"
" my_FragColor = textureOffset(u_sampler, u_texCoord, ivec3(x, 3, -8));\n"
"}\n";
if (compile(shaderString))
{
FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog;
}
}
// Test that a non-constant texture offset is not accepted for textureProjOffset with bias.
// ESSL 3.00 section 8.8
TEST_F(MalformedShaderTest, TextureProjOffsetNonConst)
{
const std::string &shaderString =
"#version 300 es\n"
"precision mediump float;\n"
"out vec4 my_FragColor;\n"
"uniform vec4 u_texCoord;\n"
"uniform mediump sampler3D u_sampler;\n"
"uniform int x;\n"
"void main()\n"
"{\n"
" my_FragColor = textureProjOffset(u_sampler, u_texCoord, ivec3(x, 3, -8), 0.0);\n"
"}\n";
if (compile(shaderString))
{
FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog;
}
}
// Test that an out-of-range texture offset is not accepted.
// GLES 3.0.4 section 3.8.10 specifies that out-of-range offset has undefined behavior.
TEST_F(MalformedShaderTest, TextureLodOffsetOutOfRange)
{
const std::string &shaderString =
"#version 300 es\n"
"precision mediump float;\n"
"out vec4 my_FragColor;\n"
"uniform vec3 u_texCoord;\n"
"uniform mediump sampler3D u_sampler;\n"
"void main()\n"
"{\n"
" my_FragColor = textureLodOffset(u_sampler, u_texCoord, 0.0, ivec3(0, 0, 8));\n"
"}\n";
if (compile(shaderString))
{
FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog;
}
}
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