summaryrefslogtreecommitdiffstats
path: root/src/3rdparty/angle/src/compiler/translator/OutputHLSL.cpp
diff options
context:
space:
mode:
authorAndrew Knight <andrew.knight@digia.com>2014-02-24 11:18:33 +0200
committerThe Qt Project <gerrit-noreply@qt-project.org>2014-02-26 07:56:50 +0100
commita7d093e740b1e20874b5ebeb37b5c5d76ae19e42 (patch)
tree573b63025dbaa090392e468e1a90de8104054578 /src/3rdparty/angle/src/compiler/translator/OutputHLSL.cpp
parent45e17d0cc74d3444e23c18f73d6ac155659cec55 (diff)
Upgrade ANGLE to 1.3.5bb7ec572d0a
This brings Qt's copy of ANGLE up to ANGLE master, which contains a number of bugfixes as well as restructuring for the upcoming ES 3.0 support. This version brings considerable stability improvements to the D3D11 renderer. The static translator project files have been merged to align with the ANGLE source tree. Two new patches have been applied to fix errors in upstream ANGLE: - 0011-ANGLE-Fix-compilation-error-on-MinGW-caused-by-trace.patch The event trace header in ANGLE's third_party directory has an unused template which causes a compilation error on MinGW. Disable this part of the code. - 0012-ANGLE-fix-semantic-index-lookup.patch The sorted semantic index table was returning a direct mapping to the new indices, instead of the old indices. This caused a mismatch in the GL type lookup for the translated attribute. All other patches have been rebased, removed if no longer needed, and renamed to clear up the application order: - 0001-Fix-compilation-for-MSVC-2008-and-std-tuple.patch No changes. - 0001-Fix-compilation-with-MinGW-mingw-tdm64-gcc-4.8.1.patch No changes. Renamed to 0002. - 0001-Fix-compilation-with-MinGW-gcc-64-bit.patch No changes. Renamed to 0003. - 0001-Make-it-possible-to-link-ANGLE-statically-for-single.patch Modified patch to adapt to new DLL loading structure. Renamed to 0004. - 0005-Fix-build-when-SSE2-is-not-available.patch No changes. - 0011-Fix-compilation-of-libGLESv2-with-older-MinGW-w64-he.patch No changes. Renamed to 0006. - 0006-Make-DX9-DX11-mutually-exclusive.patch Made the patch less invasive by allowing D3D9 code to run unless explicitly disabled (e.g. on WinRT, where it doesn't compile). This makes the patch smaller and allows Desktop Windows to compile both D3D9 and D3D11 codepaths. Renamed to 0007. - 0015-ANGLE-Dynamically-load-D3D-compiler-from-a-list-of-k.patch No changes. Renamed to 0008. - 0012-ANGLE-Support-WinRT.patch Made D3D11_level9 initialization only possible if D3D9 is disabled. This makes sure Desktop PCs use the old D3D9 codepath instead of the less-tested D3D11_level9 codepath. Renamed to 0009. - 0013-Enable-D3D11-for-feature-level-9-cards.patch Conveniently smaller patch due to buffer implementation improvements upstream. Renamed to 0010. - 0014-ANGLE-D3D11-Alwayls-execute-QueryInterface.patch This was a fix for patch 0009, so was integrated there. Removed. - 0016-ANGLE-D3D11-Fix-build-on-desktop-Windows.patch This was a fix for patch 0009, so it was integrated there. Removed. - 0001-ANGLE-Fix-compilation-with-MSVC2013.patch Fixed upstream. Removed. - 0007-ANGLE-Fix-typedefs-for-Win64.patch Fixed upstream. Removed. - 0004-Fix-black-screen-after-minimizing-OpenGL-window-with.patch The issue has been fixed in Qt itself. Removed. - 0008-DX11-Prevent-assert-when-view-is-minimized-or-.patch The cause of the problem was the same as patch 0004, but for the D3D11 codepath. Removed. Change-Id: Id69484ab3a3e013050741c462fb1b06dfb0fd112 Reviewed-by: Friedemann Kleint <Friedemann.Kleint@digia.com> Reviewed-by: Kai Koehne <kai.koehne@digia.com> Reviewed-by: Oliver Wolff <oliver.wolff@digia.com>
Diffstat (limited to 'src/3rdparty/angle/src/compiler/translator/OutputHLSL.cpp')
-rw-r--r--src/3rdparty/angle/src/compiler/translator/OutputHLSL.cpp3138
1 files changed, 3138 insertions, 0 deletions
diff --git a/src/3rdparty/angle/src/compiler/translator/OutputHLSL.cpp b/src/3rdparty/angle/src/compiler/translator/OutputHLSL.cpp
new file mode 100644
index 0000000000..af996df719
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/OutputHLSL.cpp
@@ -0,0 +1,3138 @@
+//
+// Copyright (c) 2002-2013 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "compiler/translator/OutputHLSL.h"
+
+#include "common/angleutils.h"
+#include "compiler/translator/compilerdebug.h"
+#include "compiler/translator/DetectDiscontinuity.h"
+#include "compiler/translator/InfoSink.h"
+#include "compiler/translator/SearchSymbol.h"
+#include "compiler/translator/UnfoldShortCircuit.h"
+#include "compiler/translator/NodeSearch.h"
+#include "compiler/translator/RewriteElseBlocks.h"
+
+#include <algorithm>
+#include <cfloat>
+#include <stdio.h>
+
+namespace sh
+{
+
+OutputHLSL::OutputHLSL(TParseContext &context, const ShBuiltInResources& resources, ShShaderOutput outputType)
+ : TIntermTraverser(true, true, true), mContext(context), mOutputType(outputType)
+{
+ mUnfoldShortCircuit = new UnfoldShortCircuit(context, this);
+ mInsideFunction = false;
+
+ mUsesTexture2D = false;
+ mUsesTexture2D_bias = false;
+ mUsesTexture2DProj = false;
+ mUsesTexture2DProj_bias = false;
+ mUsesTexture2DProjLod = false;
+ mUsesTexture2DLod = false;
+ mUsesTextureCube = false;
+ mUsesTextureCube_bias = false;
+ mUsesTextureCubeLod = false;
+ mUsesTexture2DLod0 = false;
+ mUsesTexture2DLod0_bias = false;
+ mUsesTexture2DProjLod0 = false;
+ mUsesTexture2DProjLod0_bias = false;
+ mUsesTextureCubeLod0 = false;
+ mUsesTextureCubeLod0_bias = false;
+ mUsesFragColor = false;
+ mUsesFragData = false;
+ mUsesDepthRange = false;
+ mUsesFragCoord = false;
+ mUsesPointCoord = false;
+ mUsesFrontFacing = false;
+ mUsesPointSize = false;
+ mUsesFragDepth = false;
+ mUsesXor = false;
+ mUsesMod1 = false;
+ mUsesMod2v = false;
+ mUsesMod2f = false;
+ mUsesMod3v = false;
+ mUsesMod3f = false;
+ mUsesMod4v = false;
+ mUsesMod4f = false;
+ mUsesFaceforward1 = false;
+ mUsesFaceforward2 = false;
+ mUsesFaceforward3 = false;
+ mUsesFaceforward4 = false;
+ mUsesAtan2_1 = false;
+ mUsesAtan2_2 = false;
+ mUsesAtan2_3 = false;
+ mUsesAtan2_4 = false;
+ mUsesDiscardRewriting = false;
+
+ mNumRenderTargets = resources.EXT_draw_buffers ? resources.MaxDrawBuffers : 1;
+
+ mScopeDepth = 0;
+
+ mUniqueIndex = 0;
+
+ mContainsLoopDiscontinuity = false;
+ mOutputLod0Function = false;
+ mInsideDiscontinuousLoop = false;
+
+ mExcessiveLoopIndex = NULL;
+
+ if (mOutputType == SH_HLSL9_OUTPUT)
+ {
+ if (mContext.shaderType == SH_FRAGMENT_SHADER)
+ {
+ mUniformRegister = 3; // Reserve registers for dx_DepthRange, dx_ViewCoords and dx_DepthFront
+ }
+ else
+ {
+ mUniformRegister = 2; // Reserve registers for dx_DepthRange and dx_ViewAdjust
+ }
+ }
+ else
+ {
+ mUniformRegister = 0;
+ }
+
+ mSamplerRegister = 0;
+}
+
+OutputHLSL::~OutputHLSL()
+{
+ delete mUnfoldShortCircuit;
+}
+
+void OutputHLSL::output()
+{
+ mContainsLoopDiscontinuity = mContext.shaderType == SH_FRAGMENT_SHADER && containsLoopDiscontinuity(mContext.treeRoot);
+
+ // Work around D3D9 bug that would manifest in vertex shaders with selection blocks which
+ // use a vertex attribute as a condition, and some related computation in the else block.
+ if (mOutputType == SH_HLSL9_OUTPUT && mContext.shaderType == SH_VERTEX_SHADER)
+ {
+ RewriteElseBlocks(mContext.treeRoot);
+ }
+
+ mContext.treeRoot->traverse(this); // Output the body first to determine what has to go in the header
+ header();
+
+ mContext.infoSink().obj << mHeader.c_str();
+ mContext.infoSink().obj << mBody.c_str();
+}
+
+TInfoSinkBase &OutputHLSL::getBodyStream()
+{
+ return mBody;
+}
+
+const ActiveUniforms &OutputHLSL::getUniforms()
+{
+ return mActiveUniforms;
+}
+
+int OutputHLSL::vectorSize(const TType &type) const
+{
+ int elementSize = type.isMatrix() ? type.getNominalSize() : 1;
+ int arraySize = type.isArray() ? type.getArraySize() : 1;
+
+ return elementSize * arraySize;
+}
+
+void OutputHLSL::header()
+{
+ ShShaderType shaderType = mContext.shaderType;
+ TInfoSinkBase &out = mHeader;
+
+ for (StructDeclarations::iterator structDeclaration = mStructDeclarations.begin(); structDeclaration != mStructDeclarations.end(); structDeclaration++)
+ {
+ out << *structDeclaration;
+ }
+
+ for (Constructors::iterator constructor = mConstructors.begin(); constructor != mConstructors.end(); constructor++)
+ {
+ out << *constructor;
+ }
+
+ TString uniforms;
+ TString varyings;
+ TString attributes;
+
+ for (ReferencedSymbols::const_iterator uniform = mReferencedUniforms.begin(); uniform != mReferencedUniforms.end(); uniform++)
+ {
+ const TType &type = uniform->second->getType();
+ const TString &name = uniform->second->getSymbol();
+
+ if (mOutputType == SH_HLSL11_OUTPUT && IsSampler(type.getBasicType())) // Also declare the texture
+ {
+ int index = samplerRegister(mReferencedUniforms[name]);
+
+ uniforms += "uniform SamplerState sampler_" + decorateUniform(name, type) + arrayString(type) +
+ " : register(s" + str(index) + ");\n";
+
+ uniforms += "uniform " + textureString(type) + " texture_" + decorateUniform(name, type) + arrayString(type) +
+ " : register(t" + str(index) + ");\n";
+ }
+ else
+ {
+ uniforms += "uniform " + typeString(type) + " " + decorateUniform(name, type) + arrayString(type) +
+ " : register(" + registerString(mReferencedUniforms[name]) + ");\n";
+ }
+ }
+
+ for (ReferencedSymbols::const_iterator varying = mReferencedVaryings.begin(); varying != mReferencedVaryings.end(); varying++)
+ {
+ const TType &type = varying->second->getType();
+ const TString &name = varying->second->getSymbol();
+
+ // Program linking depends on this exact format
+ varyings += "static " + typeString(type) + " " + decorate(name) + arrayString(type) + " = " + initializer(type) + ";\n";
+ }
+
+ for (ReferencedSymbols::const_iterator attribute = mReferencedAttributes.begin(); attribute != mReferencedAttributes.end(); attribute++)
+ {
+ const TType &type = attribute->second->getType();
+ const TString &name = attribute->second->getSymbol();
+
+ attributes += "static " + typeString(type) + " " + decorate(name) + arrayString(type) + " = " + initializer(type) + ";\n";
+ }
+
+ if (mUsesDiscardRewriting)
+ {
+ out << "#define ANGLE_USES_DISCARD_REWRITING" << "\n";
+ }
+
+ if (shaderType == SH_FRAGMENT_SHADER)
+ {
+ TExtensionBehavior::const_iterator iter = mContext.extensionBehavior().find("GL_EXT_draw_buffers");
+ const bool usingMRTExtension = (iter != mContext.extensionBehavior().end() && (iter->second == EBhEnable || iter->second == EBhRequire));
+
+ const unsigned int numColorValues = usingMRTExtension ? mNumRenderTargets : 1;
+
+ out << "// Varyings\n";
+ out << varyings;
+ out << "\n"
+ "static float4 gl_Color[" << numColorValues << "] =\n"
+ "{\n";
+ for (unsigned int i = 0; i < numColorValues; i++)
+ {
+ out << " float4(0, 0, 0, 0)";
+ if (i + 1 != numColorValues)
+ {
+ out << ",";
+ }
+ out << "\n";
+ }
+ out << "};\n";
+
+ if (mUsesFragDepth)
+ {
+ out << "static float gl_Depth = 0.0;\n";
+ }
+
+ if (mUsesFragCoord)
+ {
+ out << "static float4 gl_FragCoord = float4(0, 0, 0, 0);\n";
+ }
+
+ if (mUsesPointCoord)
+ {
+ out << "static float2 gl_PointCoord = float2(0.5, 0.5);\n";
+ }
+
+ if (mUsesFrontFacing)
+ {
+ out << "static bool gl_FrontFacing = false;\n";
+ }
+
+ out << "\n";
+
+ if (mUsesDepthRange)
+ {
+ out << "struct gl_DepthRangeParameters\n"
+ "{\n"
+ " float near;\n"
+ " float far;\n"
+ " float diff;\n"
+ "};\n"
+ "\n";
+ }
+
+ if (mOutputType == SH_HLSL11_OUTPUT)
+ {
+ out << "cbuffer DriverConstants : register(b1)\n"
+ "{\n";
+
+ if (mUsesDepthRange)
+ {
+ out << " float3 dx_DepthRange : packoffset(c0);\n";
+ }
+
+ if (mUsesFragCoord)
+ {
+ out << " float4 dx_ViewCoords : packoffset(c1);\n";
+ }
+
+ if (mUsesFragCoord || mUsesFrontFacing)
+ {
+ out << " float3 dx_DepthFront : packoffset(c2);\n";
+ }
+
+ out << "};\n";
+ }
+ else
+ {
+ if (mUsesDepthRange)
+ {
+ out << "uniform float3 dx_DepthRange : register(c0);";
+ }
+
+ if (mUsesFragCoord)
+ {
+ out << "uniform float4 dx_ViewCoords : register(c1);\n";
+ }
+
+ if (mUsesFragCoord || mUsesFrontFacing)
+ {
+ out << "uniform float3 dx_DepthFront : register(c2);\n";
+ }
+ }
+
+ out << "\n";
+
+ if (mUsesDepthRange)
+ {
+ out << "static gl_DepthRangeParameters gl_DepthRange = {dx_DepthRange.x, dx_DepthRange.y, dx_DepthRange.z};\n"
+ "\n";
+ }
+
+ out << uniforms;
+ out << "\n";
+
+ if (mUsesTexture2D)
+ {
+ if (mOutputType == SH_HLSL9_OUTPUT)
+ {
+ out << "float4 gl_texture2D(sampler2D s, float2 t)\n"
+ "{\n"
+ " return tex2D(s, t);\n"
+ "}\n"
+ "\n";
+ }
+ else if (mOutputType == SH_HLSL11_OUTPUT)
+ {
+ out << "float4 gl_texture2D(Texture2D t, SamplerState s, float2 uv)\n"
+ "{\n"
+ " return t.Sample(s, uv);\n"
+ "}\n"
+ "\n";
+ }
+ else UNREACHABLE();
+ }
+
+ if (mUsesTexture2D_bias)
+ {
+ if (mOutputType == SH_HLSL9_OUTPUT)
+ {
+ out << "float4 gl_texture2D(sampler2D s, float2 t, float bias)\n"
+ "{\n"
+ " return tex2Dbias(s, float4(t.x, t.y, 0, bias));\n"
+ "}\n"
+ "\n";
+ }
+ else if (mOutputType == SH_HLSL11_OUTPUT)
+ {
+ out << "float4 gl_texture2D(Texture2D t, SamplerState s, float2 uv, float bias)\n"
+ "{\n"
+ " return t.SampleBias(s, uv, bias);\n"
+ "}\n"
+ "\n";
+ }
+ else UNREACHABLE();
+ }
+
+ if (mUsesTexture2DProj)
+ {
+ if (mOutputType == SH_HLSL9_OUTPUT)
+ {
+ out << "float4 gl_texture2DProj(sampler2D s, float3 t)\n"
+ "{\n"
+ " return tex2Dproj(s, float4(t.x, t.y, 0, t.z));\n"
+ "}\n"
+ "\n"
+ "float4 gl_texture2DProj(sampler2D s, float4 t)\n"
+ "{\n"
+ " return tex2Dproj(s, t);\n"
+ "}\n"
+ "\n";
+ }
+ else if (mOutputType == SH_HLSL11_OUTPUT)
+ {
+ out << "float4 gl_texture2DProj(Texture2D t, SamplerState s, float3 uvw)\n"
+ "{\n"
+ " return t.Sample(s, float2(uvw.x / uvw.z, uvw.y / uvw.z));\n"
+ "}\n"
+ "\n"
+ "float4 gl_texture2DProj(Texture2D t, SamplerState s, float4 uvw)\n"
+ "{\n"
+ " return t.Sample(s, float2(uvw.x / uvw.w, uvw.y / uvw.w));\n"
+ "}\n"
+ "\n";
+ }
+ else UNREACHABLE();
+ }
+
+ if (mUsesTexture2DProj_bias)
+ {
+ if (mOutputType == SH_HLSL9_OUTPUT)
+ {
+ out << "float4 gl_texture2DProj(sampler2D s, float3 t, float bias)\n"
+ "{\n"
+ " return tex2Dbias(s, float4(t.x / t.z, t.y / t.z, 0, bias));\n"
+ "}\n"
+ "\n"
+ "float4 gl_texture2DProj(sampler2D s, float4 t, float bias)\n"
+ "{\n"
+ " return tex2Dbias(s, float4(t.x / t.w, t.y / t.w, 0, bias));\n"
+ "}\n"
+ "\n";
+ }
+ else if (mOutputType == SH_HLSL11_OUTPUT)
+ {
+ out << "float4 gl_texture2DProj(Texture2D t, SamplerState s, float3 uvw, float bias)\n"
+ "{\n"
+ " return t.SampleBias(s, float2(uvw.x / uvw.z, uvw.y / uvw.z), bias);\n"
+ "}\n"
+ "\n"
+ "float4 gl_texture2DProj(Texture2D t, SamplerState s, float4 uvw, float bias)\n"
+ "{\n"
+ " return t.SampleBias(s, float2(uvw.x / uvw.w, uvw.y / uvw.w), bias);\n"
+ "}\n"
+ "\n";
+ }
+ else UNREACHABLE();
+ }
+
+ if (mUsesTextureCube)
+ {
+ if (mOutputType == SH_HLSL9_OUTPUT)
+ {
+ out << "float4 gl_textureCube(samplerCUBE s, float3 t)\n"
+ "{\n"
+ " return texCUBE(s, t);\n"
+ "}\n"
+ "\n";
+ }
+ else if (mOutputType == SH_HLSL11_OUTPUT)
+ {
+ out << "float4 gl_textureCube(TextureCube t, SamplerState s, float3 uvw)\n"
+ "{\n"
+ " return t.Sample(s, uvw);\n"
+ "}\n"
+ "\n";
+ }
+ else UNREACHABLE();
+ }
+
+ if (mUsesTextureCube_bias)
+ {
+ if (mOutputType == SH_HLSL9_OUTPUT)
+ {
+ out << "float4 gl_textureCube(samplerCUBE s, float3 t, float bias)\n"
+ "{\n"
+ " return texCUBEbias(s, float4(t.x, t.y, t.z, bias));\n"
+ "}\n"
+ "\n";
+ }
+ else if (mOutputType == SH_HLSL11_OUTPUT)
+ {
+ out << "float4 gl_textureCube(TextureCube t, SamplerState s, float3 uvw, float bias)\n"
+ "{\n"
+ " return t.SampleBias(s, uvw, bias);\n"
+ "}\n"
+ "\n";
+ }
+ else UNREACHABLE();
+ }
+
+ // These *Lod0 intrinsics are not available in GL fragment shaders.
+ // They are used to sample using discontinuous texture coordinates.
+ if (mUsesTexture2DLod0)
+ {
+ if (mOutputType == SH_HLSL9_OUTPUT)
+ {
+ out << "float4 gl_texture2DLod0(sampler2D s, float2 t)\n"
+ "{\n"
+ " return tex2Dlod(s, float4(t.x, t.y, 0, 0));\n"
+ "}\n"
+ "\n";
+ }
+ else if (mOutputType == SH_HLSL11_OUTPUT)
+ {
+ out << "float4 gl_texture2DLod0(Texture2D t, SamplerState s, float2 uv)\n"
+ "{\n"
+ " return t.SampleLevel(s, uv, 0);\n"
+ "}\n"
+ "\n";
+ }
+ else UNREACHABLE();
+ }
+
+ if (mUsesTexture2DLod0_bias)
+ {
+ if (mOutputType == SH_HLSL9_OUTPUT)
+ {
+ out << "float4 gl_texture2DLod0(sampler2D s, float2 t, float bias)\n"
+ "{\n"
+ " return tex2Dlod(s, float4(t.x, t.y, 0, 0));\n"
+ "}\n"
+ "\n";
+ }
+ else if (mOutputType == SH_HLSL11_OUTPUT)
+ {
+ out << "float4 gl_texture2DLod0(Texture2D t, SamplerState s, float2 uv, float bias)\n"
+ "{\n"
+ " return t.SampleLevel(s, uv, 0);\n"
+ "}\n"
+ "\n";
+ }
+ else UNREACHABLE();
+ }
+
+ if (mUsesTexture2DProjLod0)
+ {
+ if (mOutputType == SH_HLSL9_OUTPUT)
+ {
+ out << "float4 gl_texture2DProjLod0(sampler2D s, float3 t)\n"
+ "{\n"
+ " return tex2Dlod(s, float4(t.x / t.z, t.y / t.z, 0, 0));\n"
+ "}\n"
+ "\n"
+ "float4 gl_texture2DProjLod(sampler2D s, float4 t)\n"
+ "{\n"
+ " return tex2Dlod(s, float4(t.x / t.w, t.y / t.w, 0, 0));\n"
+ "}\n"
+ "\n";
+ }
+ else if (mOutputType == SH_HLSL11_OUTPUT)
+ {
+ out << "float4 gl_texture2DProjLod0(Texture2D t, SamplerState s, float3 uvw)\n"
+ "{\n"
+ " return t.SampleLevel(s, float2(uvw.x / uvw.z, uvw.y / uvw.z), 0);\n"
+ "}\n"
+ "\n"
+ "float4 gl_texture2DProjLod0(Texture2D t, SamplerState s, float4 uvw)\n"
+ "{\n"
+ " return t.SampleLevel(s, float2(uvw.x / uvw.w, uvw.y / uvw.w), 0);\n"
+ "}\n"
+ "\n";
+ }
+ else UNREACHABLE();
+ }
+
+ if (mUsesTexture2DProjLod0_bias)
+ {
+ if (mOutputType == SH_HLSL9_OUTPUT)
+ {
+ out << "float4 gl_texture2DProjLod0_bias(sampler2D s, float3 t, float bias)\n"
+ "{\n"
+ " return tex2Dlod(s, float4(t.x / t.z, t.y / t.z, 0, 0));\n"
+ "}\n"
+ "\n"
+ "float4 gl_texture2DProjLod_bias(sampler2D s, float4 t, float bias)\n"
+ "{\n"
+ " return tex2Dlod(s, float4(t.x / t.w, t.y / t.w, 0, 0));\n"
+ "}\n"
+ "\n";
+ }
+ else if (mOutputType == SH_HLSL11_OUTPUT)
+ {
+ out << "float4 gl_texture2DProjLod_bias(Texture2D t, SamplerState s, float3 uvw, float bias)\n"
+ "{\n"
+ " return t.SampleLevel(s, float2(uvw.x / uvw.z, uvw.y / uvw.z), 0);\n"
+ "}\n"
+ "\n"
+ "float4 gl_texture2DProjLod_bias(Texture2D t, SamplerState s, float4 uvw, float bias)\n"
+ "{\n"
+ " return t.SampleLevel(s, float2(uvw.x / uvw.w, uvw.y / uvw.w), 0);\n"
+ "}\n"
+ "\n";
+ }
+ else UNREACHABLE();
+ }
+
+ if (mUsesTextureCubeLod0)
+ {
+ if (mOutputType == SH_HLSL9_OUTPUT)
+ {
+ out << "float4 gl_textureCubeLod0(samplerCUBE s, float3 t)\n"
+ "{\n"
+ " return texCUBElod(s, float4(t.x, t.y, t.z, 0));\n"
+ "}\n"
+ "\n";
+ }
+ else if (mOutputType == SH_HLSL11_OUTPUT)
+ {
+ out << "float4 gl_textureCubeLod0(TextureCube t, SamplerState s, float3 uvw)\n"
+ "{\n"
+ " return t.SampleLevel(s, uvw, 0);\n"
+ "}\n"
+ "\n";
+ }
+ else UNREACHABLE();
+ }
+
+ if (mUsesTextureCubeLod0_bias)
+ {
+ if (mOutputType == SH_HLSL9_OUTPUT)
+ {
+ out << "float4 gl_textureCubeLod0(samplerCUBE s, float3 t, float bias)\n"
+ "{\n"
+ " return texCUBElod(s, float4(t.x, t.y, t.z, 0));\n"
+ "}\n"
+ "\n";
+ }
+ else if (mOutputType == SH_HLSL11_OUTPUT)
+ {
+ out << "float4 gl_textureCubeLod0(TextureCube t, SamplerState s, float3 uvw, float bias)\n"
+ "{\n"
+ " return t.SampleLevel(s, uvw, 0);\n"
+ "}\n"
+ "\n";
+ }
+ else UNREACHABLE();
+ }
+
+ if (usingMRTExtension && mNumRenderTargets > 1)
+ {
+ out << "#define GL_USES_MRT\n";
+ }
+
+ if (mUsesFragColor)
+ {
+ out << "#define GL_USES_FRAG_COLOR\n";
+ }
+
+ if (mUsesFragData)
+ {
+ out << "#define GL_USES_FRAG_DATA\n";
+ }
+ }
+ else // Vertex shader
+ {
+ out << "// Attributes\n";
+ out << attributes;
+ out << "\n"
+ "static float4 gl_Position = float4(0, 0, 0, 0);\n";
+
+ if (mUsesPointSize)
+ {
+ out << "static float gl_PointSize = float(1);\n";
+ }
+
+ out << "\n"
+ "// Varyings\n";
+ out << varyings;
+ out << "\n";
+
+ if (mUsesDepthRange)
+ {
+ out << "struct gl_DepthRangeParameters\n"
+ "{\n"
+ " float near;\n"
+ " float far;\n"
+ " float diff;\n"
+ "};\n"
+ "\n";
+ }
+
+ if (mOutputType == SH_HLSL11_OUTPUT)
+ {
+ if (mUsesDepthRange)
+ {
+ out << "cbuffer DriverConstants : register(b1)\n"
+ "{\n"
+ " float3 dx_DepthRange : packoffset(c0);\n"
+ "};\n"
+ "\n";
+ }
+ }
+ else
+ {
+ if (mUsesDepthRange)
+ {
+ out << "uniform float3 dx_DepthRange : register(c0);\n";
+ }
+
+ out << "uniform float4 dx_ViewAdjust : register(c1);\n"
+ "\n";
+ }
+
+ if (mUsesDepthRange)
+ {
+ out << "static gl_DepthRangeParameters gl_DepthRange = {dx_DepthRange.x, dx_DepthRange.y, dx_DepthRange.z};\n"
+ "\n";
+ }
+
+ out << uniforms;
+ out << "\n";
+
+ if (mUsesTexture2D)
+ {
+ if (mOutputType == SH_HLSL9_OUTPUT)
+ {
+ out << "float4 gl_texture2D(sampler2D s, float2 t)\n"
+ "{\n"
+ " return tex2Dlod(s, float4(t.x, t.y, 0, 0));\n"
+ "}\n"
+ "\n";
+ }
+ else if (mOutputType == SH_HLSL11_OUTPUT)
+ {
+ out << "float4 gl_texture2D(Texture2D t, SamplerState s, float2 uv)\n"
+ "{\n"
+ " return t.SampleLevel(s, uv, 0);\n"
+ "}\n"
+ "\n";
+ }
+ else UNREACHABLE();
+ }
+
+ if (mUsesTexture2DLod)
+ {
+ if (mOutputType == SH_HLSL9_OUTPUT)
+ {
+ out << "float4 gl_texture2DLod(sampler2D s, float2 t, float lod)\n"
+ "{\n"
+ " return tex2Dlod(s, float4(t.x, t.y, 0, lod));\n"
+ "}\n"
+ "\n";
+ }
+ else if (mOutputType == SH_HLSL11_OUTPUT)
+ {
+ out << "float4 gl_texture2DLod(Texture2D t, SamplerState s, float2 uv, float lod)\n"
+ "{\n"
+ " return t.SampleLevel(s, uv, lod);\n"
+ "}\n"
+ "\n";
+ }
+ else UNREACHABLE();
+ }
+
+ if (mUsesTexture2DProj)
+ {
+ if (mOutputType == SH_HLSL9_OUTPUT)
+ {
+ out << "float4 gl_texture2DProj(sampler2D s, float3 t)\n"
+ "{\n"
+ " return tex2Dlod(s, float4(t.x / t.z, t.y / t.z, 0, 0));\n"
+ "}\n"
+ "\n"
+ "float4 gl_texture2DProj(sampler2D s, float4 t)\n"
+ "{\n"
+ " return tex2Dlod(s, float4(t.x / t.w, t.y / t.w, 0, 0));\n"
+ "}\n"
+ "\n";
+ }
+ else if (mOutputType == SH_HLSL11_OUTPUT)
+ {
+ out << "float4 gl_texture2DProj(Texture2D t, SamplerState s, float3 uvw)\n"
+ "{\n"
+ " return t.SampleLevel(s, float2(uvw.x / uvw.z, uvw.y / uvw.z), 0);\n"
+ "}\n"
+ "\n"
+ "float4 gl_texture2DProj(Texture2D t, SamplerState s, float4 uvw)\n"
+ "{\n"
+ " return t.SampleLevel(s, float2(uvw.x / uvw.w, uvw.y / uvw.w), 0);\n"
+ "}\n"
+ "\n";
+ }
+ else UNREACHABLE();
+ }
+
+ if (mUsesTexture2DProjLod)
+ {
+ if (mOutputType == SH_HLSL9_OUTPUT)
+ {
+ out << "float4 gl_texture2DProjLod(sampler2D s, float3 t, float lod)\n"
+ "{\n"
+ " return tex2Dlod(s, float4(t.x / t.z, t.y / t.z, 0, lod));\n"
+ "}\n"
+ "\n"
+ "float4 gl_texture2DProjLod(sampler2D s, float4 t, float lod)\n"
+ "{\n"
+ " return tex2Dlod(s, float4(t.x / t.w, t.y / t.w, 0, lod));\n"
+ "}\n"
+ "\n";
+ }
+ else if (mOutputType == SH_HLSL11_OUTPUT)
+ {
+ out << "float4 gl_texture2DProjLod(Texture2D t, SamplerState s, float3 uvw, float lod)\n"
+ "{\n"
+ " return t.SampleLevel(s, float2(uvw.x / uvw.z, uvw.y / uvw.z), lod);\n"
+ "}\n"
+ "\n"
+ "float4 gl_texture2DProjLod(Texture2D t, SamplerState s, float4 uvw, float lod)\n"
+ "{\n"
+ " return t.SampleLevel(s, float2(uvw.x / uvw.w, uvw.y / uvw.w), lod);\n"
+ "}\n"
+ "\n";
+ }
+ else UNREACHABLE();
+ }
+
+ if (mUsesTextureCube)
+ {
+ if (mOutputType == SH_HLSL9_OUTPUT)
+ {
+ out << "float4 gl_textureCube(samplerCUBE s, float3 t)\n"
+ "{\n"
+ " return texCUBElod(s, float4(t.x, t.y, t.z, 0));\n"
+ "}\n"
+ "\n";
+ }
+ else if (mOutputType == SH_HLSL11_OUTPUT)
+ {
+ out << "float4 gl_textureCube(TextureCube t, SamplerState s, float3 uvw)\n"
+ "{\n"
+ " return t.SampleLevel(s, uvw, 0);\n"
+ "}\n"
+ "\n";
+ }
+ else UNREACHABLE();
+ }
+
+ if (mUsesTextureCubeLod)
+ {
+ if (mOutputType == SH_HLSL9_OUTPUT)
+ {
+ out << "float4 gl_textureCubeLod(samplerCUBE s, float3 t, float lod)\n"
+ "{\n"
+ " return texCUBElod(s, float4(t.x, t.y, t.z, lod));\n"
+ "}\n"
+ "\n";
+ }
+ else if (mOutputType == SH_HLSL11_OUTPUT)
+ {
+ out << "float4 gl_textureCubeLod(TextureCube t, SamplerState s, float3 uvw, float lod)\n"
+ "{\n"
+ " return t.SampleLevel(s, uvw, lod);\n"
+ "}\n"
+ "\n";
+ }
+ else UNREACHABLE();
+ }
+ }
+
+ if (mUsesFragCoord)
+ {
+ out << "#define GL_USES_FRAG_COORD\n";
+ }
+
+ if (mUsesPointCoord)
+ {
+ out << "#define GL_USES_POINT_COORD\n";
+ }
+
+ if (mUsesFrontFacing)
+ {
+ out << "#define GL_USES_FRONT_FACING\n";
+ }
+
+ if (mUsesPointSize)
+ {
+ out << "#define GL_USES_POINT_SIZE\n";
+ }
+
+ if (mUsesFragDepth)
+ {
+ out << "#define GL_USES_FRAG_DEPTH\n";
+ }
+
+ if (mUsesDepthRange)
+ {
+ out << "#define GL_USES_DEPTH_RANGE\n";
+ }
+
+ if (mUsesXor)
+ {
+ out << "bool xor(bool p, bool q)\n"
+ "{\n"
+ " return (p || q) && !(p && q);\n"
+ "}\n"
+ "\n";
+ }
+
+ if (mUsesMod1)
+ {
+ out << "float mod(float x, float y)\n"
+ "{\n"
+ " return x - y * floor(x / y);\n"
+ "}\n"
+ "\n";
+ }
+
+ if (mUsesMod2v)
+ {
+ out << "float2 mod(float2 x, float2 y)\n"
+ "{\n"
+ " return x - y * floor(x / y);\n"
+ "}\n"
+ "\n";
+ }
+
+ if (mUsesMod2f)
+ {
+ out << "float2 mod(float2 x, float y)\n"
+ "{\n"
+ " return x - y * floor(x / y);\n"
+ "}\n"
+ "\n";
+ }
+
+ if (mUsesMod3v)
+ {
+ out << "float3 mod(float3 x, float3 y)\n"
+ "{\n"
+ " return x - y * floor(x / y);\n"
+ "}\n"
+ "\n";
+ }
+
+ if (mUsesMod3f)
+ {
+ out << "float3 mod(float3 x, float y)\n"
+ "{\n"
+ " return x - y * floor(x / y);\n"
+ "}\n"
+ "\n";
+ }
+
+ if (mUsesMod4v)
+ {
+ out << "float4 mod(float4 x, float4 y)\n"
+ "{\n"
+ " return x - y * floor(x / y);\n"
+ "}\n"
+ "\n";
+ }
+
+ if (mUsesMod4f)
+ {
+ out << "float4 mod(float4 x, float y)\n"
+ "{\n"
+ " return x - y * floor(x / y);\n"
+ "}\n"
+ "\n";
+ }
+
+ if (mUsesFaceforward1)
+ {
+ out << "float faceforward(float N, float I, float Nref)\n"
+ "{\n"
+ " if(dot(Nref, I) >= 0)\n"
+ " {\n"
+ " return -N;\n"
+ " }\n"
+ " else\n"
+ " {\n"
+ " return N;\n"
+ " }\n"
+ "}\n"
+ "\n";
+ }
+
+ if (mUsesFaceforward2)
+ {
+ out << "float2 faceforward(float2 N, float2 I, float2 Nref)\n"
+ "{\n"
+ " if(dot(Nref, I) >= 0)\n"
+ " {\n"
+ " return -N;\n"
+ " }\n"
+ " else\n"
+ " {\n"
+ " return N;\n"
+ " }\n"
+ "}\n"
+ "\n";
+ }
+
+ if (mUsesFaceforward3)
+ {
+ out << "float3 faceforward(float3 N, float3 I, float3 Nref)\n"
+ "{\n"
+ " if(dot(Nref, I) >= 0)\n"
+ " {\n"
+ " return -N;\n"
+ " }\n"
+ " else\n"
+ " {\n"
+ " return N;\n"
+ " }\n"
+ "}\n"
+ "\n";
+ }
+
+ if (mUsesFaceforward4)
+ {
+ out << "float4 faceforward(float4 N, float4 I, float4 Nref)\n"
+ "{\n"
+ " if(dot(Nref, I) >= 0)\n"
+ " {\n"
+ " return -N;\n"
+ " }\n"
+ " else\n"
+ " {\n"
+ " return N;\n"
+ " }\n"
+ "}\n"
+ "\n";
+ }
+
+ if (mUsesAtan2_1)
+ {
+ out << "float atanyx(float y, float x)\n"
+ "{\n"
+ " if(x == 0 && y == 0) x = 1;\n" // Avoid producing a NaN
+ " return atan2(y, x);\n"
+ "}\n";
+ }
+
+ if (mUsesAtan2_2)
+ {
+ out << "float2 atanyx(float2 y, float2 x)\n"
+ "{\n"
+ " if(x[0] == 0 && y[0] == 0) x[0] = 1;\n"
+ " if(x[1] == 0 && y[1] == 0) x[1] = 1;\n"
+ " return float2(atan2(y[0], x[0]), atan2(y[1], x[1]));\n"
+ "}\n";
+ }
+
+ if (mUsesAtan2_3)
+ {
+ out << "float3 atanyx(float3 y, float3 x)\n"
+ "{\n"
+ " if(x[0] == 0 && y[0] == 0) x[0] = 1;\n"
+ " if(x[1] == 0 && y[1] == 0) x[1] = 1;\n"
+ " if(x[2] == 0 && y[2] == 0) x[2] = 1;\n"
+ " return float3(atan2(y[0], x[0]), atan2(y[1], x[1]), atan2(y[2], x[2]));\n"
+ "}\n";
+ }
+
+ if (mUsesAtan2_4)
+ {
+ out << "float4 atanyx(float4 y, float4 x)\n"
+ "{\n"
+ " if(x[0] == 0 && y[0] == 0) x[0] = 1;\n"
+ " if(x[1] == 0 && y[1] == 0) x[1] = 1;\n"
+ " if(x[2] == 0 && y[2] == 0) x[2] = 1;\n"
+ " if(x[3] == 0 && y[3] == 0) x[3] = 1;\n"
+ " return float4(atan2(y[0], x[0]), atan2(y[1], x[1]), atan2(y[2], x[2]), atan2(y[3], x[3]));\n"
+ "}\n";
+ }
+}
+
+void OutputHLSL::visitSymbol(TIntermSymbol *node)
+{
+ TInfoSinkBase &out = mBody;
+
+ TString name = node->getSymbol();
+
+ if (name == "gl_FragColor")
+ {
+ out << "gl_Color[0]";
+ mUsesFragColor = true;
+ }
+ else if (name == "gl_FragData")
+ {
+ out << "gl_Color";
+ mUsesFragData = true;
+ }
+ else if (name == "gl_DepthRange")
+ {
+ mUsesDepthRange = true;
+ out << name;
+ }
+ else if (name == "gl_FragCoord")
+ {
+ mUsesFragCoord = true;
+ out << name;
+ }
+ else if (name == "gl_PointCoord")
+ {
+ mUsesPointCoord = true;
+ out << name;
+ }
+ else if (name == "gl_FrontFacing")
+ {
+ mUsesFrontFacing = true;
+ out << name;
+ }
+ else if (name == "gl_PointSize")
+ {
+ mUsesPointSize = true;
+ out << name;
+ }
+ else if (name == "gl_FragDepthEXT")
+ {
+ mUsesFragDepth = true;
+ out << "gl_Depth";
+ }
+ else
+ {
+ TQualifier qualifier = node->getQualifier();
+
+ if (qualifier == EvqUniform)
+ {
+ mReferencedUniforms[name] = node;
+ out << decorateUniform(name, node->getType());
+ }
+ else if (qualifier == EvqAttribute)
+ {
+ mReferencedAttributes[name] = node;
+ out << decorate(name);
+ }
+ else if (qualifier == EvqVaryingOut || qualifier == EvqInvariantVaryingOut || qualifier == EvqVaryingIn || qualifier == EvqInvariantVaryingIn)
+ {
+ mReferencedVaryings[name] = node;
+ out << decorate(name);
+ }
+ else if (qualifier == EvqInternal)
+ {
+ out << name;
+ }
+ else
+ {
+ out << decorate(name);
+ }
+ }
+}
+
+bool OutputHLSL::visitBinary(Visit visit, TIntermBinary *node)
+{
+ TInfoSinkBase &out = mBody;
+
+ switch (node->getOp())
+ {
+ case EOpAssign: outputTriplet(visit, "(", " = ", ")"); break;
+ case EOpInitialize:
+ if (visit == PreVisit)
+ {
+ // GLSL allows to write things like "float x = x;" where a new variable x is defined
+ // and the value of an existing variable x is assigned. HLSL uses C semantics (the
+ // new variable is created before the assignment is evaluated), so we need to convert
+ // this to "float t = x, x = t;".
+
+ TIntermSymbol *symbolNode = node->getLeft()->getAsSymbolNode();
+ TIntermTyped *expression = node->getRight();
+
+ sh::SearchSymbol searchSymbol(symbolNode->getSymbol());
+ expression->traverse(&searchSymbol);
+ bool sameSymbol = searchSymbol.foundMatch();
+
+ if (sameSymbol)
+ {
+ // Type already printed
+ out << "t" + str(mUniqueIndex) + " = ";
+ expression->traverse(this);
+ out << ", ";
+ symbolNode->traverse(this);
+ out << " = t" + str(mUniqueIndex);
+
+ mUniqueIndex++;
+ return false;
+ }
+ }
+ else if (visit == InVisit)
+ {
+ out << " = ";
+ }
+ break;
+ case EOpAddAssign: outputTriplet(visit, "(", " += ", ")"); break;
+ case EOpSubAssign: outputTriplet(visit, "(", " -= ", ")"); break;
+ case EOpMulAssign: outputTriplet(visit, "(", " *= ", ")"); break;
+ case EOpVectorTimesScalarAssign: outputTriplet(visit, "(", " *= ", ")"); break;
+ case EOpMatrixTimesScalarAssign: outputTriplet(visit, "(", " *= ", ")"); break;
+ case EOpVectorTimesMatrixAssign:
+ if (visit == PreVisit)
+ {
+ out << "(";
+ }
+ else if (visit == InVisit)
+ {
+ out << " = mul(";
+ node->getLeft()->traverse(this);
+ out << ", transpose(";
+ }
+ else
+ {
+ out << ")))";
+ }
+ break;
+ case EOpMatrixTimesMatrixAssign:
+ if (visit == PreVisit)
+ {
+ out << "(";
+ }
+ else if (visit == InVisit)
+ {
+ out << " = mul(";
+ node->getLeft()->traverse(this);
+ out << ", ";
+ }
+ else
+ {
+ out << "))";
+ }
+ break;
+ case EOpDivAssign: outputTriplet(visit, "(", " /= ", ")"); break;
+ case EOpIndexDirect: outputTriplet(visit, "", "[", "]"); break;
+ case EOpIndexIndirect: outputTriplet(visit, "", "[", "]"); break;
+ case EOpIndexDirectStruct:
+ if (visit == InVisit)
+ {
+ const TStructure* structure = node->getLeft()->getType().getStruct();
+ const TIntermConstantUnion* index = node->getRight()->getAsConstantUnion();
+ const TField* field = structure->fields()[index->getIConst(0)];
+ out << "." + decorateField(field->name(), node->getLeft()->getType());
+
+ return false;
+ }
+ break;
+ case EOpVectorSwizzle:
+ if (visit == InVisit)
+ {
+ out << ".";
+
+ TIntermAggregate *swizzle = node->getRight()->getAsAggregate();
+
+ if (swizzle)
+ {
+ TIntermSequence &sequence = swizzle->getSequence();
+
+ for (TIntermSequence::iterator sit = sequence.begin(); sit != sequence.end(); sit++)
+ {
+ TIntermConstantUnion *element = (*sit)->getAsConstantUnion();
+
+ if (element)
+ {
+ int i = element->getIConst(0);
+
+ switch (i)
+ {
+ case 0: out << "x"; break;
+ case 1: out << "y"; break;
+ case 2: out << "z"; break;
+ case 3: out << "w"; break;
+ default: UNREACHABLE();
+ }
+ }
+ else UNREACHABLE();
+ }
+ }
+ else UNREACHABLE();
+
+ return false; // Fully processed
+ }
+ break;
+ case EOpAdd: outputTriplet(visit, "(", " + ", ")"); break;
+ case EOpSub: outputTriplet(visit, "(", " - ", ")"); break;
+ case EOpMul: outputTriplet(visit, "(", " * ", ")"); break;
+ case EOpDiv: outputTriplet(visit, "(", " / ", ")"); break;
+ case EOpEqual:
+ case EOpNotEqual:
+ if (node->getLeft()->isScalar())
+ {
+ if (node->getOp() == EOpEqual)
+ {
+ outputTriplet(visit, "(", " == ", ")");
+ }
+ else
+ {
+ outputTriplet(visit, "(", " != ", ")");
+ }
+ }
+ else if (node->getLeft()->getBasicType() == EbtStruct)
+ {
+ if (node->getOp() == EOpEqual)
+ {
+ out << "(";
+ }
+ else
+ {
+ out << "!(";
+ }
+
+ const TFieldList &fields = node->getLeft()->getType().getStruct()->fields();
+
+ for (size_t i = 0; i < fields.size(); i++)
+ {
+ const TField *field = fields[i];
+
+ node->getLeft()->traverse(this);
+ out << "." + decorateField(field->name(), node->getLeft()->getType()) + " == ";
+ node->getRight()->traverse(this);
+ out << "." + decorateField(field->name(), node->getLeft()->getType());
+
+ if (i < fields.size() - 1)
+ {
+ out << " && ";
+ }
+ }
+
+ out << ")";
+
+ return false;
+ }
+ else
+ {
+ ASSERT(node->getLeft()->isMatrix() || node->getLeft()->isVector());
+
+ if (node->getOp() == EOpEqual)
+ {
+ outputTriplet(visit, "all(", " == ", ")");
+ }
+ else
+ {
+ outputTriplet(visit, "!all(", " == ", ")");
+ }
+ }
+ break;
+ case EOpLessThan: outputTriplet(visit, "(", " < ", ")"); break;
+ case EOpGreaterThan: outputTriplet(visit, "(", " > ", ")"); break;
+ case EOpLessThanEqual: outputTriplet(visit, "(", " <= ", ")"); break;
+ case EOpGreaterThanEqual: outputTriplet(visit, "(", " >= ", ")"); break;
+ case EOpVectorTimesScalar: outputTriplet(visit, "(", " * ", ")"); break;
+ case EOpMatrixTimesScalar: outputTriplet(visit, "(", " * ", ")"); break;
+ case EOpVectorTimesMatrix: outputTriplet(visit, "mul(", ", transpose(", "))"); break;
+ case EOpMatrixTimesVector: outputTriplet(visit, "mul(transpose(", "), ", ")"); break;
+ case EOpMatrixTimesMatrix: outputTriplet(visit, "transpose(mul(transpose(", "), transpose(", ")))"); break;
+ case EOpLogicalOr:
+ if (node->getRight()->hasSideEffects())
+ {
+ out << "s" << mUnfoldShortCircuit->getNextTemporaryIndex();
+ return false;
+ }
+ else
+ {
+ outputTriplet(visit, "(", " || ", ")");
+ return true;
+ }
+ case EOpLogicalXor:
+ mUsesXor = true;
+ outputTriplet(visit, "xor(", ", ", ")");
+ break;
+ case EOpLogicalAnd:
+ if (node->getRight()->hasSideEffects())
+ {
+ out << "s" << mUnfoldShortCircuit->getNextTemporaryIndex();
+ return false;
+ }
+ else
+ {
+ outputTriplet(visit, "(", " && ", ")");
+ return true;
+ }
+ default: UNREACHABLE();
+ }
+
+ return true;
+}
+
+bool OutputHLSL::visitUnary(Visit visit, TIntermUnary *node)
+{
+ switch (node->getOp())
+ {
+ case EOpNegative: outputTriplet(visit, "(-", "", ")"); break;
+ case EOpVectorLogicalNot: outputTriplet(visit, "(!", "", ")"); break;
+ case EOpLogicalNot: outputTriplet(visit, "(!", "", ")"); break;
+ case EOpPostIncrement: outputTriplet(visit, "(", "", "++)"); break;
+ case EOpPostDecrement: outputTriplet(visit, "(", "", "--)"); break;
+ case EOpPreIncrement: outputTriplet(visit, "(++", "", ")"); break;
+ case EOpPreDecrement: outputTriplet(visit, "(--", "", ")"); break;
+ case EOpConvIntToBool:
+ case EOpConvFloatToBool:
+ switch (node->getOperand()->getType().getNominalSize())
+ {
+ case 1: outputTriplet(visit, "bool(", "", ")"); break;
+ case 2: outputTriplet(visit, "bool2(", "", ")"); break;
+ case 3: outputTriplet(visit, "bool3(", "", ")"); break;
+ case 4: outputTriplet(visit, "bool4(", "", ")"); break;
+ default: UNREACHABLE();
+ }
+ break;
+ case EOpConvBoolToFloat:
+ case EOpConvIntToFloat:
+ switch (node->getOperand()->getType().getNominalSize())
+ {
+ case 1: outputTriplet(visit, "float(", "", ")"); break;
+ case 2: outputTriplet(visit, "float2(", "", ")"); break;
+ case 3: outputTriplet(visit, "float3(", "", ")"); break;
+ case 4: outputTriplet(visit, "float4(", "", ")"); break;
+ default: UNREACHABLE();
+ }
+ break;
+ case EOpConvFloatToInt:
+ case EOpConvBoolToInt:
+ switch (node->getOperand()->getType().getNominalSize())
+ {
+ case 1: outputTriplet(visit, "int(", "", ")"); break;
+ case 2: outputTriplet(visit, "int2(", "", ")"); break;
+ case 3: outputTriplet(visit, "int3(", "", ")"); break;
+ case 4: outputTriplet(visit, "int4(", "", ")"); break;
+ default: UNREACHABLE();
+ }
+ break;
+ case EOpRadians: outputTriplet(visit, "radians(", "", ")"); break;
+ case EOpDegrees: outputTriplet(visit, "degrees(", "", ")"); break;
+ case EOpSin: outputTriplet(visit, "sin(", "", ")"); break;
+ case EOpCos: outputTriplet(visit, "cos(", "", ")"); break;
+ case EOpTan: outputTriplet(visit, "tan(", "", ")"); break;
+ case EOpAsin: outputTriplet(visit, "asin(", "", ")"); break;
+ case EOpAcos: outputTriplet(visit, "acos(", "", ")"); break;
+ case EOpAtan: outputTriplet(visit, "atan(", "", ")"); break;
+ case EOpExp: outputTriplet(visit, "exp(", "", ")"); break;
+ case EOpLog: outputTriplet(visit, "log(", "", ")"); break;
+ case EOpExp2: outputTriplet(visit, "exp2(", "", ")"); break;
+ case EOpLog2: outputTriplet(visit, "log2(", "", ")"); break;
+ case EOpSqrt: outputTriplet(visit, "sqrt(", "", ")"); break;
+ case EOpInverseSqrt: outputTriplet(visit, "rsqrt(", "", ")"); break;
+ case EOpAbs: outputTriplet(visit, "abs(", "", ")"); break;
+ case EOpSign: outputTriplet(visit, "sign(", "", ")"); break;
+ case EOpFloor: outputTriplet(visit, "floor(", "", ")"); break;
+ case EOpCeil: outputTriplet(visit, "ceil(", "", ")"); break;
+ case EOpFract: outputTriplet(visit, "frac(", "", ")"); break;
+ case EOpLength: outputTriplet(visit, "length(", "", ")"); break;
+ case EOpNormalize: outputTriplet(visit, "normalize(", "", ")"); break;
+ case EOpDFdx:
+ if(mInsideDiscontinuousLoop || mOutputLod0Function)
+ {
+ outputTriplet(visit, "(", "", ", 0.0)");
+ }
+ else
+ {
+ outputTriplet(visit, "ddx(", "", ")");
+ }
+ break;
+ case EOpDFdy:
+ if(mInsideDiscontinuousLoop || mOutputLod0Function)
+ {
+ outputTriplet(visit, "(", "", ", 0.0)");
+ }
+ else
+ {
+ outputTriplet(visit, "ddy(", "", ")");
+ }
+ break;
+ case EOpFwidth:
+ if(mInsideDiscontinuousLoop || mOutputLod0Function)
+ {
+ outputTriplet(visit, "(", "", ", 0.0)");
+ }
+ else
+ {
+ outputTriplet(visit, "fwidth(", "", ")");
+ }
+ break;
+ case EOpAny: outputTriplet(visit, "any(", "", ")"); break;
+ case EOpAll: outputTriplet(visit, "all(", "", ")"); break;
+ default: UNREACHABLE();
+ }
+
+ return true;
+}
+
+bool OutputHLSL::visitAggregate(Visit visit, TIntermAggregate *node)
+{
+ TInfoSinkBase &out = mBody;
+
+ switch (node->getOp())
+ {
+ case EOpSequence:
+ {
+ if (mInsideFunction)
+ {
+ outputLineDirective(node->getLine().first_line);
+ out << "{\n";
+
+ mScopeDepth++;
+
+ if (mScopeBracket.size() < mScopeDepth)
+ {
+ mScopeBracket.push_back(0); // New scope level
+ }
+ else
+ {
+ mScopeBracket[mScopeDepth - 1]++; // New scope at existing level
+ }
+ }
+
+ for (TIntermSequence::iterator sit = node->getSequence().begin(); sit != node->getSequence().end(); sit++)
+ {
+ outputLineDirective((*sit)->getLine().first_line);
+
+ traverseStatements(*sit);
+
+ out << ";\n";
+ }
+
+ if (mInsideFunction)
+ {
+ outputLineDirective(node->getLine().last_line);
+ out << "}\n";
+
+ mScopeDepth--;
+ }
+
+ return false;
+ }
+ case EOpDeclaration:
+ if (visit == PreVisit)
+ {
+ TIntermSequence &sequence = node->getSequence();
+ TIntermTyped *variable = sequence[0]->getAsTyped();
+
+ if (variable && (variable->getQualifier() == EvqTemporary || variable->getQualifier() == EvqGlobal))
+ {
+ if (variable->getType().getStruct())
+ {
+ addConstructor(variable->getType(), scopedStruct(variable->getType().getStruct()->name()), NULL);
+ }
+
+ if (!variable->getAsSymbolNode() || variable->getAsSymbolNode()->getSymbol() != "") // Variable declaration
+ {
+ if (!mInsideFunction)
+ {
+ out << "static ";
+ }
+
+ out << typeString(variable->getType()) + " ";
+
+ for (TIntermSequence::iterator sit = sequence.begin(); sit != sequence.end(); sit++)
+ {
+ TIntermSymbol *symbol = (*sit)->getAsSymbolNode();
+
+ if (symbol)
+ {
+ symbol->traverse(this);
+ out << arrayString(symbol->getType());
+ out << " = " + initializer(symbol->getType());
+ }
+ else
+ {
+ (*sit)->traverse(this);
+ }
+
+ if (*sit != sequence.back())
+ {
+ out << ", ";
+ }
+ }
+ }
+ else if (variable->getAsSymbolNode() && variable->getAsSymbolNode()->getSymbol() == "") // Type (struct) declaration
+ {
+ // Already added to constructor map
+ }
+ else UNREACHABLE();
+ }
+ else if (variable && (variable->getQualifier() == EvqVaryingOut || variable->getQualifier() == EvqInvariantVaryingOut))
+ {
+ for (TIntermSequence::iterator sit = sequence.begin(); sit != sequence.end(); sit++)
+ {
+ TIntermSymbol *symbol = (*sit)->getAsSymbolNode();
+
+ if (symbol)
+ {
+ // Vertex (output) varyings which are declared but not written to should still be declared to allow successful linking
+ mReferencedVaryings[symbol->getSymbol()] = symbol;
+ }
+ else
+ {
+ (*sit)->traverse(this);
+ }
+ }
+ }
+
+ return false;
+ }
+ else if (visit == InVisit)
+ {
+ out << ", ";
+ }
+ break;
+ case EOpPrototype:
+ if (visit == PreVisit)
+ {
+ out << typeString(node->getType()) << " " << decorate(node->getName()) << (mOutputLod0Function ? "Lod0(" : "(");
+
+ TIntermSequence &arguments = node->getSequence();
+
+ for (unsigned int i = 0; i < arguments.size(); i++)
+ {
+ TIntermSymbol *symbol = arguments[i]->getAsSymbolNode();
+
+ if (symbol)
+ {
+ out << argumentString(symbol);
+
+ if (i < arguments.size() - 1)
+ {
+ out << ", ";
+ }
+ }
+ else UNREACHABLE();
+ }
+
+ out << ");\n";
+
+ // Also prototype the Lod0 variant if needed
+ if (mContainsLoopDiscontinuity && !mOutputLod0Function)
+ {
+ mOutputLod0Function = true;
+ node->traverse(this);
+ mOutputLod0Function = false;
+ }
+
+ return false;
+ }
+ break;
+ case EOpComma: outputTriplet(visit, "(", ", ", ")"); break;
+ case EOpFunction:
+ {
+ TString name = TFunction::unmangleName(node->getName());
+
+ out << typeString(node->getType()) << " ";
+
+ if (name == "main")
+ {
+ out << "gl_main(";
+ }
+ else
+ {
+ out << decorate(name) << (mOutputLod0Function ? "Lod0(" : "(");
+ }
+
+ TIntermSequence &sequence = node->getSequence();
+ TIntermSequence &arguments = sequence[0]->getAsAggregate()->getSequence();
+
+ for (unsigned int i = 0; i < arguments.size(); i++)
+ {
+ TIntermSymbol *symbol = arguments[i]->getAsSymbolNode();
+
+ if (symbol)
+ {
+ if (symbol->getType().getStruct())
+ {
+ addConstructor(symbol->getType(), scopedStruct(symbol->getType().getStruct()->name()), NULL);
+ }
+
+ out << argumentString(symbol);
+
+ if (i < arguments.size() - 1)
+ {
+ out << ", ";
+ }
+ }
+ else UNREACHABLE();
+ }
+
+ out << ")\n"
+ "{\n";
+
+ if (sequence.size() > 1)
+ {
+ mInsideFunction = true;
+ sequence[1]->traverse(this);
+ mInsideFunction = false;
+ }
+
+ out << "}\n";
+
+ if (mContainsLoopDiscontinuity && !mOutputLod0Function)
+ {
+ if (name != "main")
+ {
+ mOutputLod0Function = true;
+ node->traverse(this);
+ mOutputLod0Function = false;
+ }
+ }
+
+ return false;
+ }
+ break;
+ case EOpFunctionCall:
+ {
+ TString name = TFunction::unmangleName(node->getName());
+ bool lod0 = mInsideDiscontinuousLoop || mOutputLod0Function;
+
+ if (node->isUserDefined())
+ {
+ out << decorate(name) << (lod0 ? "Lod0(" : "(");
+ }
+ else
+ {
+ if (name == "texture2D")
+ {
+ if (!lod0)
+ {
+ if (node->getSequence().size() == 2)
+ {
+ mUsesTexture2D = true;
+ }
+ else if (node->getSequence().size() == 3)
+ {
+ mUsesTexture2D_bias = true;
+ }
+ else UNREACHABLE();
+
+ out << "gl_texture2D(";
+ }
+ else
+ {
+ if (node->getSequence().size() == 2)
+ {
+ mUsesTexture2DLod0 = true;
+ }
+ else if (node->getSequence().size() == 3)
+ {
+ mUsesTexture2DLod0_bias = true;
+ }
+ else UNREACHABLE();
+
+ out << "gl_texture2DLod0(";
+ }
+ }
+ else if (name == "texture2DProj")
+ {
+ if (!lod0)
+ {
+ if (node->getSequence().size() == 2)
+ {
+ mUsesTexture2DProj = true;
+ }
+ else if (node->getSequence().size() == 3)
+ {
+ mUsesTexture2DProj_bias = true;
+ }
+ else UNREACHABLE();
+
+ out << "gl_texture2DProj(";
+ }
+ else
+ {
+ if (node->getSequence().size() == 2)
+ {
+ mUsesTexture2DProjLod0 = true;
+ }
+ else if (node->getSequence().size() == 3)
+ {
+ mUsesTexture2DProjLod0_bias = true;
+ }
+ else UNREACHABLE();
+
+ out << "gl_texture2DProjLod0(";
+ }
+ }
+ else if (name == "textureCube")
+ {
+ if (!lod0)
+ {
+ if (node->getSequence().size() == 2)
+ {
+ mUsesTextureCube = true;
+ }
+ else if (node->getSequence().size() == 3)
+ {
+ mUsesTextureCube_bias = true;
+ }
+ else UNREACHABLE();
+
+ out << "gl_textureCube(";
+ }
+ else
+ {
+ if (node->getSequence().size() == 2)
+ {
+ mUsesTextureCubeLod0 = true;
+ }
+ else if (node->getSequence().size() == 3)
+ {
+ mUsesTextureCubeLod0_bias = true;
+ }
+ else UNREACHABLE();
+
+ out << "gl_textureCubeLod0(";
+ }
+ }
+ else if (name == "texture2DLod")
+ {
+ if (node->getSequence().size() == 3)
+ {
+ mUsesTexture2DLod = true;
+ }
+ else UNREACHABLE();
+
+ out << "gl_texture2DLod(";
+ }
+ else if (name == "texture2DProjLod")
+ {
+ if (node->getSequence().size() == 3)
+ {
+ mUsesTexture2DProjLod = true;
+ }
+ else UNREACHABLE();
+
+ out << "gl_texture2DProjLod(";
+ }
+ else if (name == "textureCubeLod")
+ {
+ if (node->getSequence().size() == 3)
+ {
+ mUsesTextureCubeLod = true;
+ }
+ else UNREACHABLE();
+
+ out << "gl_textureCubeLod(";
+ }
+ else UNREACHABLE();
+ }
+
+ TIntermSequence &arguments = node->getSequence();
+
+ for (TIntermSequence::iterator arg = arguments.begin(); arg != arguments.end(); arg++)
+ {
+ if (mOutputType == SH_HLSL11_OUTPUT && IsSampler((*arg)->getAsTyped()->getBasicType()))
+ {
+ out << "texture_";
+ (*arg)->traverse(this);
+ out << ", sampler_";
+ }
+
+ (*arg)->traverse(this);
+
+ if (arg < arguments.end() - 1)
+ {
+ out << ", ";
+ }
+ }
+
+ out << ")";
+
+ return false;
+ }
+ break;
+ case EOpParameters: outputTriplet(visit, "(", ", ", ")\n{\n"); break;
+ case EOpConstructFloat:
+ addConstructor(node->getType(), "vec1", &node->getSequence());
+ outputTriplet(visit, "vec1(", "", ")");
+ break;
+ case EOpConstructVec2:
+ addConstructor(node->getType(), "vec2", &node->getSequence());
+ outputTriplet(visit, "vec2(", ", ", ")");
+ break;
+ case EOpConstructVec3:
+ addConstructor(node->getType(), "vec3", &node->getSequence());
+ outputTriplet(visit, "vec3(", ", ", ")");
+ break;
+ case EOpConstructVec4:
+ addConstructor(node->getType(), "vec4", &node->getSequence());
+ outputTriplet(visit, "vec4(", ", ", ")");
+ break;
+ case EOpConstructBool:
+ addConstructor(node->getType(), "bvec1", &node->getSequence());
+ outputTriplet(visit, "bvec1(", "", ")");
+ break;
+ case EOpConstructBVec2:
+ addConstructor(node->getType(), "bvec2", &node->getSequence());
+ outputTriplet(visit, "bvec2(", ", ", ")");
+ break;
+ case EOpConstructBVec3:
+ addConstructor(node->getType(), "bvec3", &node->getSequence());
+ outputTriplet(visit, "bvec3(", ", ", ")");
+ break;
+ case EOpConstructBVec4:
+ addConstructor(node->getType(), "bvec4", &node->getSequence());
+ outputTriplet(visit, "bvec4(", ", ", ")");
+ break;
+ case EOpConstructInt:
+ addConstructor(node->getType(), "ivec1", &node->getSequence());
+ outputTriplet(visit, "ivec1(", "", ")");
+ break;
+ case EOpConstructIVec2:
+ addConstructor(node->getType(), "ivec2", &node->getSequence());
+ outputTriplet(visit, "ivec2(", ", ", ")");
+ break;
+ case EOpConstructIVec3:
+ addConstructor(node->getType(), "ivec3", &node->getSequence());
+ outputTriplet(visit, "ivec3(", ", ", ")");
+ break;
+ case EOpConstructIVec4:
+ addConstructor(node->getType(), "ivec4", &node->getSequence());
+ outputTriplet(visit, "ivec4(", ", ", ")");
+ break;
+ case EOpConstructMat2:
+ addConstructor(node->getType(), "mat2", &node->getSequence());
+ outputTriplet(visit, "mat2(", ", ", ")");
+ break;
+ case EOpConstructMat3:
+ addConstructor(node->getType(), "mat3", &node->getSequence());
+ outputTriplet(visit, "mat3(", ", ", ")");
+ break;
+ case EOpConstructMat4:
+ addConstructor(node->getType(), "mat4", &node->getSequence());
+ outputTriplet(visit, "mat4(", ", ", ")");
+ break;
+ case EOpConstructStruct:
+ addConstructor(node->getType(), scopedStruct(node->getType().getStruct()->name()), &node->getSequence());
+ outputTriplet(visit, structLookup(node->getType().getStruct()->name()) + "_ctor(", ", ", ")");
+ break;
+ case EOpLessThan: outputTriplet(visit, "(", " < ", ")"); break;
+ case EOpGreaterThan: outputTriplet(visit, "(", " > ", ")"); break;
+ case EOpLessThanEqual: outputTriplet(visit, "(", " <= ", ")"); break;
+ case EOpGreaterThanEqual: outputTriplet(visit, "(", " >= ", ")"); break;
+ case EOpVectorEqual: outputTriplet(visit, "(", " == ", ")"); break;
+ case EOpVectorNotEqual: outputTriplet(visit, "(", " != ", ")"); break;
+ case EOpMod:
+ {
+ // We need to look at the number of components in both arguments
+ switch (node->getSequence()[0]->getAsTyped()->getNominalSize() * 10
+ + node->getSequence()[1]->getAsTyped()->getNominalSize())
+ {
+ case 11: mUsesMod1 = true; break;
+ case 22: mUsesMod2v = true; break;
+ case 21: mUsesMod2f = true; break;
+ case 33: mUsesMod3v = true; break;
+ case 31: mUsesMod3f = true; break;
+ case 44: mUsesMod4v = true; break;
+ case 41: mUsesMod4f = true; break;
+ default: UNREACHABLE();
+ }
+
+ outputTriplet(visit, "mod(", ", ", ")");
+ }
+ break;
+ case EOpPow: outputTriplet(visit, "pow(", ", ", ")"); break;
+ case EOpAtan:
+ ASSERT(node->getSequence().size() == 2); // atan(x) is a unary operator
+ switch (node->getSequence()[0]->getAsTyped()->getNominalSize())
+ {
+ case 1: mUsesAtan2_1 = true; break;
+ case 2: mUsesAtan2_2 = true; break;
+ case 3: mUsesAtan2_3 = true; break;
+ case 4: mUsesAtan2_4 = true; break;
+ default: UNREACHABLE();
+ }
+ outputTriplet(visit, "atanyx(", ", ", ")");
+ break;
+ case EOpMin: outputTriplet(visit, "min(", ", ", ")"); break;
+ case EOpMax: outputTriplet(visit, "max(", ", ", ")"); break;
+ case EOpClamp: outputTriplet(visit, "clamp(", ", ", ")"); break;
+ case EOpMix: outputTriplet(visit, "lerp(", ", ", ")"); break;
+ case EOpStep: outputTriplet(visit, "step(", ", ", ")"); break;
+ case EOpSmoothStep: outputTriplet(visit, "smoothstep(", ", ", ")"); break;
+ case EOpDistance: outputTriplet(visit, "distance(", ", ", ")"); break;
+ case EOpDot: outputTriplet(visit, "dot(", ", ", ")"); break;
+ case EOpCross: outputTriplet(visit, "cross(", ", ", ")"); break;
+ case EOpFaceForward:
+ {
+ switch (node->getSequence()[0]->getAsTyped()->getNominalSize()) // Number of components in the first argument
+ {
+ case 1: mUsesFaceforward1 = true; break;
+ case 2: mUsesFaceforward2 = true; break;
+ case 3: mUsesFaceforward3 = true; break;
+ case 4: mUsesFaceforward4 = true; break;
+ default: UNREACHABLE();
+ }
+
+ outputTriplet(visit, "faceforward(", ", ", ")");
+ }
+ break;
+ case EOpReflect: outputTriplet(visit, "reflect(", ", ", ")"); break;
+ case EOpRefract: outputTriplet(visit, "refract(", ", ", ")"); break;
+ case EOpMul: outputTriplet(visit, "(", " * ", ")"); break;
+ default: UNREACHABLE();
+ }
+
+ return true;
+}
+
+bool OutputHLSL::visitSelection(Visit visit, TIntermSelection *node)
+{
+ TInfoSinkBase &out = mBody;
+
+ if (node->usesTernaryOperator())
+ {
+ out << "s" << mUnfoldShortCircuit->getNextTemporaryIndex();
+ }
+ else // if/else statement
+ {
+ mUnfoldShortCircuit->traverse(node->getCondition());
+
+ out << "if (";
+
+ node->getCondition()->traverse(this);
+
+ out << ")\n";
+
+ outputLineDirective(node->getLine().first_line);
+ out << "{\n";
+
+ bool discard = false;
+
+ if (node->getTrueBlock())
+ {
+ traverseStatements(node->getTrueBlock());
+
+ // Detect true discard
+ discard = (discard || FindDiscard::search(node->getTrueBlock()));
+ }
+
+ outputLineDirective(node->getLine().first_line);
+ out << ";\n}\n";
+
+ if (node->getFalseBlock())
+ {
+ out << "else\n";
+
+ outputLineDirective(node->getFalseBlock()->getLine().first_line);
+ out << "{\n";
+
+ outputLineDirective(node->getFalseBlock()->getLine().first_line);
+ traverseStatements(node->getFalseBlock());
+
+ outputLineDirective(node->getFalseBlock()->getLine().first_line);
+ out << ";\n}\n";
+
+ // Detect false discard
+ discard = (discard || FindDiscard::search(node->getFalseBlock()));
+ }
+
+ // ANGLE issue 486: Detect problematic conditional discard
+ if (discard && FindSideEffectRewriting::search(node))
+ {
+ mUsesDiscardRewriting = true;
+ }
+ }
+
+ return false;
+}
+
+void OutputHLSL::visitConstantUnion(TIntermConstantUnion *node)
+{
+ writeConstantUnion(node->getType(), node->getUnionArrayPointer());
+}
+
+bool OutputHLSL::visitLoop(Visit visit, TIntermLoop *node)
+{
+ bool wasDiscontinuous = mInsideDiscontinuousLoop;
+
+ if (mContainsLoopDiscontinuity && !mInsideDiscontinuousLoop)
+ {
+ mInsideDiscontinuousLoop = containsLoopDiscontinuity(node);
+ }
+
+ if (mOutputType == SH_HLSL9_OUTPUT)
+ {
+ if (handleExcessiveLoop(node))
+ {
+ return false;
+ }
+ }
+
+ TInfoSinkBase &out = mBody;
+
+ if (node->getType() == ELoopDoWhile)
+ {
+ out << "{do\n";
+
+ outputLineDirective(node->getLine().first_line);
+ out << "{\n";
+ }
+ else
+ {
+ out << "{for(";
+
+ if (node->getInit())
+ {
+ node->getInit()->traverse(this);
+ }
+
+ out << "; ";
+
+ if (node->getCondition())
+ {
+ node->getCondition()->traverse(this);
+ }
+
+ out << "; ";
+
+ if (node->getExpression())
+ {
+ node->getExpression()->traverse(this);
+ }
+
+ out << ")\n";
+
+ outputLineDirective(node->getLine().first_line);
+ out << "{\n";
+ }
+
+ if (node->getBody())
+ {
+ traverseStatements(node->getBody());
+ }
+
+ outputLineDirective(node->getLine().first_line);
+ out << ";}\n";
+
+ if (node->getType() == ELoopDoWhile)
+ {
+ outputLineDirective(node->getCondition()->getLine().first_line);
+ out << "while(\n";
+
+ node->getCondition()->traverse(this);
+
+ out << ");";
+ }
+
+ out << "}\n";
+
+ mInsideDiscontinuousLoop = wasDiscontinuous;
+
+ return false;
+}
+
+bool OutputHLSL::visitBranch(Visit visit, TIntermBranch *node)
+{
+ TInfoSinkBase &out = mBody;
+
+ switch (node->getFlowOp())
+ {
+ case EOpKill:
+ outputTriplet(visit, "discard;\n", "", "");
+ break;
+ case EOpBreak:
+ if (visit == PreVisit)
+ {
+ if (mExcessiveLoopIndex)
+ {
+ out << "{Break";
+ mExcessiveLoopIndex->traverse(this);
+ out << " = true; break;}\n";
+ }
+ else
+ {
+ out << "break;\n";
+ }
+ }
+ break;
+ case EOpContinue: outputTriplet(visit, "continue;\n", "", ""); break;
+ case EOpReturn:
+ if (visit == PreVisit)
+ {
+ if (node->getExpression())
+ {
+ out << "return ";
+ }
+ else
+ {
+ out << "return;\n";
+ }
+ }
+ else if (visit == PostVisit)
+ {
+ if (node->getExpression())
+ {
+ out << ";\n";
+ }
+ }
+ break;
+ default: UNREACHABLE();
+ }
+
+ return true;
+}
+
+void OutputHLSL::traverseStatements(TIntermNode *node)
+{
+ if (isSingleStatement(node))
+ {
+ mUnfoldShortCircuit->traverse(node);
+ }
+
+ node->traverse(this);
+}
+
+bool OutputHLSL::isSingleStatement(TIntermNode *node)
+{
+ TIntermAggregate *aggregate = node->getAsAggregate();
+
+ if (aggregate)
+ {
+ if (aggregate->getOp() == EOpSequence)
+ {
+ return false;
+ }
+ else
+ {
+ for (TIntermSequence::iterator sit = aggregate->getSequence().begin(); sit != aggregate->getSequence().end(); sit++)
+ {
+ if (!isSingleStatement(*sit))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+ }
+
+ return true;
+}
+
+// Handle loops with more than 254 iterations (unsupported by D3D9) by splitting them
+// (The D3D documentation says 255 iterations, but the compiler complains at anything more than 254).
+bool OutputHLSL::handleExcessiveLoop(TIntermLoop *node)
+{
+ const int MAX_LOOP_ITERATIONS = 254;
+ TInfoSinkBase &out = mBody;
+
+ // Parse loops of the form:
+ // for(int index = initial; index [comparator] limit; index += increment)
+ TIntermSymbol *index = NULL;
+ TOperator comparator = EOpNull;
+ int initial = 0;
+ int limit = 0;
+ int increment = 0;
+
+ // Parse index name and intial value
+ if (node->getInit())
+ {
+ TIntermAggregate *init = node->getInit()->getAsAggregate();
+
+ if (init)
+ {
+ TIntermSequence &sequence = init->getSequence();
+ TIntermTyped *variable = sequence[0]->getAsTyped();
+
+ if (variable && variable->getQualifier() == EvqTemporary)
+ {
+ TIntermBinary *assign = variable->getAsBinaryNode();
+
+ if (assign->getOp() == EOpInitialize)
+ {
+ TIntermSymbol *symbol = assign->getLeft()->getAsSymbolNode();
+ TIntermConstantUnion *constant = assign->getRight()->getAsConstantUnion();
+
+ if (symbol && constant)
+ {
+ if (constant->getBasicType() == EbtInt && constant->getNominalSize() == 1)
+ {
+ index = symbol;
+ initial = constant->getIConst(0);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Parse comparator and limit value
+ if (index != NULL && node->getCondition())
+ {
+ TIntermBinary *test = node->getCondition()->getAsBinaryNode();
+
+ if (test && test->getLeft()->getAsSymbolNode()->getId() == index->getId())
+ {
+ TIntermConstantUnion *constant = test->getRight()->getAsConstantUnion();
+
+ if (constant)
+ {
+ if (constant->getBasicType() == EbtInt && constant->getNominalSize() == 1)
+ {
+ comparator = test->getOp();
+ limit = constant->getIConst(0);
+ }
+ }
+ }
+ }
+
+ // Parse increment
+ if (index != NULL && comparator != EOpNull && node->getExpression())
+ {
+ TIntermBinary *binaryTerminal = node->getExpression()->getAsBinaryNode();
+ TIntermUnary *unaryTerminal = node->getExpression()->getAsUnaryNode();
+
+ if (binaryTerminal)
+ {
+ TOperator op = binaryTerminal->getOp();
+ TIntermConstantUnion *constant = binaryTerminal->getRight()->getAsConstantUnion();
+
+ if (constant)
+ {
+ if (constant->getBasicType() == EbtInt && constant->getNominalSize() == 1)
+ {
+ int value = constant->getIConst(0);
+
+ switch (op)
+ {
+ case EOpAddAssign: increment = value; break;
+ case EOpSubAssign: increment = -value; break;
+ default: UNIMPLEMENTED();
+ }
+ }
+ }
+ }
+ else if (unaryTerminal)
+ {
+ TOperator op = unaryTerminal->getOp();
+
+ switch (op)
+ {
+ case EOpPostIncrement: increment = 1; break;
+ case EOpPostDecrement: increment = -1; break;
+ case EOpPreIncrement: increment = 1; break;
+ case EOpPreDecrement: increment = -1; break;
+ default: UNIMPLEMENTED();
+ }
+ }
+ }
+
+ if (index != NULL && comparator != EOpNull && increment != 0)
+ {
+ if (comparator == EOpLessThanEqual)
+ {
+ comparator = EOpLessThan;
+ limit += 1;
+ }
+
+ if (comparator == EOpLessThan)
+ {
+ int iterations = (limit - initial) / increment;
+
+ if (iterations <= MAX_LOOP_ITERATIONS)
+ {
+ return false; // Not an excessive loop
+ }
+
+ TIntermSymbol *restoreIndex = mExcessiveLoopIndex;
+ mExcessiveLoopIndex = index;
+
+ out << "{int ";
+ index->traverse(this);
+ out << ";\n"
+ "bool Break";
+ index->traverse(this);
+ out << " = false;\n";
+
+ bool firstLoopFragment = true;
+
+ while (iterations > 0)
+ {
+ int clampedLimit = initial + increment * std::min(MAX_LOOP_ITERATIONS, iterations);
+
+ if (!firstLoopFragment)
+ {
+ out << "if (!Break";
+ index->traverse(this);
+ out << ") {\n";
+ }
+
+ if (iterations <= MAX_LOOP_ITERATIONS) // Last loop fragment
+ {
+ mExcessiveLoopIndex = NULL; // Stops setting the Break flag
+ }
+
+ // for(int index = initial; index < clampedLimit; index += increment)
+
+ out << "for(";
+ index->traverse(this);
+ out << " = ";
+ out << initial;
+
+ out << "; ";
+ index->traverse(this);
+ out << " < ";
+ out << clampedLimit;
+
+ out << "; ";
+ index->traverse(this);
+ out << " += ";
+ out << increment;
+ out << ")\n";
+
+ outputLineDirective(node->getLine().first_line);
+ out << "{\n";
+
+ if (node->getBody())
+ {
+ node->getBody()->traverse(this);
+ }
+
+ outputLineDirective(node->getLine().first_line);
+ out << ";}\n";
+
+ if (!firstLoopFragment)
+ {
+ out << "}\n";
+ }
+
+ firstLoopFragment = false;
+
+ initial += MAX_LOOP_ITERATIONS * increment;
+ iterations -= MAX_LOOP_ITERATIONS;
+ }
+
+ out << "}";
+
+ mExcessiveLoopIndex = restoreIndex;
+
+ return true;
+ }
+ else UNIMPLEMENTED();
+ }
+
+ return false; // Not handled as an excessive loop
+}
+
+void OutputHLSL::outputTriplet(Visit visit, const TString &preString, const TString &inString, const TString &postString)
+{
+ TInfoSinkBase &out = mBody;
+
+ if (visit == PreVisit)
+ {
+ out << preString;
+ }
+ else if (visit == InVisit)
+ {
+ out << inString;
+ }
+ else if (visit == PostVisit)
+ {
+ out << postString;
+ }
+}
+
+void OutputHLSL::outputLineDirective(int line)
+{
+ if ((mContext.compileOptions & SH_LINE_DIRECTIVES) && (line > 0))
+ {
+ mBody << "\n";
+ mBody << "#line " << line;
+
+ if (mContext.sourcePath)
+ {
+ mBody << " \"" << mContext.sourcePath << "\"";
+ }
+
+ mBody << "\n";
+ }
+}
+
+TString OutputHLSL::argumentString(const TIntermSymbol *symbol)
+{
+ TQualifier qualifier = symbol->getQualifier();
+ const TType &type = symbol->getType();
+ TString name = symbol->getSymbol();
+
+ if (name.empty()) // HLSL demands named arguments, also for prototypes
+ {
+ name = "x" + str(mUniqueIndex++);
+ }
+ else
+ {
+ name = decorate(name);
+ }
+
+ if (mOutputType == SH_HLSL11_OUTPUT && IsSampler(type.getBasicType()))
+ {
+ return qualifierString(qualifier) + " " + textureString(type) + " texture_" + name + arrayString(type) + ", " +
+ qualifierString(qualifier) + " SamplerState sampler_" + name + arrayString(type);
+ }
+
+ return qualifierString(qualifier) + " " + typeString(type) + " " + name + arrayString(type);
+}
+
+TString OutputHLSL::qualifierString(TQualifier qualifier)
+{
+ switch(qualifier)
+ {
+ case EvqIn: return "in";
+ case EvqOut: return "out";
+ case EvqInOut: return "inout";
+ case EvqConstReadOnly: return "const";
+ default: UNREACHABLE();
+ }
+
+ return "";
+}
+
+TString OutputHLSL::typeString(const TType &type)
+{
+ if (type.getBasicType() == EbtStruct)
+ {
+ const TString& typeName = type.getStruct()->name();
+ if (typeName != "")
+ {
+ return structLookup(typeName);
+ }
+ else // Nameless structure, define in place
+ {
+ const TFieldList &fields = type.getStruct()->fields();
+
+ TString string = "struct\n"
+ "{\n";
+
+ for (unsigned int i = 0; i < fields.size(); i++)
+ {
+ const TField *field = fields[i];
+
+ string += " " + typeString(*field->type()) + " " + decorate(field->name()) + arrayString(*field->type()) + ";\n";
+ }
+
+ string += "} ";
+
+ return string;
+ }
+ }
+ else if (type.isMatrix())
+ {
+ switch (type.getNominalSize())
+ {
+ case 2: return "float2x2";
+ case 3: return "float3x3";
+ case 4: return "float4x4";
+ }
+ }
+ else
+ {
+ switch (type.getBasicType())
+ {
+ case EbtFloat:
+ switch (type.getNominalSize())
+ {
+ case 1: return "float";
+ case 2: return "float2";
+ case 3: return "float3";
+ case 4: return "float4";
+ }
+ case EbtInt:
+ switch (type.getNominalSize())
+ {
+ case 1: return "int";
+ case 2: return "int2";
+ case 3: return "int3";
+ case 4: return "int4";
+ }
+ case EbtBool:
+ switch (type.getNominalSize())
+ {
+ case 1: return "bool";
+ case 2: return "bool2";
+ case 3: return "bool3";
+ case 4: return "bool4";
+ }
+ case EbtVoid:
+ return "void";
+ case EbtSampler2D:
+ return "sampler2D";
+ case EbtSamplerCube:
+ return "samplerCUBE";
+ case EbtSamplerExternalOES:
+ return "sampler2D";
+ default:
+ break;
+ }
+ }
+
+ UNREACHABLE();
+ return "<unknown type>";
+}
+
+TString OutputHLSL::textureString(const TType &type)
+{
+ switch (type.getBasicType())
+ {
+ case EbtSampler2D:
+ return "Texture2D";
+ case EbtSamplerCube:
+ return "TextureCube";
+ case EbtSamplerExternalOES:
+ return "Texture2D";
+ default:
+ break;
+ }
+
+ UNREACHABLE();
+ return "<unknown texture type>";
+}
+
+TString OutputHLSL::arrayString(const TType &type)
+{
+ if (!type.isArray())
+ {
+ return "";
+ }
+
+ return "[" + str(type.getArraySize()) + "]";
+}
+
+TString OutputHLSL::initializer(const TType &type)
+{
+ TString string;
+
+ size_t size = type.getObjectSize();
+ for (size_t component = 0; component < size; component++)
+ {
+ string += "0";
+
+ if (component + 1 < size)
+ {
+ string += ", ";
+ }
+ }
+
+ return "{" + string + "}";
+}
+
+void OutputHLSL::addConstructor(const TType &type, const TString &name, const TIntermSequence *parameters)
+{
+ if (name == "")
+ {
+ return; // Nameless structures don't have constructors
+ }
+
+ if (type.getStruct() && mStructNames.find(decorate(name)) != mStructNames.end())
+ {
+ return; // Already added
+ }
+
+ TType ctorType = type;
+ ctorType.clearArrayness();
+ ctorType.setPrecision(EbpHigh);
+ ctorType.setQualifier(EvqTemporary);
+
+ TString ctorName = type.getStruct() ? decorate(name) : name;
+
+ typedef std::vector<TType> ParameterArray;
+ ParameterArray ctorParameters;
+
+ if (type.getStruct())
+ {
+ mStructNames.insert(decorate(name));
+
+ TString structure;
+ structure += "struct " + decorate(name) + "\n"
+ "{\n";
+
+ const TFieldList &fields = type.getStruct()->fields();
+
+ for (unsigned int i = 0; i < fields.size(); i++)
+ {
+ const TField *field = fields[i];
+
+ structure += " " + typeString(*field->type()) + " " + decorateField(field->name(), type) + arrayString(*field->type()) + ";\n";
+ }
+
+ structure += "};\n";
+
+ if (std::find(mStructDeclarations.begin(), mStructDeclarations.end(), structure) == mStructDeclarations.end())
+ {
+ mStructDeclarations.push_back(structure);
+ }
+
+ for (unsigned int i = 0; i < fields.size(); i++)
+ {
+ ctorParameters.push_back(*fields[i]->type());
+ }
+ }
+ else if (parameters)
+ {
+ for (TIntermSequence::const_iterator parameter = parameters->begin(); parameter != parameters->end(); parameter++)
+ {
+ ctorParameters.push_back((*parameter)->getAsTyped()->getType());
+ }
+ }
+ else UNREACHABLE();
+
+ TString constructor;
+
+ if (ctorType.getStruct())
+ {
+ constructor += ctorName + " " + ctorName + "_ctor(";
+ }
+ else // Built-in type
+ {
+ constructor += typeString(ctorType) + " " + ctorName + "(";
+ }
+
+ for (unsigned int parameter = 0; parameter < ctorParameters.size(); parameter++)
+ {
+ const TType &type = ctorParameters[parameter];
+
+ constructor += typeString(type) + " x" + str(parameter) + arrayString(type);
+
+ if (parameter < ctorParameters.size() - 1)
+ {
+ constructor += ", ";
+ }
+ }
+
+ constructor += ")\n"
+ "{\n";
+
+ if (ctorType.getStruct())
+ {
+ constructor += " " + ctorName + " structure = {";
+ }
+ else
+ {
+ constructor += " return " + typeString(ctorType) + "(";
+ }
+
+ if (ctorType.isMatrix() && ctorParameters.size() == 1)
+ {
+ int dim = ctorType.getNominalSize();
+ const TType &parameter = ctorParameters[0];
+
+ if (parameter.isScalar())
+ {
+ for (int row = 0; row < dim; row++)
+ {
+ for (int col = 0; col < dim; col++)
+ {
+ constructor += TString((row == col) ? "x0" : "0.0");
+
+ if (row < dim - 1 || col < dim - 1)
+ {
+ constructor += ", ";
+ }
+ }
+ }
+ }
+ else if (parameter.isMatrix())
+ {
+ for (int row = 0; row < dim; row++)
+ {
+ for (int col = 0; col < dim; col++)
+ {
+ if (row < parameter.getNominalSize() && col < parameter.getNominalSize())
+ {
+ constructor += TString("x0") + "[" + str(row) + "]" + "[" + str(col) + "]";
+ }
+ else
+ {
+ constructor += TString((row == col) ? "1.0" : "0.0");
+ }
+
+ if (row < dim - 1 || col < dim - 1)
+ {
+ constructor += ", ";
+ }
+ }
+ }
+ }
+ else UNREACHABLE();
+ }
+ else
+ {
+ size_t remainingComponents = ctorType.getObjectSize();
+ size_t parameterIndex = 0;
+
+ while (remainingComponents > 0)
+ {
+ const TType &parameter = ctorParameters[parameterIndex];
+ const size_t parameterSize = parameter.getObjectSize();
+ bool moreParameters = parameterIndex + 1 < ctorParameters.size();
+
+ constructor += "x" + str(parameterIndex);
+
+ if (parameter.isScalar())
+ {
+ ASSERT(parameterSize <= remainingComponents);
+ remainingComponents -= parameterSize;
+ }
+ else if (parameter.isVector())
+ {
+ if (remainingComponents == parameterSize || moreParameters)
+ {
+ ASSERT(parameterSize <= remainingComponents);
+ remainingComponents -= parameterSize;
+ }
+ else if (remainingComponents < static_cast<size_t>(parameter.getNominalSize()))
+ {
+ switch (remainingComponents)
+ {
+ case 1: constructor += ".x"; break;
+ case 2: constructor += ".xy"; break;
+ case 3: constructor += ".xyz"; break;
+ case 4: constructor += ".xyzw"; break;
+ default: UNREACHABLE();
+ }
+
+ remainingComponents = 0;
+ }
+ else UNREACHABLE();
+ }
+ else if (parameter.isMatrix() || parameter.getStruct())
+ {
+ ASSERT(remainingComponents == parameterSize || moreParameters);
+ ASSERT(parameterSize <= remainingComponents);
+
+ remainingComponents -= parameterSize;
+ }
+ else UNREACHABLE();
+
+ if (moreParameters)
+ {
+ parameterIndex++;
+ }
+
+ if (remainingComponents)
+ {
+ constructor += ", ";
+ }
+ }
+ }
+
+ if (ctorType.getStruct())
+ {
+ constructor += "};\n"
+ " return structure;\n"
+ "}\n";
+ }
+ else
+ {
+ constructor += ");\n"
+ "}\n";
+ }
+
+ mConstructors.insert(constructor);
+}
+
+const ConstantUnion *OutputHLSL::writeConstantUnion(const TType &type, const ConstantUnion *constUnion)
+{
+ TInfoSinkBase &out = mBody;
+
+ if (type.getBasicType() == EbtStruct)
+ {
+ out << structLookup(type.getStruct()->name()) + "_ctor(";
+
+ const TFieldList &fields = type.getStruct()->fields();
+
+ for (size_t i = 0; i < fields.size(); i++)
+ {
+ const TType *fieldType = fields[i]->type();
+
+ constUnion = writeConstantUnion(*fieldType, constUnion);
+
+ if (i != fields.size() - 1)
+ {
+ out << ", ";
+ }
+ }
+
+ out << ")";
+ }
+ else
+ {
+ size_t size = type.getObjectSize();
+ bool writeType = size > 1;
+
+ if (writeType)
+ {
+ out << typeString(type) << "(";
+ }
+
+ for (size_t i = 0; i < size; i++, constUnion++)
+ {
+ switch (constUnion->getType())
+ {
+ case EbtFloat: out << std::min(FLT_MAX, std::max(-FLT_MAX, constUnion->getFConst())); break;
+ case EbtInt: out << constUnion->getIConst(); break;
+ case EbtBool: out << constUnion->getBConst(); break;
+ default: UNREACHABLE();
+ }
+
+ if (i != size - 1)
+ {
+ out << ", ";
+ }
+ }
+
+ if (writeType)
+ {
+ out << ")";
+ }
+ }
+
+ return constUnion;
+}
+
+TString OutputHLSL::scopeString(unsigned int depthLimit)
+{
+ TString string;
+
+ for (unsigned int i = 0; i < mScopeBracket.size() && i < depthLimit; i++)
+ {
+ string += "_" + str(i);
+ }
+
+ return string;
+}
+
+TString OutputHLSL::scopedStruct(const TString &typeName)
+{
+ if (typeName == "")
+ {
+ return typeName;
+ }
+
+ return typeName + scopeString(mScopeDepth);
+}
+
+TString OutputHLSL::structLookup(const TString &typeName)
+{
+ for (int depth = mScopeDepth; depth >= 0; depth--)
+ {
+ TString scopedName = decorate(typeName + scopeString(depth));
+
+ for (StructNames::iterator structName = mStructNames.begin(); structName != mStructNames.end(); structName++)
+ {
+ if (*structName == scopedName)
+ {
+ return scopedName;
+ }
+ }
+ }
+
+ UNREACHABLE(); // Should have found a matching constructor
+
+ return typeName;
+}
+
+TString OutputHLSL::decorate(const TString &string)
+{
+ if (string.compare(0, 3, "gl_") != 0 && string.compare(0, 3, "dx_") != 0)
+ {
+ return "_" + string;
+ }
+
+ return string;
+}
+
+TString OutputHLSL::decorateUniform(const TString &string, const TType &type)
+{
+ if (type.getBasicType() == EbtSamplerExternalOES)
+ {
+ return "ex_" + string;
+ }
+
+ return decorate(string);
+}
+
+TString OutputHLSL::decorateField(const TString &string, const TType &structure)
+{
+ if (structure.getStruct()->name().compare(0, 3, "gl_") != 0)
+ {
+ return decorate(string);
+ }
+
+ return string;
+}
+
+TString OutputHLSL::registerString(TIntermSymbol *operand)
+{
+ ASSERT(operand->getQualifier() == EvqUniform);
+
+ if (IsSampler(operand->getBasicType()))
+ {
+ return "s" + str(samplerRegister(operand));
+ }
+
+ return "c" + str(uniformRegister(operand));
+}
+
+int OutputHLSL::samplerRegister(TIntermSymbol *sampler)
+{
+ const TType &type = sampler->getType();
+ ASSERT(IsSampler(type.getBasicType()));
+
+ int index = mSamplerRegister;
+ mSamplerRegister += sampler->totalRegisterCount();
+
+ declareUniform(type, sampler->getSymbol(), index);
+
+ return index;
+}
+
+int OutputHLSL::uniformRegister(TIntermSymbol *uniform)
+{
+ const TType &type = uniform->getType();
+ ASSERT(!IsSampler(type.getBasicType()));
+
+ int index = mUniformRegister;
+ mUniformRegister += uniform->totalRegisterCount();
+
+ declareUniform(type, uniform->getSymbol(), index);
+
+ return index;
+}
+
+void OutputHLSL::declareUniform(const TType &type, const TString &name, int index)
+{
+ TStructure *structure = type.getStruct();
+
+ if (!structure)
+ {
+ mActiveUniforms.push_back(Uniform(glVariableType(type), glVariablePrecision(type), name.c_str(), type.getArraySize(), index));
+ }
+ else
+ {
+ const TFieldList &fields = structure->fields();
+
+ if (type.isArray())
+ {
+ int elementIndex = index;
+
+ for (int i = 0; i < type.getArraySize(); i++)
+ {
+ for (size_t j = 0; j < fields.size(); j++)
+ {
+ const TType &fieldType = *fields[j]->type();
+ const TString uniformName = name + "[" + str(i) + "]." + fields[j]->name();
+ declareUniform(fieldType, uniformName, elementIndex);
+ elementIndex += fieldType.totalRegisterCount();
+ }
+ }
+ }
+ else
+ {
+ int fieldIndex = index;
+
+ for (size_t i = 0; i < fields.size(); i++)
+ {
+ const TType &fieldType = *fields[i]->type();
+ const TString uniformName = name + "." + fields[i]->name();
+ declareUniform(fieldType, uniformName, fieldIndex);
+ fieldIndex += fieldType.totalRegisterCount();
+ }
+ }
+ }
+}
+
+GLenum OutputHLSL::glVariableType(const TType &type)
+{
+ if (type.getBasicType() == EbtFloat)
+ {
+ if (type.isScalar())
+ {
+ return GL_FLOAT;
+ }
+ else if (type.isVector())
+ {
+ switch(type.getNominalSize())
+ {
+ case 2: return GL_FLOAT_VEC2;
+ case 3: return GL_FLOAT_VEC3;
+ case 4: return GL_FLOAT_VEC4;
+ default: UNREACHABLE();
+ }
+ }
+ else if (type.isMatrix())
+ {
+ switch(type.getNominalSize())
+ {
+ case 2: return GL_FLOAT_MAT2;
+ case 3: return GL_FLOAT_MAT3;
+ case 4: return GL_FLOAT_MAT4;
+ default: UNREACHABLE();
+ }
+ }
+ else UNREACHABLE();
+ }
+ else if (type.getBasicType() == EbtInt)
+ {
+ if (type.isScalar())
+ {
+ return GL_INT;
+ }
+ else if (type.isVector())
+ {
+ switch(type.getNominalSize())
+ {
+ case 2: return GL_INT_VEC2;
+ case 3: return GL_INT_VEC3;
+ case 4: return GL_INT_VEC4;
+ default: UNREACHABLE();
+ }
+ }
+ else UNREACHABLE();
+ }
+ else if (type.getBasicType() == EbtBool)
+ {
+ if (type.isScalar())
+ {
+ return GL_BOOL;
+ }
+ else if (type.isVector())
+ {
+ switch(type.getNominalSize())
+ {
+ case 2: return GL_BOOL_VEC2;
+ case 3: return GL_BOOL_VEC3;
+ case 4: return GL_BOOL_VEC4;
+ default: UNREACHABLE();
+ }
+ }
+ else UNREACHABLE();
+ }
+ else if (type.getBasicType() == EbtSampler2D)
+ {
+ return GL_SAMPLER_2D;
+ }
+ else if (type.getBasicType() == EbtSamplerCube)
+ {
+ return GL_SAMPLER_CUBE;
+ }
+ else UNREACHABLE();
+
+ return GL_NONE;
+}
+
+GLenum OutputHLSL::glVariablePrecision(const TType &type)
+{
+ if (type.getBasicType() == EbtFloat)
+ {
+ switch (type.getPrecision())
+ {
+ case EbpHigh: return GL_HIGH_FLOAT;
+ case EbpMedium: return GL_MEDIUM_FLOAT;
+ case EbpLow: return GL_LOW_FLOAT;
+ case EbpUndefined:
+ // Should be defined as the default precision by the parser
+ default: UNREACHABLE();
+ }
+ }
+ else if (type.getBasicType() == EbtInt)
+ {
+ switch (type.getPrecision())
+ {
+ case EbpHigh: return GL_HIGH_INT;
+ case EbpMedium: return GL_MEDIUM_INT;
+ case EbpLow: return GL_LOW_INT;
+ case EbpUndefined:
+ // Should be defined as the default precision by the parser
+ default: UNREACHABLE();
+ }
+ }
+
+ // Other types (boolean, sampler) don't have a precision
+ return GL_NONE;
+}
+
+}