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: Jamie Madill <jmadill@chromium.org>
diff --git a/src/compiler/translator/OutputHLSL.cpp b/src/compiler/translator/OutputHLSL.cpp
index 5ef2e89..59b62fc 100644
--- a/src/compiler/translator/OutputHLSL.cpp
+++ b/src/compiler/translator/OutputHLSL.cpp
@@ -31,16 +31,28 @@
 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);
     switch (constUnion->getType())
     {
         case EbtFloat:
-            out << std::min(FLT_MAX, std::max(-FLT_MAX, constUnion->getFConst()));
+            writeFloat(out, constUnion->getFConst());
             break;
         case EbtInt:
             out << constUnion->getIConst();
@@ -56,14 +68,14 @@
     }
 }
 
-const TConstantUnion *WriteConstantUnionArray(TInfoSinkBase &out,
-                                              const TConstantUnion *const constUnion,
-                                              const size_t size)
+const TConstantUnion *OutputHLSL::writeConstantUnionArray(TInfoSinkBase &out,
+                                                          const TConstantUnion *const constUnion,
+                                                          const size_t size)
 {
     const TConstantUnion *constUnionIterated = constUnion;
     for (size_t i = 0; i < size; i++, constUnionIterated++)
     {
-        WriteSingleConstant(out, constUnionIterated);
+        writeSingleConstant(out, constUnionIterated);
 
         if (i != size - 1)
         {
@@ -73,8 +85,6 @@
     return constUnionIterated;
 }
 
-} // namespace
-
 OutputHLSL::OutputHLSL(sh::GLenum shaderType,
                        int shaderVersion,
                        const TExtensionBehavior &extensionBehavior,
@@ -2571,7 +2581,7 @@
         {
             out << TypeString(type) << "(";
         }
-        constUnionIterated = WriteConstantUnionArray(out, constUnionIterated, size);
+        constUnionIterated = writeConstantUnionArray(out, constUnionIterated, size);
         if (writeType)
         {
             out << ")";
@@ -2632,7 +2642,7 @@
         {
             TIntermConstantUnion *nodeConst  = expression->getAsConstantUnion();
             const TConstantUnion *constUnion = nodeConst->getUnionArrayPointer();
-            WriteConstantUnionArray(out, constUnion, nodeConst->getType().getObjectSize());
+            writeConstantUnionArray(out, constUnion, nodeConst->getType().getObjectSize());
         }
         else
         {
@@ -2643,7 +2653,7 @@
                 TIntermConstantUnion *nodeConst = node->getAsConstantUnion();
                 ASSERT(nodeConst);
                 const TConstantUnion *constUnion = nodeConst->getUnionArrayPointer();
-                WriteConstantUnionArray(out, constUnion, nodeConst->getType().getObjectSize());
+                writeConstantUnionArray(out, constUnion, nodeConst->getType().getObjectSize());
                 if (node != constructor->getSequence()->back())
                 {
                     out << ", ";