Commit 56a2f95f by Olli Etuaho Committed by Commit Bot

Output infinity and NaN literals correctly in shaders

Previously infinity and NaN resulting from constant folding would be clamped to finite 32-bit float range when they were written in shader output. Now they are written as a bit pattern in case the shader version allows it. This does not guarantee that NaNs work, but this is fine, since ESSL 3.00.6 spec has very loose requirements when it comes to NaNs. BUG=angleproject:1654 TEST=angle_end2end_tests Change-Id: I9997000beeaa8ed22523c22d5cf6929cdfc93f60 Reviewed-on: https://chromium-review.googlesource.com/417301 Commit-Queue: Olli Etuaho <oetuaho@nvidia.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org>
parent 2ad1c490
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include "compiler/translator/OutputGLSLBase.h" #include "compiler/translator/OutputGLSLBase.h"
#include "common/debug.h" #include "common/debug.h"
#include "common/mathutil.h"
#include <cfloat> #include <cfloat>
...@@ -112,6 +113,18 @@ void TOutputGLSLBase::writeInvariantQualifier(const TType &type) ...@@ -112,6 +113,18 @@ void TOutputGLSLBase::writeInvariantQualifier(const TType &type)
} }
} }
void TOutputGLSLBase::writeFloat(TInfoSinkBase &out, float f)
{
if ((gl::isInf(f) || gl::isNaN(f)) && mShaderVersion >= 300)
{
out << "uintBitsToFloat(" << gl::bitCast<uint32_t>(f) << "u)";
}
else
{
out << std::min(FLT_MAX, std::max(-FLT_MAX, f));
}
}
void TOutputGLSLBase::writeTriplet( void TOutputGLSLBase::writeTriplet(
Visit visit, const char *preStr, const char *inStr, const char *postStr) Visit visit, const char *preStr, const char *inStr, const char *postStr)
{ {
...@@ -329,8 +342,8 @@ const TConstantUnion *TOutputGLSLBase::writeConstantUnion( ...@@ -329,8 +342,8 @@ const TConstantUnion *TOutputGLSLBase::writeConstantUnion(
switch (pConstUnion->getType()) switch (pConstUnion->getType())
{ {
case EbtFloat: case EbtFloat:
out << std::min(FLT_MAX, std::max(-FLT_MAX, pConstUnion->getFConst())); writeFloat(out, pConstUnion->getFConst());
break; break;
case EbtInt: case EbtInt:
out << pConstUnion->getIConst(); out << pConstUnion->getIConst();
break; break;
......
...@@ -35,6 +35,7 @@ class TOutputGLSLBase : public TIntermTraverser ...@@ -35,6 +35,7 @@ class TOutputGLSLBase : public TIntermTraverser
protected: protected:
TInfoSinkBase &objSink() { return mObjSink; } TInfoSinkBase &objSink() { return mObjSink; }
void writeFloat(TInfoSinkBase &out, float f);
void writeTriplet(Visit visit, const char *preStr, const char *inStr, const char *postStr); void writeTriplet(Visit visit, const char *preStr, const char *inStr, const char *postStr);
void writeLayoutQualifier(const TType &type); void writeLayoutQualifier(const TType &type);
void writeInvariantQualifier(const TType &type); void writeInvariantQualifier(const TType &type);
......
...@@ -31,16 +31,28 @@ ...@@ -31,16 +31,28 @@
namespace sh namespace sh
{ {
namespace void OutputHLSL::writeFloat(TInfoSinkBase &out, float f)
{ {
// This is known not to work for NaN on all drivers but make the best effort to output NaNs
// regardless.
if ((gl::isInf(f) || gl::isNaN(f)) && mShaderVersion >= 300 &&
mOutputType == SH_HLSL_4_1_OUTPUT)
{
out << "asfloat(" << gl::bitCast<uint32_t>(f) << "u)";
}
else
{
out << std::min(FLT_MAX, std::max(-FLT_MAX, f));
}
}
void WriteSingleConstant(TInfoSinkBase &out, const TConstantUnion *const constUnion) void OutputHLSL::writeSingleConstant(TInfoSinkBase &out, const TConstantUnion *const constUnion)
{ {
ASSERT(constUnion != nullptr); ASSERT(constUnion != nullptr);
switch (constUnion->getType()) switch (constUnion->getType())
{ {
case EbtFloat: case EbtFloat:
out << std::min(FLT_MAX, std::max(-FLT_MAX, constUnion->getFConst())); writeFloat(out, constUnion->getFConst());
break; break;
case EbtInt: case EbtInt:
out << constUnion->getIConst(); out << constUnion->getIConst();
...@@ -56,14 +68,14 @@ void WriteSingleConstant(TInfoSinkBase &out, const TConstantUnion *const constUn ...@@ -56,14 +68,14 @@ void WriteSingleConstant(TInfoSinkBase &out, const TConstantUnion *const constUn
} }
} }
const TConstantUnion *WriteConstantUnionArray(TInfoSinkBase &out, const TConstantUnion *OutputHLSL::writeConstantUnionArray(TInfoSinkBase &out,
const TConstantUnion *const constUnion, const TConstantUnion *const constUnion,
const size_t size) const size_t size)
{ {
const TConstantUnion *constUnionIterated = constUnion; const TConstantUnion *constUnionIterated = constUnion;
for (size_t i = 0; i < size; i++, constUnionIterated++) for (size_t i = 0; i < size; i++, constUnionIterated++)
{ {
WriteSingleConstant(out, constUnionIterated); writeSingleConstant(out, constUnionIterated);
if (i != size - 1) if (i != size - 1)
{ {
...@@ -73,8 +85,6 @@ const TConstantUnion *WriteConstantUnionArray(TInfoSinkBase &out, ...@@ -73,8 +85,6 @@ const TConstantUnion *WriteConstantUnionArray(TInfoSinkBase &out,
return constUnionIterated; return constUnionIterated;
} }
} // namespace
OutputHLSL::OutputHLSL(sh::GLenum shaderType, OutputHLSL::OutputHLSL(sh::GLenum shaderType,
int shaderVersion, int shaderVersion,
const TExtensionBehavior &extensionBehavior, const TExtensionBehavior &extensionBehavior,
...@@ -2571,7 +2581,7 @@ const TConstantUnion *OutputHLSL::writeConstantUnion(TInfoSinkBase &out, ...@@ -2571,7 +2581,7 @@ const TConstantUnion *OutputHLSL::writeConstantUnion(TInfoSinkBase &out,
{ {
out << TypeString(type) << "("; out << TypeString(type) << "(";
} }
constUnionIterated = WriteConstantUnionArray(out, constUnionIterated, size); constUnionIterated = writeConstantUnionArray(out, constUnionIterated, size);
if (writeType) if (writeType)
{ {
out << ")"; out << ")";
...@@ -2632,7 +2642,7 @@ bool OutputHLSL::writeConstantInitialization(TInfoSinkBase &out, ...@@ -2632,7 +2642,7 @@ bool OutputHLSL::writeConstantInitialization(TInfoSinkBase &out,
{ {
TIntermConstantUnion *nodeConst = expression->getAsConstantUnion(); TIntermConstantUnion *nodeConst = expression->getAsConstantUnion();
const TConstantUnion *constUnion = nodeConst->getUnionArrayPointer(); const TConstantUnion *constUnion = nodeConst->getUnionArrayPointer();
WriteConstantUnionArray(out, constUnion, nodeConst->getType().getObjectSize()); writeConstantUnionArray(out, constUnion, nodeConst->getType().getObjectSize());
} }
else else
{ {
...@@ -2643,7 +2653,7 @@ bool OutputHLSL::writeConstantInitialization(TInfoSinkBase &out, ...@@ -2643,7 +2653,7 @@ bool OutputHLSL::writeConstantInitialization(TInfoSinkBase &out,
TIntermConstantUnion *nodeConst = node->getAsConstantUnion(); TIntermConstantUnion *nodeConst = node->getAsConstantUnion();
ASSERT(nodeConst); ASSERT(nodeConst);
const TConstantUnion *constUnion = nodeConst->getUnionArrayPointer(); const TConstantUnion *constUnion = nodeConst->getUnionArrayPointer();
WriteConstantUnionArray(out, constUnion, nodeConst->getType().getObjectSize()); writeConstantUnionArray(out, constUnion, nodeConst->getType().getObjectSize());
if (node != constructor->getSequence()->back()) if (node != constructor->getSequence()->back())
{ {
out << ", "; out << ", ";
......
...@@ -55,6 +55,12 @@ class OutputHLSL : public TIntermTraverser ...@@ -55,6 +55,12 @@ class OutputHLSL : public TIntermTraverser
protected: protected:
void header(TInfoSinkBase &out, const BuiltInFunctionEmulator *builtInFunctionEmulator); void header(TInfoSinkBase &out, const BuiltInFunctionEmulator *builtInFunctionEmulator);
void writeFloat(TInfoSinkBase &out, float f);
void writeSingleConstant(TInfoSinkBase &out, const TConstantUnion *const constUnion);
const TConstantUnion *writeConstantUnionArray(TInfoSinkBase &out,
const TConstantUnion *const constUnion,
const size_t size);
// Visit AST nodes and output their code to the body stream // Visit AST nodes and output their code to the body stream
void visitSymbol(TIntermSymbol*); void visitSymbol(TIntermSymbol*);
void visitRaw(TIntermRaw*); void visitRaw(TIntermRaw*);
......
...@@ -2353,6 +2353,48 @@ TEST_P(GLSLTest_ES3, FoldedInvalidLeftShift) ...@@ -2353,6 +2353,48 @@ TEST_P(GLSLTest_ES3, FoldedInvalidLeftShift)
glDeleteProgram(program); glDeleteProgram(program);
} }
// Test that literal infinity can be written out from the shader translator.
// A similar test can't be made for NaNs, since ESSL 3.00.6 requirements for NaNs are very loose.
TEST_P(GLSLTest_ES3, LiteralInfinityOutput)
{
const std::string &fragmentShader =
"#version 300 es\n"
"precision highp float;\n"
"out vec4 out_color;\n"
"uniform float u;\n"
"void main()\n"
"{\n"
" float infVar = 1.0e40 - u;\n"
" bool correct = isinf(infVar) && infVar > 0.0;\n"
" out_color = correct ? vec4(0.0, 1.0, 0.0, 1.0) : vec4(1.0, 0.0, 0.0, 1.0);\n"
"}\n";
ANGLE_GL_PROGRAM(program, mSimpleVSSource, fragmentShader);
drawQuad(program.get(), "inputAttribute", 0.5f);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
}
// Test that literal negative infinity can be written out from the shader translator.
// A similar test can't be made for NaNs, since ESSL 3.00.6 requirements for NaNs are very loose.
TEST_P(GLSLTest_ES3, LiteralNegativeInfinityOutput)
{
const std::string &fragmentShader =
"#version 300 es\n"
"precision highp float;\n"
"out vec4 out_color;\n"
"uniform float u;\n"
"void main()\n"
"{\n"
" float infVar = -1.0e40 + u;\n"
" bool correct = isinf(infVar) && infVar < 0.0;\n"
" out_color = correct ? vec4(0.0, 1.0, 0.0, 1.0) : vec4(1.0, 0.0, 0.0, 1.0);\n"
"}\n";
ANGLE_GL_PROGRAM(program, mSimpleVSSource, fragmentShader);
drawQuad(program.get(), "inputAttribute", 0.5f);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
}
} // anonymous namespace } // anonymous namespace
// Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against. // Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against.
......
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