From d2d2007366d9f91f2f43372d791da234ad1068a3 Mon Sep 17 00:00:00 2001 From: Matthew Bellew Date: Tue, 29 May 2018 07:14:13 -0700 Subject: [PATCH 1/2] fix for issue 64 --- src/libprojectM/MilkdropPresetFactory/Expr.cpp | 6 ++++-- src/libprojectM/MilkdropPresetFactory/Param.cpp | 2 ++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/libprojectM/MilkdropPresetFactory/Expr.cpp b/src/libprojectM/MilkdropPresetFactory/Expr.cpp index 5eed87954..8a92c5465 100755 --- a/src/libprojectM/MilkdropPresetFactory/Expr.cpp +++ b/src/libprojectM/MilkdropPresetFactory/Expr.cpp @@ -140,7 +140,7 @@ public: }; /* TODO optimize FloatParameterExpr::eval() further. // - flag "never matrix" parameters -// - always pass in 2d matrix. instead of using (i, -1) for 1d matrix, we could just use (0, i) and avoid check +// - always pass in 2d matrix. instead of using (i, -1) for 1d matrix, we could just use (0, i) and avoid check // - instead of using matrix_flag to give "copy on write" behavior, maybe pre-copy from engine_val to matrix[] */ float FloatParameterExpr::eval ( int mesh_i, int mesh_j ) @@ -160,7 +160,9 @@ float FloatParameterExpr::eval ( int mesh_i, int mesh_j ) } else { - return ( ( ( float* ) term.param->matrix ) [mesh_i] ); + // issue 64, make sure we're not reading a P_FLAG_PER_PIXEL matrix variable + if (!(term.param->flags & P_FLAG_PER_PIXEL)) + return ( ( ( float* ) term.param->matrix ) [mesh_i] ); } } //assert(mesh_i >=0); diff --git a/src/libprojectM/MilkdropPresetFactory/Param.cpp b/src/libprojectM/MilkdropPresetFactory/Param.cpp index d49875ff3..bb72a672a 100755 --- a/src/libprojectM/MilkdropPresetFactory/Param.cpp +++ b/src/libprojectM/MilkdropPresetFactory/Param.cpp @@ -47,6 +47,7 @@ Param::Param( std::string _name, short int _type, short int _flags, void * _engi flags (_flags), matrix_flag (0), engine_val(_engine_val), + local_value(0.0), matrix (_matrix), default_init_val (_default_init_val), upper_bound (_upper_bound), @@ -62,6 +63,7 @@ Param::Param(std::string _name) : type(P_TYPE_DOUBLE), flags(P_FLAG_USERDEF), matrix_flag(0), + local_value(0.0), matrix(0) { From b91078e311689c5ba5e93db1323c2899b3b134d1 Mon Sep 17 00:00:00 2001 From: Mischa Spiegelmock Date: Sun, 3 Jun 2018 15:07:41 +0200 Subject: [PATCH 2/2] copying this into master to make diffing easier --- .../Renderer/hlslparser/.gitignore | 17 + .../Renderer/hlslparser/.travis.yml | 7 + src/libprojectM/Renderer/hlslparser/LICENSE | 21 + src/libprojectM/Renderer/hlslparser/README.md | 54 + .../Renderer/hlslparser/hlslparser.sln | 22 + .../Renderer/hlslparser/hlslparser.vcxproj | 90 + .../hlslparser/hlslparser.vcxproj.filters | 72 + .../Renderer/hlslparser/premake4.lua | 18 + .../Renderer/hlslparser/src/CodeWriter.cpp | 148 + .../Renderer/hlslparser/src/CodeWriter.h | 60 + .../Renderer/hlslparser/src/Engine.cpp | 185 + .../Renderer/hlslparser/src/Engine.h | 193 + .../Renderer/hlslparser/src/GLSLGenerator.cpp | 1912 ++++++++ .../Renderer/hlslparser/src/GLSLGenerator.h | 164 + .../Renderer/hlslparser/src/HLSLGenerator.cpp | 1196 +++++ .../Renderer/hlslparser/src/HLSLGenerator.h | 100 + .../Renderer/hlslparser/src/HLSLParser.cpp | 3868 +++++++++++++++++ .../Renderer/hlslparser/src/HLSLParser.h | 145 + .../Renderer/hlslparser/src/HLSLTokenizer.cpp | 635 +++ .../Renderer/hlslparser/src/HLSLTokenizer.h | 172 + .../Renderer/hlslparser/src/HLSLTree.cpp | 1964 +++++++++ .../Renderer/hlslparser/src/HLSLTree.h | 969 +++++ .../Renderer/hlslparser/src/MSLGenerator.cpp | 2302 ++++++++++ .../Renderer/hlslparser/src/MSLGenerator.h | 132 + .../Renderer/hlslparser/src/Main.cpp | 169 + 25 files changed, 14615 insertions(+) create mode 100644 src/libprojectM/Renderer/hlslparser/.gitignore create mode 100644 src/libprojectM/Renderer/hlslparser/.travis.yml create mode 100644 src/libprojectM/Renderer/hlslparser/LICENSE create mode 100644 src/libprojectM/Renderer/hlslparser/README.md create mode 100644 src/libprojectM/Renderer/hlslparser/hlslparser.sln create mode 100644 src/libprojectM/Renderer/hlslparser/hlslparser.vcxproj create mode 100644 src/libprojectM/Renderer/hlslparser/hlslparser.vcxproj.filters create mode 100644 src/libprojectM/Renderer/hlslparser/premake4.lua create mode 100755 src/libprojectM/Renderer/hlslparser/src/CodeWriter.cpp create mode 100755 src/libprojectM/Renderer/hlslparser/src/CodeWriter.h create mode 100755 src/libprojectM/Renderer/hlslparser/src/Engine.cpp create mode 100755 src/libprojectM/Renderer/hlslparser/src/Engine.h create mode 100755 src/libprojectM/Renderer/hlslparser/src/GLSLGenerator.cpp create mode 100755 src/libprojectM/Renderer/hlslparser/src/GLSLGenerator.h create mode 100755 src/libprojectM/Renderer/hlslparser/src/HLSLGenerator.cpp create mode 100755 src/libprojectM/Renderer/hlslparser/src/HLSLGenerator.h create mode 100755 src/libprojectM/Renderer/hlslparser/src/HLSLParser.cpp create mode 100755 src/libprojectM/Renderer/hlslparser/src/HLSLParser.h create mode 100755 src/libprojectM/Renderer/hlslparser/src/HLSLTokenizer.cpp create mode 100755 src/libprojectM/Renderer/hlslparser/src/HLSLTokenizer.h create mode 100755 src/libprojectM/Renderer/hlslparser/src/HLSLTree.cpp create mode 100755 src/libprojectM/Renderer/hlslparser/src/HLSLTree.h create mode 100755 src/libprojectM/Renderer/hlslparser/src/MSLGenerator.cpp create mode 100755 src/libprojectM/Renderer/hlslparser/src/MSLGenerator.h create mode 100755 src/libprojectM/Renderer/hlslparser/src/Main.cpp diff --git a/src/libprojectM/Renderer/hlslparser/.gitignore b/src/libprojectM/Renderer/hlslparser/.gitignore new file mode 100644 index 000000000..69b6369f5 --- /dev/null +++ b/src/libprojectM/Renderer/hlslparser/.gitignore @@ -0,0 +1,17 @@ +*.user +*.ncb +*.suo +*.tlog +*.lastbuildstate +*.unsuccessfulbuild +*.ipch +*.resources +*.pdb +*.aps +*.bak +/Debug +/Release +*.log +*.sdf +*.opensdf +/build diff --git a/src/libprojectM/Renderer/hlslparser/.travis.yml b/src/libprojectM/Renderer/hlslparser/.travis.yml new file mode 100644 index 000000000..1d0d18117 --- /dev/null +++ b/src/libprojectM/Renderer/hlslparser/.travis.yml @@ -0,0 +1,7 @@ +os: + - linux + - osx +sudo: required +dist: trusty +language: cpp +script: make diff --git a/src/libprojectM/Renderer/hlslparser/LICENSE b/src/libprojectM/Renderer/hlslparser/LICENSE new file mode 100644 index 000000000..7439620b2 --- /dev/null +++ b/src/libprojectM/Renderer/hlslparser/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2013-2014 Unknown Worlds Entertainment, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/src/libprojectM/Renderer/hlslparser/README.md b/src/libprojectM/Renderer/hlslparser/README.md new file mode 100644 index 000000000..391d2f133 --- /dev/null +++ b/src/libprojectM/Renderer/hlslparser/README.md @@ -0,0 +1,54 @@ +HLSLParser +========== + +This is a fork of [Unknownworld's hlslparser](https://github.com/unknownworlds/hlslparser) adapted to our needs in [The Witness](http://the-witness.net). We currently use it to translate pseudo-HLSL shaders (using the legacy D3D9 syntax) to HLSL10 and Metal Shading Language (MSL). There's also a GLSL translator available that we do not use yet, but that is being maintained by community contributions. + +The HLSL parser has been extended with many HLSL10 features, but retaining the original HLSL C-based syntax. + +For example, the following functions in our HLSL dialect: + +```C +float tex2Dcmp(sampler2DShadow s, float3 texcoord_comparevalue); +float4 tex2DMSfetch(sampler2DMS s, int2 texcoord, int sample); +int2 tex2Dsize(sampler2D s); +``` + +Are equivalent to these methods in HLSL10: + +```C++ +float Texture2D::SampleCmp(SamplerComparisonState s, float2 texcoord, float comparevalue); +float4 Texture2DMS::Load(int2 texcoord, int sample); +void Texture2D::GetDimensions(out uint w, out uint h); +``` + + + +Here are the original release notes: + + +> HLSL Parser and GLSL code generator +> +> This is the code we used in Natural Selection 2 to convert HLSL shader code to +GLSL for use with OpenGL. The code is pulled from a larger codebase and has some +dependencies which have been replaced with stubs. These dependencies are all very +basic (array classes, memory allocators, etc.) so replacing them with our own +equivalent should be simple if you want to use this code. +> +> The parser is designed to work with HLSL code written in the legacy Direct3D 9 +style (e.g. D3DCOMPILE_ENABLE_BACKWARDS_COMPATIBILITY should be used with D3D11). +The parser works with cbuffers for uniforms, so in addition to generating GLSL, +there is a class provided for generating D3D9-compatible HLSL which doesn't +support cbuffers. The GLSL code requires version 3.1 for support of uniform blocks. +The parser is designed to catch all errors and generate "clean" GLSL which can +then be compiled without any errors. +> +> The HLSL parsing is done though a basic recursive descent parser coded by hand +rather than using a parser generator. We believe makes the code easier to +understand and work with. +> +> To get consistent results from Direct3D and OpenGL, our engine renders in OpenGL +"upside down". This is automatically added into the generated GLSL vertex shaders. +> +> Although this code was written specifically for our use, we hope that it may be +useful as an educational tool or a base for someone who wants to do something +similar. diff --git a/src/libprojectM/Renderer/hlslparser/hlslparser.sln b/src/libprojectM/Renderer/hlslparser/hlslparser.sln new file mode 100644 index 000000000..55fce6cc6 --- /dev/null +++ b/src/libprojectM/Renderer/hlslparser/hlslparser.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.40629.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hlslparser", "hlslparser.vcxproj", "{FAA5AD82-3351-479F-A315-F287EBD0A816}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {FAA5AD82-3351-479F-A315-F287EBD0A816}.Debug|Win32.ActiveCfg = Debug|Win32 + {FAA5AD82-3351-479F-A315-F287EBD0A816}.Debug|Win32.Build.0 = Debug|Win32 + {FAA5AD82-3351-479F-A315-F287EBD0A816}.Release|Win32.ActiveCfg = Release|Win32 + {FAA5AD82-3351-479F-A315-F287EBD0A816}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/src/libprojectM/Renderer/hlslparser/hlslparser.vcxproj b/src/libprojectM/Renderer/hlslparser/hlslparser.vcxproj new file mode 100644 index 000000000..47154e3cb --- /dev/null +++ b/src/libprojectM/Renderer/hlslparser/hlslparser.vcxproj @@ -0,0 +1,90 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {FAA5AD82-3351-479F-A315-F287EBD0A816} + hlslparser + + + + Application + true + v120 + MultiByte + + + Application + false + v120 + true + MultiByte + + + + + + + + + + + + + + + Level3 + Disabled + true + + + true + + + + + Level3 + MaxSpeed + true + true + true + + + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/libprojectM/Renderer/hlslparser/hlslparser.vcxproj.filters b/src/libprojectM/Renderer/hlslparser/hlslparser.vcxproj.filters new file mode 100644 index 000000000..0109126f3 --- /dev/null +++ b/src/libprojectM/Renderer/hlslparser/hlslparser.vcxproj.filters @@ -0,0 +1,72 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/src/libprojectM/Renderer/hlslparser/premake4.lua b/src/libprojectM/Renderer/hlslparser/premake4.lua new file mode 100644 index 000000000..b51682c90 --- /dev/null +++ b/src/libprojectM/Renderer/hlslparser/premake4.lua @@ -0,0 +1,18 @@ +solution "HLSLParser" + location "build" + configurations { "Debug", "Release" } + + project "HLSLParser" + kind "ConsoleApp" + language "C++" + files { "src/**.h", "src/**.cpp" } + + configuration "Debug" + targetdir "bin/debug" + defines { "DEBUG" } + flags { "Symbols" } + + configuration "Release" + targetdir "bin/release" + defines { "NDEBUG" } + flags { "Optimize" } \ No newline at end of file diff --git a/src/libprojectM/Renderer/hlslparser/src/CodeWriter.cpp b/src/libprojectM/Renderer/hlslparser/src/CodeWriter.cpp new file mode 100755 index 000000000..3cd0413c3 --- /dev/null +++ b/src/libprojectM/Renderer/hlslparser/src/CodeWriter.cpp @@ -0,0 +1,148 @@ +//============================================================================= +// +// Render/CodeWriter.cpp +// +// Created by Max McGuire (max@unknownworlds.com) +// Copyright (c) 2013, Unknown Worlds Entertainment, Inc. +// +//============================================================================= + +//#include "Engine/Assert.h" +//#include "Engine/String.h" +#include "Engine.h" + +#include "CodeWriter.h" + +#include + +namespace M4 +{ + +static const int _maxLineLength = 2048; + +CodeWriter::CodeWriter(bool writeFileNames) +{ + m_currentLine = 1; + m_currentFileName = NULL; + m_spacesPerIndent = 4; + m_writeLines = true; + m_writeFileNames = writeFileNames; +} + +void CodeWriter::BeginLine(int indent, const char* fileName, int lineNumber) +{ + if (m_writeLines) + { + bool outputLine = false; + bool outputFile = false; + + // Output a line number pragma if necessary. + if (fileName != NULL && m_currentFileName != fileName) + { + m_currentFileName = fileName; + fileName = m_currentFileName; + outputFile = true; + } + if (lineNumber != -1 && m_currentLine != lineNumber) + { + m_currentLine = lineNumber; + outputLine = true; + } + if (outputLine || outputFile) + { + char buffer[256]; + String_Printf(buffer, sizeof(buffer), "#line %d", lineNumber); + m_buffer += buffer; + if (outputFile && m_writeFileNames) + { + m_buffer += " \""; + m_buffer += fileName; + m_buffer += "\"\n"; + } + else + { + m_buffer += "\n"; + } + } + } + + // Handle the indentation. + for (int i = 0; i < indent * m_spacesPerIndent; ++i) + { + m_buffer += " "; + } +} + +void CodeWriter::EndLine(const char* text) +{ + if (text != NULL) + { + m_buffer += text; + } + m_buffer += "\n"; + ++m_currentLine; +} + +void CodeWriter::Write(const char* format, ...) +{ + va_list args; + va_start(args, format); + + char buffer[_maxLineLength]; + String_PrintfArgList(buffer, sizeof(buffer), format, args); + + m_buffer += buffer; + + va_end(args); +} + +void CodeWriter::WriteLine(int indent, const char* format, ...) +{ + va_list args; + va_start(args, format); + + char buffer[_maxLineLength]; + + int result = String_PrintfArgList(buffer, sizeof(buffer), format, args); + ASSERT(result != -1); + + for (int i = 0; i < indent * m_spacesPerIndent; ++i) + { + m_buffer += " "; + } + m_buffer += buffer; + + EndLine(); + + va_end(args); +} + +void CodeWriter::WriteLineTagged(int indent, const char* fileName, int lineNumber, const char* format, ...) +{ + va_list args; + va_start(args, format); + + BeginLine(indent, fileName, lineNumber); + + char buffer[_maxLineLength]; + int result = String_PrintfArgList(buffer, sizeof(buffer), format, args); + ASSERT(result != -1); + + m_buffer += buffer; + + EndLine(); + + va_end(args); +} + +const char* CodeWriter::GetResult() const +{ + return m_buffer.c_str(); +} + +void CodeWriter::Reset() +{ + m_buffer.clear(); +} + +} \ No newline at end of file diff --git a/src/libprojectM/Renderer/hlslparser/src/CodeWriter.h b/src/libprojectM/Renderer/hlslparser/src/CodeWriter.h new file mode 100755 index 000000000..8a4e1473d --- /dev/null +++ b/src/libprojectM/Renderer/hlslparser/src/CodeWriter.h @@ -0,0 +1,60 @@ +//============================================================================= +// +// Render/CodeWriter.h +// +// Created by Max McGuire (max@unknownworlds.com) +// Copyright (c) 2013, Unknown Worlds Entertainment, Inc. +// +//============================================================================= + +#ifndef CODE_WRITER_H +#define CODE_WRITER_H + +#include "Engine.h" +#include + +#if defined(__GNUC__) +#define M4_PRINTF_ATTR(string_index, first_to_check) __attribute__((format(printf, string_index, first_to_check))) +#else +#define M4_PRINTF_ATTR(string_index, first_to_check) +#endif + +namespace M4 +{ + +class Allocator; + +/** + * This class is used for outputting code. It handles indentation and inserting #line markers + * to match the desired output line numbers. + */ +class CodeWriter +{ + +public: + CodeWriter(bool writeFileNames = true); + + void BeginLine(int indent, const char* fileName = NULL, int lineNumber = -1); + M4_PRINTF_ATTR(2, 3) void Write(const char* format, ...); + void EndLine(const char* text = NULL); + + M4_PRINTF_ATTR(3, 4) void WriteLine(int indent, const char* format, ...); + M4_PRINTF_ATTR(5, 6) void WriteLineTagged(int indent, const char* fileName, int lineNumber, const char* format, ...); + + const char* GetResult() const; + void Reset(); + +private: + + std::string m_buffer; + int m_currentLine; + const char* m_currentFileName; + int m_spacesPerIndent; + bool m_writeLines; + bool m_writeFileNames; + +}; + +} + +#endif \ No newline at end of file diff --git a/src/libprojectM/Renderer/hlslparser/src/Engine.cpp b/src/libprojectM/Renderer/hlslparser/src/Engine.cpp new file mode 100755 index 000000000..5b76ca4c8 --- /dev/null +++ b/src/libprojectM/Renderer/hlslparser/src/Engine.cpp @@ -0,0 +1,185 @@ + +#include "Engine.h" + +#include // vsnprintf +#include // strcmp, strcasecmp +#include // strtod, strtol + + +namespace M4 { + +// Engine/String.cpp + +int String_PrintfArgList(char * buffer, int size, const char * format, va_list args) { + + va_list tmp; + va_copy(tmp, args); + +#if _MSC_VER >= 1400 + int n = vsnprintf_s(buffer, size, _TRUNCATE, format, tmp); +#else + int n = vsnprintf(buffer, size, format, tmp); +#endif + + va_end(tmp); + + if (n < 0 || n > size) return -1; + return n; +} + +int String_Printf(char * buffer, int size, const char * format, ...) { + + va_list args; + va_start(args, format); + + int n = String_PrintfArgList(buffer, size, format, args); + + va_end(args); + + return n; +} + +int String_FormatFloat(char * buffer, int size, float value) { + return String_Printf(buffer, size, "%f", value); +} + +bool String_Equal(const char * a, const char * b) { + if (a == b) return true; + if (a == NULL || b == NULL) return false; + return strcmp(a, b) == 0; +} + +bool String_EqualNoCase(const char * a, const char * b) { + if (a == b) return true; + if (a == NULL || b == NULL) return false; +#if _MSC_VER + return _stricmp(a, b) == 0; +#else + return strcasecmp(a, b) == 0; +#endif +} + +double String_ToDouble(const char * str, char ** endptr) { + return strtod(str, endptr); +} + +int String_ToInteger(const char * str, char ** endptr) { + return strtol(str, endptr, 10); +} + +int String_ToIntegerHex(const char * str, char ** endptr) { + return strtol(str, endptr, 16); +} + + + +// Engine/Log.cpp + +void Log_Error(const char * format, ...) { + va_list args; + va_start(args, format); + Log_ErrorArgList(format, args); + va_end(args); +} + +void Log_ErrorArgList(const char * format, va_list args) { +#if 1 // @@ Don't we need to do this? + va_list tmp; + va_copy(tmp, args); + vprintf( format, args ); + va_end(tmp); +#else + vprintf( format, args ); +#endif +} + + +// Engine/StringPool.cpp + +StringPool::StringPool(Allocator * allocator) : stringArray(allocator) { +} +StringPool::~StringPool() { + for (int i = 0; i < stringArray.GetSize(); i++) { + free((void *)stringArray[i]); + stringArray[i] = NULL; + } +} + +const char * StringPool::AddString(const char * string) { + for (int i = 0; i < stringArray.GetSize(); i++) { + if (String_Equal(stringArray[i], string)) return stringArray[i]; + } +#if _MSC_VER + const char * dup = _strdup(string); +#else + const char * dup = strdup(string); +#endif + stringArray.PushBack(dup); + return dup; +} + +// @@ From mprintf.cpp +static char *mprintf_valist(int size, const char *fmt, va_list args) { + ASSERT(size > 0); + char *res = NULL; + va_list tmp; + + while (1) { + res = new char[size]; + if (!res) return NULL; + + va_copy(tmp, args); + int len = vsnprintf(res, size, fmt, tmp); + va_end(tmp); + + if ((len >= 0) && (size >= len + 1)) { + break; + } + + delete [] res; + + if (len > -1 ) { + size = len + 1; + } + else { + size *= 2; + } + } + + return res; +} + +const char * StringPool::AddStringFormatList(const char * format, va_list args) { + va_list tmp; + va_copy(tmp, args); + const char * string = mprintf_valist(256, format, tmp); + va_end(tmp); + + for (int i = 0; i < stringArray.GetSize(); i++) { + if (String_Equal(stringArray[i], string)) { + delete [] string; + return stringArray[i]; + } + } + + stringArray.PushBack(string); + return string; +} + +const char * StringPool::AddStringFormat(const char * format, ...) { + va_list args; + va_start(args, format); + const char * string = AddStringFormatList(format, args); + va_end(args); + + return string; +} + +bool StringPool::GetContainsString(const char * string) const { + for (int i = 0; i < stringArray.GetSize(); i++) { + if (String_Equal(stringArray[i], string)) return true; + } + return false; +} + +} // M4 namespace diff --git a/src/libprojectM/Renderer/hlslparser/src/Engine.h b/src/libprojectM/Renderer/hlslparser/src/Engine.h new file mode 100755 index 000000000..ade7f2335 --- /dev/null +++ b/src/libprojectM/Renderer/hlslparser/src/Engine.h @@ -0,0 +1,193 @@ +#ifndef ENGINE_H +#define ENGINE_H + +#if _MSC_VER +#define _CRT_SECURE_NO_WARNINGS 1 +#endif + +#include // va_list, vsnprintf +#include // malloc +#include // for placement new + +#ifndef NULL +#define NULL 0 +#endif + +#ifndef va_copy +#define va_copy(a, b) (a) = (b) +#endif + +// Engine/Assert.h + +#define ASSERT(...) + +namespace M4 { + + +// Engine/Allocator.h + +class Allocator { +public: + template T * New() { + return (T *)malloc(sizeof(T)); + } + template T * New(size_t count) { + return (T *)malloc(sizeof(T) * count); + } + template void Delete(T * ptr) { + free((void *)ptr); + } + template T * Realloc(T * ptr, size_t count) { + return (T *)realloc(ptr, sizeof(T) * count); + } +}; + + +// Engine/String.h + +int String_Printf(char * buffer, int size, const char * format, ...); +int String_PrintfArgList(char * buffer, int size, const char * format, va_list args); +int String_FormatFloat(char * buffer, int size, float value); +bool String_Equal(const char * a, const char * b); +bool String_EqualNoCase(const char * a, const char * b); +double String_ToDouble(const char * str, char ** end); +int String_ToInteger(const char * str, char ** end); + + +// Engine/Log.h + +void Log_Error(const char * format, ...); +void Log_ErrorArgList(const char * format, va_list args); + + +// Engine/Array.h + +template +void ConstructRange(T * buffer, int new_size, int old_size) { + for (int i = old_size; i < new_size; i++) { + new(buffer+i) T; // placement new + } +} + +template +void ConstructRange(T * buffer, int new_size, int old_size, const T & val) { + for (int i = old_size; i < new_size; i++) { + new(buffer+i) T(val); // placement new + } +} + +template +void DestroyRange(T * buffer, int new_size, int old_size) { + for (int i = new_size; i < old_size; i++) { + (buffer+i)->~T(); // Explicit call to the destructor + } +} + + +template +class Array { +public: + Array(Allocator * allocator) : allocator(allocator), buffer(NULL), size(0), capacity(0) {} + + void PushBack(const T & val) { + ASSERT(&val < buffer || &val >= buffer+size); + + int old_size = size; + int new_size = size + 1; + + SetSize(new_size); + + ConstructRange(buffer, new_size, old_size, val); + } + T & PushBackNew() { + int old_size = size; + int new_size = size + 1; + + SetSize(new_size); + + ConstructRange(buffer, new_size, old_size); + + return buffer[old_size]; + } + void Resize(int new_size) { + int old_size = size; + + DestroyRange(buffer, new_size, old_size); + + SetSize(new_size); + + ConstructRange(buffer, new_size, old_size); + } + + int GetSize() const { return size; } + const T & operator[](int i) const { ASSERT(i < size); return buffer[i]; } + T & operator[](int i) { ASSERT(i < size); return buffer[i]; } + +private: + + // Change array size. + void SetSize(int new_size) { + size = new_size; + + if (new_size > capacity) { + int new_buffer_size; + if (capacity == 0) { + // first allocation is exact + new_buffer_size = new_size; + } + else { + // following allocations grow array by 25% + new_buffer_size = new_size + (new_size >> 2); + } + + SetCapacity(new_buffer_size); + } + } + + // Change array capacity. + void SetCapacity(int new_capacity) { + ASSERT(new_capacity >= size); + + if (new_capacity == 0) { + // free the buffer. + if (buffer != NULL) { + allocator->Delete(buffer); + buffer = NULL; + } + } + else { + // realloc the buffer + buffer = allocator->Realloc(buffer, new_capacity); + } + + capacity = new_capacity; + } + + +private: + Allocator * allocator; // @@ Do we really have to keep a pointer to this? + T * buffer; + int size; + int capacity; +}; + + +// Engine/StringPool.h + +// @@ Implement this with a hash table! +struct StringPool { + StringPool(Allocator * allocator); + ~StringPool(); + + const char * AddString(const char * string); + const char * AddStringFormat(const char * fmt, ...); + const char * AddStringFormatList(const char * fmt, va_list args); + bool GetContainsString(const char * string) const; + + Array stringArray; +}; + + +} // M4 namespace + +#endif // ENGINE_H diff --git a/src/libprojectM/Renderer/hlslparser/src/GLSLGenerator.cpp b/src/libprojectM/Renderer/hlslparser/src/GLSLGenerator.cpp new file mode 100755 index 000000000..7ca9cca38 --- /dev/null +++ b/src/libprojectM/Renderer/hlslparser/src/GLSLGenerator.cpp @@ -0,0 +1,1912 @@ +//============================================================================= +// +// Render/GLSLGenerator.cpp +// +// Created by Max McGuire (max@unknownworlds.com) +// Copyright (c) 2013, Unknown Worlds Entertainment, Inc. +// +//============================================================================= + +#include "GLSLGenerator.h" +#include "HLSLParser.h" +#include "HLSLTree.h" + +//#include "Engine/String.h" +//#include "Engine/Log.h" +#include "Engine.h" + +#include +#include + +namespace M4 +{ + +static const HLSLType kFloatType(HLSLBaseType_Float); +static const HLSLType kUintType(HLSLBaseType_Uint); +static const HLSLType kIntType(HLSLBaseType_Int); +static const HLSLType kBoolType(HLSLBaseType_Bool); + +// These are reserved words in GLSL that aren't reserved in HLSL. +const char* GLSLGenerator::s_reservedWord[] = + { + "output", + "input", + "mod", + "mix", + "fract", + "dFdx", + "dFdy", + }; + +static const char* GetTypeName(const HLSLType& type) +{ + switch (type.baseType) + { + case HLSLBaseType_Void: return "void"; + case HLSLBaseType_Float: return "float"; + case HLSLBaseType_Float2: return "vec2"; + case HLSLBaseType_Float3: return "vec3"; + case HLSLBaseType_Float4: return "vec4"; + case HLSLBaseType_Float2x2: return "mat2"; + case HLSLBaseType_Float3x3: return "mat3"; + case HLSLBaseType_Float4x4: return "mat4"; + case HLSLBaseType_Half: return "float"; + case HLSLBaseType_Half2: return "vec2"; + case HLSLBaseType_Half3: return "vec3"; + case HLSLBaseType_Half4: return "vec4"; + case HLSLBaseType_Half2x2: return "mat2"; + case HLSLBaseType_Half3x3: return "mat3"; + case HLSLBaseType_Half4x4: return "mat4"; + case HLSLBaseType_Bool: return "bool"; + case HLSLBaseType_Bool2: return "bvec2"; + case HLSLBaseType_Bool3: return "bvec3"; + case HLSLBaseType_Bool4: return "bvec4"; + case HLSLBaseType_Int: return "int"; + case HLSLBaseType_Int2: return "ivec2"; + case HLSLBaseType_Int3: return "ivec3"; + case HLSLBaseType_Int4: return "ivec4"; + case HLSLBaseType_Uint: return "uint"; + case HLSLBaseType_Uint2: return "uvec2"; + case HLSLBaseType_Uint3: return "uvec3"; + case HLSLBaseType_Uint4: return "uvec4"; + case HLSLBaseType_Texture: return "texture"; + case HLSLBaseType_Sampler: return "sampler"; + case HLSLBaseType_Sampler2D: return "sampler2D"; + case HLSLBaseType_Sampler3D: return "sampler3D"; + case HLSLBaseType_SamplerCube: return "samplerCube"; + case HLSLBaseType_Sampler2DMS: return "sampler2DMS"; + case HLSLBaseType_Sampler2DArray: return "sampler2DArray"; + case HLSLBaseType_UserDefined: return type.typeName; + default: return "?"; + } +} + +static bool GetCanImplicitCast(const HLSLType& srcType, const HLSLType& dstType) +{ + return srcType.baseType == dstType.baseType; +} + +static int GetFunctionArguments(HLSLFunctionCall* functionCall, HLSLExpression* expression[], int maxArguments) +{ + HLSLExpression* argument = functionCall->argument; + int numArguments = 0; + while (argument != NULL) + { + if (numArguments < maxArguments) + { + expression[numArguments] = argument; + } + argument = argument->nextExpression; + ++numArguments; + } + return numArguments; +} + +GLSLGenerator::GLSLGenerator() : + m_writer(/* writeFileNames= */ false) +{ + m_tree = NULL; + m_entryName = NULL; + m_target = Target_VertexShader; + m_version = Version_140; + m_versionLegacy = false; + m_inAttribPrefix = NULL; + m_outAttribPrefix = NULL; + m_error = false; + m_matrixRowFunction[0] = 0; + m_matrixCtorFunction[0] = 0; + m_matrixMulFunction[0] = 0; + m_clipFunction[0] = 0; + m_tex2DlodFunction[0] = 0; + m_tex2DbiasFunction[0] = 0; + m_tex3DlodFunction[0] = 0; + m_texCUBEbiasFunction[0] = 0; + m_texCUBElodFunction[ 0 ] = 0; + m_scalarSwizzle2Function[0] = 0; + m_scalarSwizzle3Function[0] = 0; + m_scalarSwizzle4Function[0] = 0; + m_sinCosFunction[0] = 0; + m_bvecTernary[ 0 ] = 0; + m_outputPosition = false; + m_outputTargets = 0; +} + +bool GLSLGenerator::Generate(HLSLTree* tree, Target target, Version version, const char* entryName, const Options& options) +{ + + m_tree = tree; + m_entryName = entryName; + m_target = target; + m_version = version; + m_versionLegacy = (version == Version_110 || version == Version_100_ES); + m_options = options; + + ChooseUniqueName("matrix_row", m_matrixRowFunction, sizeof(m_matrixRowFunction)); + ChooseUniqueName("matrix_ctor", m_matrixCtorFunction, sizeof(m_matrixCtorFunction)); + ChooseUniqueName("matrix_mul", m_matrixMulFunction, sizeof(m_matrixMulFunction)); + ChooseUniqueName("clip", m_clipFunction, sizeof(m_clipFunction)); + ChooseUniqueName("tex2Dlod", m_tex2DlodFunction, sizeof(m_tex2DlodFunction)); + ChooseUniqueName("tex2Dbias", m_tex2DbiasFunction, sizeof(m_tex2DbiasFunction)); + ChooseUniqueName("tex2Dgrad", m_tex2DgradFunction, sizeof(m_tex2DgradFunction)); + ChooseUniqueName("tex3Dlod", m_tex3DlodFunction, sizeof(m_tex3DlodFunction)); + ChooseUniqueName("texCUBEbias", m_texCUBEbiasFunction, sizeof(m_texCUBEbiasFunction)); + ChooseUniqueName( "texCUBElod", m_texCUBElodFunction, sizeof( m_texCUBElodFunction ) ); + + for (int i = 0; i < s_numReservedWords; ++i) + { + ChooseUniqueName( s_reservedWord[i], m_reservedWord[i], sizeof(m_reservedWord[i]) ); + } + + ChooseUniqueName("m_scalar_swizzle2", m_scalarSwizzle2Function, sizeof(m_scalarSwizzle2Function)); + ChooseUniqueName("m_scalar_swizzle3", m_scalarSwizzle3Function, sizeof(m_scalarSwizzle3Function)); + ChooseUniqueName("m_scalar_swizzle4", m_scalarSwizzle4Function, sizeof(m_scalarSwizzle4Function)); + + ChooseUniqueName("sincos", m_sinCosFunction, sizeof(m_sinCosFunction)); + + ChooseUniqueName( "bvecTernary", m_bvecTernary, sizeof( m_bvecTernary ) ); + + if (target == Target_VertexShader) + { + m_inAttribPrefix = ""; + m_outAttribPrefix = "frag_"; + } + else + { + m_inAttribPrefix = "frag_"; + m_outAttribPrefix = "rast_"; + } + + HLSLRoot* root = m_tree->GetRoot(); + HLSLStatement* statement = root->statement; + + // Find the entry point function. + HLSLFunction* entryFunction = FindFunction(root, m_entryName); + if (entryFunction == NULL) + { + Error("Entry point '%s' doesn't exist", m_entryName); + return false; + } + + if (m_version == Version_110) + { + m_writer.WriteLine(0, "#version 110"); + } + else if (m_version == Version_140) + { + m_writer.WriteLine(0, "#version 140"); + + // Pragmas for NVIDIA. + m_writer.WriteLine(0, "#pragma optionNV(fastmath on)"); + //m_writer.WriteLine(0, "#pragma optionNV(fastprecision on)"); + m_writer.WriteLine(0, "#pragma optionNV(ifcvt none)"); + m_writer.WriteLine(0, "#pragma optionNV(inline all)"); + m_writer.WriteLine(0, "#pragma optionNV(strict on)"); + m_writer.WriteLine(0, "#pragma optionNV(unroll all)"); + } + else if (m_version == Version_150) + { + m_writer.WriteLine(0, "#version 150"); + } + else if (m_version == Version_100_ES) + { + m_writer.WriteLine(0, "#version 100"); + m_writer.WriteLine(0, "precision highp float;"); + } + else if (m_version == Version_300_ES) + { + m_writer.WriteLine(0, "#version 300 es"); + m_writer.WriteLine(0, "precision highp float;"); + } + else + { + Error("Unrecognized target version"); + return false; + } + + // Output the special function used to access rows in a matrix. + m_writer.WriteLine(0, "vec3 %s(mat3 m, int i) { return vec3( m[0][i], m[1][i], m[2][i] ); }", m_matrixRowFunction); + m_writer.WriteLine(0, "vec4 %s(mat4 m, int i) { return vec4( m[0][i], m[1][i], m[2][i], m[3][i] ); }", m_matrixRowFunction); + + // Output the special function used to do matrix cast for OpenGL 2.0 + if (m_version == Version_110) + { + m_writer.WriteLine(0, "mat3 %s(mat4 m) { return mat3(m[0][0], m[0][1], m[0][2], m[1][0], m[1][1], m[1][2], m[2][0], m[2][1], m[2][2]); }", m_matrixCtorFunction); + } + + // Output the special functions used for matrix multiplication lowering + // They make sure glsl-optimizer can fold expressions better + if (m_tree->NeedsFunction("mul") && (m_options.flags & Flag_LowerMatrixMultiplication)) + { + m_writer.WriteLine(0, "vec2 %s(mat2 m, vec2 v) { return m[0] * v.x + m[1] * v.y; }", m_matrixMulFunction); + m_writer.WriteLine(0, "vec2 %s(vec2 v, mat2 m) { return vec2(dot(m[0], v), dot(m[1], v)); }", m_matrixMulFunction); + m_writer.WriteLine(0, "vec3 %s(mat3 m, vec3 v) { return m[0] * v.x + m[1] * v.y + m[2] * v.z; }", m_matrixMulFunction); + m_writer.WriteLine(0, "vec3 %s(vec3 v, mat3 m) { return vec3(dot(m[0], v), dot(m[1], v), dot(m[2], v)); }", m_matrixMulFunction); + m_writer.WriteLine(0, "vec4 %s(mat4 m, vec4 v) { return m[0] * v.x + m[1] * v.y + m[2] * v.z + m[3] * v.w; }", m_matrixMulFunction); + m_writer.WriteLine(0, "vec4 %s(vec4 v, mat4 m) { return vec4(dot(m[0], v), dot(m[1], v), dot(m[2], v), dot(m[3], v)); }", m_matrixMulFunction); + } + + // Output the special function used to emulate HLSL clip. + if (m_tree->NeedsFunction("clip")) + { + const char* discard = m_target == Target_FragmentShader ? "discard" : ""; + m_writer.WriteLine(0, "void %s(float x) { if (x < 0.0) %s; }", m_clipFunction, discard); + m_writer.WriteLine(0, "void %s(vec2 x) { if (any(lessThan(x, vec2(0.0, 0.0)))) %s; }", m_clipFunction, discard); + m_writer.WriteLine(0, "void %s(vec3 x) { if (any(lessThan(x, vec3(0.0, 0.0, 0.0)))) %s; }", m_clipFunction, discard); + m_writer.WriteLine(0, "void %s(vec4 x) { if (any(lessThan(x, vec4(0.0, 0.0, 0.0, 0.0)))) %s; }", m_clipFunction, discard); + } + + // Output the special function used to emulate tex2Dlod. + if (m_tree->NeedsFunction("tex2Dlod")) + { + const char* function = "textureLod"; + + if (m_version == Version_110) + { + m_writer.WriteLine(0, "#extension GL_ARB_shader_texture_lod : require"); + function = "texture2DLod"; + } + else if (m_version == Version_100_ES) + { + m_writer.WriteLine(0, "#extension GL_EXT_shader_texture_lod : require"); + function = "texture2DLodEXT"; + } + + m_writer.WriteLine(0, "vec4 %s(sampler2D samp, vec4 texCoord) { return %s(samp, texCoord.xy, texCoord.w); }", m_tex2DlodFunction, function); + } + + // Output the special function used to emulate tex2Dgrad. + if (m_tree->NeedsFunction("tex2Dgrad")) + { + const char* function = "textureGrad"; + + if (m_version == Version_110) + { + m_writer.WriteLine(0, "#extension GL_ARB_shader_texture_lod : require"); + function = "texture2DGradARB"; + } + else if (m_version == Version_100_ES) + { + m_writer.WriteLine(0, "#extension GL_EXT_shader_texture_lod : require"); + function = "texture2DGradEXT"; + } + + m_writer.WriteLine(0, "vec4 %s(sampler2D samp, vec2 texCoord, vec2 dx, vec2 dy) { return %s(samp, texCoord, dx, dy); }", m_tex2DgradFunction, function); + } + + // Output the special function used to emulate tex2Dbias. + if (m_tree->NeedsFunction("tex2Dbias")) + { + if (target == Target_FragmentShader) + { + m_writer.WriteLine(0, "vec4 %s(sampler2D samp, vec4 texCoord) { return %s(samp, texCoord.xy, texCoord.w); }", m_tex2DbiasFunction, m_versionLegacy ? "texture2D" : "texture" ); + } + else + { + // Bias value is not supported in vertex shader. + m_writer.WriteLine(0, "vec4 %s(sampler2D samp, vec4 texCoord) { return texture(samp, texCoord.xy); }", m_tex2DbiasFunction ); + } + } + + // Output the special function used to emulate tex2DMSfetch. + if (m_tree->NeedsFunction("tex2DMSfetch")) + { + m_writer.WriteLine(0, "vec4 tex2DMSfetch(sampler2DMS samp, ivec2 texCoord, int sample) {"); + m_writer.WriteLine(1, "return texelFetch(samp, texCoord, sample);"); + m_writer.WriteLine(0, "}"); + } + + // Output the special function used to emulate tex3Dlod. + if (m_tree->NeedsFunction("tex3Dlod")) + { + m_writer.WriteLine(0, "vec4 %s(sampler3D samp, vec4 texCoord) { return %s(samp, texCoord.xyz, texCoord.w); }", m_tex3DlodFunction, m_versionLegacy ? "texture3D" : "texture" ); + } + + // Output the special function used to emulate texCUBEbias. + if (m_tree->NeedsFunction("texCUBEbias")) + { + if (target == Target_FragmentShader) + { + m_writer.WriteLine(0, "vec4 %s(samplerCube samp, vec4 texCoord) { return %s(samp, texCoord.xyz, texCoord.w); }", m_texCUBEbiasFunction, m_versionLegacy ? "textureCube" : "texture" ); + } + else + { + // Bias value is not supported in vertex shader. + m_writer.WriteLine(0, "vec4 %s(samplerCube samp, vec4 texCoord) { return texture(samp, texCoord.xyz); }", m_texCUBEbiasFunction ); + } + } + + // Output the special function used to emulate texCUBElod + if (m_tree->NeedsFunction("texCUBElod")) + { + const char* function = "textureLod"; + + if (m_version == Version_110) + { + m_writer.WriteLine(0, "#extension GL_ARB_shader_texture_lod : require"); + function = "textureCubeLod"; + } + else if (m_version == Version_100_ES) + { + m_writer.WriteLine(0, "#extension GL_EXT_shader_texture_lod : require"); + function = "textureCubeLodEXT"; + } + + m_writer.WriteLine( 0, "vec4 %s(samplerCube samp, vec4 texCoord) { return %s(samp, texCoord.xyz, texCoord.w); }", m_texCUBElodFunction, function); + } + + m_writer.WriteLine(0, "vec2 %s(float x) { return vec2(x, x); }", m_scalarSwizzle2Function); + m_writer.WriteLine(0, "ivec2 %s(int x) { return ivec2(x, x); }", m_scalarSwizzle2Function); + + m_writer.WriteLine(0, "vec3 %s(float x) { return vec3(x, x, x); }", m_scalarSwizzle3Function); + m_writer.WriteLine(0, "ivec3 %s(int x) { return ivec3(x, x, x); }", m_scalarSwizzle3Function); + + m_writer.WriteLine(0, "vec4 %s(float x) { return vec4(x, x, x, x); }", m_scalarSwizzle4Function); + m_writer.WriteLine(0, "ivec4 %s(int x) { return ivec4(x, x, x, x); }", m_scalarSwizzle4Function); + + if (!m_versionLegacy) + { + m_writer.WriteLine(0, "uvec2 %s(uint x) { return uvec2(x, x); }", m_scalarSwizzle2Function); + m_writer.WriteLine(0, "uvec3 %s(uint x) { return uvec3(x, x, x); }", m_scalarSwizzle3Function); + m_writer.WriteLine(0, "uvec4 %s(uint x) { return uvec4(x, x, x, x); }", m_scalarSwizzle4Function); + } + + if (m_tree->NeedsFunction("sincos")) + { + const char* floatTypes[] = { "float", "vec2", "vec3", "vec4" }; + for (int i = 0; i < 4; ++i) + { + m_writer.WriteLine(0, "void %s(%s x, out %s s, out %s c) { s = sin(x); c = cos(x); }", m_sinCosFunction, + floatTypes[i], floatTypes[i], floatTypes[i]); + } + } + + // special function to emulate ?: with bool{2,3,4} condition type + m_writer.WriteLine( 0, "vec2 %s(bvec2 cond, vec2 trueExpr, vec2 falseExpr) { vec2 ret; ret.x = cond.x ? trueExpr.x : falseExpr.x; ret.y = cond.y ? trueExpr.y : falseExpr.y; return ret; }", m_bvecTernary ); + m_writer.WriteLine( 0, "vec3 %s(bvec3 cond, vec3 trueExpr, vec3 falseExpr) { vec3 ret; ret.x = cond.x ? trueExpr.x : falseExpr.x; ret.y = cond.y ? trueExpr.y : falseExpr.y; ret.z = cond.z ? trueExpr.z : falseExpr.z; return ret; }", m_bvecTernary ); + m_writer.WriteLine( 0, "vec4 %s(bvec4 cond, vec4 trueExpr, vec4 falseExpr) { vec4 ret; ret.x = cond.x ? trueExpr.x : falseExpr.x; ret.y = cond.y ? trueExpr.y : falseExpr.y; ret.z = cond.z ? trueExpr.z : falseExpr.z; ret.w = cond.w ? trueExpr.w : falseExpr.w; return ret; }", m_bvecTernary ); + + // Output the extension used for dFdx/dFdy in GLES2 + if (m_version == Version_100_ES && (m_tree->NeedsFunction("ddx") || m_tree->NeedsFunction("ddy"))) + { + m_writer.WriteLine(0, "#extension GL_OES_standard_derivatives : require"); + } + + OutputAttributes(entryFunction); + + if (m_target == Target_FragmentShader) + { + if (!m_outputTargets) + Error("Fragment shader must output a color"); + + if (!m_versionLegacy) + m_writer.WriteLine(0, "out vec4 rast_FragData[%d];", m_outputTargets); + } + + OutputStatements(0, statement); + OutputEntryCaller(entryFunction); + + m_tree = NULL; + + // The GLSL compilers don't check for this, so generate our own error message. + if (target == Target_VertexShader && !m_outputPosition) + { + Error("Vertex shader must output a position"); + } + + return !m_error; + +} + +const char* GLSLGenerator::GetResult() const +{ + return m_writer.GetResult(); +} + +void GLSLGenerator::OutputExpressionList(HLSLExpression* expression, HLSLArgument* argument) +{ + int numExpressions = 0; + while (expression != NULL) + { + if (numExpressions > 0) + { + m_writer.Write(", "); + } + + HLSLType* expectedType = NULL; + if (argument != NULL) + { + expectedType = &argument->type; + argument = argument->nextArgument; + } + + OutputExpression(expression, expectedType); + expression = expression->nextExpression; + ++numExpressions; + } +} + +const HLSLType* commonScalarType(const HLSLType& lhs, const HLSLType& rhs) +{ + if (!IsScalarType(lhs) || !IsScalarType(rhs)) + return NULL; + + if (lhs.baseType == HLSLBaseType_Float || lhs.baseType == HLSLBaseType_Half || + rhs.baseType == HLSLBaseType_Float || rhs.baseType == HLSLBaseType_Half) + return &kFloatType; + + if (lhs.baseType == HLSLBaseType_Uint || rhs.baseType == HLSLBaseType_Uint) + return &kUintType; + + if (lhs.baseType == HLSLBaseType_Int || rhs.baseType == HLSLBaseType_Int) + return &kIntType; + + if (lhs.baseType == HLSLBaseType_Bool || rhs.baseType == HLSLBaseType_Bool) + return &kBoolType; + + return NULL; +} + +void GLSLGenerator::OutputExpression(HLSLExpression* expression, const HLSLType* dstType) +{ + + bool cast = dstType != NULL && !GetCanImplicitCast(expression->expressionType, *dstType); + if (expression->nodeType == HLSLNodeType_CastingExpression) + { + // No need to include a cast if the expression is already doing it. + cast = false; + } + + if (cast) + { + OutputCast(*dstType); + m_writer.Write("("); + } + + HLSLBuffer* bufferAccess = (m_options.flags & Flag_EmulateConstantBuffer) ? GetBufferAccessExpression(expression) : 0; + + if (bufferAccess) + { + OutputBufferAccessExpression(bufferAccess, expression, expression->expressionType, 0); + } + else if (expression->nodeType == HLSLNodeType_IdentifierExpression) + { + HLSLIdentifierExpression* identifierExpression = static_cast(expression); + OutputIdentifier(identifierExpression->name); + } + else if (expression->nodeType == HLSLNodeType_ConstructorExpression) + { + HLSLConstructorExpression* constructorExpression = static_cast(expression); + m_writer.Write("%s(", GetTypeName(constructorExpression->type)); + OutputExpressionList(constructorExpression->argument); + m_writer.Write(")"); + } + else if (expression->nodeType == HLSLNodeType_CastingExpression) + { + HLSLCastingExpression* castingExpression = static_cast(expression); + OutputCast(castingExpression->type); + m_writer.Write("("); + OutputExpression(castingExpression->expression); + m_writer.Write(")"); + } + else if (expression->nodeType == HLSLNodeType_LiteralExpression) + { + HLSLLiteralExpression* literalExpression = static_cast(expression); + switch (literalExpression->type) + { + case HLSLBaseType_Half: + case HLSLBaseType_Float: + { + // Don't use printf directly so that we don't use the system locale. + char buffer[64]; + String_FormatFloat(buffer, sizeof(buffer), literalExpression->fValue); + m_writer.Write("%s", buffer); + } + break; + case HLSLBaseType_Int: + case HLSLBaseType_Uint: + m_writer.Write("%d", literalExpression->iValue); + break; + case HLSLBaseType_Bool: + m_writer.Write("%s", literalExpression->bValue ? "true" : "false"); + break; + default: + ASSERT(0); + } + } + else if (expression->nodeType == HLSLNodeType_UnaryExpression) + { + HLSLUnaryExpression* unaryExpression = static_cast(expression); + const char* op = "?"; + bool pre = true; + const HLSLType* dstType = NULL; + switch (unaryExpression->unaryOp) + { + case HLSLUnaryOp_Negative: op = "-"; break; + case HLSLUnaryOp_Positive: op = "+"; break; + case HLSLUnaryOp_Not: op = "!"; dstType = &unaryExpression->expressionType; break; + case HLSLUnaryOp_PreIncrement: op = "++"; break; + case HLSLUnaryOp_PreDecrement: op = "--"; break; + case HLSLUnaryOp_PostIncrement: op = "++"; pre = false; break; + case HLSLUnaryOp_PostDecrement: op = "--"; pre = false; break; + case HLSLUnaryOp_BitNot: op = "~"; break; + } + m_writer.Write("("); + if (pre) + { + m_writer.Write("%s", op); + OutputExpression(unaryExpression->expression, dstType); + } + else + { + OutputExpression(unaryExpression->expression, dstType); + m_writer.Write("%s", op); + } + m_writer.Write(")"); + } + else if (expression->nodeType == HLSLNodeType_BinaryExpression) + { + HLSLBinaryExpression* binaryExpression = static_cast(expression); + const char* op = "?"; + const HLSLType* dstType1 = NULL; + const HLSLType* dstType2 = NULL; + + // + bool vectorExpression = IsVectorType( binaryExpression->expression1->expressionType ) || IsVectorType( binaryExpression->expression2->expressionType ); + if( vectorExpression && IsCompareOp( binaryExpression->binaryOp )) + { + switch (binaryExpression->binaryOp) + { + case HLSLBinaryOp_Less: m_writer.Write("lessThan("); break; + case HLSLBinaryOp_Greater: m_writer.Write("greaterThan("); break; + case HLSLBinaryOp_LessEqual: m_writer.Write("lessThanEqual("); break; + case HLSLBinaryOp_GreaterEqual: m_writer.Write("greaterThanEqual("); break; + case HLSLBinaryOp_Equal: m_writer.Write("equal("); break; + case HLSLBinaryOp_NotEqual: m_writer.Write("notEqual("); break; + default: + ASSERT(0); // is so, check isCompareOp + } + + if( IsVectorType( binaryExpression->expression1->expressionType ) && IsScalarType( binaryExpression->expression2->expressionType ) ) + dstType2 = &binaryExpression->expression1->expressionType; + else if( IsScalarType( binaryExpression->expression1->expressionType ) && IsVectorType( binaryExpression->expression2->expressionType ) ) + dstType1 = &binaryExpression->expression2->expressionType; + // TODO if both expressions are vector but with different dimension handle it here or in parser? + + OutputExpression(binaryExpression->expression1, dstType1); + m_writer.Write(", "); + OutputExpression(binaryExpression->expression2, dstType2); + m_writer.Write(")"); + } + else + { + switch (binaryExpression->binaryOp) + { + case HLSLBinaryOp_Add: op = " + "; dstType1 = dstType2 = &binaryExpression->expressionType; break; + case HLSLBinaryOp_Sub: op = " - "; dstType1 = dstType2 = &binaryExpression->expressionType; break; + case HLSLBinaryOp_Mul: op = " * "; dstType1 = dstType2 = &binaryExpression->expressionType; break; + case HLSLBinaryOp_Div: op = " / "; dstType1 = dstType2 = &binaryExpression->expressionType; break; + case HLSLBinaryOp_Less: op = " < "; dstType1 = dstType2 = commonScalarType(binaryExpression->expression1->expressionType, binaryExpression->expression2->expressionType); break; + case HLSLBinaryOp_Greater: op = " > "; dstType1 = dstType2 = commonScalarType(binaryExpression->expression1->expressionType, binaryExpression->expression2->expressionType); break; + case HLSLBinaryOp_LessEqual: op = " <= "; dstType1 = dstType2 = commonScalarType(binaryExpression->expression1->expressionType, binaryExpression->expression2->expressionType); break; + case HLSLBinaryOp_GreaterEqual: op = " >= "; dstType1 = dstType2 = commonScalarType(binaryExpression->expression1->expressionType, binaryExpression->expression2->expressionType); break; + case HLSLBinaryOp_Equal: op = " == "; dstType1 = dstType2 = commonScalarType(binaryExpression->expression1->expressionType, binaryExpression->expression2->expressionType); break; + case HLSLBinaryOp_NotEqual: op = " != "; dstType1 = dstType2 = commonScalarType(binaryExpression->expression1->expressionType, binaryExpression->expression2->expressionType); break; + case HLSLBinaryOp_Assign: op = " = "; dstType2 = &binaryExpression->expressionType; break; + case HLSLBinaryOp_AddAssign: op = " += "; dstType2 = &binaryExpression->expressionType; break; + case HLSLBinaryOp_SubAssign: op = " -= "; dstType2 = &binaryExpression->expressionType; break; + case HLSLBinaryOp_MulAssign: op = " *= "; dstType2 = &binaryExpression->expressionType; break; + case HLSLBinaryOp_DivAssign: op = " /= "; dstType2 = &binaryExpression->expressionType; break; + case HLSLBinaryOp_And: op = " && "; dstType1 = dstType2 = &binaryExpression->expressionType; break; + case HLSLBinaryOp_Or: op = " || "; dstType1 = dstType2 = &binaryExpression->expressionType; break; + case HLSLBinaryOp_BitAnd: op = " & "; dstType1 = dstType2 = commonScalarType(binaryExpression->expression1->expressionType, binaryExpression->expression2->expressionType); break; + case HLSLBinaryOp_BitOr: op = " | "; dstType1 = dstType2 = commonScalarType(binaryExpression->expression1->expressionType, binaryExpression->expression2->expressionType); break; + case HLSLBinaryOp_BitXor: op = " ^ "; dstType1 = dstType2 = commonScalarType(binaryExpression->expression1->expressionType, binaryExpression->expression2->expressionType); break; + default: + ASSERT(0); + } + m_writer.Write("("); + OutputExpression(binaryExpression->expression1, dstType1); + m_writer.Write("%s", op); + OutputExpression(binaryExpression->expression2, dstType2); + m_writer.Write(")"); + } + } + else if (expression->nodeType == HLSLNodeType_ConditionalExpression) + { + HLSLConditionalExpression* conditionalExpression = static_cast(expression); + if( IsVectorType( conditionalExpression->condition->expressionType ) ) + { + m_writer.Write( "%s", m_bvecTernary ); + m_writer.Write( "( " ); + OutputExpression( conditionalExpression->condition ); + m_writer.Write( ", " ); + OutputExpression( conditionalExpression->trueExpression, &conditionalExpression->expressionType ); + m_writer.Write( ", " ); + OutputExpression( conditionalExpression->falseExpression, &conditionalExpression->expressionType ); + m_writer.Write( " )" ); + } + else + { + m_writer.Write( "((" ); + OutputExpression( conditionalExpression->condition, &kBoolType ); + m_writer.Write( ")?(" ); + OutputExpression( conditionalExpression->trueExpression, dstType ); + m_writer.Write( "):(" ); + OutputExpression( conditionalExpression->falseExpression, dstType ); + m_writer.Write( "))" ); + } + } + else if (expression->nodeType == HLSLNodeType_MemberAccess) + { + + HLSLMemberAccess* memberAccess = static_cast(expression); + + if (memberAccess->object->expressionType.baseType == HLSLBaseType_Half || + memberAccess->object->expressionType.baseType == HLSLBaseType_Float || + memberAccess->object->expressionType.baseType == HLSLBaseType_Int || + memberAccess->object->expressionType.baseType == HLSLBaseType_Uint) + { + // Handle swizzling on scalar values. + size_t swizzleLength = strlen(memberAccess->field); + if (swizzleLength == 2) + { + m_writer.Write("%s", m_scalarSwizzle2Function); + } + else if (swizzleLength == 3) + { + m_writer.Write("%s", m_scalarSwizzle3Function); + } + else if (swizzleLength == 4) + { + m_writer.Write("%s", m_scalarSwizzle4Function); + } + m_writer.Write("("); + OutputExpression(memberAccess->object); + m_writer.Write(")"); + } + else + { + m_writer.Write("("); + OutputExpression(memberAccess->object); + m_writer.Write(")"); + + if( memberAccess->object->expressionType.baseType == HLSLBaseType_Float2x2 || + memberAccess->object->expressionType.baseType == HLSLBaseType_Float3x3 || + memberAccess->object->expressionType.baseType == HLSLBaseType_Float4x4 || + memberAccess->object->expressionType.baseType == HLSLBaseType_Half2x2 || + memberAccess->object->expressionType.baseType == HLSLBaseType_Half3x3 || + memberAccess->object->expressionType.baseType == HLSLBaseType_Half4x4 ) + { + // Handle HLSL matrix "swizzling". + // TODO: Properly handle multiple element selection such as _m00_m12 + const char* n = memberAccess->field; + while (n[0] != 0) + { + if ( n[0] != '_' ) + { + ASSERT(0); + break; + } + ++n; + char base = '1'; + if (n[0] == 'm') + { + base = '0'; + ++n; + } + if (isdigit(n[0]) && isdigit(n[1]) ) + { + m_writer.Write("[%d][%d]", n[1] - base, n[0] - base); + n += 2; + } + else + { + ASSERT(0); + break; + } + } + } + else + { + m_writer.Write(".%s", memberAccess->field); + } + + } + + } + else if (expression->nodeType == HLSLNodeType_ArrayAccess) + { + HLSLArrayAccess* arrayAccess = static_cast(expression); + + if (!arrayAccess->array->expressionType.array && + (arrayAccess->array->expressionType.baseType == HLSLBaseType_Float2x2 || + arrayAccess->array->expressionType.baseType == HLSLBaseType_Float3x3 || + arrayAccess->array->expressionType.baseType == HLSLBaseType_Float4x4 || + arrayAccess->array->expressionType.baseType == HLSLBaseType_Half2x2 || + arrayAccess->array->expressionType.baseType == HLSLBaseType_Half3x3 || + arrayAccess->array->expressionType.baseType == HLSLBaseType_Half4x4 ) ) + { + // GLSL access a matrix as m[c][r] while HLSL is m[r][c], so use our + // special row access function to convert. + m_writer.Write("%s(", m_matrixRowFunction); + OutputExpression(arrayAccess->array); + m_writer.Write(","); + OutputExpression(arrayAccess->index); + m_writer.Write(")"); + } + else + { + OutputExpression(arrayAccess->array); + m_writer.Write("["); + OutputExpression(arrayAccess->index); + m_writer.Write("]"); + } + + } + else if (expression->nodeType == HLSLNodeType_FunctionCall) + { + HLSLFunctionCall* functionCall = static_cast(expression); + + // Handle intrinsic funtions that are different between HLSL and GLSL. + bool handled = false; + const char* functionName = functionCall->function->name; + + if (String_Equal(functionName, "mul")) + { + HLSLExpression* argument[2]; + if (GetFunctionArguments(functionCall, argument, 2) != 2) + { + Error("mul expects 2 arguments"); + return; + } + + const HLSLType& type0 = functionCall->function->argument->type; + const HLSLType& type1 = functionCall->function->argument->nextArgument->type; + + const char* prefix = (m_options.flags & Flag_LowerMatrixMultiplication) ? m_matrixMulFunction : ""; + const char* infix = (m_options.flags & Flag_LowerMatrixMultiplication) ? "," : "*"; + + if (m_options.flags & Flag_PackMatrixRowMajor) + { + m_writer.Write("%s((", prefix); + OutputExpression(argument[1], &type1); + m_writer.Write(")%s(", infix); + OutputExpression(argument[0], &type0); + m_writer.Write("))"); + } + else + { + m_writer.Write("%s((", prefix); + OutputExpression(argument[0], &type0); + m_writer.Write(")%s(", infix); + OutputExpression(argument[1], &type1); + m_writer.Write("))"); + } + + handled = true; + } + else if (String_Equal(functionName, "saturate")) + { + HLSLExpression* argument[1]; + if (GetFunctionArguments(functionCall, argument, 1) != 1) + { + Error("saturate expects 1 argument"); + return; + } + m_writer.Write("clamp("); + OutputExpression(argument[0]); + m_writer.Write(", 0.0, 1.0)"); + handled = true; + } + + if (!handled) + { + OutputIdentifier(functionName); + m_writer.Write("("); + OutputExpressionList(functionCall->argument, functionCall->function->argument); + m_writer.Write(")"); + } + } + else + { + m_writer.Write(""); + } + + if (cast) + { +/* + const BaseTypeDescription& srcTypeDesc = _baseTypeDescriptions[expression->expressionType.baseType]; + const BaseTypeDescription& dstTypeDesc = _baseTypeDescriptions[dstType->baseType]; + + if (dstTypeDesc.numDimensions == 1 && dstTypeDesc.numComponents > 1) + { + // Casting to a vector - pad with 0s + for (int i = srcTypeDesc.numComponents; i < dstTypeDesc.numComponents; ++i) + { + m_writer.Write(", 0"); + } + } +*/ + + m_writer.Write(")"); + } + +} + +void GLSLGenerator::OutputIdentifier(const char* name) +{ + + // Remap intrinstic functions. + if (String_Equal(name, "tex2D")) + { + name = m_versionLegacy ? "texture2D" : "texture"; + } + else if (String_Equal(name, "tex2Dproj")) + { + name = m_versionLegacy ? "texture2DProj" : "textureProj"; + } + else if (String_Equal(name, "texCUBE")) + { + name = m_versionLegacy ? "textureCube" : "texture"; + } + else if (String_Equal(name, "tex3D")) + { + name = m_versionLegacy ? "texture3D" : "texture"; + } + else if (String_Equal(name, "clip")) + { + name = m_clipFunction; + } + else if (String_Equal(name, "tex2Dlod")) + { + name = m_tex2DlodFunction; + } + else if (String_Equal(name, "tex2Dbias")) + { + name = m_tex2DbiasFunction; + } + else if (String_Equal(name, "tex2Dgrad")) + { + name = m_tex2DgradFunction; + } + else if (String_Equal(name, "tex2DArray")) + { + name = "texture"; + } + else if (String_Equal(name, "texCUBEbias")) + { + name = m_texCUBEbiasFunction; + } + else if( String_Equal( name, "texCUBElod" ) ) + { + name = m_texCUBElodFunction; + } + else if (String_Equal(name, "atan2")) + { + name = "atan"; + } + else if (String_Equal(name, "sincos")) + { + name = m_sinCosFunction; + } + else if (String_Equal(name, "fmod")) + { + // mod is not the same as fmod if the parameter is negative! + // The equivalent of fmod(x, y) is x - y * floor(x/y) + // We use the mod version for performance. + name = "mod"; + } + else if (String_Equal(name, "lerp")) + { + name = "mix"; + } + else if (String_Equal(name, "frac")) + { + name = "fract"; + } + else if (String_Equal(name, "ddx")) + { + name = "dFdx"; + } + else if (String_Equal(name, "ddy")) + { + name = "dFdy"; + } + else + { + // The identifier could be a GLSL reserved word (if it's not also a HLSL reserved word). + name = GetSafeIdentifierName(name); + } + m_writer.Write("%s", name); + +} + +void GLSLGenerator::OutputArguments(HLSLArgument* argument) +{ + int numArgs = 0; + while (argument != NULL) + { + if (numArgs > 0) + { + m_writer.Write(", "); + } + + switch (argument->modifier) + { + case HLSLArgumentModifier_In: + m_writer.Write("in "); + break; + case HLSLArgumentModifier_Out: + m_writer.Write("out "); + break; + case HLSLArgumentModifier_Inout: + m_writer.Write("inout "); + break; + default: + break; + } + + OutputDeclaration(argument->type, argument->name); + argument = argument->nextArgument; + ++numArgs; + } +} + +void GLSLGenerator::OutputStatements(int indent, HLSLStatement* statement, const HLSLType* returnType) +{ + + while (statement != NULL) + { + if (statement->hidden) + { + statement = statement->nextStatement; + continue; + } + + if (statement->nodeType == HLSLNodeType_Declaration) + { + HLSLDeclaration* declaration = static_cast(statement); + + // GLSL doesn't seem have texture uniforms, so just ignore them. + if (declaration->type.baseType != HLSLBaseType_Texture) + { + m_writer.BeginLine(indent, declaration->fileName, declaration->line); + if (indent == 0) + { + // At the top level, we need the "uniform" keyword. + m_writer.Write("uniform "); + } + OutputDeclaration(declaration); + m_writer.EndLine(";"); + } + } + else if (statement->nodeType == HLSLNodeType_Struct) + { + HLSLStruct* structure = static_cast(statement); + m_writer.WriteLine(indent, "struct %s {", structure->name); + HLSLStructField* field = structure->field; + while (field != NULL) + { + m_writer.BeginLine(indent + 1, field->fileName, field->line); + OutputDeclaration(field->type, field->name); + m_writer.Write(";"); + m_writer.EndLine(); + field = field->nextField; + } + m_writer.WriteLine(indent, "};"); + } + else if (statement->nodeType == HLSLNodeType_Buffer) + { + HLSLBuffer* buffer = static_cast(statement); + OutputBuffer(indent, buffer); + } + else if (statement->nodeType == HLSLNodeType_Function) + { + HLSLFunction* function = static_cast(statement); + + // Use an alternate name for the function which is supposed to be entry point + // so that we can supply our own function which will be the actual entry point. + const char* functionName = GetSafeIdentifierName(function->name); + const char* returnTypeName = GetTypeName(function->returnType); + + m_writer.BeginLine(indent, function->fileName, function->line); + m_writer.Write("%s %s(", returnTypeName, functionName); + + OutputArguments(function->argument); + + if (function->forward) + { + m_writer.WriteLine(indent, ");"); + } + else + { + m_writer.Write(") {"); + m_writer.EndLine(); + + OutputStatements(indent + 1, function->statement, &function->returnType); + m_writer.WriteLine(indent, "}"); + } + } + else if (statement->nodeType == HLSLNodeType_ExpressionStatement) + { + HLSLExpressionStatement* expressionStatement = static_cast(statement); + m_writer.BeginLine(indent, statement->fileName, statement->line); + OutputExpression(expressionStatement->expression); + m_writer.EndLine(";"); + } + else if (statement->nodeType == HLSLNodeType_ReturnStatement) + { + HLSLReturnStatement* returnStatement = static_cast(statement); + if (returnStatement->expression != NULL) + { + m_writer.BeginLine(indent, returnStatement->fileName, returnStatement->line); + m_writer.Write("return "); + OutputExpression(returnStatement->expression, returnType); + m_writer.EndLine(";"); + } + else + { + m_writer.WriteLineTagged(indent, returnStatement->fileName, returnStatement->line, "return;"); + } + } + else if (statement->nodeType == HLSLNodeType_DiscardStatement) + { + HLSLDiscardStatement* discardStatement = static_cast(statement); + if (m_target == Target_FragmentShader) + { + m_writer.WriteLineTagged(indent, discardStatement->fileName, discardStatement->line, "discard;"); + } + } + else if (statement->nodeType == HLSLNodeType_BreakStatement) + { + HLSLBreakStatement* breakStatement = static_cast(statement); + m_writer.WriteLineTagged(indent, breakStatement->fileName, breakStatement->line, "break;"); + } + else if (statement->nodeType == HLSLNodeType_ContinueStatement) + { + HLSLContinueStatement* continueStatement = static_cast(statement); + m_writer.WriteLineTagged(indent, continueStatement->fileName, continueStatement->line, "continue;"); + } + else if (statement->nodeType == HLSLNodeType_IfStatement) + { + HLSLIfStatement* ifStatement = static_cast(statement); + m_writer.BeginLine(indent, ifStatement->fileName, ifStatement->line); + m_writer.Write("if ("); + OutputExpression(ifStatement->condition, &kBoolType); + m_writer.Write(") {"); + m_writer.EndLine(); + OutputStatements(indent + 1, ifStatement->statement, returnType); + m_writer.WriteLine(indent, "}"); + if (ifStatement->elseStatement != NULL) + { + m_writer.WriteLine(indent, "else {"); + OutputStatements(indent + 1, ifStatement->elseStatement, returnType); + m_writer.WriteLine(indent, "}"); + } + } + else if (statement->nodeType == HLSLNodeType_ForStatement) + { + HLSLForStatement* forStatement = static_cast(statement); + m_writer.BeginLine(indent, forStatement->fileName, forStatement->line); + m_writer.Write("for ("); + OutputDeclaration(forStatement->initialization); + m_writer.Write("; "); + OutputExpression(forStatement->condition, &kBoolType); + m_writer.Write("; "); + OutputExpression(forStatement->increment); + m_writer.Write(") {"); + m_writer.EndLine(); + OutputStatements(indent + 1, forStatement->statement, returnType); + m_writer.WriteLine(indent, "}"); + } + else + { + // Unhanded statement type. + ASSERT(0); + } + + statement = statement->nextStatement; + + } + +} + +void GLSLGenerator::OutputBuffer(int indent, HLSLBuffer* buffer) +{ + // Empty uniform blocks cause compilation errors on NVIDIA, so don't emit them. + if (buffer->field == NULL) + return; + + if (m_options.flags & Flag_EmulateConstantBuffer) + { + unsigned int size = 0; + LayoutBuffer(buffer, size); + + unsigned int uniformSize = (size + 3) / 4; + + m_writer.WriteLineTagged(indent, buffer->fileName, buffer->line, "uniform vec4 %s%s[%d];", m_options.constantBufferPrefix, buffer->name, uniformSize); + } + else + { + m_writer.WriteLineTagged(indent, buffer->fileName, buffer->line, "layout (std140) uniform %s%s {", m_options.constantBufferPrefix, buffer->name); + HLSLDeclaration* field = buffer->field; + while (field != NULL) + { + m_writer.BeginLine(indent + 1, field->fileName, field->line); + OutputDeclaration(field->type, field->name); + m_writer.Write(";"); + m_writer.EndLine(); + field = (HLSLDeclaration*)field->nextStatement; + } + m_writer.WriteLine(indent, "};"); + } +} + +inline void alignForWrite(unsigned int& offset, unsigned int size) +{ + ASSERT(size <= 4); + + if (offset / 4 != (offset + size - 1) / 4) + offset = (offset + 3) & ~3; +} + +void GLSLGenerator::LayoutBuffer(HLSLBuffer* buffer, unsigned int& offset) +{ + for (HLSLDeclaration* field = buffer->field; field; field = (HLSLDeclaration*)field->nextStatement) + { + LayoutBuffer(field->type, offset); + } +} + +void GLSLGenerator::LayoutBuffer(const HLSLType& type, unsigned int& offset) +{ + LayoutBufferAlign(type, offset); + + if (type.array) + { + int arraySize = 0; + m_tree->GetExpressionValue(type.arraySize, arraySize); + + unsigned int elementSize = 0; + LayoutBufferElement(type, elementSize); + + unsigned int alignedElementSize = (elementSize + 3) & ~3; + + offset += alignedElementSize * arraySize; + } + else + { + LayoutBufferElement(type, offset); + } +} + +void GLSLGenerator::LayoutBufferElement(const HLSLType& type, unsigned int& offset) +{ + if (type.baseType == HLSLBaseType_Float) + { + offset += 1; + } + else if (type.baseType == HLSLBaseType_Float2) + { + offset += 2; + } + else if (type.baseType == HLSLBaseType_Float3) + { + offset += 3; + } + else if (type.baseType == HLSLBaseType_Float4) + { + offset += 4; + } + else if (type.baseType == HLSLBaseType_Float4x4) + { + offset += 16; + } + else if (type.baseType == HLSLBaseType_UserDefined) + { + HLSLStruct * st = m_tree->FindGlobalStruct(type.typeName); + + if (st) + { + for (HLSLStructField* field = st->field; field; field = field->nextField) + { + LayoutBuffer(field->type, offset); + } + } + else + { + Error("Unknown type %s", type.typeName); + } + } + else + { + Error("Constant buffer layout is not supported for %s", GetTypeName(type)); + } +} + +void GLSLGenerator::LayoutBufferAlign(const HLSLType& type, unsigned int& offset) +{ + if (type.array) + { + alignForWrite(offset, 4); + } + else if (type.baseType == HLSLBaseType_Float) + { + alignForWrite(offset, 1); + } + else if (type.baseType == HLSLBaseType_Float2) + { + alignForWrite(offset, 2); + } + else if (type.baseType == HLSLBaseType_Float3) + { + alignForWrite(offset, 3); + } + else if (type.baseType == HLSLBaseType_Float4) + { + alignForWrite(offset, 4); + } + else if (type.baseType == HLSLBaseType_Float4x4) + { + alignForWrite(offset, 4); + } + else if (type.baseType == HLSLBaseType_UserDefined) + { + alignForWrite(offset, 4); + } + else + { + Error("Constant buffer layout is not supported for %s", GetTypeName(type)); + } +} + +HLSLBuffer* GLSLGenerator::GetBufferAccessExpression(HLSLExpression* expression) +{ + if (expression->nodeType == HLSLNodeType_IdentifierExpression) + { + HLSLIdentifierExpression* identifierExpression = static_cast(expression); + + if (identifierExpression->global) + { + HLSLDeclaration * declaration = m_tree->FindGlobalDeclaration(identifierExpression->name); + + if (declaration && declaration->buffer) + return declaration->buffer; + } + } + else if (expression->nodeType == HLSLNodeType_MemberAccess) + { + HLSLMemberAccess* memberAccess = static_cast(expression); + + if (memberAccess->object->expressionType.baseType == HLSLBaseType_UserDefined) + return GetBufferAccessExpression(memberAccess->object); + } + else if (expression->nodeType == HLSLNodeType_ArrayAccess) + { + HLSLArrayAccess* arrayAccess = static_cast(expression); + + if (arrayAccess->array->expressionType.array) + return GetBufferAccessExpression(arrayAccess->array); + } + + return 0; +} + +void GLSLGenerator::OutputBufferAccessExpression(HLSLBuffer* buffer, HLSLExpression* expression, const HLSLType& type, unsigned int postOffset) +{ + if (type.array) + { + Error("Constant buffer access is not supported for arrays (use indexing instead)"); + } + else if (type.baseType == HLSLBaseType_Float) + { + m_writer.Write("%s%s[", m_options.constantBufferPrefix, buffer->name); + unsigned int index = OutputBufferAccessIndex(expression, postOffset); + m_writer.Write("%d].%c", index / 4, "xyzw"[index % 4]); + } + else if (type.baseType == HLSLBaseType_Float2) + { + m_writer.Write("%s%s[", m_options.constantBufferPrefix, buffer->name); + unsigned int index = OutputBufferAccessIndex(expression, postOffset); + m_writer.Write("%d].%s", index / 4, index % 4 == 0 ? "xy" : index % 4 == 1 ? "yz" : "zw"); + } + else if (type.baseType == HLSLBaseType_Float3) + { + m_writer.Write("%s%s[", m_options.constantBufferPrefix, buffer->name); + unsigned int index = OutputBufferAccessIndex(expression, postOffset); + m_writer.Write("%d].%s", index / 4, index % 4 == 0 ? "xyz" : "yzw"); + } + else if (type.baseType == HLSLBaseType_Float4) + { + m_writer.Write("%s%s[", m_options.constantBufferPrefix, buffer->name); + unsigned int index = OutputBufferAccessIndex(expression, postOffset); + ASSERT(index % 4 == 0); + m_writer.Write("%d]", index / 4); + } + else if (type.baseType == HLSLBaseType_Float4x4) + { + m_writer.Write("mat4("); + for (int i = 0; i < 4; ++i) + { + m_writer.Write("%s%s[", m_options.constantBufferPrefix, buffer->name); + unsigned int index = OutputBufferAccessIndex(expression, postOffset + i * 4); + ASSERT(index % 4 == 0); + m_writer.Write("%d]%c", index / 4, i == 3 ? ')' : ','); + } + } + else if (type.baseType == HLSLBaseType_UserDefined) + { + HLSLStruct * st = m_tree->FindGlobalStruct(type.typeName); + + if (st) + { + m_writer.Write("%s(", st->name); + + unsigned int offset = postOffset; + + for (HLSLStructField* field = st->field; field; field = field->nextField) + { + OutputBufferAccessExpression(buffer, expression, field->type, offset); + + if (field->nextField) + m_writer.Write(","); + + LayoutBuffer(field->type, offset); + } + + m_writer.Write(")"); + } + else + { + Error("Unknown type %s", type.typeName); + } + } + else + { + Error("Constant buffer layout is not supported for %s", GetTypeName(type)); + } +} + +unsigned int GLSLGenerator::OutputBufferAccessIndex(HLSLExpression* expression, unsigned int postOffset) +{ + if (expression->nodeType == HLSLNodeType_IdentifierExpression) + { + HLSLIdentifierExpression* identifierExpression = static_cast(expression); + ASSERT(identifierExpression->global); + + HLSLDeclaration * declaration = m_tree->FindGlobalDeclaration(identifierExpression->name); + ASSERT(declaration); + + HLSLBuffer * buffer = declaration->buffer; + ASSERT(buffer); + + unsigned int offset = 0; + + for (HLSLDeclaration* field = buffer->field; field; field = (HLSLDeclaration*)field->nextStatement) + { + if (field == declaration) + { + LayoutBufferAlign(field->type, offset); + break; + } + + LayoutBuffer(field->type, offset); + } + + return offset + postOffset; + } + else if (expression->nodeType == HLSLNodeType_MemberAccess) + { + HLSLMemberAccess* memberAccess = static_cast(expression); + + const HLSLType& type = memberAccess->object->expressionType; + ASSERT(type.baseType == HLSLBaseType_UserDefined); + + HLSLStruct * st = m_tree->FindGlobalStruct(type.typeName); + + if (st) + { + unsigned int offset = 0; + + for (HLSLStructField* field = st->field; field; field = field->nextField) + { + if (field->name == memberAccess->field) + { + LayoutBufferAlign(field->type, offset); + break; + } + + LayoutBuffer(field->type, offset); + } + + return offset + OutputBufferAccessIndex(memberAccess->object, postOffset); + } + else + { + Error("Unknown type %s", type.typeName); + } + } + else if (expression->nodeType == HLSLNodeType_ArrayAccess) + { + HLSLArrayAccess* arrayAccess = static_cast(expression); + + const HLSLType& type = arrayAccess->array->expressionType; + ASSERT(type.array); + + unsigned int elementSize = 0; + LayoutBufferElement(type, elementSize); + + unsigned int alignedElementSize = (elementSize + 3) & ~3; + + int arrayIndex = 0; + if (m_tree->GetExpressionValue(arrayAccess->index, arrayIndex)) + { + unsigned int offset = arrayIndex * alignedElementSize; + + return offset + OutputBufferAccessIndex(arrayAccess->array, postOffset); + } + else + { + m_writer.Write("%d*(", alignedElementSize / 4); + OutputExpression(arrayAccess->index); + m_writer.Write(")+"); + + return OutputBufferAccessIndex(arrayAccess->array, postOffset); + } + } + else + { + ASSERT(!"IsBufferAccessExpression should have returned false"); + } + + return 0; +} + +HLSLFunction* GLSLGenerator::FindFunction(HLSLRoot* root, const char* name) +{ + HLSLStatement* statement = root->statement; + while (statement != NULL) + { + if (statement->nodeType == HLSLNodeType_Function) + { + HLSLFunction* function = static_cast(statement); + if (String_Equal(function->name, name)) + { + return function; + } + } + statement = statement->nextStatement; + } + return NULL; +} + +HLSLStruct* GLSLGenerator::FindStruct(HLSLRoot* root, const char* name) +{ + HLSLStatement* statement = root->statement; + while (statement != NULL) + { + if (statement->nodeType == HLSLNodeType_Struct) + { + HLSLStruct* structDeclaration = static_cast(statement); + if (String_Equal(structDeclaration->name, name)) + { + return structDeclaration; + } + } + statement = statement->nextStatement; + } + return NULL; +} + + +const char* GLSLGenerator::GetAttribQualifier(AttributeModifier modifier) +{ + if (m_versionLegacy) + { + if (m_target == Target_VertexShader) + return (modifier == AttributeModifier_In) ? "attribute" : "varying"; + else + return (modifier == AttributeModifier_In) ? "varying" : "out"; + } + else + { + return (modifier == AttributeModifier_In) ? "in" : "out"; + } +} + +void GLSLGenerator::OutputAttribute(const HLSLType& type, const char* semantic, AttributeModifier modifier) +{ + const char* qualifier = GetAttribQualifier(modifier); + const char* prefix = (modifier == AttributeModifier_In) ? m_inAttribPrefix : m_outAttribPrefix; + + HLSLRoot* root = m_tree->GetRoot(); + if (type.baseType == HLSLBaseType_UserDefined) + { + // If the argument is a struct with semantics specified, we need to + // grab them. + HLSLStruct* structDeclaration = FindStruct(root, type.typeName); + ASSERT(structDeclaration != NULL); + HLSLStructField* field = structDeclaration->field; + while (field != NULL) + { + if (field->semantic != NULL && GetBuiltInSemantic(field->semantic, modifier) == NULL) + { + m_writer.Write( "%s ", qualifier ); + char attribName[ 64 ]; + String_Printf( attribName, 64, "%s%s", prefix, field->semantic ); + OutputDeclaration( field->type, attribName ); + m_writer.EndLine(";"); + } + field = field->nextField; + } + } + else if (semantic != NULL && GetBuiltInSemantic(semantic, modifier) == NULL) + { + m_writer.Write( "%s ", qualifier ); + char attribName[ 64 ]; + String_Printf( attribName, 64, "%s%s", prefix, semantic ); + OutputDeclaration( type, attribName ); + m_writer.EndLine(";"); + } +} + +void GLSLGenerator::OutputAttributes(HLSLFunction* entryFunction) +{ + // Write out the input/output attributes to the shader. + HLSLArgument* argument = entryFunction->argument; + while (argument != NULL) + { + if (argument->modifier == HLSLArgumentModifier_None || argument->modifier == HLSLArgumentModifier_In) + OutputAttribute(argument->type, argument->semantic, AttributeModifier_In); + if (argument->modifier == HLSLArgumentModifier_Out) + OutputAttribute(argument->type, argument->semantic, AttributeModifier_Out); + + argument = argument->nextArgument; + } + + // Write out the output attributes from the shader. + OutputAttribute(entryFunction->returnType, entryFunction->semantic, AttributeModifier_Out); +} + +void GLSLGenerator::OutputSetOutAttribute(const char* semantic, const char* resultName) +{ + int outputIndex = -1; + const char* builtInSemantic = GetBuiltInSemantic(semantic, AttributeModifier_Out, &outputIndex); + if (builtInSemantic != NULL) + { + if (String_Equal(builtInSemantic, "gl_Position")) + { + if (m_options.flags & Flag_FlipPositionOutput) + { + // Mirror the y-coordinate when we're outputing from + // the vertex shader so that we match the D3D texture + // coordinate origin convention in render-to-texture + // operations. + // We also need to convert the normalized device + // coordinates from the D3D convention of 0 to 1 to the + // OpenGL convention of -1 to 1. + m_writer.WriteLine(1, "vec4 temp = %s;", resultName); + m_writer.WriteLine(1, "%s = temp * vec4(1,-1,2,1) - vec4(0,0,temp.w,0);", builtInSemantic); + } + else + { + m_writer.WriteLine(1, "%s = %s;", builtInSemantic, resultName); + } + + m_outputPosition = true; + } + else if (String_Equal(builtInSemantic, "gl_FragDepth")) + { + // If the value goes outside of the 0 to 1 range, the + // fragment will be rejected unlike in D3D, so clamp it. + m_writer.WriteLine(1, "%s = clamp(float(%s), 0.0, 1.0);", builtInSemantic, resultName); + } + else if (outputIndex >= 0) + { + m_writer.WriteLine(1, "%s[%d] = %s;", builtInSemantic, outputIndex, resultName); + } + else + { + m_writer.WriteLine(1, "%s = %s;", builtInSemantic, resultName); + } + } + else if (m_target == Target_FragmentShader) + { + Error("Output attribute %s does not map to any built-ins", semantic); + } + else + { + m_writer.WriteLine(1, "%s%s = %s;", m_outAttribPrefix, semantic, resultName); + } +} + +void GLSLGenerator::OutputEntryCaller(HLSLFunction* entryFunction) +{ + HLSLRoot* root = m_tree->GetRoot(); + + m_writer.WriteLine(0, "void main() {"); + + // Create local variables for each of the parameters we'll need to pass + // into the entry point function. + HLSLArgument* argument = entryFunction->argument; + while (argument != NULL) + { + m_writer.BeginLine(1); + OutputDeclaration(argument->type, argument->name); + m_writer.EndLine(";"); + + if (argument->modifier != HLSLArgumentModifier_Out) + { + // Set the value for the local variable. + if (argument->type.baseType == HLSLBaseType_UserDefined) + { + HLSLStruct* structDeclaration = FindStruct(root, argument->type.typeName); + ASSERT(structDeclaration != NULL); + HLSLStructField* field = structDeclaration->field; + while (field != NULL) + { + if (field->semantic != NULL) + { + const char* builtInSemantic = GetBuiltInSemantic(field->semantic, AttributeModifier_In); + if (builtInSemantic) + { + m_writer.WriteLine(1, "%s.%s = %s;", GetSafeIdentifierName(argument->name), GetSafeIdentifierName(field->name), builtInSemantic); + } + else + { + m_writer.WriteLine(1, "%s.%s = %s%s;", GetSafeIdentifierName(argument->name), GetSafeIdentifierName(field->name), m_inAttribPrefix, field->semantic); + } + } + field = field->nextField; + } + } + else if (argument->semantic != NULL) + { + const char* builtInSemantic = GetBuiltInSemantic(argument->semantic, AttributeModifier_In); + if (builtInSemantic) + { + m_writer.WriteLine(1, "%s = %s;", GetSafeIdentifierName(argument->name), builtInSemantic); + } + else + { + m_writer.WriteLine(1, "%s = %s%s;", GetSafeIdentifierName(argument->name), m_inAttribPrefix, argument->semantic); + } + } + } + + argument = argument->nextArgument; + } + + const char* resultName = "result"; + + // Call the original entry function. + m_writer.BeginLine(1); + if (entryFunction->returnType.baseType != HLSLBaseType_Void) + m_writer.Write("%s %s = ", GetTypeName(entryFunction->returnType), resultName); + m_writer.Write("%s(", m_entryName); + + int numArgs = 0; + argument = entryFunction->argument; + while (argument != NULL) + { + if (numArgs > 0) + { + m_writer.Write(", "); + } + + m_writer.Write("%s", GetSafeIdentifierName(argument->name)); + + argument = argument->nextArgument; + ++numArgs; + } + m_writer.EndLine(");"); + + // Copy values from the result into the out attributes as necessary. + argument = entryFunction->argument; + while (argument != NULL) + { + if (argument->modifier == HLSLArgumentModifier_Out && argument->semantic) + OutputSetOutAttribute(argument->semantic, GetSafeIdentifierName(argument->name)); + + argument = argument->nextArgument; + } + + if (entryFunction->returnType.baseType == HLSLBaseType_UserDefined) + { + HLSLStruct* structDeclaration = FindStruct(root, entryFunction->returnType.typeName); + ASSERT(structDeclaration != NULL); + HLSLStructField* field = structDeclaration->field; + while (field != NULL) + { + char fieldResultName[1024]; + String_Printf( fieldResultName, sizeof(fieldResultName), "%s.%s", resultName, field->name ); + OutputSetOutAttribute( field->semantic, fieldResultName ); + field = field->nextField; + } + } + else if (entryFunction->semantic != NULL) + { + OutputSetOutAttribute(entryFunction->semantic, resultName); + } + + m_writer.WriteLine(0, "}"); +} + +void GLSLGenerator::OutputDeclaration(HLSLDeclaration* declaration) +{ + OutputDeclarationType( declaration->type ); + + HLSLDeclaration* lastDecl = nullptr; + while( declaration ) + { + if( lastDecl ) + m_writer.Write( ", " ); + + OutputDeclarationBody( declaration->type, GetSafeIdentifierName( declaration->name ) ); + + if( declaration->assignment != NULL ) + { + m_writer.Write( " = " ); + if( declaration->type.array ) + { + m_writer.Write( "%s[]( ", GetTypeName( declaration->type ) ); + OutputExpressionList( declaration->assignment ); + m_writer.Write( " )" ); + } + else + { + OutputExpression( declaration->assignment, &declaration->type ); + } + } + + lastDecl = declaration; + declaration = declaration->nextDeclaration; + } +} + +void GLSLGenerator::OutputDeclaration(const HLSLType& type, const char* name) +{ + OutputDeclarationType( type ); + OutputDeclarationBody( type, name ); +} + +void GLSLGenerator::OutputDeclarationType( const HLSLType& type ) +{ + m_writer.Write( "%s ", GetTypeName( type ) ); +} + +void GLSLGenerator::OutputDeclarationBody( const HLSLType& type, const char* name ) +{ + if( !type.array ) + { + m_writer.Write( "%s", GetSafeIdentifierName( name ) ); + } + else + { + m_writer.Write( "%s[", GetSafeIdentifierName( name ) ); + if( type.arraySize != NULL ) + { + OutputExpression( type.arraySize ); + } + m_writer.Write( "]" ); + } +} + +void GLSLGenerator::OutputCast(const HLSLType& type) +{ + if (m_version == Version_110 && type.baseType == HLSLBaseType_Float3x3) + m_writer.Write("%s", m_matrixCtorFunction); + else + OutputDeclaration(type, ""); +} + +void GLSLGenerator::Error(const char* format, ...) +{ + // It's not always convenient to stop executing when an error occurs, + // so just track once we've hit an error and stop reporting them until + // we successfully bail out of execution. + if (m_error) + { + return; + } + m_error = true; + + va_list arg; + va_start(arg, format); + Log_ErrorArgList(format, arg); + va_end(arg); +} + +const char* GLSLGenerator::GetSafeIdentifierName(const char* name) const +{ + for (int i = 0; i < s_numReservedWords; ++i) + { + if (String_Equal(s_reservedWord[i], name)) + { + return m_reservedWord[i]; + } + } + return name; +} + +bool GLSLGenerator::ChooseUniqueName(const char* base, char* dst, int dstLength) const +{ + for (int i = 0; i < 1024; ++i) + { + String_Printf(dst, dstLength, "%s%d", base, i); + if (!m_tree->GetContainsString(dst)) + { + return true; + } + } + return false; +} + +const char* GLSLGenerator::GetBuiltInSemantic(const char* semantic, AttributeModifier modifier, int* outputIndex) +{ + if (outputIndex) + *outputIndex = -1; + + if (m_target == Target_VertexShader && modifier == AttributeModifier_Out && String_Equal(semantic, "POSITION")) + return "gl_Position"; + + if (m_target == Target_VertexShader && modifier == AttributeModifier_Out && String_Equal(semantic, "SV_Position")) + return "gl_Position"; + + if (m_target == Target_VertexShader && modifier == AttributeModifier_Out && String_Equal(semantic, "PSIZE")) + return "gl_PointSize"; + + if (m_target == Target_VertexShader && modifier == AttributeModifier_In && String_Equal(semantic, "SV_InstanceID")) + return "gl_InstanceID"; + + if (m_target == Target_FragmentShader && modifier == AttributeModifier_Out && String_Equal(semantic, "SV_Depth")) + return "gl_FragDepth"; + + if (m_target == Target_FragmentShader && modifier == AttributeModifier_In && String_Equal(semantic, "SV_Position")) + return "gl_FragCoord"; + + if (m_target == Target_FragmentShader && modifier == AttributeModifier_Out) + { + int index = -1; + + if (strncmp(semantic, "COLOR", 5) == 0) + index = atoi(semantic + 5); + else if (strncmp(semantic, "SV_Target", 9) == 0) + index = atoi(semantic + 9); + + if (index >= 0) + { + if (m_outputTargets <= index) + m_outputTargets = index + 1; + + if (outputIndex) + *outputIndex = index; + + return m_versionLegacy ? "gl_FragData" : "rast_FragData"; + } + } + + return NULL; +} + +} diff --git a/src/libprojectM/Renderer/hlslparser/src/GLSLGenerator.h b/src/libprojectM/Renderer/hlslparser/src/GLSLGenerator.h new file mode 100755 index 000000000..ecd727c44 --- /dev/null +++ b/src/libprojectM/Renderer/hlslparser/src/GLSLGenerator.h @@ -0,0 +1,164 @@ +//============================================================================= +// +// Render/GLSLGenerator.h +// +// Created by Max McGuire (max@unknownworlds.com) +// Copyright (c) 2013, Unknown Worlds Entertainment, Inc. +// +//============================================================================= + +#ifndef GLSL_GENERATOR_H +#define GLSL_GENERATOR_H + +#include "CodeWriter.h" +#include "HLSLTree.h" + +namespace M4 +{ + +class GLSLGenerator +{ + +public: + enum Target + { + Target_VertexShader, + Target_FragmentShader, + }; + + enum Version + { + Version_110, // OpenGL 2.0 + Version_140, // OpenGL 3.1 + Version_150, // OpenGL 3.2 + Version_100_ES, // OpenGL ES 2.0 + Version_300_ES, // OpenGL ES 3.0 + }; + + enum Flags + { + Flag_FlipPositionOutput = 1 << 0, + Flag_EmulateConstantBuffer = 1 << 1, + Flag_PackMatrixRowMajor = 1 << 2, + Flag_LowerMatrixMultiplication = 1 << 3, + }; + + struct Options + { + unsigned int flags; + const char* constantBufferPrefix; + + Options() + { + flags = 0; + constantBufferPrefix = ""; + } + }; + + GLSLGenerator(); + + bool Generate(HLSLTree* tree, Target target, Version versiom, const char* entryName, const Options& options = Options()); + const char* GetResult() const; + +private: + + enum AttributeModifier + { + AttributeModifier_In, + AttributeModifier_Out, + }; + + void OutputExpressionList(HLSLExpression* expression, HLSLArgument* argument = NULL); + void OutputExpression(HLSLExpression* expression, const HLSLType* dstType = NULL); + void OutputIdentifier(const char* name); + void OutputArguments(HLSLArgument* argument); + + /** + * If the statements are part of a function, then returnType can be used to specify the type + * that a return statement is expected to produce so that correct casts will be generated. + */ + void OutputStatements(int indent, HLSLStatement* statement, const HLSLType* returnType = NULL); + + void OutputAttribute(const HLSLType& type, const char* semantic, AttributeModifier modifier); + void OutputAttributes(HLSLFunction* entryFunction); + void OutputEntryCaller(HLSLFunction* entryFunction); + void OutputDeclaration(HLSLDeclaration* declaration); + void OutputDeclarationType( const HLSLType& type ); + void OutputDeclarationBody( const HLSLType& type, const char* name ); + void OutputDeclaration(const HLSLType& type, const char* name); + void OutputCast(const HLSLType& type); + + void OutputSetOutAttribute(const char* semantic, const char* resultName); + + void LayoutBuffer(HLSLBuffer* buffer, unsigned int& offset); + void LayoutBuffer(const HLSLType& type, unsigned int& offset); + void LayoutBufferElement(const HLSLType& type, unsigned int& offset); + void LayoutBufferAlign(const HLSLType& type, unsigned int& offset); + + HLSLBuffer* GetBufferAccessExpression(HLSLExpression* expression); + void OutputBufferAccessExpression(HLSLBuffer* buffer, HLSLExpression* expression, const HLSLType& type, unsigned int postOffset); + unsigned int OutputBufferAccessIndex(HLSLExpression* expression, unsigned int postOffset); + + void OutputBuffer(int indent, HLSLBuffer* buffer); + + HLSLFunction* FindFunction(HLSLRoot* root, const char* name); + HLSLStruct* FindStruct(HLSLRoot* root, const char* name); + + void Error(const char* format, ...); + + /** GLSL contains some reserved words that don't exist in HLSL. This function will + * sanitize those names. */ + const char* GetSafeIdentifierName(const char* name) const; + + /** Generates a name of the format "base+n" where n is an integer such that the name + * isn't used in the syntax tree. */ + bool ChooseUniqueName(const char* base, char* dst, int dstLength) const; + + const char* GetBuiltInSemantic(const char* semantic, AttributeModifier modifier, int* outputIndex = 0); + const char* GetAttribQualifier(AttributeModifier modifier); + +private: + + static const int s_numReservedWords = 7; + static const char* s_reservedWord[s_numReservedWords]; + + CodeWriter m_writer; + + HLSLTree* m_tree; + const char* m_entryName; + Target m_target; + Version m_version; + bool m_versionLegacy; + Options m_options; + + bool m_outputPosition; + int m_outputTargets; + + const char* m_outAttribPrefix; + const char* m_inAttribPrefix; + + char m_matrixRowFunction[64]; + char m_matrixCtorFunction[64]; + char m_matrixMulFunction[64]; + char m_clipFunction[64]; + char m_tex2DlodFunction[64]; + char m_tex2DbiasFunction[64]; + char m_tex2DgradFunction[64]; + char m_tex3DlodFunction[64]; + char m_texCUBEbiasFunction[64]; + char m_texCUBElodFunction[ 64 ]; + char m_scalarSwizzle2Function[64]; + char m_scalarSwizzle3Function[64]; + char m_scalarSwizzle4Function[64]; + char m_sinCosFunction[64]; + char m_bvecTernary[ 64 ]; + + bool m_error; + + char m_reservedWord[s_numReservedWords][64]; + +}; + +} + +#endif \ No newline at end of file diff --git a/src/libprojectM/Renderer/hlslparser/src/HLSLGenerator.cpp b/src/libprojectM/Renderer/hlslparser/src/HLSLGenerator.cpp new file mode 100755 index 000000000..57123b669 --- /dev/null +++ b/src/libprojectM/Renderer/hlslparser/src/HLSLGenerator.cpp @@ -0,0 +1,1196 @@ +//============================================================================= +// +// Render/HLSLGenerator.cpp +// +// Created by Max McGuire (max@unknownworlds.com) +// Copyright (c) 2013, Unknown Worlds Entertainment, Inc. +// +//============================================================================= + +//#include "Engine/String.h" +//#include "Engine/Log.h" +#include "Engine.h" + +#include "HLSLGenerator.h" +#include "HLSLParser.h" +#include "HLSLTree.h" + +namespace M4 +{ + +static const char* GetTypeName(const HLSLType& type) +{ + switch (type.baseType) + { + case HLSLBaseType_Void: return "void"; + case HLSLBaseType_Float: return "float"; + case HLSLBaseType_Float2: return "float2"; + case HLSLBaseType_Float3: return "float3"; + case HLSLBaseType_Float4: return "float4"; + case HLSLBaseType_Float2x2: return "float2x2"; + case HLSLBaseType_Float3x3: return "float3x3"; + case HLSLBaseType_Float4x4: return "float4x4"; + case HLSLBaseType_Float4x3: return "float4x3"; + case HLSLBaseType_Float4x2: return "float4x2"; + case HLSLBaseType_Half: return "float"; + case HLSLBaseType_Half2: return "float2"; + case HLSLBaseType_Half3: return "float3"; + case HLSLBaseType_Half4: return "float4"; + case HLSLBaseType_Half2x2: return "float2x2"; + case HLSLBaseType_Half3x3: return "float3x3"; + case HLSLBaseType_Half4x4: return "float4x4"; + case HLSLBaseType_Half4x3: return "float4x3"; + case HLSLBaseType_Half4x2: return "float4x2"; + case HLSLBaseType_Bool: return "bool"; + case HLSLBaseType_Bool2: return "bool2"; + case HLSLBaseType_Bool3: return "bool3"; + case HLSLBaseType_Bool4: return "bool4"; + case HLSLBaseType_Int: return "int"; + case HLSLBaseType_Int2: return "int2"; + case HLSLBaseType_Int3: return "int3"; + case HLSLBaseType_Int4: return "int4"; + case HLSLBaseType_Uint: return "uint"; + case HLSLBaseType_Uint2: return "uint2"; + case HLSLBaseType_Uint3: return "uint3"; + case HLSLBaseType_Uint4: return "uint4"; + case HLSLBaseType_Texture: return "texture"; + case HLSLBaseType_Sampler: return "sampler"; + case HLSLBaseType_Sampler2D: return "sampler2D"; + case HLSLBaseType_Sampler3D: return "sampler3D"; + case HLSLBaseType_SamplerCube: return "samplerCUBE"; + case HLSLBaseType_Sampler2DShadow: return "sampler2DShadow"; + case HLSLBaseType_Sampler2DMS: return "sampler2DMS"; + case HLSLBaseType_Sampler2DArray: return "sampler2DArray"; + case HLSLBaseType_UserDefined: return type.typeName; + default: return ""; + } +} + +static int GetFunctionArguments(HLSLFunctionCall* functionCall, HLSLExpression* expression[], int maxArguments) +{ + HLSLExpression* argument = functionCall->argument; + int numArguments = 0; + while (argument != NULL) + { + if (numArguments < maxArguments) + { + expression[numArguments] = argument; + } + argument = argument->nextExpression; + ++numArguments; + } + return numArguments; +} + +HLSLGenerator::HLSLGenerator() +{ + m_tree = NULL; + m_entryName = NULL; + m_legacy = false; + m_target = Target_VertexShader; + m_isInsideBuffer = false; + m_textureSampler2DStruct[0] = 0; + m_textureSampler2DCtor[0] = 0; + m_textureSampler3DStruct[0] = 0; + m_textureSampler3DCtor[0] = 0; + m_textureSamplerCubeStruct[0] = 0; + m_textureSamplerCubeCtor[0] = 0; + m_tex2DFunction[0] = 0; + m_tex2DProjFunction[0] = 0; + m_tex2DLodFunction[0] = 0; + m_tex2DBiasFunction[0] = 0; + m_tex2DGradFunction[0] = 0; + m_tex2DGatherFunction[0] = 0; + m_tex2DSizeFunction[0] = 0; + m_tex2DFetchFunction[0] = 0; + m_tex2DCmpFunction[0] = 0; + m_tex2DMSFetchFunction[0] = 0; + m_tex3DFunction[0] = 0; + m_tex3DLodFunction[0] = 0; + m_tex3DBiasFunction[0] = 0; + m_texCubeFunction[0] = 0; + m_texCubeLodFunction[0] = 0; + m_texCubeBiasFunction[0] = 0; +} + + +// @@ We need a better way of doing semantic replacement: +// - Look at the function being generated. +// - Return semantic, semantics associated to fields of the return structure, or output arguments, or fields of structures associated to output arguments -> output semantic replacement. +// - Semantics associated input arguments or fields of the input arguments -> input semantic replacement. +static const char * TranslateSemantic(const char* semantic, bool output, HLSLGenerator::Target target) +{ + if (target == HLSLGenerator::Target_VertexShader) + { + if (output) + { + if (String_Equal("POSITION", semantic)) return "SV_Position"; + } + else { + if (String_Equal("INSTANCE_ID", semantic)) return "SV_InstanceID"; + } + } + else if (target == HLSLGenerator::Target_PixelShader) + { + if (output) + { + if (String_Equal("DEPTH", semantic)) return "SV_Depth"; + if (String_Equal("COLOR", semantic)) return "SV_Target"; + if (String_Equal("COLOR0", semantic)) return "SV_Target0"; + if (String_Equal("COLOR0_1", semantic)) return "SV_Target1"; + if (String_Equal("COLOR1", semantic)) return "SV_Target1"; + if (String_Equal("COLOR2", semantic)) return "SV_Target2"; + if (String_Equal("COLOR3", semantic)) return "SV_Target3"; + } + else + { + if (String_Equal("VPOS", semantic)) return "SV_Position"; + if (String_Equal("VFACE", semantic)) return "SV_IsFrontFace"; // bool @@ Should we do type replacement too? + } + } + return NULL; +} + +bool HLSLGenerator::Generate(HLSLTree* tree, Target target, const char* entryName, bool legacy) +{ + m_tree = tree; + m_entryName = entryName; + m_target = target; + m_legacy = legacy; + m_isInsideBuffer = false; + + m_writer.Reset(); + + // @@ Should we generate an entirely new copy of the tree so that we can modify it in place? + if (!legacy) + { + HLSLFunction * function = tree->FindFunction(entryName); + + // Handle return value semantics + if (function->semantic != NULL) { + function->sv_semantic = TranslateSemantic(function->semantic, /*output=*/true, target); + } + if (function->returnType.baseType == HLSLBaseType_UserDefined) { + HLSLStruct * s = tree->FindGlobalStruct(function->returnType.typeName); + + HLSLStructField * sv_fields = NULL; + + HLSLStructField * lastField = NULL; + HLSLStructField * field = s->field; + while (field) { + HLSLStructField * nextField = field->nextField; + + if (field->semantic) { + field->hidden = false; + field->sv_semantic = TranslateSemantic(field->semantic, /*output=*/true, target); + + // Fields with SV semantics are stored at the end to avoid linkage problems. + if (field->sv_semantic != NULL) { + // Unlink from last. + if (lastField != NULL) lastField->nextField = nextField; + else s->field = nextField; + + // Add to sv_fields. + field->nextField = sv_fields; + sv_fields = field; + } + } + + if (field != sv_fields) lastField = field; + field = nextField; + } + + // Append SV fields at the end. + if (sv_fields != NULL) { + if (lastField == NULL) { + s->field = sv_fields; + } + else { + ASSERT(lastField->nextField == NULL); + lastField->nextField = sv_fields; + } + } + } + + // Handle argument semantics. + // @@ It would be nice to flag arguments that are used by the program and skip or hide the unused ones. + HLSLArgument * argument = function->argument; + while (argument) { + bool output = argument->modifier == HLSLArgumentModifier_Out; + if (argument->semantic) { + argument->sv_semantic = TranslateSemantic(argument->semantic, output, target); + } + + if (argument->type.baseType == HLSLBaseType_UserDefined) { + HLSLStruct * s = tree->FindGlobalStruct(argument->type.typeName); + + HLSLStructField * field = s->field; + while (field) { + if (field->semantic) { + field->hidden = false; + + if (target == Target_PixelShader && !output && String_EqualNoCase(field->semantic, "POSITION")) { + ASSERT(String_EqualNoCase(field->sv_semantic, "SV_Position")); + field->hidden = true; + } + + field->sv_semantic = TranslateSemantic(field->semantic, output, target); + } + + field = field->nextField; + } + } + + argument = argument->nextArgument; + } + } + + ChooseUniqueName("TextureSampler2D", m_textureSampler2DStruct, sizeof(m_textureSampler2DStruct)); + ChooseUniqueName("CreateTextureSampler2D", m_textureSampler2DCtor, sizeof(m_textureSampler2DCtor)); + ChooseUniqueName("TextureSampler2DShadow", m_textureSampler2DShadowStruct, sizeof(m_textureSampler2DShadowStruct)); + ChooseUniqueName("CreateTextureSampler2DShadow",m_textureSampler2DShadowCtor, sizeof(m_textureSampler2DShadowCtor)); + ChooseUniqueName("TextureSampler3D", m_textureSampler3DStruct, sizeof(m_textureSampler3DStruct)); + ChooseUniqueName("CreateTextureSampler3D", m_textureSampler3DCtor, sizeof(m_textureSampler3DCtor)); + ChooseUniqueName("TextureSamplerCube", m_textureSamplerCubeStruct, sizeof(m_textureSamplerCubeStruct)); + ChooseUniqueName("CreateTextureSamplerCube", m_textureSamplerCubeCtor, sizeof(m_textureSamplerCubeCtor)); + ChooseUniqueName("tex2D", m_tex2DFunction, sizeof(m_tex2DFunction)); + ChooseUniqueName("tex2Dproj", m_tex2DProjFunction, sizeof(m_tex2DProjFunction)); + ChooseUniqueName("tex2Dlod", m_tex2DLodFunction, sizeof(m_tex2DLodFunction)); + ChooseUniqueName("tex2Dbias", m_tex2DBiasFunction, sizeof(m_tex2DBiasFunction)); + ChooseUniqueName("tex2Dgrad", m_tex2DGradFunction, sizeof(m_tex2DGradFunction)); + ChooseUniqueName("tex2Dgather", m_tex2DGatherFunction, sizeof(m_tex2DGatherFunction)); + ChooseUniqueName("tex2Dsize", m_tex2DSizeFunction, sizeof(m_tex2DSizeFunction)); + ChooseUniqueName("tex2Dfetch", m_tex2DFetchFunction, sizeof(m_tex2DFetchFunction)); + ChooseUniqueName("tex2Dcmp", m_tex2DCmpFunction, sizeof(m_tex2DCmpFunction)); + ChooseUniqueName("tex2DMSfetch", m_tex2DMSFetchFunction, sizeof(m_tex2DMSFetchFunction)); + ChooseUniqueName("tex2DMSsize", m_tex2DMSSizeFunction, sizeof(m_tex2DMSSizeFunction)); + ChooseUniqueName("tex3D", m_tex3DFunction, sizeof(m_tex3DFunction)); + ChooseUniqueName("tex3Dlod", m_tex3DLodFunction, sizeof(m_tex3DLodFunction)); + ChooseUniqueName("tex3Dbias", m_tex3DBiasFunction, sizeof(m_tex3DBiasFunction)); + ChooseUniqueName("tex3Dsize", m_tex3DSizeFunction, sizeof(m_tex3DSizeFunction)); + ChooseUniqueName("texCUBE", m_texCubeFunction, sizeof(m_texCubeFunction)); + ChooseUniqueName("texCUBElod", m_texCubeLodFunction, sizeof(m_texCubeLodFunction)); + ChooseUniqueName("texCUBEbias", m_texCubeBiasFunction, sizeof(m_texCubeBiasFunction)); + ChooseUniqueName("texCUBEsize", m_texCubeSizeFunction, sizeof(m_texCubeSizeFunction)); + + if (!m_legacy) + { + // @@ Only emit code for sampler types that are actually used? + + m_writer.WriteLine(0, "struct %s {", m_textureSampler2DStruct); + m_writer.WriteLine(1, "Texture2D t;"); + m_writer.WriteLine(1, "SamplerState s;"); + m_writer.WriteLine(0, "};"); + + m_writer.WriteLine(0, "struct %s {", m_textureSampler2DShadowStruct); + m_writer.WriteLine(1, "Texture2D t;"); + m_writer.WriteLine(1, "SamplerComparisonState s;"); + m_writer.WriteLine(0, "};"); + + m_writer.WriteLine(0, "struct %s {", m_textureSampler3DStruct); + m_writer.WriteLine(1, "Texture3D t;"); + m_writer.WriteLine(1, "SamplerState s;"); + m_writer.WriteLine(0, "};"); + + m_writer.WriteLine(0, "struct %s {", m_textureSamplerCubeStruct); + m_writer.WriteLine(1, "TextureCube t;"); + m_writer.WriteLine(1, "SamplerState s;"); + m_writer.WriteLine(0, "};"); + + m_writer.WriteLine(0, "%s %s(Texture2D t, SamplerState s) {", m_textureSampler2DStruct, m_textureSampler2DCtor); + m_writer.WriteLine(1, "%s ts;", m_textureSampler2DStruct); + m_writer.WriteLine(1, "ts.t = t; ts.s = s;"); + m_writer.WriteLine(1, "return ts;"); + m_writer.WriteLine(0, "}"); + + m_writer.WriteLine(0, "%s %s(Texture2D t, SamplerComparisonState s) {", m_textureSampler2DShadowStruct, m_textureSampler2DShadowCtor); + m_writer.WriteLine(1, "%s ts;", m_textureSampler2DShadowStruct); + m_writer.WriteLine(1, "ts.t = t; ts.s = s;"); + m_writer.WriteLine(1, "return ts;"); + m_writer.WriteLine(0, "}"); + + m_writer.WriteLine(0, "%s %s(Texture3D t, SamplerState s) {", m_textureSampler3DStruct, m_textureSampler3DCtor); + m_writer.WriteLine(1, "%s ts;", m_textureSampler3DStruct); + m_writer.WriteLine(1, "ts.t = t; ts.s = s;"); + m_writer.WriteLine(1, "return ts;"); + m_writer.WriteLine(0, "}"); + + m_writer.WriteLine(0, "%s %s(TextureCube t, SamplerState s) {", m_textureSamplerCubeStruct, m_textureSamplerCubeCtor); + m_writer.WriteLine(1, "%s ts;", m_textureSamplerCubeStruct); + m_writer.WriteLine(1, "ts.t = t; ts.s = s;"); + m_writer.WriteLine(1, "return ts;"); + m_writer.WriteLine(0, "}"); + + if (m_tree->GetContainsString("tex2D")) + { + m_writer.WriteLine(0, "float4 %s(%s ts, float2 texCoord) {", m_tex2DFunction, m_textureSampler2DStruct); + m_writer.WriteLine(1, "return ts.t.Sample(ts.s, texCoord);"); + m_writer.WriteLine(0, "}"); + } + if (m_tree->GetContainsString("tex2Dproj")) + { + m_writer.WriteLine(0, "float4 %s(%s ts, float4 texCoord) {", m_tex2DProjFunction, m_textureSampler2DStruct); + m_writer.WriteLine(1, "return ts.t.Sample(ts.s, texCoord.xy / texCoord.w);"); + m_writer.WriteLine(0, "}"); + } + if (m_tree->GetContainsString("tex2Dlod")) + { + m_writer.WriteLine(0, "float4 %s(%s ts, float4 texCoord, int2 offset=0) {", m_tex2DLodFunction, m_textureSampler2DStruct); + m_writer.WriteLine(1, "return ts.t.SampleLevel(ts.s, texCoord.xy, texCoord.w, offset);"); + m_writer.WriteLine(0, "}"); + } + if (m_tree->GetContainsString("tex2Dbias")) + { + m_writer.WriteLine(0, "float4 %s(%s ts, float4 texCoord) {", m_tex2DBiasFunction, m_textureSampler2DStruct); + m_writer.WriteLine(1, "return ts.t.SampleBias(ts.s, texCoord.xy, texCoord.w);"); + m_writer.WriteLine(0, "}"); + } + if (m_tree->GetContainsString("tex2Dgrad")) + { + m_writer.WriteLine(0, "float4 %s(%s ts, float2 texCoord, float2 ddx, float2 ddy) {", m_tex2DGradFunction, m_textureSampler2DStruct); + m_writer.WriteLine(1, "return ts.t.SampleGrad(ts.s, texCoord.xy, ddx, ddy);"); + m_writer.WriteLine(0, "}"); + } + if (m_tree->GetContainsString("tex2Dgather")) + { + m_writer.WriteLine(0, "float4 %s(%s ts, float2 texCoord, int component, int2 offset=0) {", m_tex2DGatherFunction, m_textureSampler2DStruct); + m_writer.WriteLine(1, "if(component == 0) return ts.t.GatherRed(ts.s, texCoord, offset);"); + m_writer.WriteLine(1, "if(component == 1) return ts.t.GatherGreen(ts.s, texCoord, offset);"); + m_writer.WriteLine(1, "if(component == 2) return ts.t.GatherBlue(ts.s, texCoord, offset);"); + m_writer.WriteLine(1, "return ts.t.GatherAlpha(ts.s, texCoord, offset);"); + m_writer.WriteLine(0, "}"); + } + if (m_tree->GetContainsString("tex2Dsize")) + { + m_writer.WriteLine(0, "int2 %s(%s ts) {", m_tex2DSizeFunction, m_textureSampler2DStruct); + m_writer.WriteLine(1, "int2 size; ts.t.GetDimensions(size.x, size.y); return size;"); + m_writer.WriteLine(0, "}"); + } + if (m_tree->GetContainsString("tex2Dfetch")) + { + m_writer.WriteLine(0, "int2 %s(%s ts, int3 texCoord) {", m_tex2DFetchFunction, m_textureSampler2DStruct); + m_writer.WriteLine(1, "return ts.t.Load(texCoord.xyz);"); + m_writer.WriteLine(0, "}"); + } + if (m_tree->GetContainsString("tex2Dcmp")) + { + m_writer.WriteLine(0, "float4 %s(%s ts, float4 texCoord) {", m_tex2DCmpFunction, m_textureSampler2DShadowStruct); + m_writer.WriteLine(1, "return ts.t.SampleCmpLevelZero(ts.s, texCoord.xy, texCoord.z);"); + m_writer.WriteLine(0, "}"); + } + if (m_tree->GetContainsString("tex2DMSfetch")) + { + m_writer.WriteLine(0, "float4 %s(Texture2DMS t, int2 texCoord, int sample) {", m_tex2DMSFetchFunction); + m_writer.WriteLine(1, "return t.Load(texCoord, sample);"); + m_writer.WriteLine(0, "}"); + } + if (m_tree->GetContainsString("tex2DMSsize")) + { + m_writer.WriteLine(0, "int3 %s(Texture2DMS t) {", m_tex2DMSSizeFunction); + m_writer.WriteLine(1, "int3 size; t.GetDimensions(size.x, size.y, size.z); return size;"); // @@ Not tested, does this return the number of samples in the third argument? + m_writer.WriteLine(0, "}"); + } + if (m_tree->GetContainsString("tex3D")) + { + m_writer.WriteLine(0, "float4 %s(%s ts, float3 texCoord) {", m_tex3DFunction, m_textureSampler3DStruct); + m_writer.WriteLine(1, "return ts.t.Sample(ts.s, texCoord);"); + m_writer.WriteLine(0, "}"); + } + if (m_tree->GetContainsString("tex3Dlod")) + { + m_writer.WriteLine(0, "float4 %s(%s ts, float4 texCoord) {", m_tex3DLodFunction, m_textureSampler3DStruct); + m_writer.WriteLine(1, "return ts.t.SampleLevel(ts.s, texCoord.xyz, texCoord.w);"); + m_writer.WriteLine(0, "}"); + } + if (m_tree->GetContainsString("tex3Dbias")) + { + m_writer.WriteLine(0, "float4 %s(%s ts, float4 texCoord) {", m_tex3DBiasFunction, m_textureSampler3DStruct); + m_writer.WriteLine(1, "return ts.t.SampleBias(ts.s, texCoord.xyz, texCoord.w);"); + m_writer.WriteLine(0, "}"); + } + if (m_tree->GetContainsString("tex3Dsize")) + { + m_writer.WriteLine(0, "int3 %s(%s ts) {", m_tex3DSizeFunction, m_textureSampler3DStruct); + m_writer.WriteLine(1, "int3 size; ts.t.GetDimensions(size.x, size.y, size.z); return size;"); + m_writer.WriteLine(0, "}"); + } + if (m_tree->GetContainsString("texCUBE")) + { + m_writer.WriteLine(0, "float4 %s(%s ts, float3 texCoord) {", m_texCubeFunction, m_textureSamplerCubeStruct); + m_writer.WriteLine(1, "return ts.t.Sample(ts.s, texCoord);"); + m_writer.WriteLine(0, "}"); + } + if (m_tree->GetContainsString("texCUBElod")) + { + m_writer.WriteLine(0, "float4 %s(%s ts, float4 texCoord) {", m_texCubeLodFunction, m_textureSamplerCubeStruct); + m_writer.WriteLine(1, "return ts.t.SampleLevel(ts.s, texCoord.xyz, texCoord.w);"); + m_writer.WriteLine(0, "}"); + } + if (m_tree->GetContainsString("texCUBEbias")) + { + m_writer.WriteLine(0, "float4 %s(%s ts, float4 texCoord) {", m_texCubeBiasFunction, m_textureSamplerCubeStruct); + m_writer.WriteLine(1, "return ts.t.SampleBias(ts.s, texCoord.xyz, texCoord.w);"); + m_writer.WriteLine(0, "}"); + } + if (m_tree->GetContainsString("texCUBEsize")) + { + m_writer.WriteLine(0, "int %s(%s ts) {", m_texCubeSizeFunction, m_textureSamplerCubeStruct); + m_writer.WriteLine(1, "int size; ts.t.GetDimensions(size); return size;"); // @@ Not tested, does this return a single value? + m_writer.WriteLine(0, "}"); + } + } + + HLSLRoot* root = m_tree->GetRoot(); + OutputStatements(0, root->statement); + + m_tree = NULL; + return true; +} + +const char* HLSLGenerator::GetResult() const +{ + return m_writer.GetResult(); +} + +void HLSLGenerator::OutputExpressionList(HLSLExpression* expression) +{ + int numExpressions = 0; + while (expression != NULL) + { + if (numExpressions > 0) + { + m_writer.Write(", "); + } + OutputExpression(expression); + expression = expression->nextExpression; + ++numExpressions; + } +} + +void HLSLGenerator::OutputExpression(HLSLExpression* expression) +{ + if (expression->nodeType == HLSLNodeType_IdentifierExpression) + { + HLSLIdentifierExpression* identifierExpression = static_cast(expression); + const char* name = identifierExpression->name; + if (!m_legacy && IsSamplerType(identifierExpression->expressionType) && identifierExpression->global) + { + // @@ Handle generic sampler type. + + if (identifierExpression->expressionType.baseType == HLSLBaseType_Sampler2D) + { + m_writer.Write("%s(%s_texture, %s_sampler)", m_textureSampler2DCtor, name, name); + } + else if (identifierExpression->expressionType.baseType == HLSLBaseType_Sampler3D) + { + m_writer.Write("%s(%s_texture, %s_sampler)", m_textureSampler3DCtor, name, name); + } + else if (identifierExpression->expressionType.baseType == HLSLBaseType_SamplerCube) + { + m_writer.Write("%s(%s_texture, %s_sampler)", m_textureSamplerCubeCtor, name, name); + } + else if (identifierExpression->expressionType.baseType == HLSLBaseType_Sampler2DShadow) + { + m_writer.Write("%s(%s_texture, %s_sampler)", m_textureSampler2DShadowCtor, name, name); + } + else if (identifierExpression->expressionType.baseType == HLSLBaseType_Sampler2DMS) + { + m_writer.Write("%s", name); + } + } + else + { + m_writer.Write("%s", name); + } + } + else if (expression->nodeType == HLSLNodeType_CastingExpression) + { + HLSLCastingExpression* castingExpression = static_cast(expression); + m_writer.Write("("); + OutputDeclaration(castingExpression->type, ""); + m_writer.Write(")("); + OutputExpression(castingExpression->expression); + m_writer.Write(")"); + } + else if (expression->nodeType == HLSLNodeType_ConstructorExpression) + { + HLSLConstructorExpression* constructorExpression = static_cast(expression); + m_writer.Write("%s(", GetTypeName(constructorExpression->type)); + OutputExpressionList(constructorExpression->argument); + m_writer.Write(")"); + } + else if (expression->nodeType == HLSLNodeType_LiteralExpression) + { + HLSLLiteralExpression* literalExpression = static_cast(expression); + switch (literalExpression->type) + { + case HLSLBaseType_Half: + case HLSLBaseType_Float: + { + // Don't use printf directly so that we don't use the system locale. + char buffer[64]; + String_FormatFloat(buffer, sizeof(buffer), literalExpression->fValue); + m_writer.Write("%s", buffer); + } + break; + case HLSLBaseType_Int: + m_writer.Write("%d", literalExpression->iValue); + break; + case HLSLBaseType_Bool: + m_writer.Write("%s", literalExpression->bValue ? "true" : "false"); + break; + default: + ASSERT(0); + } + } + else if (expression->nodeType == HLSLNodeType_UnaryExpression) + { + HLSLUnaryExpression* unaryExpression = static_cast(expression); + const char* op = "?"; + bool pre = true; + switch (unaryExpression->unaryOp) + { + case HLSLUnaryOp_Negative: op = "-"; break; + case HLSLUnaryOp_Positive: op = "+"; break; + case HLSLUnaryOp_Not: op = "!"; break; + case HLSLUnaryOp_PreIncrement: op = "++"; break; + case HLSLUnaryOp_PreDecrement: op = "--"; break; + case HLSLUnaryOp_PostIncrement: op = "++"; pre = false; break; + case HLSLUnaryOp_PostDecrement: op = "--"; pre = false; break; + case HLSLUnaryOp_BitNot: op = "~"; break; + } + m_writer.Write("("); + if (pre) + { + m_writer.Write("%s", op); + OutputExpression(unaryExpression->expression); + } + else + { + OutputExpression(unaryExpression->expression); + m_writer.Write("%s", op); + } + m_writer.Write(")"); + } + else if (expression->nodeType == HLSLNodeType_BinaryExpression) + { + HLSLBinaryExpression* binaryExpression = static_cast(expression); + m_writer.Write("("); + OutputExpression(binaryExpression->expression1); + const char* op = "?"; + switch (binaryExpression->binaryOp) + { + case HLSLBinaryOp_Add: op = " + "; break; + case HLSLBinaryOp_Sub: op = " - "; break; + case HLSLBinaryOp_Mul: op = " * "; break; + case HLSLBinaryOp_Div: op = " / "; break; + case HLSLBinaryOp_Less: op = " < "; break; + case HLSLBinaryOp_Greater: op = " > "; break; + case HLSLBinaryOp_LessEqual: op = " <= "; break; + case HLSLBinaryOp_GreaterEqual: op = " >= "; break; + case HLSLBinaryOp_Equal: op = " == "; break; + case HLSLBinaryOp_NotEqual: op = " != "; break; + case HLSLBinaryOp_Assign: op = " = "; break; + case HLSLBinaryOp_AddAssign: op = " += "; break; + case HLSLBinaryOp_SubAssign: op = " -= "; break; + case HLSLBinaryOp_MulAssign: op = " *= "; break; + case HLSLBinaryOp_DivAssign: op = " /= "; break; + case HLSLBinaryOp_And: op = " && "; break; + case HLSLBinaryOp_Or: op = " || "; break; + case HLSLBinaryOp_BitAnd: op = " & "; break; + case HLSLBinaryOp_BitOr: op = " | "; break; + case HLSLBinaryOp_BitXor: op = " ^ "; break; + default: + ASSERT(0); + } + m_writer.Write("%s", op); + OutputExpression(binaryExpression->expression2); + m_writer.Write(")"); + } + else if (expression->nodeType == HLSLNodeType_ConditionalExpression) + { + HLSLConditionalExpression* conditionalExpression = static_cast(expression); + m_writer.Write("(("); + OutputExpression(conditionalExpression->condition); + m_writer.Write(")?("); + OutputExpression(conditionalExpression->trueExpression); + m_writer.Write("):("); + OutputExpression(conditionalExpression->falseExpression); + m_writer.Write("))"); + } + else if (expression->nodeType == HLSLNodeType_MemberAccess) + { + HLSLMemberAccess* memberAccess = static_cast(expression); + m_writer.Write("("); + OutputExpression(memberAccess->object); + m_writer.Write(").%s", memberAccess->field); + } + else if (expression->nodeType == HLSLNodeType_ArrayAccess) + { + HLSLArrayAccess* arrayAccess = static_cast(expression); + OutputExpression(arrayAccess->array); + m_writer.Write("["); + OutputExpression(arrayAccess->index); + m_writer.Write("]"); + } + else if (expression->nodeType == HLSLNodeType_FunctionCall) + { + HLSLFunctionCall* functionCall = static_cast(expression); + const char* name = functionCall->function->name; + if (!m_legacy) + { + if (String_Equal(name, "tex2D")) + { + name = m_tex2DFunction; + } + else if (String_Equal(name, "tex2Dproj")) + { + name = m_tex2DProjFunction; + } + else if (String_Equal(name, "tex2Dlod")) + { + name = m_tex2DLodFunction; + } + else if (String_Equal(name, "tex2Dbias")) + { + name = m_tex2DBiasFunction; + } + else if (String_Equal(name, "tex2Dgrad")) + { + name = m_tex2DGradFunction; + } + else if (String_Equal(name, "tex2Dgather")) + { + name = m_tex2DGatherFunction; + } + else if (String_Equal(name, "tex2Dsize")) + { + name = m_tex2DSizeFunction; + } + else if (String_Equal(name, "tex2Dfetch")) + { + name = m_tex2DFetchFunction; + } + else if (String_Equal(name, "tex2Dcmp")) + { + name = m_tex2DCmpFunction; + } + else if (String_Equal(name, "tex2DMSfetch")) + { + name = m_tex2DMSFetchFunction; + } + else if (String_Equal(name, "tex2DMSsize")) + { + name = m_tex2DMSSizeFunction; + } + else if (String_Equal(name, "tex3D")) + { + name = m_tex3DFunction; + } + else if (String_Equal(name, "tex3Dlod")) + { + name = m_tex3DLodFunction; + } + else if (String_Equal(name, "tex3Dbias")) + { + name = m_tex3DBiasFunction; + } + else if (String_Equal(name, "tex3Dsize")) + { + name = m_tex3DSizeFunction; + } + else if (String_Equal(name, "texCUBE")) + { + name = m_texCubeFunction; + } + else if (String_Equal(name, "texCUBElod")) + { + name = m_texCubeLodFunction; + } + else if (String_Equal(name, "texCUBEbias")) + { + name = m_texCubeBiasFunction; + } + else if (String_Equal(name, "texCUBEsize")) + { + name = m_texCubeSizeFunction; + } + } + m_writer.Write("%s(", name); + OutputExpressionList(functionCall->argument); + m_writer.Write(")"); + } + else + { + m_writer.Write(""); + } +} + +void HLSLGenerator::OutputArguments(HLSLArgument* argument) +{ + int numArgs = 0; + while (argument != NULL) + { + if (numArgs > 0) + { + m_writer.Write(", "); + } + + switch (argument->modifier) + { + case HLSLArgumentModifier_In: + m_writer.Write("in "); + break; + case HLSLArgumentModifier_Out: + m_writer.Write("out "); + break; + case HLSLArgumentModifier_Inout: + m_writer.Write("inout "); + break; + case HLSLArgumentModifier_Uniform: + m_writer.Write("uniform "); + break; + default: + break; + } + + const char * semantic = argument->sv_semantic ? argument->sv_semantic : argument->semantic; + + OutputDeclaration(argument->type, argument->name, semantic, /*registerName=*/NULL, argument->defaultValue); + argument = argument->nextArgument; + ++numArgs; + } +} + +static const char * GetAttributeName(HLSLAttributeType attributeType) +{ + if (attributeType == HLSLAttributeType_Unroll) return "unroll"; + if (attributeType == HLSLAttributeType_Branch) return "branch"; + if (attributeType == HLSLAttributeType_Flatten) return "flatten"; + return NULL; +} + +void HLSLGenerator::OutputAttributes(int indent, HLSLAttribute* attribute) +{ + while (attribute != NULL) + { + const char * attributeName = GetAttributeName(attribute->attributeType); + + if (attributeName != NULL) + { + m_writer.WriteLineTagged(indent, attribute->fileName, attribute->line, "[%s]", attributeName); + } + + attribute = attribute->nextAttribute; + } +} + +void HLSLGenerator::OutputStatements(int indent, HLSLStatement* statement) +{ + while (statement != NULL) + { + if (statement->hidden) + { + statement = statement->nextStatement; + continue; + } + + OutputAttributes(indent, statement->attributes); + + if (statement->nodeType == HLSLNodeType_Declaration) + { + HLSLDeclaration* declaration = static_cast(statement); + m_writer.BeginLine(indent, declaration->fileName, declaration->line); + OutputDeclaration(declaration); + m_writer.EndLine(";"); + } + else if (statement->nodeType == HLSLNodeType_Struct) + { + HLSLStruct* structure = static_cast(statement); + m_writer.WriteLineTagged(indent, structure->fileName, structure->line, "struct %s {", structure->name); + HLSLStructField* field = structure->field; + while (field != NULL) + { + if (!field->hidden) + { + m_writer.BeginLine(indent + 1, field->fileName, field->line); + const char * semantic = field->sv_semantic ? field->sv_semantic : field->semantic; + OutputDeclaration(field->type, field->name, semantic); + m_writer.Write(";"); + m_writer.EndLine(); + } + field = field->nextField; + } + m_writer.WriteLine(indent, "};"); + } + else if (statement->nodeType == HLSLNodeType_Buffer) + { + HLSLBuffer* buffer = static_cast(statement); + HLSLDeclaration* field = buffer->field; + + if (!m_legacy) + { + m_writer.BeginLine(indent, buffer->fileName, buffer->line); + m_writer.Write("cbuffer %s", buffer->name); + if (buffer->registerName != NULL) + { + m_writer.Write(" : register(%s)", buffer->registerName); + } + m_writer.EndLine(" {"); + } + + m_isInsideBuffer = true; + + while (field != NULL) + { + if (!field->hidden) + { + m_writer.BeginLine(indent + 1, field->fileName, field->line); + OutputDeclaration(field->type, field->name, /*semantic=*/NULL, /*registerName*/field->registerName, field->assignment); + m_writer.Write(";"); + m_writer.EndLine(); + } + field = (HLSLDeclaration*)field->nextStatement; + } + + m_isInsideBuffer = false; + + if (!m_legacy) + { + m_writer.WriteLine(indent, "};"); + } + } + else if (statement->nodeType == HLSLNodeType_Function) + { + HLSLFunction* function = static_cast(statement); + + // Use an alternate name for the function which is supposed to be entry point + // so that we can supply our own function which will be the actual entry point. + const char* functionName = function->name; + const char* returnTypeName = GetTypeName(function->returnType); + + m_writer.BeginLine(indent, function->fileName, function->line); + m_writer.Write("%s %s(", returnTypeName, functionName); + + OutputArguments(function->argument); + + const char * semantic = function->sv_semantic ? function->sv_semantic : function->semantic; + if (semantic != NULL) + { + m_writer.Write(") : %s {", semantic); + } + else + { + m_writer.Write(") {"); + } + + m_writer.EndLine(); + + OutputStatements(indent + 1, function->statement); + m_writer.WriteLine(indent, "};"); + } + else if (statement->nodeType == HLSLNodeType_ExpressionStatement) + { + HLSLExpressionStatement* expressionStatement = static_cast(statement); + m_writer.BeginLine(indent, statement->fileName, statement->line); + OutputExpression(expressionStatement->expression); + m_writer.EndLine(";"); + } + else if (statement->nodeType == HLSLNodeType_ReturnStatement) + { + HLSLReturnStatement* returnStatement = static_cast(statement); + if (returnStatement->expression != NULL) + { + m_writer.BeginLine(indent, returnStatement->fileName, returnStatement->line); + m_writer.Write("return "); + OutputExpression(returnStatement->expression); + m_writer.EndLine(";"); + } + else + { + m_writer.WriteLineTagged(indent, returnStatement->fileName, returnStatement->line, "return;"); + } + } + else if (statement->nodeType == HLSLNodeType_DiscardStatement) + { + HLSLDiscardStatement* discardStatement = static_cast(statement); + m_writer.WriteLineTagged(indent, discardStatement->fileName, discardStatement->line, "discard;"); + } + else if (statement->nodeType == HLSLNodeType_BreakStatement) + { + HLSLBreakStatement* breakStatement = static_cast(statement); + m_writer.WriteLineTagged(indent, breakStatement->fileName, breakStatement->line, "break;"); + } + else if (statement->nodeType == HLSLNodeType_ContinueStatement) + { + HLSLContinueStatement* continueStatement = static_cast(statement); + m_writer.WriteLineTagged(indent, continueStatement->fileName, continueStatement->line, "continue;"); + } + else if (statement->nodeType == HLSLNodeType_IfStatement) + { + HLSLIfStatement* ifStatement = static_cast(statement); + m_writer.BeginLine(indent, ifStatement->fileName, ifStatement->line); + m_writer.Write("if ("); + OutputExpression(ifStatement->condition); + m_writer.Write(") {"); + m_writer.EndLine(); + OutputStatements(indent + 1, ifStatement->statement); + m_writer.WriteLine(indent, "}"); + if (ifStatement->elseStatement != NULL) + { + m_writer.WriteLine(indent, "else {"); + OutputStatements(indent + 1, ifStatement->elseStatement); + m_writer.WriteLine(indent, "}"); + } + } + else if (statement->nodeType == HLSLNodeType_ForStatement) + { + HLSLForStatement* forStatement = static_cast(statement); + m_writer.BeginLine(indent, forStatement->fileName, forStatement->line); + m_writer.Write("for ("); + OutputDeclaration(forStatement->initialization); + m_writer.Write("; "); + OutputExpression(forStatement->condition); + m_writer.Write("; "); + OutputExpression(forStatement->increment); + m_writer.Write(") {"); + m_writer.EndLine(); + OutputStatements(indent + 1, forStatement->statement); + m_writer.WriteLine(indent, "}"); + } + else if (statement->nodeType == HLSLNodeType_BlockStatement) + { + HLSLBlockStatement* blockStatement = static_cast(statement); + m_writer.WriteLineTagged(indent, blockStatement->fileName, blockStatement->line, "{"); + OutputStatements(indent + 1, blockStatement->statement); + m_writer.WriteLine(indent, "}"); + } + else if (statement->nodeType == HLSLNodeType_Technique) + { + // Techniques are ignored. + } + else if (statement->nodeType == HLSLNodeType_Pipeline) + { + // Pipelines are ignored. + } + else + { + // Unhanded statement type. + ASSERT(0); + } + + statement = statement->nextStatement; + } +} + +void HLSLGenerator::OutputDeclaration(HLSLDeclaration* declaration) +{ + bool isSamplerType = IsSamplerType(declaration->type); + + if (!m_legacy && isSamplerType) + { + int reg = -1; + if (declaration->registerName != NULL) + { + sscanf(declaration->registerName, "s%d", ®); + } + + const char* textureType = NULL; + const char* samplerType = "SamplerState"; + // @@ Handle generic sampler type. + + if (declaration->type.baseType == HLSLBaseType_Sampler2D) + { + textureType = "Texture2D"; + } + else if (declaration->type.baseType == HLSLBaseType_Sampler3D) + { + textureType = "Texture3D"; + } + else if (declaration->type.baseType == HLSLBaseType_SamplerCube) + { + textureType = "TextureCube"; + } + else if (declaration->type.baseType == HLSLBaseType_Sampler2DShadow) + { + textureType = "Texture2D"; + samplerType = "SamplerComparisonState"; + } + else if (declaration->type.baseType == HLSLBaseType_Sampler2DMS) + { + textureType = "Texture2DMS"; // @@ Is template argument required? + samplerType = NULL; + } + + if (samplerType != NULL) + { + if (reg != -1) + { + m_writer.Write("%s %s_texture : register(t%d); %s %s_sampler : register(s%d)", textureType, declaration->name, reg, samplerType, declaration->name, reg); + } + else + { + m_writer.Write("%s %s_texture; %s %s_sampler", textureType, declaration->name, samplerType, declaration->name); + } + } + else + { + if (reg != -1) + { + m_writer.Write("%s %s : register(t%d)", textureType, declaration->name, reg); + } + else + { + m_writer.Write("%s %s", textureType, declaration->name); + } + } + return; + } + + OutputDeclarationType(declaration->type); + OutputDeclarationBody(declaration->type, declaration->name, declaration->semantic, declaration->registerName, declaration->assignment); + declaration = declaration->nextDeclaration; + + while(declaration != NULL) { + m_writer.Write(", "); + OutputDeclarationBody(declaration->type, declaration->name, declaration->semantic, declaration->registerName, declaration->assignment); + declaration = declaration->nextDeclaration; + }; +} + +void HLSLGenerator::OutputDeclarationType(const HLSLType& type) +{ + const char* typeName = GetTypeName(type); + if (!m_legacy) + { + if (type.baseType == HLSLBaseType_Sampler2D) + { + typeName = m_textureSampler2DStruct; + } + else if (type.baseType == HLSLBaseType_Sampler3D) + { + typeName = m_textureSampler3DStruct; + } + else if (type.baseType == HLSLBaseType_SamplerCube) + { + typeName = m_textureSamplerCubeStruct; + } + else if (type.baseType == HLSLBaseType_Sampler2DShadow) + { + typeName = m_textureSampler2DShadowStruct; + } + else if (type.baseType == HLSLBaseType_Sampler2DMS) + { + typeName = "Texture2DMS"; + } + } + + if (type.flags & HLSLTypeFlag_Const) + { + m_writer.Write("const "); + } + if (type.flags & HLSLTypeFlag_Static) + { + m_writer.Write("static "); + } + + // Interpolation modifiers. + if (type.flags & HLSLTypeFlag_Centroid) + { + m_writer.Write("centroid "); + } + if (type.flags & HLSLTypeFlag_Linear) + { + m_writer.Write("linear "); + } + if (type.flags & HLSLTypeFlag_NoInterpolation) + { + m_writer.Write("nointerpolation "); + } + if (type.flags & HLSLTypeFlag_NoPerspective) + { + m_writer.Write("noperspective "); + } + if (type.flags & HLSLTypeFlag_Sample) // @@ Only in shader model >= 4.1 + { + m_writer.Write("sample "); + } + + m_writer.Write("%s ", typeName); +} + +void HLSLGenerator::OutputDeclarationBody(const HLSLType& type, const char* name, const char* semantic/*=NULL*/, const char* registerName/*=NULL*/, HLSLExpression * assignment/*=NULL*/) +{ + m_writer.Write("%s", name); + + if (type.array) + { + ASSERT(semantic == NULL); + m_writer.Write("["); + if (type.arraySize != NULL) + { + OutputExpression(type.arraySize); + } + m_writer.Write("]"); + } + + if (semantic != NULL) + { + m_writer.Write(" : %s", semantic); + } + + if (registerName != NULL) + { + if (m_isInsideBuffer) + { + m_writer.Write(" : packoffset(%s)", registerName); + } + else + { + m_writer.Write(" : register(%s)", registerName); + } + } + + if (assignment != NULL && !IsSamplerType(type)) + { + m_writer.Write(" = "); + if (type.array) + { + m_writer.Write("{ "); + OutputExpressionList(assignment); + m_writer.Write(" }"); + } + else + { + OutputExpression(assignment); + } + } +} + +void HLSLGenerator::OutputDeclaration(const HLSLType& type, const char* name, const char* semantic/*=NULL*/, const char* registerName/*=NULL*/, HLSLExpression * assignment/*=NULL*/) +{ + OutputDeclarationType(type); + OutputDeclarationBody(type, name, semantic, registerName, assignment); +} + +bool HLSLGenerator::ChooseUniqueName(const char* base, char* dst, int dstLength) const +{ + // IC: Try without suffix first. + String_Printf(dst, dstLength, "%s", base); + if (!m_tree->GetContainsString(base)) + { + return true; + } + + for (int i = 1; i < 1024; ++i) + { + String_Printf(dst, dstLength, "%s%d", base, i); + if (!m_tree->GetContainsString(dst)) + { + return true; + } + } + return false; +} + +} diff --git a/src/libprojectM/Renderer/hlslparser/src/HLSLGenerator.h b/src/libprojectM/Renderer/hlslparser/src/HLSLGenerator.h new file mode 100755 index 000000000..598e6b7d6 --- /dev/null +++ b/src/libprojectM/Renderer/hlslparser/src/HLSLGenerator.h @@ -0,0 +1,100 @@ +//============================================================================= +// +// Render/HLSLGenerator.h +// +// Created by Max McGuire (max@unknownworlds.com) +// Copyright (c) 2013, Unknown Worlds Entertainment, Inc. +// +//============================================================================= + +#ifndef HLSL_GENERATOR_H +#define HLSL_GENERATOR_H + +#include "CodeWriter.h" +#include "HLSLTree.h" + +namespace M4 +{ + +class HLSLTree; +struct HLSLFunction; +struct HLSLStruct; + +/** + * This class is used to generate HLSL which is compatible with the D3D9 + * compiler (i.e. no cbuffers). + */ +class HLSLGenerator +{ + +public: + + enum Target + { + Target_VertexShader, + Target_PixelShader, + }; + + HLSLGenerator(); + + bool Generate(HLSLTree* tree, Target target, const char* entryName, bool legacy); + const char* GetResult() const; + +private: + + void OutputExpressionList(HLSLExpression* expression); + void OutputExpression(HLSLExpression* expression); + void OutputArguments(HLSLArgument* argument); + void OutputAttributes(int indent, HLSLAttribute* attribute); + void OutputStatements(int indent, HLSLStatement* statement); + void OutputDeclaration(HLSLDeclaration* declaration); + void OutputDeclaration(const HLSLType& type, const char* name, const char* semantic = NULL, const char* registerName = NULL, HLSLExpression* defaultValue = NULL); + void OutputDeclarationType(const HLSLType& type); + void OutputDeclarationBody(const HLSLType& type, const char* name, const char* semantic =NULL, const char* registerName = NULL, HLSLExpression * assignment = NULL); + + /** Generates a name of the format "base+n" where n is an integer such that the name + * isn't used in the syntax tree. */ + bool ChooseUniqueName(const char* base, char* dst, int dstLength) const; + +private: + + CodeWriter m_writer; + + const HLSLTree* m_tree; + const char* m_entryName; + Target m_target; + bool m_legacy; + bool m_isInsideBuffer; + + char m_textureSampler2DStruct[64]; + char m_textureSampler2DCtor[64]; + char m_textureSampler2DShadowStruct[64]; + char m_textureSampler2DShadowCtor[64]; + char m_textureSampler3DStruct[64]; + char m_textureSampler3DCtor[64]; + char m_textureSamplerCubeStruct[64]; + char m_textureSamplerCubeCtor[64]; + char m_tex2DFunction[64]; + char m_tex2DProjFunction[64]; + char m_tex2DLodFunction[64]; + char m_tex2DBiasFunction[64]; + char m_tex2DGradFunction[64]; + char m_tex2DGatherFunction[64]; + char m_tex2DSizeFunction[64]; + char m_tex2DFetchFunction[64]; + char m_tex2DCmpFunction[64]; + char m_tex2DMSFetchFunction[64]; + char m_tex2DMSSizeFunction[64]; + char m_tex3DFunction[64]; + char m_tex3DLodFunction[64]; + char m_tex3DBiasFunction[64]; + char m_tex3DSizeFunction[64]; + char m_texCubeFunction[64]; + char m_texCubeLodFunction[64]; + char m_texCubeBiasFunction[64]; + char m_texCubeSizeFunction[64]; +}; + +} // M4 + +#endif \ No newline at end of file diff --git a/src/libprojectM/Renderer/hlslparser/src/HLSLParser.cpp b/src/libprojectM/Renderer/hlslparser/src/HLSLParser.cpp new file mode 100755 index 000000000..d61738c99 --- /dev/null +++ b/src/libprojectM/Renderer/hlslparser/src/HLSLParser.cpp @@ -0,0 +1,3868 @@ +//============================================================================= +// +// Render/HLSLParser.cpp +// +// Created by Max McGuire (max@unknownworlds.com) +// Copyright (c) 2013, Unknown Worlds Entertainment, Inc. +// +//============================================================================= + +//#include "Engine/String.h" +#include "Engine.h" + +#include "HLSLParser.h" +#include "HLSLTree.h" + +#include +#include +#include + +namespace M4 +{ + +enum CompareFunctionsResult +{ + FunctionsEqual, + Function1Better, + Function2Better +}; + + +/** This structure stores a HLSLFunction-like declaration for an intrinsic function */ +struct Intrinsic +{ + explicit Intrinsic(const char* name, HLSLBaseType returnType) + { + function.name = name; + function.returnType.baseType = returnType; + function.numArguments = 0; + } + explicit Intrinsic(const char* name, HLSLBaseType returnType, HLSLBaseType arg1) + { + function.name = name; + function.returnType.baseType = returnType; + function.numArguments = 1; + function.argument = argument + 0; + argument[0].type.baseType = arg1; + argument[0].type.flags = HLSLTypeFlag_Const; + } + explicit Intrinsic(const char* name, HLSLBaseType returnType, HLSLBaseType arg1, HLSLBaseType arg2) + { + function.name = name; + function.returnType.baseType = returnType; + function.argument = argument + 0; + function.numArguments = 2; + argument[0].type.baseType = arg1; + argument[0].type.flags = HLSLTypeFlag_Const; + argument[0].nextArgument = argument + 1; + argument[1].type.baseType = arg2; + argument[1].type.flags = HLSLTypeFlag_Const; + } + explicit Intrinsic(const char* name, HLSLBaseType returnType, HLSLBaseType arg1, HLSLBaseType arg2, HLSLBaseType arg3) + { + function.name = name; + function.returnType.baseType = returnType; + function.argument = argument + 0; + function.numArguments = 3; + argument[0].type.baseType = arg1; + argument[0].type.flags = HLSLTypeFlag_Const; + argument[0].nextArgument = argument + 1; + argument[1].type.baseType = arg2; + argument[1].type.flags = HLSLTypeFlag_Const; + argument[1].nextArgument = argument + 2; + argument[2].type.baseType = arg3; + argument[2].type.flags = HLSLTypeFlag_Const; + } + explicit Intrinsic(const char* name, HLSLBaseType returnType, HLSLBaseType arg1, HLSLBaseType arg2, HLSLBaseType arg3, HLSLBaseType arg4) + { + function.name = name; + function.returnType.baseType = returnType; + function.argument = argument + 0; + function.numArguments = 4; + argument[0].type.baseType = arg1; + argument[0].type.flags = HLSLTypeFlag_Const; + argument[0].nextArgument = argument + 1; + argument[1].type.baseType = arg2; + argument[1].type.flags = HLSLTypeFlag_Const; + argument[1].nextArgument = argument + 2; + argument[2].type.baseType = arg3; + argument[2].type.flags = HLSLTypeFlag_Const; + argument[2].nextArgument = argument + 3; + argument[3].type.baseType = arg4; + argument[3].type.flags = HLSLTypeFlag_Const; + } + HLSLFunction function; + HLSLArgument argument[4]; +}; + +Intrinsic SamplerIntrinsic(const char* name, HLSLBaseType returnType, HLSLBaseType arg1, HLSLBaseType samplerType, HLSLBaseType arg2) +{ + Intrinsic i(name, returnType, arg1, arg2); + i.argument[0].type.samplerType = samplerType; + return i; +} + + +enum NumericType +{ + NumericType_Float, + NumericType_Half, + NumericType_Bool, + NumericType_Int, + NumericType_Uint, + NumericType_Count, + NumericType_NaN, +}; + +static const int _numberTypeRank[NumericType_Count][NumericType_Count] = +{ + //F H B I U + { 0, 4, 4, 4, 4 }, // NumericType_Float + { 1, 0, 4, 4, 4 }, // NumericType_Half + { 5, 5, 0, 5, 5 }, // NumericType_Bool + { 5, 5, 4, 0, 3 }, // NumericType_Int + { 5, 5, 4, 2, 0 } // NumericType_Uint +}; + + +struct EffectStateValue +{ + const char * name; + int value; +}; + +static const EffectStateValue textureFilteringValues[] = { + {"None", 0}, + {"Point", 1}, + {"Linear", 2}, + {"Anisotropic", 3}, + {NULL, 0} +}; + +static const EffectStateValue textureAddressingValues[] = { + {"Wrap", 1}, + {"Mirror", 2}, + {"Clamp", 3}, + {"Border", 4}, + {"MirrorOnce", 5}, + {NULL, 0} +}; + +static const EffectStateValue booleanValues[] = { + {"False", 0}, + {"True", 1}, + {NULL, 0} +}; + +static const EffectStateValue cullValues[] = { + {"None", 1}, + {"CW", 2}, + {"CCW", 3}, + {NULL, 0} +}; + +static const EffectStateValue cmpValues[] = { + {"Never", 1}, + {"Less", 2}, + {"Equal", 3}, + {"LessEqual", 4}, + {"Greater", 5}, + {"NotEqual", 6}, + {"GreaterEqual", 7}, + {"Always", 8}, + {NULL, 0} +}; + +static const EffectStateValue blendValues[] = { + {"Zero", 1}, + {"One", 2}, + {"SrcColor", 3}, + {"InvSrcColor", 4}, + {"SrcAlpha", 5}, + {"InvSrcAlpha", 6}, + {"DestAlpha", 7}, + {"InvDestAlpha", 8}, + {"DestColor", 9}, + {"InvDestColor", 10}, + {"SrcAlphaSat", 11}, + {"BothSrcAlpha", 12}, + {"BothInvSrcAlpha", 13}, + {"BlendFactor", 14}, + {"InvBlendFactor", 15}, + {"SrcColor2", 16}, // Dual source blending. D3D9Ex only. + {"InvSrcColor2", 17}, + {NULL, 0} +}; + +static const EffectStateValue blendOpValues[] = { + {"Add", 1}, + {"Subtract", 2}, + {"RevSubtract", 3}, + {"Min", 4}, + {"Max", 5}, + {NULL, 0} +}; + +static const EffectStateValue fillModeValues[] = { + {"Point", 1}, + {"Wireframe", 2}, + {"Solid", 3}, + {NULL, 0} +}; + +static const EffectStateValue stencilOpValues[] = { + {"Keep", 1}, + {"Zero", 2}, + {"Replace", 3}, + {"IncrSat", 4}, + {"DecrSat", 5}, + {"Invert", 6}, + {"Incr", 7}, + {"Decr", 8}, + {NULL, 0} +}; + +// These are flags. +static const EffectStateValue colorMaskValues[] = { + {"False", 0}, + {"Red", 1<<0}, + {"Green", 1<<1}, + {"Blue", 1<<2}, + {"Alpha", 1<<3}, + {"X", 1<<0}, + {"Y", 1<<1}, + {"Z", 1<<2}, + {"W", 1<<3}, + {NULL, 0} +}; + +static const EffectStateValue integerValues[] = { + {NULL, 0} +}; + +static const EffectStateValue floatValues[] = { + {NULL, 0} +}; + + +struct EffectState +{ + const char * name; + int d3drs; + const EffectStateValue * values; +}; + +static const EffectState samplerStates[] = { + {"AddressU", 1, textureAddressingValues}, + {"AddressV", 2, textureAddressingValues}, + {"AddressW", 3, textureAddressingValues}, + // "BorderColor", 4, D3DCOLOR + {"MagFilter", 5, textureFilteringValues}, + {"MinFilter", 6, textureFilteringValues}, + {"MipFilter", 7, textureFilteringValues}, + {"MipMapLodBias", 8, floatValues}, + {"MaxMipLevel", 9, integerValues}, + {"MaxAnisotropy", 10, integerValues}, + {"sRGBTexture", 11, booleanValues}, +}; + +static const EffectState effectStates[] = { + {"VertexShader", 0, NULL}, + {"PixelShader", 0, NULL}, + {"AlphaBlendEnable", 27, booleanValues}, + {"SrcBlend", 19, blendValues}, + {"DestBlend", 20, blendValues}, + {"BlendOp", 171, blendOpValues}, + {"SeparateAlphaBlendEanble", 206, booleanValues}, + {"SrcBlendAlpha", 207, blendValues}, + {"DestBlendAlpha", 208, blendValues}, + {"BlendOpAlpha", 209, blendOpValues}, + {"AlphaTestEnable", 15, booleanValues}, + {"AlphaRef", 24, integerValues}, + {"AlphaFunc", 25, cmpValues}, + {"CullMode", 22, cullValues}, + {"ZEnable", 7, booleanValues}, + {"ZWriteEnable", 14, booleanValues}, + {"ZFunc", 23, cmpValues}, + {"StencilEnable", 52, booleanValues}, + {"StencilFail", 53, stencilOpValues}, + {"StencilZFail", 54, stencilOpValues}, + {"StencilPass", 55, stencilOpValues}, + {"StencilFunc", 56, cmpValues}, + {"StencilRef", 57, integerValues}, + {"StencilMask", 58, integerValues}, + {"StencilWriteMask", 59, integerValues}, + {"TwoSidedStencilMode", 185, booleanValues}, + {"CCW_StencilFail", 186, stencilOpValues}, + {"CCW_StencilZFail", 187, stencilOpValues}, + {"CCW_StencilPass", 188, stencilOpValues}, + {"CCW_StencilFunc", 189, cmpValues}, + {"ColorWriteEnable", 168, colorMaskValues}, + {"FillMode", 8, fillModeValues}, + {"MultisampleAlias", 161, booleanValues}, + {"MultisampleMask", 162, integerValues}, + {"ScissorTestEnable", 174, booleanValues}, + {"SlopeScaleDepthBias", 175, floatValues}, + {"DepthBias", 195, floatValues} +}; + + +static const EffectStateValue witnessCullModeValues[] = { + {"None", 0}, + {"Back", 1}, + {"Front", 2}, + {NULL, 0} +}; + +static const EffectStateValue witnessFillModeValues[] = { + {"Solid", 0}, + {"Wireframe", 1}, + {NULL, 0} +}; + +static const EffectStateValue witnessBlendModeValues[] = { + {"Disabled", 0}, + {"AlphaBlend", 1}, // src * a + dst * (1-a) + {"Add", 2}, // src + dst + {"Mixed", 3}, // src + dst * (1-a) + {"Multiply", 4}, // src * dst + {"Multiply2", 5}, // 2 * src * dst + {NULL, 0} +}; + +static const EffectStateValue witnessDepthFuncValues[] = { + {"LessEqual", 0}, + {"Less", 1}, + {"Equal", 2}, + {"Greater", 3}, + {"Always", 4}, + {NULL, 0} +}; + +static const EffectStateValue witnessStencilModeValues[] = { + {"Disabled", 0}, + {"Set", 1}, + {"Test", 2}, + {NULL, 0} +}; + +static const EffectStateValue witnessFilterModeValues[] = { + {"Point", 0}, + {"Linear", 1}, + {"Mipmap_Nearest", 2}, + {"Mipmap_Best", 3}, // Quality of mipmap filtering depends on render settings. + {"Anisotropic", 4}, // Aniso without mipmaps for reflection maps. + {NULL, 0} +}; + +static const EffectStateValue witnessWrapModeValues[] = { + {"Repeat", 0}, + {"Clamp", 1}, + {"ClampToBorder", 2}, + {NULL, 0} +}; + +static const EffectState pipelineStates[] = { + {"VertexShader", 0, NULL}, + {"PixelShader", 0, NULL}, + + // Depth_Stencil_State + {"DepthWrite", 0, booleanValues}, + {"DepthEnable", 0, booleanValues}, + {"DepthFunc", 0, witnessDepthFuncValues}, + {"StencilMode", 0, witnessStencilModeValues}, + + // Raster_State + {"CullMode", 0, witnessCullModeValues}, + {"FillMode", 0, witnessFillModeValues}, + {"MultisampleEnable", 0, booleanValues}, + {"PolygonOffset", 0, booleanValues}, + + // Blend_State + {"BlendMode", 0, witnessBlendModeValues}, + {"ColorWrite", 0, booleanValues}, + {"AlphaWrite", 0, booleanValues}, + {"AlphaTest", 0, booleanValues}, // This is really alpha to coverage. +}; + + + +struct BaseTypeDescription +{ + const char* typeName; + NumericType numericType; + int numComponents; + int numDimensions; + int height; + int binaryOpRank; +}; + + +#define INTRINSIC_FLOAT1_FUNCTION(name) \ + Intrinsic( name, HLSLBaseType_Float, HLSLBaseType_Float ), \ + Intrinsic( name, HLSLBaseType_Float2, HLSLBaseType_Float2 ), \ + Intrinsic( name, HLSLBaseType_Float3, HLSLBaseType_Float3 ), \ + Intrinsic( name, HLSLBaseType_Float4, HLSLBaseType_Float4 ), \ + Intrinsic( name, HLSLBaseType_Half, HLSLBaseType_Half ), \ + Intrinsic( name, HLSLBaseType_Half2, HLSLBaseType_Half2 ), \ + Intrinsic( name, HLSLBaseType_Half3, HLSLBaseType_Half3 ), \ + Intrinsic( name, HLSLBaseType_Half4, HLSLBaseType_Half4 ) + +#define INTRINSIC_FLOAT2_FUNCTION(name) \ + Intrinsic( name, HLSLBaseType_Float, HLSLBaseType_Float, HLSLBaseType_Float ), \ + Intrinsic( name, HLSLBaseType_Float2, HLSLBaseType_Float2, HLSLBaseType_Float2 ), \ + Intrinsic( name, HLSLBaseType_Float3, HLSLBaseType_Float3, HLSLBaseType_Float3 ), \ + Intrinsic( name, HLSLBaseType_Float4, HLSLBaseType_Float4, HLSLBaseType_Float4 ), \ + Intrinsic( name, HLSLBaseType_Half, HLSLBaseType_Half, HLSLBaseType_Half ), \ + Intrinsic( name, HLSLBaseType_Half2, HLSLBaseType_Half2, HLSLBaseType_Half2 ), \ + Intrinsic( name, HLSLBaseType_Half3, HLSLBaseType_Half3, HLSLBaseType_Half3 ), \ + Intrinsic( name, HLSLBaseType_Half4, HLSLBaseType_Half4, HLSLBaseType_Half4 ) + +#define INTRINSIC_FLOAT3_FUNCTION(name) \ + Intrinsic( name, HLSLBaseType_Float, HLSLBaseType_Float, HLSLBaseType_Float, HLSLBaseType_Float ), \ + Intrinsic( name, HLSLBaseType_Float2, HLSLBaseType_Float2, HLSLBaseType_Float2, HLSLBaseType_Float2 ), \ + Intrinsic( name, HLSLBaseType_Float3, HLSLBaseType_Float3, HLSLBaseType_Float3, HLSLBaseType_Float3 ), \ + Intrinsic( name, HLSLBaseType_Float4, HLSLBaseType_Float4, HLSLBaseType_Float4, HLSLBaseType_Float4 ), \ + Intrinsic( name, HLSLBaseType_Half, HLSLBaseType_Half, HLSLBaseType_Half, HLSLBaseType_Half ), \ + Intrinsic( name, HLSLBaseType_Half2, HLSLBaseType_Half2, HLSLBaseType_Half2, HLSLBaseType_Half2 ), \ + Intrinsic( name, HLSLBaseType_Half3, HLSLBaseType_Half3, HLSLBaseType_Half3, HLSLBaseType_Half3 ), \ + Intrinsic( name, HLSLBaseType_Half4, HLSLBaseType_Half4, HLSLBaseType_Half4, HLSLBaseType_Half4 ) + +#if 0 +// @@ IC: For some reason this is not working with the Visual Studio compiler: +#define SAMPLER_INTRINSIC_FUNCTION(name, sampler, arg1) \ + SamplerIntrinsic( name, HLSLBaseType_Float4, sampler, HLSLBaseType_Float, arg1), \ + SamplerIntrinsic( name, HLSLBaseType_Half4, sampler, HLSLBaseType_Half, arg1 ) +#else +#define SAMPLER_INTRINSIC_FUNCTION(name, sampler, arg1) \ + Intrinsic( name, HLSLBaseType_Float4, sampler, arg1) +#endif + +const Intrinsic _intrinsic[] = + { + INTRINSIC_FLOAT1_FUNCTION( "abs" ), + INTRINSIC_FLOAT1_FUNCTION( "acos" ), + + Intrinsic( "any", HLSLBaseType_Bool, HLSLBaseType_Float ), + Intrinsic( "any", HLSLBaseType_Bool, HLSLBaseType_Float2 ), + Intrinsic( "any", HLSLBaseType_Bool, HLSLBaseType_Float3 ), + Intrinsic( "any", HLSLBaseType_Bool, HLSLBaseType_Float4 ), + Intrinsic( "any", HLSLBaseType_Bool, HLSLBaseType_Float2x2 ), + Intrinsic( "any", HLSLBaseType_Bool, HLSLBaseType_Float3x3 ), + Intrinsic( "any", HLSLBaseType_Bool, HLSLBaseType_Float4x4 ), + Intrinsic( "any", HLSLBaseType_Bool, HLSLBaseType_Float4x3 ), + Intrinsic( "any", HLSLBaseType_Bool, HLSLBaseType_Float4x2 ), + Intrinsic( "any", HLSLBaseType_Bool, HLSLBaseType_Half ), + Intrinsic( "any", HLSLBaseType_Bool, HLSLBaseType_Half2 ), + Intrinsic( "any", HLSLBaseType_Bool, HLSLBaseType_Half3 ), + Intrinsic( "any", HLSLBaseType_Bool, HLSLBaseType_Half4 ), + Intrinsic( "any", HLSLBaseType_Bool, HLSLBaseType_Half2x2 ), + Intrinsic( "any", HLSLBaseType_Bool, HLSLBaseType_Half3x3 ), + Intrinsic( "any", HLSLBaseType_Bool, HLSLBaseType_Half4x4 ), + Intrinsic( "any", HLSLBaseType_Bool, HLSLBaseType_Half4x3 ), + Intrinsic( "any", HLSLBaseType_Bool, HLSLBaseType_Half4x2 ), + Intrinsic( "any", HLSLBaseType_Bool, HLSLBaseType_Bool ), + Intrinsic( "any", HLSLBaseType_Bool, HLSLBaseType_Int ), + Intrinsic( "any", HLSLBaseType_Bool, HLSLBaseType_Int2 ), + Intrinsic( "any", HLSLBaseType_Bool, HLSLBaseType_Int3 ), + Intrinsic( "any", HLSLBaseType_Bool, HLSLBaseType_Int4 ), + Intrinsic( "any", HLSLBaseType_Bool, HLSLBaseType_Uint ), + Intrinsic( "any", HLSLBaseType_Bool, HLSLBaseType_Uint2 ), + Intrinsic( "any", HLSLBaseType_Bool, HLSLBaseType_Uint3 ), + Intrinsic( "any", HLSLBaseType_Bool, HLSLBaseType_Uint4 ), + + INTRINSIC_FLOAT1_FUNCTION( "asin" ), + INTRINSIC_FLOAT1_FUNCTION( "atan" ), + INTRINSIC_FLOAT2_FUNCTION( "atan2" ), + INTRINSIC_FLOAT3_FUNCTION( "clamp" ), + INTRINSIC_FLOAT1_FUNCTION( "cos" ), + + INTRINSIC_FLOAT3_FUNCTION( "lerp" ), + INTRINSIC_FLOAT3_FUNCTION( "smoothstep" ), + + INTRINSIC_FLOAT1_FUNCTION( "floor" ), + INTRINSIC_FLOAT1_FUNCTION( "ceil" ), + INTRINSIC_FLOAT1_FUNCTION( "frac" ), + + INTRINSIC_FLOAT2_FUNCTION( "fmod" ), + + Intrinsic( "clip", HLSLBaseType_Void, HLSLBaseType_Float ), + Intrinsic( "clip", HLSLBaseType_Void, HLSLBaseType_Float2 ), + Intrinsic( "clip", HLSLBaseType_Void, HLSLBaseType_Float3 ), + Intrinsic( "clip", HLSLBaseType_Void, HLSLBaseType_Float4 ), + Intrinsic( "clip", HLSLBaseType_Void, HLSLBaseType_Half ), + Intrinsic( "clip", HLSLBaseType_Void, HLSLBaseType_Half2 ), + Intrinsic( "clip", HLSLBaseType_Void, HLSLBaseType_Half3 ), + Intrinsic( "clip", HLSLBaseType_Void, HLSLBaseType_Half4 ), + + Intrinsic( "dot", HLSLBaseType_Float, HLSLBaseType_Float, HLSLBaseType_Float ), + Intrinsic( "dot", HLSLBaseType_Float, HLSLBaseType_Float2, HLSLBaseType_Float2 ), + Intrinsic( "dot", HLSLBaseType_Float, HLSLBaseType_Float3, HLSLBaseType_Float3 ), + Intrinsic( "dot", HLSLBaseType_Float, HLSLBaseType_Float4, HLSLBaseType_Float4 ), + Intrinsic( "dot", HLSLBaseType_Half, HLSLBaseType_Half, HLSLBaseType_Half ), + Intrinsic( "dot", HLSLBaseType_Half, HLSLBaseType_Half2, HLSLBaseType_Half2 ), + Intrinsic( "dot", HLSLBaseType_Half, HLSLBaseType_Half3, HLSLBaseType_Half3 ), + Intrinsic( "dot", HLSLBaseType_Half, HLSLBaseType_Half4, HLSLBaseType_Half4 ), + + Intrinsic( "cross", HLSLBaseType_Float3, HLSLBaseType_Float3, HLSLBaseType_Float3 ), + + Intrinsic( "length", HLSLBaseType_Float, HLSLBaseType_Float ), + Intrinsic( "length", HLSLBaseType_Float, HLSLBaseType_Float2 ), + Intrinsic( "length", HLSLBaseType_Float, HLSLBaseType_Float3 ), + Intrinsic( "length", HLSLBaseType_Float, HLSLBaseType_Float4 ), + Intrinsic( "length", HLSLBaseType_Half, HLSLBaseType_Half ), + Intrinsic( "length", HLSLBaseType_Half, HLSLBaseType_Half2 ), + Intrinsic( "length", HLSLBaseType_Half, HLSLBaseType_Half3 ), + Intrinsic( "length", HLSLBaseType_Half, HLSLBaseType_Half4 ), + + INTRINSIC_FLOAT2_FUNCTION( "max" ), + INTRINSIC_FLOAT2_FUNCTION( "min" ), + + // @@ Add all combinations. + // scalar = mul(scalar, scalar) + // vector = mul(scalar, vector) + // vector = mul(vector, scalar) + // vector = mul(vector, vector) + // vector = mul(vector, matrix) ? + // vector = mul(matrix, vector) ? + // matrix = mul(matrix, matrix) ? + + INTRINSIC_FLOAT2_FUNCTION( "mul" ), + Intrinsic( "mul", HLSLBaseType_Float2, HLSLBaseType_Float2, HLSLBaseType_Float2x2 ), + Intrinsic( "mul", HLSLBaseType_Float3, HLSLBaseType_Float3, HLSLBaseType_Float3x3 ), + Intrinsic( "mul", HLSLBaseType_Float4, HLSLBaseType_Float4, HLSLBaseType_Float4x4 ), + Intrinsic( "mul", HLSLBaseType_Float2, HLSLBaseType_Float2x2, HLSLBaseType_Float2 ), + Intrinsic( "mul", HLSLBaseType_Float3, HLSLBaseType_Float3x3, HLSLBaseType_Float3 ), + Intrinsic( "mul", HLSLBaseType_Float4, HLSLBaseType_Float4x4, HLSLBaseType_Float4 ), + Intrinsic( "mul", HLSLBaseType_Float3, HLSLBaseType_Float4, HLSLBaseType_Float4x3 ), + Intrinsic( "mul", HLSLBaseType_Float2, HLSLBaseType_Float4, HLSLBaseType_Float4x2 ), + + Intrinsic( "transpose", HLSLBaseType_Float2x2, HLSLBaseType_Float2x2 ), + Intrinsic( "transpose", HLSLBaseType_Float3x3, HLSLBaseType_Float3x3 ), + Intrinsic( "transpose", HLSLBaseType_Float4x4, HLSLBaseType_Float4x4 ), + Intrinsic( "transpose", HLSLBaseType_Half2x2, HLSLBaseType_Half2x2 ), + Intrinsic( "transpose", HLSLBaseType_Half3x3, HLSLBaseType_Half3x3 ), + Intrinsic( "transpose", HLSLBaseType_Half4x4, HLSLBaseType_Half4x4 ), + + INTRINSIC_FLOAT1_FUNCTION( "normalize" ), + INTRINSIC_FLOAT2_FUNCTION( "pow" ), + INTRINSIC_FLOAT1_FUNCTION( "saturate" ), + INTRINSIC_FLOAT1_FUNCTION( "sin" ), + INTRINSIC_FLOAT1_FUNCTION( "sqrt" ), + INTRINSIC_FLOAT1_FUNCTION( "rsqrt" ), + INTRINSIC_FLOAT1_FUNCTION( "rcp" ), + INTRINSIC_FLOAT1_FUNCTION( "exp" ), + INTRINSIC_FLOAT1_FUNCTION( "exp2" ), + INTRINSIC_FLOAT1_FUNCTION( "log" ), + INTRINSIC_FLOAT1_FUNCTION( "log2" ), + + INTRINSIC_FLOAT1_FUNCTION( "ddx" ), + INTRINSIC_FLOAT1_FUNCTION( "ddy" ), + + INTRINSIC_FLOAT1_FUNCTION( "sign" ), + INTRINSIC_FLOAT2_FUNCTION( "step" ), + INTRINSIC_FLOAT2_FUNCTION( "reflect" ), + + INTRINSIC_FLOAT1_FUNCTION("isnan"), + INTRINSIC_FLOAT1_FUNCTION("isinf"), + + Intrinsic("asuint", HLSLBaseType_Uint, HLSLBaseType_Float), + + SAMPLER_INTRINSIC_FUNCTION("tex2D", HLSLBaseType_Sampler2D, HLSLBaseType_Float2), + + Intrinsic("tex2Dproj", HLSLBaseType_Float4, HLSLBaseType_Sampler2D, HLSLBaseType_Float4), + + SAMPLER_INTRINSIC_FUNCTION("tex2Dlod", HLSLBaseType_Sampler2D, HLSLBaseType_Float4), + + Intrinsic("tex2Dlod", HLSLBaseType_Float4, HLSLBaseType_Sampler2D, HLSLBaseType_Float4, HLSLBaseType_Int2), // With offset. + + SAMPLER_INTRINSIC_FUNCTION("tex2Dbias", HLSLBaseType_Sampler2D, HLSLBaseType_Float4), + + Intrinsic("tex2Dgrad", HLSLBaseType_Float4, HLSLBaseType_Sampler2D, HLSLBaseType_Float2, HLSLBaseType_Float2, HLSLBaseType_Float2), + Intrinsic("tex2Dgather", HLSLBaseType_Float4, HLSLBaseType_Sampler2D, HLSLBaseType_Float2, HLSLBaseType_Int), + Intrinsic("tex2Dgather", HLSLBaseType_Float4, HLSLBaseType_Sampler2D, HLSLBaseType_Float2, HLSLBaseType_Int2, HLSLBaseType_Int), // With offset. + Intrinsic("tex2Dsize", HLSLBaseType_Int2, HLSLBaseType_Sampler2D), + Intrinsic("tex2Dfetch", HLSLBaseType_Float4, HLSLBaseType_Sampler2D, HLSLBaseType_Int3), // u,v,mipmap + + Intrinsic("tex2Dcmp", HLSLBaseType_Float4, HLSLBaseType_Sampler2DShadow, HLSLBaseType_Float4), // @@ IC: This really takes a float3 (uvz) and returns a float. + + Intrinsic("tex2DMSfetch", HLSLBaseType_Float4, HLSLBaseType_Sampler2DMS, HLSLBaseType_Int2, HLSLBaseType_Int), + Intrinsic("tex2DMSsize", HLSLBaseType_Int3, HLSLBaseType_Sampler2DMS), + + Intrinsic("tex2DArray", HLSLBaseType_Float4, HLSLBaseType_Sampler2DArray, HLSLBaseType_Float3), + + Intrinsic("tex3D", HLSLBaseType_Float4, HLSLBaseType_Sampler3D, HLSLBaseType_Float3), + Intrinsic("tex3Dlod", HLSLBaseType_Float4, HLSLBaseType_Sampler3D, HLSLBaseType_Float4), + Intrinsic("tex3Dbias", HLSLBaseType_Float4, HLSLBaseType_Sampler3D, HLSLBaseType_Float4), + Intrinsic("tex3Dsize", HLSLBaseType_Int3, HLSLBaseType_Sampler3D), + + Intrinsic("texCUBE", HLSLBaseType_Float4, HLSLBaseType_SamplerCube, HLSLBaseType_Float3), + Intrinsic("texCUBElod", HLSLBaseType_Float4, HLSLBaseType_SamplerCube, HLSLBaseType_Float4), + Intrinsic("texCUBEbias", HLSLBaseType_Float4, HLSLBaseType_SamplerCube, HLSLBaseType_Float4), + Intrinsic("texCUBEsize", HLSLBaseType_Int, HLSLBaseType_SamplerCube), + + Intrinsic( "sincos", HLSLBaseType_Void, HLSLBaseType_Float, HLSLBaseType_Float, HLSLBaseType_Float ), + Intrinsic( "sincos", HLSLBaseType_Void, HLSLBaseType_Float2, HLSLBaseType_Float, HLSLBaseType_Float2 ), + Intrinsic( "sincos", HLSLBaseType_Void, HLSLBaseType_Float3, HLSLBaseType_Float, HLSLBaseType_Float3 ), + Intrinsic( "sincos", HLSLBaseType_Void, HLSLBaseType_Float4, HLSLBaseType_Float, HLSLBaseType_Float4 ), + Intrinsic( "sincos", HLSLBaseType_Void, HLSLBaseType_Half, HLSLBaseType_Half, HLSLBaseType_Half ), + Intrinsic( "sincos", HLSLBaseType_Void, HLSLBaseType_Half2, HLSLBaseType_Half2, HLSLBaseType_Half2 ), + Intrinsic( "sincos", HLSLBaseType_Void, HLSLBaseType_Half3, HLSLBaseType_Half3, HLSLBaseType_Half3 ), + Intrinsic( "sincos", HLSLBaseType_Void, HLSLBaseType_Half4, HLSLBaseType_Half4, HLSLBaseType_Half4 ), + + Intrinsic( "mad", HLSLBaseType_Float, HLSLBaseType_Float, HLSLBaseType_Float, HLSLBaseType_Float ), + Intrinsic( "mad", HLSLBaseType_Float2, HLSLBaseType_Float2, HLSLBaseType_Float2, HLSLBaseType_Float2 ), + Intrinsic( "mad", HLSLBaseType_Float3, HLSLBaseType_Float3, HLSLBaseType_Float3, HLSLBaseType_Float3 ), + Intrinsic( "mad", HLSLBaseType_Float4, HLSLBaseType_Float4, HLSLBaseType_Float4, HLSLBaseType_Float4 ), + Intrinsic( "mad", HLSLBaseType_Half, HLSLBaseType_Half, HLSLBaseType_Half, HLSLBaseType_Half ), + Intrinsic( "mad", HLSLBaseType_Half2, HLSLBaseType_Half2, HLSLBaseType_Half2, HLSLBaseType_Half2 ), + Intrinsic( "mad", HLSLBaseType_Half3, HLSLBaseType_Half3, HLSLBaseType_Half3, HLSLBaseType_Half3 ), + Intrinsic( "mad", HLSLBaseType_Half4, HLSLBaseType_Half4, HLSLBaseType_Half4, HLSLBaseType_Half4 ), + }; + +const int _numIntrinsics = sizeof(_intrinsic) / sizeof(Intrinsic); + +// The order in this array must match up with HLSLBinaryOp +const int _binaryOpPriority[] = + { + 2, 1, // &&, || + 8, 8, // +, - + 9, 9, // *, / + 7, 7, // <, >, + 7, 7, // <=, >=, + 6, 6, // ==, != + 5, 3, 4, // &, |, ^ + }; + +const BaseTypeDescription _baseTypeDescriptions[HLSLBaseType_Count] = + { + { "unknown type", NumericType_NaN, 0, 0, 0, -1 }, // HLSLBaseType_Unknown + { "void", NumericType_NaN, 0, 0, 0, -1 }, // HLSLBaseType_Void + { "float", NumericType_Float, 1, 0, 1, 0 }, // HLSLBaseType_Float + { "float2", NumericType_Float, 2, 1, 1, 0 }, // HLSLBaseType_Float2 + { "float3", NumericType_Float, 3, 1, 1, 0 }, // HLSLBaseType_Float3 + { "float4", NumericType_Float, 4, 1, 1, 0 }, // HLSLBaseType_Float4 + { "float2x2", NumericType_Float, 2, 2, 2, 0 }, // HLSLBaseType_Float2x2 + { "float3x3", NumericType_Float, 3, 2, 3, 0 }, // HLSLBaseType_Float3x3 + { "float4x4", NumericType_Float, 4, 2, 4, 0 }, // HLSLBaseType_Float4x4 + { "float4x3", NumericType_Float, 4, 2, 3, 0 }, // HLSLBaseType_Float4x3 + { "float4x2", NumericType_Float, 4, 2, 2, 0 }, // HLSLBaseType_Float4x2 + + { "half", NumericType_Half, 1, 0, 1, 1 }, // HLSLBaseType_Half + { "half2", NumericType_Half, 2, 1, 1, 1 }, // HLSLBaseType_Half2 + { "half3", NumericType_Half, 3, 1, 1, 1 }, // HLSLBaseType_Half3 + { "half4", NumericType_Half, 4, 1, 1, 1 }, // HLSLBaseType_Half4 + { "half2x2", NumericType_Float, 2, 2, 2, 0 }, // HLSLBaseType_Half2x2 + { "half3x3", NumericType_Half, 3, 2, 3, 1 }, // HLSLBaseType_Half3x3 + { "half4x4", NumericType_Half, 4, 2, 4, 1 }, // HLSLBaseType_Half4x4 + { "half4x3", NumericType_Half, 4, 2, 3, 1 }, // HLSLBaseType_Half4x3 + { "half4x2", NumericType_Half, 4, 2, 2, 1 }, // HLSLBaseType_Half4x2 + + { "bool", NumericType_Bool, 1, 0, 1, 4 }, // HLSLBaseType_Bool + { "bool2", NumericType_Bool, 2, 1, 1, 4 }, // HLSLBaseType_Bool2 + { "bool3", NumericType_Bool, 3, 1, 1, 4 }, // HLSLBaseType_Bool3 + { "bool4", NumericType_Bool, 4, 1, 1, 4 }, // HLSLBaseType_Bool4 + + { "int", NumericType_Int, 1, 0, 1, 3 }, // HLSLBaseType_Int + { "int2", NumericType_Int, 2, 1, 1, 3 }, // HLSLBaseType_Int2 + { "int3", NumericType_Int, 3, 1, 1, 3 }, // HLSLBaseType_Int3 + { "int4", NumericType_Int, 4, 1, 1, 3 }, // HLSLBaseType_Int4 + + { "uint", NumericType_Uint, 1, 0, 1, 2 }, // HLSLBaseType_Uint + { "uint2", NumericType_Uint, 2, 1, 1, 2 }, // HLSLBaseType_Uint2 + { "uint3", NumericType_Uint, 3, 1, 1, 2 }, // HLSLBaseType_Uint3 + { "uint4", NumericType_Uint, 4, 1, 1, 2 }, // HLSLBaseType_Uint4 + + { "texture", NumericType_NaN, 1, 0, 0, -1 }, // HLSLBaseType_Texture + { "sampler", NumericType_NaN, 1, 0, 0, -1 }, // HLSLBaseType_Sampler + { "sampler2D", NumericType_NaN, 1, 0, 0, -1 }, // HLSLBaseType_Sampler2D + { "sampler3D", NumericType_NaN, 1, 0, 0, -1 }, // HLSLBaseType_Sampler3D + { "samplerCUBE", NumericType_NaN, 1, 0, 0, -1 }, // HLSLBaseType_SamplerCube + { "sampler2DShadow", NumericType_NaN, 1, 0, 0, -1 }, // HLSLBaseType_Sampler2DShadow + { "sampler2DMS", NumericType_NaN, 1, 0, 0, -1 }, // HLSLBaseType_Sampler2DMS + { "sampler2DArray", NumericType_NaN, 1, 0, 0, -1 }, // HLSLBaseType_Sampler2DArray + { "user defined", NumericType_NaN, 1, 0, 0, -1 }, // HLSLBaseType_UserDefined + { "expression", NumericType_NaN, 1, 0, 0, -1 } // HLSLBaseType_Expression + }; + +// IC: I'm not sure this table is right, but any errors should be caught by the backend compiler. +// Also, this is operator dependent. The type resulting from (float4 * float4x4) is not the same as (float4 + float4x4). +// We should probably distinguish between component-wise operator and only allow same dimensions +HLSLBaseType _binaryOpTypeLookup[HLSLBaseType_NumericCount][HLSLBaseType_NumericCount] = + { + { // float + HLSLBaseType_Float, HLSLBaseType_Float2, HLSLBaseType_Float3, HLSLBaseType_Float4, HLSLBaseType_Float2x2, HLSLBaseType_Float3x3, HLSLBaseType_Float4x4, HLSLBaseType_Float4x3, HLSLBaseType_Float4x2, + HLSLBaseType_Float, HLSLBaseType_Float2, HLSLBaseType_Float3, HLSLBaseType_Float4, HLSLBaseType_Float2x2, HLSLBaseType_Float3x3, HLSLBaseType_Float4x4, HLSLBaseType_Float4x3, HLSLBaseType_Float4x2, + HLSLBaseType_Float, HLSLBaseType_Float2, HLSLBaseType_Float3, HLSLBaseType_Float4, + HLSLBaseType_Float, HLSLBaseType_Float2, HLSLBaseType_Float3, HLSLBaseType_Float4, + HLSLBaseType_Float, HLSLBaseType_Float2, HLSLBaseType_Float3, HLSLBaseType_Float4 + }, + { // float2 + HLSLBaseType_Float2, HLSLBaseType_Float2, HLSLBaseType_Float2, HLSLBaseType_Float2, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, + HLSLBaseType_Float2, HLSLBaseType_Float2, HLSLBaseType_Float2, HLSLBaseType_Float2, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, + HLSLBaseType_Float2, HLSLBaseType_Float2, HLSLBaseType_Float2, HLSLBaseType_Float2, + HLSLBaseType_Float2, HLSLBaseType_Float2, HLSLBaseType_Float2, HLSLBaseType_Float2, + HLSLBaseType_Float2, HLSLBaseType_Float2, HLSLBaseType_Float2, HLSLBaseType_Float2 + }, + { // float3 + HLSLBaseType_Float3, HLSLBaseType_Float2, HLSLBaseType_Float3, HLSLBaseType_Float3, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, + HLSLBaseType_Float3, HLSLBaseType_Float2, HLSLBaseType_Float3, HLSLBaseType_Float3, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, + HLSLBaseType_Float3, HLSLBaseType_Float2, HLSLBaseType_Float3, HLSLBaseType_Float3, + HLSLBaseType_Float3, HLSLBaseType_Float2, HLSLBaseType_Float3, HLSLBaseType_Float3, + HLSLBaseType_Float3, HLSLBaseType_Float2, HLSLBaseType_Float3, HLSLBaseType_Float3 + }, + { // float4 + HLSLBaseType_Float4, HLSLBaseType_Float2, HLSLBaseType_Float3, HLSLBaseType_Float4, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, + HLSLBaseType_Float4, HLSLBaseType_Float2, HLSLBaseType_Float3, HLSLBaseType_Float4, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, + HLSLBaseType_Float4, HLSLBaseType_Float2, HLSLBaseType_Float3, HLSLBaseType_Float4, + HLSLBaseType_Float4, HLSLBaseType_Float2, HLSLBaseType_Float3, HLSLBaseType_Float4, + HLSLBaseType_Float4, HLSLBaseType_Float2, HLSLBaseType_Float3, HLSLBaseType_Float4 + }, + { // float2x2 + HLSLBaseType_Float2x2, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Float2x2, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, + HLSLBaseType_Float2x2, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Float2x2, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, + HLSLBaseType_Float2x2, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, + HLSLBaseType_Float2x2, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, + HLSLBaseType_Float2x2, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown + }, + { // float3x3 + HLSLBaseType_Float3x3, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Float3x3, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, + HLSLBaseType_Float3x3, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Float3x3, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, + HLSLBaseType_Float3x3, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, + HLSLBaseType_Float3x3, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, + HLSLBaseType_Float3x3, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown + }, + { // float4x4 + HLSLBaseType_Float4x4, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Float4x4, HLSLBaseType_Unknown, HLSLBaseType_Unknown, + HLSLBaseType_Float4x4, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Float4x4, HLSLBaseType_Unknown, HLSLBaseType_Unknown, + HLSLBaseType_Float4x4, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, + HLSLBaseType_Float4x4, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, + HLSLBaseType_Float4x4, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown + }, + { // float4x3 + HLSLBaseType_Float4x3, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Float4x3, HLSLBaseType_Unknown, + HLSLBaseType_Float4x3, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Float4x3, HLSLBaseType_Unknown, + HLSLBaseType_Float4x3, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, + HLSLBaseType_Float4x3, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, + HLSLBaseType_Float4x3, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown + }, + { // float4x2 + HLSLBaseType_Float4x2, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Float4x2, + HLSLBaseType_Float4x2, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Float4x2, + HLSLBaseType_Float4x2, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, + HLSLBaseType_Float4x2, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, + HLSLBaseType_Float4x2, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown + }, + { // half + HLSLBaseType_Float, HLSLBaseType_Float2, HLSLBaseType_Float3, HLSLBaseType_Float4, HLSLBaseType_Float2x2, HLSLBaseType_Float3x3, HLSLBaseType_Float4x4, HLSLBaseType_Float4x3, HLSLBaseType_Float4x2, + HLSLBaseType_Half, HLSLBaseType_Half2, HLSLBaseType_Half3, HLSLBaseType_Half4, HLSLBaseType_Half2x2, HLSLBaseType_Half3x3, HLSLBaseType_Half4x4, HLSLBaseType_Half4x3, HLSLBaseType_Half4x2, + HLSLBaseType_Half, HLSLBaseType_Half2, HLSLBaseType_Half3, HLSLBaseType_Half4, + HLSLBaseType_Half, HLSLBaseType_Half2, HLSLBaseType_Half3, HLSLBaseType_Half4, + HLSLBaseType_Half, HLSLBaseType_Half2, HLSLBaseType_Half3, HLSLBaseType_Half4 + }, + { // half2 + HLSLBaseType_Float2, HLSLBaseType_Float2, HLSLBaseType_Float2, HLSLBaseType_Float2, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, + HLSLBaseType_Half2, HLSLBaseType_Half2, HLSLBaseType_Half2, HLSLBaseType_Half2, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, + HLSLBaseType_Half2, HLSLBaseType_Half2, HLSLBaseType_Half2, HLSLBaseType_Half2, + HLSLBaseType_Half2, HLSLBaseType_Half2, HLSLBaseType_Half2, HLSLBaseType_Half2, + HLSLBaseType_Half2, HLSLBaseType_Half2, HLSLBaseType_Half2, HLSLBaseType_Half2 + }, + { // half3 + HLSLBaseType_Float3, HLSLBaseType_Float2, HLSLBaseType_Float3, HLSLBaseType_Float3, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, + HLSLBaseType_Half3, HLSLBaseType_Half2, HLSLBaseType_Half3, HLSLBaseType_Half3, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, + HLSLBaseType_Half3, HLSLBaseType_Half2, HLSLBaseType_Half3, HLSLBaseType_Half3, + HLSLBaseType_Half3, HLSLBaseType_Half2, HLSLBaseType_Half3, HLSLBaseType_Half3, + HLSLBaseType_Half3, HLSLBaseType_Half2, HLSLBaseType_Half3, HLSLBaseType_Half3 + }, + { // half4 + HLSLBaseType_Float4, HLSLBaseType_Float2, HLSLBaseType_Float3, HLSLBaseType_Float4, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, + HLSLBaseType_Half4, HLSLBaseType_Half2, HLSLBaseType_Half3, HLSLBaseType_Half4, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, + HLSLBaseType_Half4, HLSLBaseType_Half2, HLSLBaseType_Half3, HLSLBaseType_Half4, + HLSLBaseType_Half4, HLSLBaseType_Half2, HLSLBaseType_Half3, HLSLBaseType_Half4, + HLSLBaseType_Half4, HLSLBaseType_Half2, HLSLBaseType_Half3, HLSLBaseType_Half4 + }, + { // half2x2 + HLSLBaseType_Float2x2, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Float2x2, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, + HLSLBaseType_Half2x2, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Half2x2, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, + HLSLBaseType_Half2x2, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, + HLSLBaseType_Half2x2, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, + HLSLBaseType_Half2x2, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown + }, + { // half3x3 + HLSLBaseType_Float3x3, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Float3x3, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, + HLSLBaseType_Half3x3, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Half3x3, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, + HLSLBaseType_Half3x3, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, + HLSLBaseType_Half3x3, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, + HLSLBaseType_Half3x3, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown + }, + { // half4x4 + HLSLBaseType_Float4x4, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Float4x4, HLSLBaseType_Unknown, HLSLBaseType_Unknown, + HLSLBaseType_Half4x4, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Half4x4, HLSLBaseType_Unknown, HLSLBaseType_Unknown, + HLSLBaseType_Half4x4, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, + HLSLBaseType_Half4x4, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, + HLSLBaseType_Half4x4, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown + }, + { // float4x3 + HLSLBaseType_Float4x3, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Float4x3, HLSLBaseType_Unknown, + HLSLBaseType_Half4x3, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Half4x3, HLSLBaseType_Unknown, + HLSLBaseType_Half4x3, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, + HLSLBaseType_Half4x3, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, + HLSLBaseType_Half4x3, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown + }, + { // float4x2 + HLSLBaseType_Float4x2, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Float4x2, + HLSLBaseType_Half4x2, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Half4x2, + HLSLBaseType_Half4x2, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, + HLSLBaseType_Half4x2, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, + HLSLBaseType_Half4x2, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown + }, + { // bool + HLSLBaseType_Float, HLSLBaseType_Float2, HLSLBaseType_Float3, HLSLBaseType_Float4, HLSLBaseType_Float2x2, HLSLBaseType_Float3x3, HLSLBaseType_Float4x4, HLSLBaseType_Float4x3, HLSLBaseType_Float4x2, + HLSLBaseType_Half, HLSLBaseType_Half2, HLSLBaseType_Half3, HLSLBaseType_Half4, HLSLBaseType_Half2x2, HLSLBaseType_Half3x3, HLSLBaseType_Half4x4, HLSLBaseType_Half4x3, HLSLBaseType_Half4x2, + HLSLBaseType_Int, HLSLBaseType_Int2, HLSLBaseType_Int3, HLSLBaseType_Int4, + HLSLBaseType_Int, HLSLBaseType_Int2, HLSLBaseType_Int3, HLSLBaseType_Int4, + HLSLBaseType_Uint, HLSLBaseType_Uint2, HLSLBaseType_Uint3, HLSLBaseType_Uint4 + }, + { // bool2 + HLSLBaseType_Float2, HLSLBaseType_Float2, HLSLBaseType_Float3, HLSLBaseType_Float4, HLSLBaseType_Float2x2, HLSLBaseType_Float3x3, HLSLBaseType_Float4x4, HLSLBaseType_Float4x3, HLSLBaseType_Float4x2, + HLSLBaseType_Half2, HLSLBaseType_Half2, HLSLBaseType_Half3, HLSLBaseType_Half4, HLSLBaseType_Half2x2, HLSLBaseType_Half3x3, HLSLBaseType_Half4x4, HLSLBaseType_Half4x3, HLSLBaseType_Half4x2, + HLSLBaseType_Int2, HLSLBaseType_Int2, HLSLBaseType_Int3, HLSLBaseType_Int4, + HLSLBaseType_Int2, HLSLBaseType_Int2, HLSLBaseType_Int3, HLSLBaseType_Int4, + HLSLBaseType_Uint2, HLSLBaseType_Uint2, HLSLBaseType_Uint3, HLSLBaseType_Uint4 + }, + { // bool3 + HLSLBaseType_Float3, HLSLBaseType_Float3, HLSLBaseType_Float3, HLSLBaseType_Float4, HLSLBaseType_Float2x2, HLSLBaseType_Float3x3, HLSLBaseType_Float4x4, HLSLBaseType_Float4x3, HLSLBaseType_Float4x2, + HLSLBaseType_Half3, HLSLBaseType_Half3, HLSLBaseType_Half3, HLSLBaseType_Half4, HLSLBaseType_Half2x2, HLSLBaseType_Half3x3, HLSLBaseType_Half4x4, HLSLBaseType_Half4x3, HLSLBaseType_Half4x2, + HLSLBaseType_Int3, HLSLBaseType_Int2, HLSLBaseType_Int3, HLSLBaseType_Int4, + HLSLBaseType_Int3, HLSLBaseType_Int2, HLSLBaseType_Int3, HLSLBaseType_Int4, + HLSLBaseType_Uint3, HLSLBaseType_Uint2, HLSLBaseType_Uint3, HLSLBaseType_Uint4 + }, + { // bool4 + HLSLBaseType_Float, HLSLBaseType_Float2, HLSLBaseType_Float3, HLSLBaseType_Float4, HLSLBaseType_Float2x2, HLSLBaseType_Float3x3, HLSLBaseType_Float4x4, HLSLBaseType_Float4x3, HLSLBaseType_Float4x2, + HLSLBaseType_Half, HLSLBaseType_Half2, HLSLBaseType_Half3, HLSLBaseType_Half4, HLSLBaseType_Half2x2, HLSLBaseType_Half3x3, HLSLBaseType_Half4x4, HLSLBaseType_Half4x3, HLSLBaseType_Half4x2, + HLSLBaseType_Int, HLSLBaseType_Int2, HLSLBaseType_Int3, HLSLBaseType_Int4, + HLSLBaseType_Int, HLSLBaseType_Int2, HLSLBaseType_Int3, HLSLBaseType_Int4, + HLSLBaseType_Uint, HLSLBaseType_Uint2, HLSLBaseType_Uint3, HLSLBaseType_Uint4 + }, + { // int + HLSLBaseType_Float, HLSLBaseType_Float2, HLSLBaseType_Float3, HLSLBaseType_Float4, HLSLBaseType_Float2x2, HLSLBaseType_Float3x3, HLSLBaseType_Float4x4, HLSLBaseType_Float4x3, HLSLBaseType_Float4x2, + HLSLBaseType_Half, HLSLBaseType_Half2, HLSLBaseType_Half3, HLSLBaseType_Half4, HLSLBaseType_Half2x2, HLSLBaseType_Half3x3, HLSLBaseType_Half4x4, HLSLBaseType_Half4x3, HLSLBaseType_Half4x2, + HLSLBaseType_Int, HLSLBaseType_Int2, HLSLBaseType_Int2, HLSLBaseType_Int2, + HLSLBaseType_Int, HLSLBaseType_Int2, HLSLBaseType_Int3, HLSLBaseType_Int4, + HLSLBaseType_Uint, HLSLBaseType_Uint2, HLSLBaseType_Uint3, HLSLBaseType_Uint4 + }, + { // int2 + HLSLBaseType_Float2, HLSLBaseType_Float2, HLSLBaseType_Float2, HLSLBaseType_Float2, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, + HLSLBaseType_Half2, HLSLBaseType_Half2, HLSLBaseType_Half2, HLSLBaseType_Half2, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, + HLSLBaseType_Int2, HLSLBaseType_Int2, HLSLBaseType_Int2, HLSLBaseType_Int2, + HLSLBaseType_Int2, HLSLBaseType_Int2, HLSLBaseType_Int2, HLSLBaseType_Int2, + HLSLBaseType_Uint2, HLSLBaseType_Uint2, HLSLBaseType_Uint2, HLSLBaseType_Uint2 + }, + { // int3 + HLSLBaseType_Float3, HLSLBaseType_Float2, HLSLBaseType_Float3, HLSLBaseType_Float3, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, + HLSLBaseType_Half3, HLSLBaseType_Half2, HLSLBaseType_Half3, HLSLBaseType_Half3, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, + HLSLBaseType_Int3, HLSLBaseType_Int2, HLSLBaseType_Int3, HLSLBaseType_Int3, + HLSLBaseType_Int3, HLSLBaseType_Int2, HLSLBaseType_Int3, HLSLBaseType_Int3, + HLSLBaseType_Uint3, HLSLBaseType_Uint2, HLSLBaseType_Uint3, HLSLBaseType_Uint3 + }, + { // int4 + HLSLBaseType_Float4, HLSLBaseType_Float2, HLSLBaseType_Float3, HLSLBaseType_Float4, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, + HLSLBaseType_Half4, HLSLBaseType_Half2, HLSLBaseType_Half3, HLSLBaseType_Half4, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, + HLSLBaseType_Int4, HLSLBaseType_Int2, HLSLBaseType_Int3, HLSLBaseType_Int4, + HLSLBaseType_Int4, HLSLBaseType_Int2, HLSLBaseType_Int3, HLSLBaseType_Int4, + HLSLBaseType_Uint4, HLSLBaseType_Uint2, HLSLBaseType_Uint3, HLSLBaseType_Uint4 + }, + { // uint + HLSLBaseType_Float, HLSLBaseType_Float2, HLSLBaseType_Float3, HLSLBaseType_Float4, HLSLBaseType_Float2x2, HLSLBaseType_Float3x3, HLSLBaseType_Float4x4, HLSLBaseType_Float4x3, HLSLBaseType_Float4x2, + HLSLBaseType_Half, HLSLBaseType_Half2, HLSLBaseType_Half3, HLSLBaseType_Half4, HLSLBaseType_Half2x2, HLSLBaseType_Half3x3, HLSLBaseType_Half4x4, HLSLBaseType_Half4x3, HLSLBaseType_Half4x2, + HLSLBaseType_Uint, HLSLBaseType_Uint2, HLSLBaseType_Uint3, HLSLBaseType_Uint4, + HLSLBaseType_Uint, HLSLBaseType_Uint2, HLSLBaseType_Uint3, HLSLBaseType_Uint4, + HLSLBaseType_Uint, HLSLBaseType_Uint2, HLSLBaseType_Uint3, HLSLBaseType_Uint4 + }, + { // uint2 + HLSLBaseType_Float2, HLSLBaseType_Float2, HLSLBaseType_Float2, HLSLBaseType_Float2, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, + HLSLBaseType_Half2, HLSLBaseType_Half2, HLSLBaseType_Half2, HLSLBaseType_Half2, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, + HLSLBaseType_Uint2, HLSLBaseType_Uint2, HLSLBaseType_Uint2, HLSLBaseType_Uint2, + HLSLBaseType_Uint2, HLSLBaseType_Uint2, HLSLBaseType_Uint2, HLSLBaseType_Uint2, + HLSLBaseType_Uint2, HLSLBaseType_Uint2, HLSLBaseType_Uint2, HLSLBaseType_Uint2 + }, + { // uint3 + HLSLBaseType_Float3, HLSLBaseType_Float2, HLSLBaseType_Float3, HLSLBaseType_Float3, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, + HLSLBaseType_Half3, HLSLBaseType_Half2, HLSLBaseType_Half3, HLSLBaseType_Half3, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, + HLSLBaseType_Uint3, HLSLBaseType_Uint2, HLSLBaseType_Uint3, HLSLBaseType_Uint3, + HLSLBaseType_Uint3, HLSLBaseType_Uint2, HLSLBaseType_Uint3, HLSLBaseType_Uint3, + HLSLBaseType_Uint3, HLSLBaseType_Uint2, HLSLBaseType_Uint3, HLSLBaseType_Uint3 + }, + { // uint4 + HLSLBaseType_Float4, HLSLBaseType_Float2, HLSLBaseType_Float3, HLSLBaseType_Float4, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, + HLSLBaseType_Half4, HLSLBaseType_Half2, HLSLBaseType_Half3, HLSLBaseType_Half4, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, HLSLBaseType_Unknown, + HLSLBaseType_Uint4, HLSLBaseType_Uint2, HLSLBaseType_Uint3, HLSLBaseType_Uint4, + HLSLBaseType_Uint4, HLSLBaseType_Uint2, HLSLBaseType_Uint3, HLSLBaseType_Uint4, + HLSLBaseType_Uint4, HLSLBaseType_Uint2, HLSLBaseType_Uint3, HLSLBaseType_Uint4 + }, + }; + +// Priority of the ? : operator. +const int _conditionalOpPriority = 1; + +static const char* GetTypeName(const HLSLType& type) +{ + if (type.baseType == HLSLBaseType_UserDefined) + { + return type.typeName; + } + else + { + return _baseTypeDescriptions[type.baseType].typeName; + } +} + +static const char* GetBinaryOpName(HLSLBinaryOp binaryOp) +{ + switch (binaryOp) + { + case HLSLBinaryOp_And: return "&&"; + case HLSLBinaryOp_Or: return "||"; + case HLSLBinaryOp_Add: return "+"; + case HLSLBinaryOp_Sub: return "-"; + case HLSLBinaryOp_Mul: return "*"; + case HLSLBinaryOp_Div: return "/"; + case HLSLBinaryOp_Less: return "<"; + case HLSLBinaryOp_Greater: return ">"; + case HLSLBinaryOp_LessEqual: return "<="; + case HLSLBinaryOp_GreaterEqual: return ">="; + case HLSLBinaryOp_Equal: return "=="; + case HLSLBinaryOp_NotEqual: return "!="; + case HLSLBinaryOp_BitAnd: return "&"; + case HLSLBinaryOp_BitOr: return "|"; + case HLSLBinaryOp_BitXor: return "^"; + case HLSLBinaryOp_Assign: return "="; + case HLSLBinaryOp_AddAssign: return "+="; + case HLSLBinaryOp_SubAssign: return "-="; + case HLSLBinaryOp_MulAssign: return "*="; + case HLSLBinaryOp_DivAssign: return "/="; + default: + ASSERT(0); + return "???"; + } +} + + +/* + * 1.) Match + * 2.) Scalar dimension promotion (scalar -> vector/matrix) + * 3.) Conversion + * 4.) Conversion + scalar dimension promotion + * 5.) Truncation (vector -> scalar or lower component vector, matrix -> scalar or lower component matrix) + * 6.) Conversion + truncation + */ +static int GetTypeCastRank(HLSLTree * tree, const HLSLType& srcType, const HLSLType& dstType) +{ + /*if (srcType.array != dstType.array || srcType.arraySize != dstType.arraySize) + { + return -1; + }*/ + + if (srcType.array != dstType.array) + { + return -1; + } + + if (srcType.array == true) + { + ASSERT(dstType.array == true); + int srcArraySize = -1; + int dstArraySize = -1; + + tree->GetExpressionValue(srcType.arraySize, srcArraySize); + tree->GetExpressionValue(dstType.arraySize, dstArraySize); + + if (srcArraySize != dstArraySize) { + return -1; + } + } + + if (srcType.baseType == HLSLBaseType_UserDefined && dstType.baseType == HLSLBaseType_UserDefined) + { + return strcmp(srcType.typeName, dstType.typeName) == 0 ? 0 : -1; + } + + if (srcType.baseType == dstType.baseType) + { + if (IsSamplerType(srcType.baseType)) + { + return srcType.samplerType == dstType.samplerType ? 0 : -1; + } + + return 0; + } + + const BaseTypeDescription& srcDesc = _baseTypeDescriptions[srcType.baseType]; + const BaseTypeDescription& dstDesc = _baseTypeDescriptions[dstType.baseType]; + if (srcDesc.numericType == NumericType_NaN || dstDesc.numericType == NumericType_NaN) + { + return -1; + } + + // Result bits: T R R R P (T = truncation, R = conversion rank, P = dimension promotion) + int result = _numberTypeRank[srcDesc.numericType][dstDesc.numericType] << 1; + + if (srcDesc.numDimensions == 0 && dstDesc.numDimensions > 0) + { + // Scalar dimension promotion + result |= (1 << 0); + } + else if ((srcDesc.numDimensions == dstDesc.numDimensions && (srcDesc.numComponents > dstDesc.numComponents || srcDesc.height > dstDesc.height)) || + (srcDesc.numDimensions > 0 && dstDesc.numDimensions == 0)) + { + // Truncation + result |= (1 << 4); + } + else if (srcDesc.numDimensions != dstDesc.numDimensions || + srcDesc.numComponents != dstDesc.numComponents || + srcDesc.height != dstDesc.height) + { + // Can't convert + return -1; + } + + return result; + +} + +static bool GetFunctionCallCastRanks(HLSLTree* tree, const HLSLFunctionCall* call, const HLSLFunction* function, int* rankBuffer) +{ + + if (function == NULL || function->numArguments < call->numArguments) + { + // Function not viable + return false; + } + + const HLSLExpression* expression = call->argument; + const HLSLArgument* argument = function->argument; + + for (int i = 0; i < call->numArguments; ++i) + { + int rank = GetTypeCastRank(tree, expression->expressionType, argument->type); + if (rank == -1) + { + return false; + } + + rankBuffer[i] = rank; + + argument = argument->nextArgument; + expression = expression->nextExpression; + } + + for (int i = call->numArguments; i < function->numArguments; ++i) + { + if (argument->defaultValue == NULL) + { + // Function not viable. + return false; + } + } + + return true; + +} + +struct CompareRanks +{ + bool operator() (const int& rank1, const int& rank2) { return rank1 > rank2; } +}; + +static CompareFunctionsResult CompareFunctions(HLSLTree* tree, const HLSLFunctionCall* call, const HLSLFunction* function1, const HLSLFunction* function2) +{ + + int* function1Ranks = static_cast(alloca(sizeof(int) * call->numArguments)); + int* function2Ranks = static_cast(alloca(sizeof(int) * call->numArguments)); + + const bool function1Viable = GetFunctionCallCastRanks(tree, call, function1, function1Ranks); + const bool function2Viable = GetFunctionCallCastRanks(tree, call, function2, function2Ranks); + + // Both functions have to be viable to be able to compare them + if (!(function1Viable && function2Viable)) + { + if (function1Viable) + { + return Function1Better; + } + else if (function2Viable) + { + return Function2Better; + } + else + { + return FunctionsEqual; + } + } + + std::sort(function1Ranks, function1Ranks + call->numArguments, CompareRanks()); + std::sort(function2Ranks, function2Ranks + call->numArguments, CompareRanks()); + + for (int i = 0; i < call->numArguments; ++i) + { + if (function1Ranks[i] < function2Ranks[i]) + { + return Function1Better; + } + else if (function2Ranks[i] < function1Ranks[i]) + { + return Function2Better; + } + } + + return FunctionsEqual; + +} + +static bool GetBinaryOpResultType(HLSLBinaryOp binaryOp, const HLSLType& type1, const HLSLType& type2, HLSLType& result) +{ + + if (type1.baseType < HLSLBaseType_FirstNumeric || type1.baseType > HLSLBaseType_LastNumeric || type1.array || + type2.baseType < HLSLBaseType_FirstNumeric || type2.baseType > HLSLBaseType_LastNumeric || type2.array) + { + return false; + } + + if (binaryOp == HLSLBinaryOp_BitAnd || binaryOp == HLSLBinaryOp_BitOr || binaryOp == HLSLBinaryOp_BitXor) + { + if (type1.baseType < HLSLBaseType_FirstInteger || type1.baseType > HLSLBaseType_LastInteger) + { + return false; + } + } + + switch (binaryOp) + { + case HLSLBinaryOp_And: + case HLSLBinaryOp_Or: + case HLSLBinaryOp_Less: + case HLSLBinaryOp_Greater: + case HLSLBinaryOp_LessEqual: + case HLSLBinaryOp_GreaterEqual: + case HLSLBinaryOp_Equal: + case HLSLBinaryOp_NotEqual: + { + int numComponents = std::max( _baseTypeDescriptions[ type1.baseType ].numComponents, _baseTypeDescriptions[ type2.baseType ].numComponents ); + result.baseType = HLSLBaseType( HLSLBaseType_Bool + numComponents - 1 ); + break; + } + default: + result.baseType = _binaryOpTypeLookup[type1.baseType - HLSLBaseType_FirstNumeric][type2.baseType - HLSLBaseType_FirstNumeric]; + break; + } + + result.typeName = NULL; + result.array = false; + result.arraySize = NULL; + result.flags = (type1.flags & type2.flags) & HLSLTypeFlag_Const; // Propagate constness. + + return result.baseType != HLSLBaseType_Unknown; + +} + +HLSLParser::HLSLParser(Allocator* allocator, const char* fileName, const char* buffer, size_t length) : + m_tokenizer(fileName, buffer, length), + m_userTypes(allocator), + m_variables(allocator), + m_functions(allocator) +{ + m_numGlobals = 0; + m_tree = NULL; +} + +bool HLSLParser::Accept(int token) +{ + if (m_tokenizer.GetToken() == token) + { + m_tokenizer.Next(); + return true; + } + return false; +} + +bool HLSLParser::Accept(const char* token) +{ + if (m_tokenizer.GetToken() == HLSLToken_Identifier && String_Equal( token, m_tokenizer.GetIdentifier() ) ) + { + m_tokenizer.Next(); + return true; + } + return false; +} + +bool HLSLParser::Expect(int token) +{ + if (!Accept(token)) + { + char want[HLSLTokenizer::s_maxIdentifier]; + m_tokenizer.GetTokenName(token, want); + char near[HLSLTokenizer::s_maxIdentifier]; + m_tokenizer.GetTokenName(near); + m_tokenizer.Error("Syntax error: expected '%s' near '%s'", want, near); + return false; + } + return true; +} + +bool HLSLParser::Expect(const char * token) +{ + if (!Accept(token)) + { + const char * want = token; + char near[HLSLTokenizer::s_maxIdentifier]; + m_tokenizer.GetTokenName(near); + m_tokenizer.Error("Syntax error: expected '%s' near '%s'", want, near); + return false; + } + return true; +} + + +bool HLSLParser::AcceptIdentifier(const char*& identifier) +{ + if (m_tokenizer.GetToken() == HLSLToken_Identifier) + { + identifier = m_tree->AddString( m_tokenizer.GetIdentifier() ); + m_tokenizer.Next(); + return true; + } + return false; +} + +bool HLSLParser::ExpectIdentifier(const char*& identifier) +{ + if (!AcceptIdentifier(identifier)) + { + char near[HLSLTokenizer::s_maxIdentifier]; + m_tokenizer.GetTokenName(near); + m_tokenizer.Error("Syntax error: expected identifier near '%s'", near); + identifier = ""; + return false; + } + return true; +} + +bool HLSLParser::AcceptFloat(float& value) +{ + if (m_tokenizer.GetToken() == HLSLToken_FloatLiteral) + { + value = m_tokenizer.GetFloat(); + m_tokenizer.Next(); + return true; + } + return false; +} + +bool HLSLParser::AcceptHalf( float& value ) +{ + if( m_tokenizer.GetToken() == HLSLToken_HalfLiteral ) + { + value = m_tokenizer.GetFloat(); + m_tokenizer.Next(); + return true; + } + return false; +} + +bool HLSLParser::AcceptInt(int& value) +{ + if (m_tokenizer.GetToken() == HLSLToken_IntLiteral) + { + value = m_tokenizer.GetInt(); + m_tokenizer.Next(); + return true; + } + return false; +} + +bool HLSLParser::ParseTopLevel(HLSLStatement*& statement) +{ + HLSLAttribute * attributes = NULL; + ParseAttributeBlock(attributes); + + int line = GetLineNumber(); + const char* fileName = GetFileName(); + + HLSLType type; + //HLSLBaseType type; + //const char* typeName = NULL; + //int typeFlags = false; + + bool doesNotExpectSemicolon = false; + + if (Accept(HLSLToken_Struct)) + { + // Struct declaration. + + const char* structName = NULL; + if (!ExpectIdentifier(structName)) + { + return false; + } + if (FindUserDefinedType(structName) != NULL) + { + m_tokenizer.Error("struct %s already defined", structName); + return false; + } + + if (!Expect('{')) + { + return false; + } + + HLSLStruct* structure = m_tree->AddNode(fileName, line); + structure->name = structName; + + m_userTypes.PushBack(structure); + + HLSLStructField* lastField = NULL; + + // Add the struct to our list of user defined types. + while (!Accept('}')) + { + if (CheckForUnexpectedEndOfStream('}')) + { + return false; + } + HLSLStructField* field = NULL; + if (!ParseFieldDeclaration(field)) + { + return false; + } + ASSERT(field != NULL); + if (lastField == NULL) + { + structure->field = field; + } + else + { + lastField->nextField = field; + } + lastField = field; + } + + statement = structure; + } + else if (Accept(HLSLToken_CBuffer) || Accept(HLSLToken_TBuffer)) + { + // cbuffer/tbuffer declaration. + + HLSLBuffer* buffer = m_tree->AddNode(fileName, line); + AcceptIdentifier(buffer->name); + + // Optional register assignment. + if (Accept(':')) + { + if (!Expect(HLSLToken_Register) || !Expect('(') || !ExpectIdentifier(buffer->registerName) || !Expect(')')) + { + return false; + } + // TODO: Check that we aren't re-using a register. + } + + // Fields. + if (!Expect('{')) + { + return false; + } + HLSLDeclaration* lastField = NULL; + while (!Accept('}')) + { + if (CheckForUnexpectedEndOfStream('}')) + { + return false; + } + HLSLDeclaration* field = NULL; + if (!ParseDeclaration(field)) + { + m_tokenizer.Error("Expected variable declaration"); + return false; + } + DeclareVariable( field->name, field->type ); + field->buffer = buffer; + if (buffer->field == NULL) + { + buffer->field = field; + } + else + { + lastField->nextStatement = field; + } + lastField = field; + + if (!Expect(';')) { + return false; + } + } + + statement = buffer; + } + else if (AcceptType(true, type)) + { + // Global declaration (uniform or function). + const char* globalName = NULL; + if (!ExpectIdentifier(globalName)) + { + return false; + } + + if (Accept('(')) + { + // Function declaration. + + HLSLFunction* function = m_tree->AddNode(fileName, line); + function->name = globalName; + function->returnType.baseType = type.baseType; + function->returnType.typeName = type.typeName; + function->attributes = attributes; + + BeginScope(); + + if (!ParseArgumentList(function->argument, function->numArguments, function->numOutputArguments)) + { + return false; + } + + const HLSLFunction* declaration = FindFunction(function); + + // Forward declaration + if (Accept(';')) + { + // Add a function entry so that calls can refer to it + if (!declaration) + { + m_functions.PushBack( function ); + statement = function; + } + EndScope(); + return true; + } + + // Optional semantic. + if (Accept(':') && !ExpectIdentifier(function->semantic)) + { + return false; + } + + if (declaration) + { + if (declaration->forward || declaration->statement) + { + m_tokenizer.Error("Duplicate function definition"); + return false; + } + + const_cast(declaration)->forward = function; + } + else + { + m_functions.PushBack( function ); + } + + if (!Expect('{') || !ParseBlock(function->statement, function->returnType)) + { + return false; + } + + EndScope(); + + // Note, no semi-colon at the end of a function declaration. + statement = function; + + return true; + } + else + { + // Uniform declaration. + HLSLDeclaration* declaration = m_tree->AddNode(fileName, line); + declaration->name = globalName; + declaration->type = type; + + // Handle array syntax. + if (Accept('[')) + { + if (!Accept(']')) + { + if (!ParseExpression(declaration->type.arraySize) || !Expect(']')) + { + return false; + } + } + declaration->type.array = true; + } + + // Handle optional register. + if (Accept(':')) + { + // @@ Currently we support either a semantic or a register, but not both. + if (AcceptIdentifier(declaration->semantic)) { + int k = 1; + } + else if (!Expect(HLSLToken_Register) || !Expect('(') || !ExpectIdentifier(declaration->registerName) || !Expect(')')) + { + return false; + } + } + + DeclareVariable( globalName, declaration->type ); + + if (!ParseDeclarationAssignment(declaration)) + { + return false; + } + + // TODO: Multiple variables declared on one line. + + statement = declaration; + } + } + else if (ParseTechnique(statement)) { + doesNotExpectSemicolon = true; + } + else if (ParsePipeline(statement)) { + doesNotExpectSemicolon = true; + } + else if (ParseStage(statement)) { + doesNotExpectSemicolon = true; + } + + if (statement != NULL) { + statement->attributes = attributes; + } + + return doesNotExpectSemicolon || Expect(';'); +} + +bool HLSLParser::ParseStatementOrBlock(HLSLStatement*& firstStatement, const HLSLType& returnType, bool scoped/*=true*/) +{ + if (scoped) + { + BeginScope(); + } + if (Accept('{')) + { + if (!ParseBlock(firstStatement, returnType)) + { + return false; + } + } + else + { + if (!ParseStatement(firstStatement, returnType)) + { + return false; + } + } + if (scoped) + { + EndScope(); + } + return true; +} + +bool HLSLParser::ParseBlock(HLSLStatement*& firstStatement, const HLSLType& returnType) +{ + HLSLStatement* lastStatement = NULL; + while (!Accept('}')) + { + if (CheckForUnexpectedEndOfStream('}')) + { + return false; + } + HLSLStatement* statement = NULL; + if (!ParseStatement(statement, returnType)) + { + return false; + } + if (statement != NULL) + { + if (firstStatement == NULL) + { + firstStatement = statement; + } + else + { + lastStatement->nextStatement = statement; + } + lastStatement = statement; + while (lastStatement->nextStatement) lastStatement = lastStatement->nextStatement; + } + } + return true; +} + +bool HLSLParser::ParseStatement(HLSLStatement*& statement, const HLSLType& returnType) +{ + const char* fileName = GetFileName(); + int line = GetLineNumber(); + + // Empty statements. + if (Accept(';')) + { + return true; + } + + HLSLAttribute * attributes = NULL; + ParseAttributeBlock(attributes); // @@ Leak if not assigned to node? + +#if 0 // @@ Work in progress. + // Static statements: @if only for now. + if (Accept('@')) + { + if (Accept(HLSLToken_If)) + { + //HLSLIfStatement* ifStatement = m_tree->AddNode(fileName, line); + //ifStatement->isStatic = true; + //ifStatement->attributes = attributes; + + HLSLExpression * condition = NULL; + + m_allowUndeclaredIdentifiers = true; // Not really correct... better to push to stack? + if (!Expect('(') || !ParseExpression(condition) || !Expect(')')) + { + m_allowUndeclaredIdentifiers = false; + return false; + } + m_allowUndeclaredIdentifiers = false; + + if ((condition->expressionType.flags & HLSLTypeFlag_Const) == 0) + { + m_tokenizer.Error("Syntax error: @if condition is not constant"); + return false; + } + + int conditionValue; + if (!m_tree->GetExpressionValue(condition, conditionValue)) + { + m_tokenizer.Error("Syntax error: Cannot evaluate @if condition"); + return false; + } + + if (!conditionValue) m_disableSemanticValidation = true; + + HLSLStatement * ifStatements = NULL; + HLSLStatement * elseStatements = NULL; + + if (!ParseStatementOrBlock(ifStatements, returnType, /*scoped=*/false)) + { + m_disableSemanticValidation = false; + return false; + } + if (Accept(HLSLToken_Else)) + { + if (conditionValue) m_disableSemanticValidation = true; + + if (!ParseStatementOrBlock(elseStatements, returnType, /*scoped=*/false)) + { + m_disableSemanticValidation = false; + return false; + } + } + m_disableSemanticValidation = false; + + if (conditionValue) statement = ifStatements; + else statement = elseStatements; + + // @@ Free the pruned statements? + + return true; + } + else { + m_tokenizer.Error("Syntax error: unexpected token '@'"); + } + } +#endif + + // If statement. + if (Accept(HLSLToken_If)) + { + HLSLIfStatement* ifStatement = m_tree->AddNode(fileName, line); + ifStatement->attributes = attributes; + if (!Expect('(') || !ParseExpression(ifStatement->condition) || !Expect(')')) + { + return false; + } + statement = ifStatement; + if (!ParseStatementOrBlock(ifStatement->statement, returnType)) + { + return false; + } + if (Accept(HLSLToken_Else)) + { + return ParseStatementOrBlock(ifStatement->elseStatement, returnType); + } + return true; + } + + // For statement. + if (Accept(HLSLToken_For)) + { + HLSLForStatement* forStatement = m_tree->AddNode(fileName, line); + forStatement->attributes = attributes; + if (!Expect('(')) + { + return false; + } + BeginScope(); + if (!ParseDeclaration(forStatement->initialization)) + { + return false; + } + if (!Expect(';')) + { + return false; + } + ParseExpression(forStatement->condition); + if (!Expect(';')) + { + return false; + } + ParseExpression(forStatement->increment); + if (!Expect(')')) + { + return false; + } + statement = forStatement; + if (!ParseStatementOrBlock(forStatement->statement, returnType)) + { + return false; + } + EndScope(); + return true; + } + + if (attributes != NULL) + { + // @@ Error. Unexpected attribute. We only support attributes associated to if and for statements. + } + + // Block statement. + if (Accept('{')) + { + HLSLBlockStatement* blockStatement = m_tree->AddNode(fileName, line); + statement = blockStatement; + BeginScope(); + bool success = ParseBlock(blockStatement->statement, returnType); + EndScope(); + return success; + } + + // Discard statement. + if (Accept(HLSLToken_Discard)) + { + HLSLDiscardStatement* discardStatement = m_tree->AddNode(fileName, line); + statement = discardStatement; + return Expect(';'); + } + + // Break statement. + if (Accept(HLSLToken_Break)) + { + HLSLBreakStatement* breakStatement = m_tree->AddNode(fileName, line); + statement = breakStatement; + return Expect(';'); + } + + // Continue statement. + if (Accept(HLSLToken_Continue)) + { + HLSLContinueStatement* continueStatement = m_tree->AddNode(fileName, line); + statement = continueStatement; + return Expect(';'); + } + + // Return statement + if (Accept(HLSLToken_Return)) + { + HLSLReturnStatement* returnStatement = m_tree->AddNode(fileName, line); + if (!Accept(';') && !ParseExpression(returnStatement->expression)) + { + return false; + } + // Check that the return expression can be cast to the return type of the function. + HLSLType voidType(HLSLBaseType_Void); + if (!CheckTypeCast(returnStatement->expression ? returnStatement->expression->expressionType : voidType, returnType)) + { + return false; + } + + statement = returnStatement; + return Expect(';'); + } + + HLSLDeclaration* declaration = NULL; + HLSLExpression* expression = NULL; + + if (ParseDeclaration(declaration)) + { + statement = declaration; + } + else if (ParseExpression(expression)) + { + HLSLExpressionStatement* expressionStatement; + expressionStatement = m_tree->AddNode(fileName, line); + expressionStatement->expression = expression; + statement = expressionStatement; + } + + return Expect(';'); +} + + +// IC: This is only used in block statements, or within control flow statements. So, it doesn't support semantics or layout modifiers. +// @@ We should add suport for semantics for inline input/output declarations. +bool HLSLParser::ParseDeclaration(HLSLDeclaration*& declaration) +{ + const char* fileName = GetFileName(); + int line = GetLineNumber(); + + HLSLType type; + if (!AcceptType(/*allowVoid=*/false, type)) + { + return false; + } + + bool allowUnsizedArray = true; // @@ Really? + + HLSLDeclaration * firstDeclaration = NULL; + HLSLDeclaration * lastDeclaration = NULL; + + do { + const char* name; + if (!ExpectIdentifier(name)) + { + // TODO: false means we didn't accept a declaration and we had an error! + return false; + } + // Handle array syntax. + if (Accept('[')) + { + type.array = true; + // Optionally allow no size to the specified for the array. + if (Accept(']') && allowUnsizedArray) + { + return true; + } + if (!ParseExpression(type.arraySize) || !Expect(']')) + { + return false; + } + } + + HLSLDeclaration * declaration = m_tree->AddNode(fileName, line); + declaration->type = type; + declaration->name = name; + + DeclareVariable( declaration->name, declaration->type ); + + // Handle option assignment of the declared variables(s). + if (!ParseDeclarationAssignment( declaration )) { + return false; + } + + if (firstDeclaration == NULL) firstDeclaration = declaration; + if (lastDeclaration != NULL) lastDeclaration->nextDeclaration = declaration; + lastDeclaration = declaration; + + } while(Accept(',')); + + declaration = firstDeclaration; + + return true; +} + +bool HLSLParser::ParseDeclarationAssignment(HLSLDeclaration* declaration) +{ + if (Accept('=')) + { + // Handle array initialization syntax. + if (declaration->type.array) + { + int numValues = 0; + if (!Expect('{') || !ParseExpressionList('}', true, declaration->assignment, numValues)) + { + return false; + } + } + else if (IsSamplerType(declaration->type.baseType)) + { + if (!ParseSamplerState(declaration->assignment)) + { + return false; + } + } + else if (!ParseExpression(declaration->assignment)) + { + return false; + } + } + return true; +} + +bool HLSLParser::ParseFieldDeclaration(HLSLStructField*& field) +{ + field = m_tree->AddNode( GetFileName(), GetLineNumber() ); + if (!ExpectDeclaration(false, field->type, field->name)) + { + return false; + } + // Handle optional semantics. + if (Accept(':')) + { + if (!ExpectIdentifier(field->semantic)) + { + return false; + } + } + return Expect(';'); +} + +// @@ Add support for packoffset to general declarations. +/*bool HLSLParser::ParseBufferFieldDeclaration(HLSLBufferField*& field) +{ + field = m_tree->AddNode( GetFileName(), GetLineNumber() ); + if (AcceptDeclaration(false, field->type, field->name)) + { + // Handle optional packoffset. + if (Accept(':')) + { + if (!Expect("packoffset")) + { + return false; + } + const char* constantName = NULL; + const char* swizzleMask = NULL; + if (!Expect('(') || !ExpectIdentifier(constantName) || !Expect('.') || !ExpectIdentifier(swizzleMask) || !Expect(')')) + { + return false; + } + } + return Expect(';'); + } + return false; +}*/ + +bool HLSLParser::CheckTypeCast(const HLSLType& srcType, const HLSLType& dstType) +{ + if (GetTypeCastRank(m_tree, srcType, dstType) == -1) + { + const char* srcTypeName = GetTypeName(srcType); + const char* dstTypeName = GetTypeName(dstType); + m_tokenizer.Error("Cannot implicitly convert from '%s' to '%s'", srcTypeName, dstTypeName); + return false; + } + return true; +} + +bool HLSLParser::ParseExpression(HLSLExpression*& expression) +{ + if (!ParseBinaryExpression(0, expression)) + { + return false; + } + + HLSLBinaryOp assignOp; + if (AcceptAssign(assignOp)) + { + HLSLExpression* expression2 = NULL; + if (!ParseExpression(expression2)) + { + return false; + } + HLSLBinaryExpression* binaryExpression = m_tree->AddNode(expression->fileName, expression->line); + binaryExpression->binaryOp = assignOp; + binaryExpression->expression1 = expression; + binaryExpression->expression2 = expression2; + // This type is not strictly correct, since the type should be a reference. + // However, for our usage of the types it should be sufficient. + binaryExpression->expressionType = expression->expressionType; + + if (!CheckTypeCast(expression2->expressionType, expression->expressionType)) + { + const char* srcTypeName = GetTypeName(expression2->expressionType); + const char* dstTypeName = GetTypeName(expression->expressionType); + m_tokenizer.Error("Cannot implicitly convert from '%s' to '%s'", srcTypeName, dstTypeName); + return false; + } + + expression = binaryExpression; + } + + return true; +} + +bool HLSLParser::AcceptBinaryOperator(int priority, HLSLBinaryOp& binaryOp) +{ + int token = m_tokenizer.GetToken(); + switch (token) + { + case HLSLToken_AndAnd: binaryOp = HLSLBinaryOp_And; break; + case HLSLToken_BarBar: binaryOp = HLSLBinaryOp_Or; break; + case '+': binaryOp = HLSLBinaryOp_Add; break; + case '-': binaryOp = HLSLBinaryOp_Sub; break; + case '*': binaryOp = HLSLBinaryOp_Mul; break; + case '/': binaryOp = HLSLBinaryOp_Div; break; + case '<': binaryOp = HLSLBinaryOp_Less; break; + case '>': binaryOp = HLSLBinaryOp_Greater; break; + case HLSLToken_LessEqual: binaryOp = HLSLBinaryOp_LessEqual; break; + case HLSLToken_GreaterEqual: binaryOp = HLSLBinaryOp_GreaterEqual; break; + case HLSLToken_EqualEqual: binaryOp = HLSLBinaryOp_Equal; break; + case HLSLToken_NotEqual: binaryOp = HLSLBinaryOp_NotEqual; break; + case '&': binaryOp = HLSLBinaryOp_BitAnd; break; + case '|': binaryOp = HLSLBinaryOp_BitOr; break; + case '^': binaryOp = HLSLBinaryOp_BitXor; break; + default: + return false; + } + if (_binaryOpPriority[binaryOp] > priority) + { + m_tokenizer.Next(); + return true; + } + return false; +} + +bool HLSLParser::AcceptUnaryOperator(bool pre, HLSLUnaryOp& unaryOp) +{ + int token = m_tokenizer.GetToken(); + if (token == HLSLToken_PlusPlus) + { + unaryOp = pre ? HLSLUnaryOp_PreIncrement : HLSLUnaryOp_PostIncrement; + } + else if (token == HLSLToken_MinusMinus) + { + unaryOp = pre ? HLSLUnaryOp_PreDecrement : HLSLUnaryOp_PostDecrement; + } + else if (pre && token == '-') + { + unaryOp = HLSLUnaryOp_Negative; + } + else if (pre && token == '+') + { + unaryOp = HLSLUnaryOp_Positive; + } + else if (pre && token == '!') + { + unaryOp = HLSLUnaryOp_Not; + } + else if (pre && token == '~') + { + unaryOp = HLSLUnaryOp_Not; + } + else + { + return false; + } + m_tokenizer.Next(); + return true; +} + +bool HLSLParser::AcceptAssign(HLSLBinaryOp& binaryOp) +{ + if (Accept('=')) + { + binaryOp = HLSLBinaryOp_Assign; + } + else if (Accept(HLSLToken_PlusEqual)) + { + binaryOp = HLSLBinaryOp_AddAssign; + } + else if (Accept(HLSLToken_MinusEqual)) + { + binaryOp = HLSLBinaryOp_SubAssign; + } + else if (Accept(HLSLToken_TimesEqual)) + { + binaryOp = HLSLBinaryOp_MulAssign; + } + else if (Accept(HLSLToken_DivideEqual)) + { + binaryOp = HLSLBinaryOp_DivAssign; + } + else + { + return false; + } + return true; +} + +bool HLSLParser::ParseBinaryExpression(int priority, HLSLExpression*& expression) +{ + const char* fileName = GetFileName(); + int line = GetLineNumber(); + + bool needsEndParen; + + if (!ParseTerminalExpression(expression, needsEndParen)) + { + return false; + } + + // reset priority cause openned parenthesis + if( needsEndParen ) + priority = 0; + + while (1) + { + HLSLBinaryOp binaryOp; + if (AcceptBinaryOperator(priority, binaryOp)) + { + + HLSLExpression* expression2 = NULL; + ASSERT( binaryOp < sizeof(_binaryOpPriority) / sizeof(int) ); + if (!ParseBinaryExpression(_binaryOpPriority[binaryOp], expression2)) + { + return false; + } + HLSLBinaryExpression* binaryExpression = m_tree->AddNode(fileName, line); + binaryExpression->binaryOp = binaryOp; + binaryExpression->expression1 = expression; + binaryExpression->expression2 = expression2; + if (!GetBinaryOpResultType( binaryOp, expression->expressionType, expression2->expressionType, binaryExpression->expressionType )) + { + const char* typeName1 = GetTypeName( binaryExpression->expression1->expressionType ); + const char* typeName2 = GetTypeName( binaryExpression->expression2->expressionType ); + m_tokenizer.Error("binary '%s' : no global operator found which takes types '%s' and '%s' (or there is no acceptable conversion)", + GetBinaryOpName(binaryOp), typeName1, typeName2); + + return false; + } + + // Propagate constness. + binaryExpression->expressionType.flags = (expression->expressionType.flags | expression2->expressionType.flags) & HLSLTypeFlag_Const; + + expression = binaryExpression; + } + else if (_conditionalOpPriority > priority && Accept('?')) + { + + HLSLConditionalExpression* conditionalExpression = m_tree->AddNode(fileName, line); + conditionalExpression->condition = expression; + + HLSLExpression* expression1 = NULL; + HLSLExpression* expression2 = NULL; + if (!ParseBinaryExpression(_conditionalOpPriority, expression1) || !Expect(':') || !ParseBinaryExpression(_conditionalOpPriority, expression2)) + { + return false; + } + + // Make sure both cases have compatible types. + if (GetTypeCastRank(m_tree, expression1->expressionType, expression2->expressionType) == -1) + { + const char* srcTypeName = GetTypeName(expression2->expressionType); + const char* dstTypeName = GetTypeName(expression1->expressionType); + m_tokenizer.Error("':' no possible conversion from from '%s' to '%s'", srcTypeName, dstTypeName); + return false; + } + + conditionalExpression->trueExpression = expression1; + conditionalExpression->falseExpression = expression2; + conditionalExpression->expressionType = expression1->expressionType; + + expression = conditionalExpression; + } + else + { + break; + } + + if( needsEndParen ) + { + if( !Expect( ')' ) ) + return false; + needsEndParen = false; + } + } + + return !needsEndParen || Expect(')'); +} + +bool HLSLParser::ParsePartialConstructor(HLSLExpression*& expression, HLSLBaseType type, const char* typeName) +{ + const char* fileName = GetFileName(); + int line = GetLineNumber(); + + HLSLConstructorExpression* constructorExpression = m_tree->AddNode(fileName, line); + constructorExpression->type.baseType = type; + constructorExpression->type.typeName = typeName; + int numArguments = 0; + if (!ParseExpressionList(')', false, constructorExpression->argument, numArguments)) + { + return false; + } + constructorExpression->expressionType = constructorExpression->type; + constructorExpression->expressionType.flags = HLSLTypeFlag_Const; + expression = constructorExpression; + return true; +} + +bool HLSLParser::ParseTerminalExpression(HLSLExpression*& expression, bool& needsEndParen) +{ + const char* fileName = GetFileName(); + int line = GetLineNumber(); + + needsEndParen = false; + + HLSLUnaryOp unaryOp; + if (AcceptUnaryOperator(true, unaryOp)) + { + HLSLUnaryExpression* unaryExpression = m_tree->AddNode(fileName, line); + unaryExpression->unaryOp = unaryOp; + if (!ParseTerminalExpression(unaryExpression->expression, needsEndParen)) + { + return false; + } + if (unaryOp == HLSLUnaryOp_BitNot) + { + if (unaryExpression->expression->expressionType.baseType < HLSLBaseType_FirstInteger || + unaryExpression->expression->expressionType.baseType > HLSLBaseType_LastInteger) + { + const char * typeName = GetTypeName(unaryExpression->expression->expressionType); + m_tokenizer.Error("unary '~' : no global operator found which takes type '%s' (or there is no acceptable conversion)", typeName); + return false; + } + } + if (unaryOp == HLSLUnaryOp_Not) + { + unaryExpression->expressionType = HLSLType(HLSLBaseType_Bool); + + // Propagate constness. + unaryExpression->expressionType.flags = unaryExpression->expression->expressionType.flags & HLSLTypeFlag_Const; + } + else + { + unaryExpression->expressionType = unaryExpression->expression->expressionType; + } + expression = unaryExpression; + return true; + } + + // Expressions inside parenthesis or casts. + if (Accept('(')) + { + // Check for a casting operator. + HLSLType type; + if (AcceptType(false, type)) + { + // This is actually a type constructor like (float2(... + if (Accept('(')) + { + needsEndParen = true; + return ParsePartialConstructor(expression, type.baseType, type.typeName); + } + HLSLCastingExpression* castingExpression = m_tree->AddNode(fileName, line); + castingExpression->type = type; + expression = castingExpression; + castingExpression->expressionType = type; + return Expect(')') && ParseExpression(castingExpression->expression); + } + + if (!ParseExpression(expression) || !Expect(')')) + { + return false; + } + } + else + { + // Terminal values. + float fValue = 0.0f; + int iValue = 0; + + if (AcceptFloat(fValue)) + { + HLSLLiteralExpression* literalExpression = m_tree->AddNode(fileName, line); + literalExpression->type = HLSLBaseType_Float; + literalExpression->fValue = fValue; + literalExpression->expressionType.baseType = literalExpression->type; + literalExpression->expressionType.flags = HLSLTypeFlag_Const; + expression = literalExpression; + return true; + } + if( AcceptHalf( fValue ) ) + { + HLSLLiteralExpression* literalExpression = m_tree->AddNode( fileName, line ); + literalExpression->type = HLSLBaseType_Half; + literalExpression->fValue = fValue; + literalExpression->expressionType.baseType = literalExpression->type; + literalExpression->expressionType.flags = HLSLTypeFlag_Const; + expression = literalExpression; + return true; + } + else if (AcceptInt(iValue)) + { + HLSLLiteralExpression* literalExpression = m_tree->AddNode(fileName, line); + literalExpression->type = HLSLBaseType_Int; + literalExpression->iValue = iValue; + literalExpression->expressionType.baseType = literalExpression->type; + literalExpression->expressionType.flags = HLSLTypeFlag_Const; + expression = literalExpression; + return true; + } + else if (Accept(HLSLToken_True)) + { + HLSLLiteralExpression* literalExpression = m_tree->AddNode(fileName, line); + literalExpression->type = HLSLBaseType_Bool; + literalExpression->bValue = true; + literalExpression->expressionType.baseType = literalExpression->type; + literalExpression->expressionType.flags = HLSLTypeFlag_Const; + expression = literalExpression; + return true; + } + else if (Accept(HLSLToken_False)) + { + HLSLLiteralExpression* literalExpression = m_tree->AddNode(fileName, line); + literalExpression->type = HLSLBaseType_Bool; + literalExpression->bValue = false; + literalExpression->expressionType.baseType = literalExpression->type; + literalExpression->expressionType.flags = HLSLTypeFlag_Const; + expression = literalExpression; + return true; + } + + // Type constructor. + HLSLType type; + if (AcceptType(/*allowVoid=*/false, type)) + { + Expect('('); + if (!ParsePartialConstructor(expression, type.baseType, type.typeName)) + { + return false; + } + } + else + { + HLSLIdentifierExpression* identifierExpression = m_tree->AddNode(fileName, line); + if (!ExpectIdentifier(identifierExpression->name)) + { + return false; + } + + bool undeclaredIdentifier = false; + + const HLSLType* identifierType = FindVariable(identifierExpression->name, identifierExpression->global); + if (identifierType != NULL) + { + identifierExpression->expressionType = *identifierType; + } + else + { + if (GetIsFunction(identifierExpression->name)) + { + // Functions are always global scope. + identifierExpression->global = true; + } + else + { + undeclaredIdentifier = true; + } + } + + if (undeclaredIdentifier) + { + if (m_allowUndeclaredIdentifiers) + { + HLSLLiteralExpression* literalExpression = m_tree->AddNode(fileName, line); + literalExpression->bValue = false; + literalExpression->type = HLSLBaseType_Bool; + literalExpression->expressionType.baseType = literalExpression->type; + literalExpression->expressionType.flags = HLSLTypeFlag_Const; + expression = literalExpression; + } + else + { + m_tokenizer.Error("Undeclared identifier '%s'", identifierExpression->name); + return false; + } + } + else { + expression = identifierExpression; + } + } + } + + bool done = false; + while (!done) + { + done = true; + + // Post fix unary operator + HLSLUnaryOp unaryOp; + while (AcceptUnaryOperator(false, unaryOp)) + { + HLSLUnaryExpression* unaryExpression = m_tree->AddNode(fileName, line); + unaryExpression->unaryOp = unaryOp; + unaryExpression->expression = expression; + unaryExpression->expressionType = unaryExpression->expression->expressionType; + expression = unaryExpression; + done = false; + } + + // Member access operator. + while (Accept('.')) + { + HLSLMemberAccess* memberAccess = m_tree->AddNode(fileName, line); + memberAccess->object = expression; + if (!ExpectIdentifier(memberAccess->field)) + { + return false; + } + if (!GetMemberType( expression->expressionType, memberAccess)) + { + m_tokenizer.Error("Couldn't access '%s'", memberAccess->field); + return false; + } + expression = memberAccess; + done = false; + } + + // Handle array access. + while (Accept('[')) + { + HLSLArrayAccess* arrayAccess = m_tree->AddNode(fileName, line); + arrayAccess->array = expression; + if (!ParseExpression(arrayAccess->index) || !Expect(']')) + { + return false; + } + + if (expression->expressionType.array) + { + arrayAccess->expressionType = expression->expressionType; + arrayAccess->expressionType.array = false; + arrayAccess->expressionType.arraySize = NULL; + } + else + { + switch (expression->expressionType.baseType) + { + case HLSLBaseType_Float2: + case HLSLBaseType_Float3: + case HLSLBaseType_Float4: + arrayAccess->expressionType.baseType = HLSLBaseType_Float; + break; + case HLSLBaseType_Float2x2: + arrayAccess->expressionType.baseType = HLSLBaseType_Float2; + break; + case HLSLBaseType_Float3x3: + arrayAccess->expressionType.baseType = HLSLBaseType_Float3; + break; + case HLSLBaseType_Float4x4: + arrayAccess->expressionType.baseType = HLSLBaseType_Float4; + break; + case HLSLBaseType_Float4x3: + arrayAccess->expressionType.baseType = HLSLBaseType_Float3; + break; + case HLSLBaseType_Float4x2: + arrayAccess->expressionType.baseType = HLSLBaseType_Float2; + break; + case HLSLBaseType_Half2: + case HLSLBaseType_Half3: + case HLSLBaseType_Half4: + arrayAccess->expressionType.baseType = HLSLBaseType_Half; + break; + case HLSLBaseType_Half2x2: + arrayAccess->expressionType.baseType = HLSLBaseType_Half2; + break; + case HLSLBaseType_Half3x3: + arrayAccess->expressionType.baseType = HLSLBaseType_Half3; + break; + case HLSLBaseType_Half4x4: + arrayAccess->expressionType.baseType = HLSLBaseType_Half4; + break; + case HLSLBaseType_Half4x3: + arrayAccess->expressionType.baseType = HLSLBaseType_Half3; + break; + case HLSLBaseType_Half4x2: + arrayAccess->expressionType.baseType = HLSLBaseType_Half2; + break; + case HLSLBaseType_Int2: + case HLSLBaseType_Int3: + case HLSLBaseType_Int4: + arrayAccess->expressionType.baseType = HLSLBaseType_Int; + break; + case HLSLBaseType_Uint2: + case HLSLBaseType_Uint3: + case HLSLBaseType_Uint4: + arrayAccess->expressionType.baseType = HLSLBaseType_Uint; + break; + default: + m_tokenizer.Error("array, matrix, vector, or indexable object type expected in index expression"); + return false; + } + } + + expression = arrayAccess; + done = false; + } + + // Handle function calls. Note, HLSL functions aren't like C function + // pointers -- we can only directly call on an identifier, not on an + // expression. + if (Accept('(')) + { + HLSLFunctionCall* functionCall = m_tree->AddNode(fileName, line); + done = false; + if (!ParseExpressionList(')', false, functionCall->argument, functionCall->numArguments)) + { + return false; + } + + if (expression->nodeType != HLSLNodeType_IdentifierExpression) + { + m_tokenizer.Error("Expected function identifier"); + return false; + } + + const HLSLIdentifierExpression* identifierExpression = static_cast(expression); + const HLSLFunction* function = MatchFunctionCall( functionCall, identifierExpression->name ); + if (function == NULL) + { + return false; + } + + functionCall->function = function; + functionCall->expressionType = function->returnType; + expression = functionCall; + } + + } + return true; + +} + +bool HLSLParser::ParseExpressionList(int endToken, bool allowEmptyEnd, HLSLExpression*& firstExpression, int& numExpressions) +{ + numExpressions = 0; + HLSLExpression* lastExpression = NULL; + while (!Accept(endToken)) + { + if (CheckForUnexpectedEndOfStream(endToken)) + { + return false; + } + if (numExpressions > 0 && !Expect(',')) + { + return false; + } + // It is acceptable for the final element in the initialization list to + // have a trailing comma in some cases, like array initialization such as {1, 2, 3,} + if (allowEmptyEnd && Accept(endToken)) + { + break; + } + HLSLExpression* expression = NULL; + if (!ParseExpression(expression)) + { + return false; + } + if (firstExpression == NULL) + { + firstExpression = expression; + } + else + { + lastExpression->nextExpression = expression; + } + lastExpression = expression; + ++numExpressions; + } + return true; +} + +bool HLSLParser::ParseArgumentList(HLSLArgument*& firstArgument, int& numArguments, int& numOutputArguments) +{ + const char* fileName = GetFileName(); + int line = GetLineNumber(); + + HLSLArgument* lastArgument = NULL; + numArguments = 0; + + while (!Accept(')')) + { + if (CheckForUnexpectedEndOfStream(')')) + { + return false; + } + if (numArguments > 0 && !Expect(',')) + { + return false; + } + + HLSLArgument* argument = m_tree->AddNode(fileName, line); + + if (Accept(HLSLToken_Uniform)) { argument->modifier = HLSLArgumentModifier_Uniform; } + else if (Accept(HLSLToken_In)) { argument->modifier = HLSLArgumentModifier_In; } + else if (Accept(HLSLToken_Out)) { argument->modifier = HLSLArgumentModifier_Out; } + else if (Accept(HLSLToken_InOut)) { argument->modifier = HLSLArgumentModifier_Inout; } + else if (Accept(HLSLToken_Const)) { argument->modifier = HLSLArgumentModifier_Const; } + + if (!ExpectDeclaration(/*allowUnsizedArray=*/true, argument->type, argument->name)) + { + return false; + } + + DeclareVariable( argument->name, argument->type ); + + // Optional semantic. + if (Accept(':') && !ExpectIdentifier(argument->semantic)) + { + return false; + } + + if (Accept('=') && !ParseExpression(argument->defaultValue)) + { + // @@ Print error! + return false; + } + + if (lastArgument != NULL) + { + lastArgument->nextArgument = argument; + } + else + { + firstArgument = argument; + } + lastArgument = argument; + + ++numArguments; + if (argument->modifier == HLSLArgumentModifier_Out || argument->modifier == HLSLArgumentModifier_Inout) + { + ++numOutputArguments; + } + } + return true; +} + + +bool HLSLParser::ParseSamplerState(HLSLExpression*& expression) +{ + if (!Expect(HLSLToken_SamplerState)) + { + return false; + } + + const char* fileName = GetFileName(); + int line = GetLineNumber(); + + HLSLSamplerState* samplerState = m_tree->AddNode(fileName, line); + + if (!Expect('{')) + { + return false; + } + + HLSLStateAssignment* lastStateAssignment = NULL; + + // Parse state assignments. + while (!Accept('}')) + { + if (CheckForUnexpectedEndOfStream('}')) + { + return false; + } + + HLSLStateAssignment* stateAssignment = NULL; + if (!ParseStateAssignment(stateAssignment, /*isSamplerState=*/true, /*isPipeline=*/false)) + { + return false; + } + ASSERT(stateAssignment != NULL); + if (lastStateAssignment == NULL) + { + samplerState->stateAssignments = stateAssignment; + } + else + { + lastStateAssignment->nextStateAssignment = stateAssignment; + } + lastStateAssignment = stateAssignment; + samplerState->numStateAssignments++; + } + + expression = samplerState; + return true; +} + +bool HLSLParser::ParseTechnique(HLSLStatement*& statement) +{ + if (!Accept(HLSLToken_Technique)) { + return false; + } + + const char* techniqueName = NULL; + if (!ExpectIdentifier(techniqueName)) + { + return false; + } + + if (!Expect('{')) + { + return false; + } + + HLSLTechnique* technique = m_tree->AddNode(GetFileName(), GetLineNumber()); + technique->name = techniqueName; + + //m_techniques.PushBack(technique); + + HLSLPass* lastPass = NULL; + + // Parse state assignments. + while (!Accept('}')) + { + if (CheckForUnexpectedEndOfStream('}')) + { + return false; + } + + HLSLPass* pass = NULL; + if (!ParsePass(pass)) + { + return false; + } + ASSERT(pass != NULL); + if (lastPass == NULL) + { + technique->passes = pass; + } + else + { + lastPass->nextPass = pass; + } + lastPass = pass; + technique->numPasses++; + } + + statement = technique; + return true; +} + +bool HLSLParser::ParsePass(HLSLPass*& pass) +{ + if (!Accept(HLSLToken_Pass)) { + return false; + } + + // Optional pass name. + const char* passName = NULL; + AcceptIdentifier(passName); + + if (!Expect('{')) + { + return false; + } + + const char* fileName = GetFileName(); + int line = GetLineNumber(); + + pass = m_tree->AddNode(fileName, line); + pass->name = passName; + + HLSLStateAssignment* lastStateAssignment = NULL; + + // Parse state assignments. + while (!Accept('}')) + { + if (CheckForUnexpectedEndOfStream('}')) + { + return false; + } + + HLSLStateAssignment* stateAssignment = NULL; + if (!ParseStateAssignment(stateAssignment, /*isSamplerState=*/false, /*isPipelineState=*/false)) + { + return false; + } + ASSERT(stateAssignment != NULL); + if (lastStateAssignment == NULL) + { + pass->stateAssignments = stateAssignment; + } + else + { + lastStateAssignment->nextStateAssignment = stateAssignment; + } + lastStateAssignment = stateAssignment; + pass->numStateAssignments++; + } + return true; +} + + +bool HLSLParser::ParsePipeline(HLSLStatement*& statement) +{ + if (!Accept("pipeline")) { + return false; + } + + // Optional pipeline name. + const char* pipelineName = NULL; + AcceptIdentifier(pipelineName); + + if (!Expect('{')) + { + return false; + } + + HLSLPipeline* pipeline = m_tree->AddNode(GetFileName(), GetLineNumber()); + pipeline->name = pipelineName; + + HLSLStateAssignment* lastStateAssignment = NULL; + + // Parse state assignments. + while (!Accept('}')) + { + if (CheckForUnexpectedEndOfStream('}')) + { + return false; + } + + HLSLStateAssignment* stateAssignment = NULL; + if (!ParseStateAssignment(stateAssignment, /*isSamplerState=*/false, /*isPipeline=*/true)) + { + return false; + } + ASSERT(stateAssignment != NULL); + if (lastStateAssignment == NULL) + { + pipeline->stateAssignments = stateAssignment; + } + else + { + lastStateAssignment->nextStateAssignment = stateAssignment; + } + lastStateAssignment = stateAssignment; + pipeline->numStateAssignments++; + } + + statement = pipeline; + return true; +} + + +const EffectState* GetEffectState(const char* name, bool isSamplerState, bool isPipeline) +{ + const EffectState* validStates = effectStates; + int count = sizeof(effectStates)/sizeof(effectStates[0]); + + if (isPipeline) + { + validStates = pipelineStates; + count = sizeof(pipelineStates) / sizeof(pipelineStates[0]); + } + + if (isSamplerState) + { + validStates = samplerStates; + count = sizeof(samplerStates)/sizeof(samplerStates[0]); + } + + // Case insensitive comparison. + for (int i = 0; i < count; i++) + { + if (String_EqualNoCase(name, validStates[i].name)) + { + return &validStates[i]; + } + } + + return NULL; +} + +static const EffectStateValue* GetStateValue(const char* name, const EffectState* state) +{ + // Case insensitive comparison. + for (int i = 0; ; i++) + { + const EffectStateValue & value = state->values[i]; + if (value.name == NULL) break; + + if (String_EqualNoCase(name, value.name)) + { + return &value; + } + } + + return NULL; +} + + +bool HLSLParser::ParseStateName(bool isSamplerState, bool isPipelineState, const char*& name, const EffectState *& state) +{ + if (m_tokenizer.GetToken() != HLSLToken_Identifier) + { + char near[HLSLTokenizer::s_maxIdentifier]; + m_tokenizer.GetTokenName(near); + m_tokenizer.Error("Syntax error: expected identifier near '%s'", near); + return false; + } + + state = GetEffectState(m_tokenizer.GetIdentifier(), isSamplerState, isPipelineState); + if (state == NULL) + { + m_tokenizer.Error("Syntax error: unexpected identifier '%s'", m_tokenizer.GetIdentifier()); + return false; + } + + m_tokenizer.Next(); + return true; +} + +bool HLSLParser::ParseColorMask(int& mask) +{ + mask = 0; + + do { + if (m_tokenizer.GetToken() == HLSLToken_IntLiteral) { + mask |= m_tokenizer.GetInt(); + } + else if (m_tokenizer.GetToken() == HLSLToken_Identifier) { + const char * ident = m_tokenizer.GetIdentifier(); + const EffectStateValue * stateValue = colorMaskValues; + while (stateValue->name != NULL) { + if (String_EqualNoCase(stateValue->name, ident)) { + mask |= stateValue->value; + break; + } + ++stateValue; + } + } + else { + return false; + } + m_tokenizer.Next(); + } while (Accept('|')); + + return true; +} + +bool HLSLParser::ParseStateValue(const EffectState * state, HLSLStateAssignment* stateAssignment) +{ + const bool expectsExpression = state->values == colorMaskValues; + const bool expectsInteger = state->values == integerValues; + const bool expectsFloat = state->values == floatValues; + const bool expectsBoolean = state->values == booleanValues; + + if (!expectsExpression && !expectsInteger && !expectsFloat && !expectsBoolean) + { + if (m_tokenizer.GetToken() != HLSLToken_Identifier) + { + char near[HLSLTokenizer::s_maxIdentifier]; + m_tokenizer.GetTokenName(near); + m_tokenizer.Error("Syntax error: expected identifier near '%s'", near); + stateAssignment->iValue = 0; + return false; + } + } + + if (state->values == NULL) + { + if (strcmp(m_tokenizer.GetIdentifier(), "compile") != 0) + { + m_tokenizer.Error("Syntax error: unexpected identifier '%s' expected compile statement", m_tokenizer.GetIdentifier()); + stateAssignment->iValue = 0; + return false; + } + + // @@ Parse profile name, function name, argument expressions. + + // Skip the rest of the compile statement. + while(m_tokenizer.GetToken() != ';') + { + m_tokenizer.Next(); + } + } + else { + if (expectsInteger) + { + if (!AcceptInt(stateAssignment->iValue)) + { + m_tokenizer.Error("Syntax error: expected integer near '%s'", m_tokenizer.GetIdentifier()); + stateAssignment->iValue = 0; + return false; + } + } + else if (expectsFloat) + { + if (!AcceptFloat(stateAssignment->fValue)) + { + m_tokenizer.Error("Syntax error: expected float near '%s'", m_tokenizer.GetIdentifier()); + stateAssignment->iValue = 0; + return false; + } + } + else if (expectsBoolean) + { + const EffectStateValue * stateValue = GetStateValue(m_tokenizer.GetIdentifier(), state); + + if (stateValue != NULL) + { + stateAssignment->iValue = stateValue->value; + + m_tokenizer.Next(); + } + else if (AcceptInt(stateAssignment->iValue)) + { + stateAssignment->iValue = (stateAssignment->iValue != 0); + } + else { + m_tokenizer.Error("Syntax error: expected bool near '%s'", m_tokenizer.GetIdentifier()); + stateAssignment->iValue = 0; + return false; + } + } + else if (expectsExpression) + { + if (!ParseColorMask(stateAssignment->iValue)) + { + m_tokenizer.Error("Syntax error: expected color mask near '%s'", m_tokenizer.GetIdentifier()); + stateAssignment->iValue = 0; + return false; + } + } + else + { + // Expect one of the allowed values. + const EffectStateValue * stateValue = GetStateValue(m_tokenizer.GetIdentifier(), state); + + if (stateValue == NULL) + { + m_tokenizer.Error("Syntax error: unexpected value '%s' for state '%s'", m_tokenizer.GetIdentifier(), state->name); + stateAssignment->iValue = 0; + return false; + } + + stateAssignment->iValue = stateValue->value; + + m_tokenizer.Next(); + } + } + + return true; +} + +bool HLSLParser::ParseStateAssignment(HLSLStateAssignment*& stateAssignment, bool isSamplerState, bool isPipelineState) +{ + const char* fileName = GetFileName(); + int line = GetLineNumber(); + + stateAssignment = m_tree->AddNode(fileName, line); + + const EffectState * state; + if (!ParseStateName(isSamplerState, isPipelineState, stateAssignment->stateName, state)) { + return false; + } + + //stateAssignment->name = m_tree->AddString(m_tokenizer.GetIdentifier()); + stateAssignment->stateName = state->name; + stateAssignment->d3dRenderState = state->d3drs; + + if (!Expect('=')) { + return false; + } + + if (!ParseStateValue(state, stateAssignment)) { + return false; + } + + if (!Expect(';')) { + return false; + } + + return true; +} + + +bool HLSLParser::ParseAttributeList(HLSLAttribute*& firstAttribute) +{ + const char* fileName = GetFileName(); + int line = GetLineNumber(); + + HLSLAttribute * lastAttribute = firstAttribute; + do { + const char * identifier = NULL; + if (!ExpectIdentifier(identifier)) { + return false; + } + + HLSLAttribute * attribute = m_tree->AddNode(fileName, line); + + if (strcmp(identifier, "unroll") == 0) attribute->attributeType = HLSLAttributeType_Unroll; + else if (strcmp(identifier, "flatten") == 0) attribute->attributeType = HLSLAttributeType_Flatten; + else if (strcmp(identifier, "branch") == 0) attribute->attributeType = HLSLAttributeType_Branch; + else if (strcmp(identifier, "nofastmath") == 0) attribute->attributeType = HLSLAttributeType_NoFastMath; + + // @@ parse arguments, () not required if attribute constructor has no arguments. + + if (firstAttribute == NULL) + { + firstAttribute = attribute; + } + else + { + lastAttribute->nextAttribute = attribute; + } + lastAttribute = attribute; + + } while(Accept(',')); + + return true; +} + +// Attributes can have all these forms: +// [A] statement; +// [A,B] statement; +// [A][B] statement; +// These are not supported yet: +// [A] statement [B]; +// [A()] statement; +// [A(a)] statement; +bool HLSLParser::ParseAttributeBlock(HLSLAttribute*& attribute) +{ + HLSLAttribute ** lastAttribute = &attribute; + while (*lastAttribute != NULL) { lastAttribute = &(*lastAttribute)->nextAttribute; } + + if (!Accept('[')) + { + return false; + } + + // Parse list of attribute constructors. + ParseAttributeList(*lastAttribute); + + if (!Expect(']')) + { + return false; + } + + // Parse additional [] blocks. + ParseAttributeBlock(*lastAttribute); + + return true; +} + +bool HLSLParser::ParseStage(HLSLStatement*& statement) +{ + if (!Accept("stage")) + { + return false; + } + + // Required stage name. + const char* stageName = NULL; + if (!ExpectIdentifier(stageName)) + { + return false; + } + + if (!Expect('{')) + { + return false; + } + + HLSLStage* stage = m_tree->AddNode(GetFileName(), GetLineNumber()); + stage->name = stageName; + + BeginScope(); + + HLSLType voidType(HLSLBaseType_Void); + if (!Expect('{') || !ParseBlock(stage->statement, voidType)) + { + return false; + } + + EndScope(); + + // @@ To finish the stage definition we should traverse the statements recursively (including function calls) and find all the input/output declarations. + + statement = stage; + return true; +} + + + + +bool HLSLParser::Parse(HLSLTree* tree) +{ + m_tree = tree; + + HLSLRoot* root = m_tree->GetRoot(); + HLSLStatement* lastStatement = NULL; + + while (!Accept(HLSLToken_EndOfStream)) + { + HLSLStatement* statement = NULL; + if (!ParseTopLevel(statement)) + { + return false; + } + if (statement != NULL) + { + if (lastStatement == NULL) + { + root->statement = statement; + } + else + { + lastStatement->nextStatement = statement; + } + lastStatement = statement; + while (lastStatement->nextStatement) lastStatement = lastStatement->nextStatement; + } + } + return true; +} + +bool HLSLParser::AcceptTypeModifier(int& flags) +{ + if (Accept(HLSLToken_Const)) + { + flags |= HLSLTypeFlag_Const; + return true; + } + else if (Accept(HLSLToken_Static)) + { + flags |= HLSLTypeFlag_Static; + return true; + } + else if (Accept(HLSLToken_Uniform)) + { + //flags |= HLSLTypeFlag_Uniform; // @@ Ignored. + return true; + } + else if (Accept(HLSLToken_Inline)) + { + //flags |= HLSLTypeFlag_Uniform; // @@ Ignored. In HLSL all functions are inline. + return true; + } + /*else if (Accept("in")) + { + flags |= HLSLTypeFlag_Input; + return true; + } + else if (Accept("out")) + { + flags |= HLSLTypeFlag_Output; + return true; + }*/ + + // Not an usage keyword. + return false; +} + +bool HLSLParser::AcceptInterpolationModifier(int& flags) +{ + if (Accept("linear")) + { + flags |= HLSLTypeFlag_Linear; + return true; + } + else if (Accept("centroid")) + { + flags |= HLSLTypeFlag_Centroid; + return true; + } + else if (Accept("nointerpolation")) + { + flags |= HLSLTypeFlag_NoInterpolation; + return true; + } + else if (Accept("noperspective")) + { + flags |= HLSLTypeFlag_NoPerspective; + return true; + } + else if (Accept("sample")) + { + flags |= HLSLTypeFlag_Sample; + return true; + } + + return false; +} + + +bool HLSLParser::AcceptType(bool allowVoid, HLSLType& type/*, bool acceptFlags*/) +{ + //if (type.flags != NULL) + { + type.flags = 0; + while(AcceptTypeModifier(type.flags) || AcceptInterpolationModifier(type.flags)) {} + } + + int token = m_tokenizer.GetToken(); + + // Check built-in types. + type.baseType = HLSLBaseType_Void; + switch (token) + { + case HLSLToken_Float: + type.baseType = HLSLBaseType_Float; + break; + case HLSLToken_Float2: + type.baseType = HLSLBaseType_Float2; + break; + case HLSLToken_Float3: + type.baseType = HLSLBaseType_Float3; + break; + case HLSLToken_Float4: + type.baseType = HLSLBaseType_Float4; + break; + case HLSLToken_Float2x2: + type.baseType = HLSLBaseType_Float2x2; + break; + case HLSLToken_Float3x3: + type.baseType = HLSLBaseType_Float3x3; + break; + case HLSLToken_Float4x4: + type.baseType = HLSLBaseType_Float4x4; + break; + case HLSLToken_Float4x3: + type.baseType = HLSLBaseType_Float4x3; + break; + case HLSLToken_Float4x2: + type.baseType = HLSLBaseType_Float4x2; + break; + case HLSLToken_Half: + type.baseType = HLSLBaseType_Half; + break; + case HLSLToken_Half2: + type.baseType = HLSLBaseType_Half2; + break; + case HLSLToken_Half3: + type.baseType = HLSLBaseType_Half3; + break; + case HLSLToken_Half4: + type.baseType = HLSLBaseType_Half4; + break; + case HLSLToken_Half2x2: + type.baseType = HLSLBaseType_Half2x2; + break; + case HLSLToken_Half3x3: + type.baseType = HLSLBaseType_Half3x3; + break; + case HLSLToken_Half4x4: + type.baseType = HLSLBaseType_Half4x4; + break; + case HLSLToken_Half4x3: + type.baseType = HLSLBaseType_Half4x3; + break; + case HLSLToken_Half4x2: + type.baseType = HLSLBaseType_Half4x2; + break; + case HLSLToken_Bool: + type.baseType = HLSLBaseType_Bool; + break; + case HLSLToken_Bool2: + type.baseType = HLSLBaseType_Bool2; + break; + case HLSLToken_Bool3: + type.baseType = HLSLBaseType_Bool3; + break; + case HLSLToken_Bool4: + type.baseType = HLSLBaseType_Bool4; + break; + case HLSLToken_Int: + type.baseType = HLSLBaseType_Int; + break; + case HLSLToken_Int2: + type.baseType = HLSLBaseType_Int2; + break; + case HLSLToken_Int3: + type.baseType = HLSLBaseType_Int3; + break; + case HLSLToken_Int4: + type.baseType = HLSLBaseType_Int4; + break; + case HLSLToken_Uint: + type.baseType = HLSLBaseType_Uint; + break; + case HLSLToken_Uint2: + type.baseType = HLSLBaseType_Uint2; + break; + case HLSLToken_Uint3: + type.baseType = HLSLBaseType_Uint3; + break; + case HLSLToken_Uint4: + type.baseType = HLSLBaseType_Uint4; + break; + case HLSLToken_Texture: + type.baseType = HLSLBaseType_Texture; + break; + case HLSLToken_Sampler: + type.baseType = HLSLBaseType_Sampler2D; // @@ IC: For now we assume that generic samplers are always sampler2D + break; + case HLSLToken_Sampler2D: + type.baseType = HLSLBaseType_Sampler2D; + break; + case HLSLToken_Sampler3D: + type.baseType = HLSLBaseType_Sampler3D; + break; + case HLSLToken_SamplerCube: + type.baseType = HLSLBaseType_SamplerCube; + break; + case HLSLToken_Sampler2DShadow: + type.baseType = HLSLBaseType_Sampler2DShadow; + break; + case HLSLToken_Sampler2DMS: + type.baseType = HLSLBaseType_Sampler2DMS; + break; + case HLSLToken_Sampler2DArray: + type.baseType = HLSLBaseType_Sampler2DArray; + break; + } + if (type.baseType != HLSLBaseType_Void) + { + m_tokenizer.Next(); + + if (IsSamplerType(type.baseType)) + { + // Parse optional sampler type. + if (Accept('<')) + { + int token = m_tokenizer.GetToken(); + if (token == HLSLToken_Float) + { + type.samplerType = HLSLBaseType_Float; + } + else if (token == HLSLToken_Half) + { + type.samplerType = HLSLBaseType_Half; + } + else + { + m_tokenizer.Error("Expected half or float."); + return false; + } + m_tokenizer.Next(); + + if (!Expect('>')) + { + return false; + } + } + } + return true; + } + + if (allowVoid && Accept(HLSLToken_Void)) + { + type.baseType = HLSLBaseType_Void; + return true; + } + if (token == HLSLToken_Identifier) + { + const char* identifier = m_tree->AddString( m_tokenizer.GetIdentifier() ); + if (FindUserDefinedType(identifier) != NULL) + { + m_tokenizer.Next(); + type.baseType = HLSLBaseType_UserDefined; + type.typeName = identifier; + return true; + } + } + return false; +} + +bool HLSLParser::ExpectType(bool allowVoid, HLSLType& type) +{ + if (!AcceptType(allowVoid, type)) + { + m_tokenizer.Error("Expected type"); + return false; + } + return true; +} + +bool HLSLParser::AcceptDeclaration(bool allowUnsizedArray, HLSLType& type, const char*& name) +{ + if (!AcceptType(/*allowVoid=*/false, type)) + { + return false; + } + + if (!ExpectIdentifier(name)) + { + // TODO: false means we didn't accept a declaration and we had an error! + return false; + } + // Handle array syntax. + if (Accept('[')) + { + type.array = true; + // Optionally allow no size to the specified for the array. + if (Accept(']') && allowUnsizedArray) + { + return true; + } + if (!ParseExpression(type.arraySize) || !Expect(']')) + { + return false; + } + } + return true; +} + +bool HLSLParser::ExpectDeclaration(bool allowUnsizedArray, HLSLType& type, const char*& name) +{ + if (!AcceptDeclaration(allowUnsizedArray, type, name)) + { + m_tokenizer.Error("Expected declaration"); + return false; + } + return true; +} + +const HLSLStruct* HLSLParser::FindUserDefinedType(const char* name) const +{ + // Pointer comparison is sufficient for strings since they exist in the + // string pool. + for (int i = 0; i < m_userTypes.GetSize(); ++i) + { + if (m_userTypes[i]->name == name) + { + return m_userTypes[i]; + } + } + return NULL; +} + +bool HLSLParser::CheckForUnexpectedEndOfStream(int endToken) +{ + if (Accept(HLSLToken_EndOfStream)) + { + char what[HLSLTokenizer::s_maxIdentifier]; + m_tokenizer.GetTokenName(endToken, what); + m_tokenizer.Error("Unexpected end of file while looking for '%s'", what); + return true; + } + return false; +} + +int HLSLParser::GetLineNumber() const +{ + return m_tokenizer.GetLineNumber(); +} + +const char* HLSLParser::GetFileName() +{ + return m_tree->AddString( m_tokenizer.GetFileName() ); +} + +void HLSLParser::BeginScope() +{ + // Use NULL as a sentinel that indices a new scope level. + Variable& variable = m_variables.PushBackNew(); + variable.name = NULL; +} + +void HLSLParser::EndScope() +{ + int numVariables = m_variables.GetSize() - 1; + while (m_variables[numVariables].name != NULL) + { + --numVariables; + ASSERT(numVariables >= 0); + } + m_variables.Resize(numVariables); +} + +const HLSLType* HLSLParser::FindVariable(const char* name, bool& global) const +{ + for (int i = m_variables.GetSize() - 1; i >= 0; --i) + { + if (m_variables[i].name == name) + { + global = (i < m_numGlobals); + return &m_variables[i].type; + } + } + return NULL; +} + +const HLSLFunction* HLSLParser::FindFunction(const char* name) const +{ + for (int i = 0; i < m_functions.GetSize(); ++i) + { + if (m_functions[i]->name == name) + { + return m_functions[i]; + } + } + return NULL; +} + +static bool AreTypesEqual(HLSLTree* tree, const HLSLType& lhs, const HLSLType& rhs) +{ + return GetTypeCastRank(tree, lhs, rhs) == 0; +} + +static bool AreArgumentListsEqual(HLSLTree* tree, HLSLArgument* lhs, HLSLArgument* rhs) +{ + while (lhs && rhs) + { + if (!AreTypesEqual(tree, lhs->type, rhs->type)) + return false; + + if (lhs->modifier != rhs->modifier) + return false; + + if (lhs->semantic != rhs->semantic || lhs->sv_semantic != rhs->sv_semantic) + return false; + + lhs = lhs->nextArgument; + rhs = rhs->nextArgument; + } + + return lhs == NULL && rhs == NULL; +} + +const HLSLFunction* HLSLParser::FindFunction(const HLSLFunction* fun) const +{ + for (int i = 0; i < m_functions.GetSize(); ++i) + { + if (m_functions[i]->name == fun->name && + AreTypesEqual(m_tree, m_functions[i]->returnType, fun->returnType) && + AreArgumentListsEqual(m_tree, m_functions[i]->argument, fun->argument)) + { + return m_functions[i]; + } + } + return NULL; +} + +void HLSLParser::DeclareVariable(const char* name, const HLSLType& type) +{ + if (m_variables.GetSize() == m_numGlobals) + { + ++m_numGlobals; + } + Variable& variable = m_variables.PushBackNew(); + variable.name = name; + variable.type = type; +} + +bool HLSLParser::GetIsFunction(const char* name) const +{ + for (int i = 0; i < m_functions.GetSize(); ++i) + { + // == is ok here because we're passed the strings through the string pool. + if (m_functions[i]->name == name) + { + return true; + } + } + for (int i = 0; i < _numIntrinsics; ++i) + { + // Intrinsic names are not in the string pool (since they are compile time + // constants, so we need full string compare). + if (String_Equal(name, _intrinsic[i].function.name)) + { + return true; + } + } + + return false; +} + +const HLSLFunction* HLSLParser::MatchFunctionCall(const HLSLFunctionCall* functionCall, const char* name) +{ + const HLSLFunction* matchedFunction = NULL; + + int numArguments = functionCall->numArguments; + int numMatchedOverloads = 0; + bool nameMatches = false; + + // Get the user defined functions with the specified name. + for (int i = 0; i < m_functions.GetSize(); ++i) + { + const HLSLFunction* function = m_functions[i]; + if (function->name == name) + { + nameMatches = true; + + CompareFunctionsResult result = CompareFunctions( m_tree, functionCall, function, matchedFunction ); + if (result == Function1Better) + { + matchedFunction = function; + numMatchedOverloads = 1; + } + else if (result == FunctionsEqual) + { + ++numMatchedOverloads; + } + } + } + + // Get the intrinsic functions with the specified name. + for (int i = 0; i < _numIntrinsics; ++i) + { + const HLSLFunction* function = &_intrinsic[i].function; + if (String_Equal(function->name, name)) + { + nameMatches = true; + + CompareFunctionsResult result = CompareFunctions( m_tree, functionCall, function, matchedFunction ); + if (result == Function1Better) + { + matchedFunction = function; + numMatchedOverloads = 1; + } + else if (result == FunctionsEqual) + { + ++numMatchedOverloads; + } + } + } + + if (matchedFunction != NULL && numMatchedOverloads > 1) + { + // Multiple overloads match. + m_tokenizer.Error("'%s' %d overloads have similar conversions", name, numMatchedOverloads); + return NULL; + } + else if (matchedFunction == NULL) + { + if (nameMatches) + { + m_tokenizer.Error("'%s' no overloaded function matched all of the arguments", name); + } + else + { + m_tokenizer.Error("Undeclared identifier '%s'", name); + } + } + + return matchedFunction; +} + +bool HLSLParser::GetMemberType(const HLSLType& objectType, HLSLMemberAccess * memberAccess) +{ + const char* fieldName = memberAccess->field; + + if (objectType.baseType == HLSLBaseType_UserDefined) + { + const HLSLStruct* structure = FindUserDefinedType( objectType.typeName ); + ASSERT(structure != NULL); + + const HLSLStructField* field = structure->field; + while (field != NULL) + { + if (field->name == fieldName) + { + memberAccess->expressionType = field->type; + return true; + } + field = field->nextField; + } + + return false; + } + + if (_baseTypeDescriptions[objectType.baseType].numericType == NumericType_NaN) + { + // Currently we don't have an non-numeric types that allow member access. + return false; + } + + int swizzleLength = 0; + + if (_baseTypeDescriptions[objectType.baseType].numDimensions <= 1) + { + // Check for a swizzle on the scalar/vector types. + for (int i = 0; fieldName[i] != 0; ++i) + { + if (fieldName[i] != 'x' && fieldName[i] != 'y' && fieldName[i] != 'z' && fieldName[i] != 'w' && + fieldName[i] != 'r' && fieldName[i] != 'g' && fieldName[i] != 'b' && fieldName[i] != 'a') + { + m_tokenizer.Error("Invalid swizzle '%s'", fieldName); + return false; + } + ++swizzleLength; + } + ASSERT(swizzleLength > 0); + } + else + { + + // Check for a matrix element access (e.g. _m00 or _11) + + const char* n = fieldName; + while (n[0] == '_') + { + ++n; + int base = 1; + if (n[0] == 'm') + { + base = 0; + ++n; + } + if (!isdigit(n[0]) || !isdigit(n[1])) + { + return false; + } + + int r = (n[0] - '0') - base; + int c = (n[1] - '0') - base; + if (r >= _baseTypeDescriptions[objectType.baseType].height || + c >= _baseTypeDescriptions[objectType.baseType].numComponents) + { + return false; + } + ++swizzleLength; + n += 2; + + } + + if (n[0] != 0) + { + return false; + } + + } + + if (swizzleLength > 4) + { + m_tokenizer.Error("Invalid swizzle '%s'", fieldName); + return false; + } + + static const HLSLBaseType floatType[] = { HLSLBaseType_Float, HLSLBaseType_Float2, HLSLBaseType_Float3, HLSLBaseType_Float4 }; + static const HLSLBaseType halfType[] = { HLSLBaseType_Half, HLSLBaseType_Half2, HLSLBaseType_Half3, HLSLBaseType_Half4 }; + static const HLSLBaseType intType[] = { HLSLBaseType_Int, HLSLBaseType_Int2, HLSLBaseType_Int3, HLSLBaseType_Int4 }; + static const HLSLBaseType uintType[] = { HLSLBaseType_Uint, HLSLBaseType_Uint2, HLSLBaseType_Uint3, HLSLBaseType_Uint4 }; + static const HLSLBaseType boolType[] = { HLSLBaseType_Bool, HLSLBaseType_Bool2, HLSLBaseType_Bool3, HLSLBaseType_Bool4 }; + + switch (_baseTypeDescriptions[objectType.baseType].numericType) + { + case NumericType_Float: + memberAccess->expressionType.baseType = floatType[swizzleLength - 1]; + break; + case NumericType_Half: + memberAccess->expressionType.baseType = halfType[swizzleLength - 1]; + break; + case NumericType_Int: + memberAccess->expressionType.baseType = intType[swizzleLength - 1]; + break; + case NumericType_Uint: + memberAccess->expressionType.baseType = uintType[swizzleLength - 1]; + break; + case NumericType_Bool: + memberAccess->expressionType.baseType = boolType[swizzleLength - 1]; + break; + default: + ASSERT(0); + } + + memberAccess->swizzle = true; + + return true; +} + +} diff --git a/src/libprojectM/Renderer/hlslparser/src/HLSLParser.h b/src/libprojectM/Renderer/hlslparser/src/HLSLParser.h new file mode 100755 index 000000000..f08791b41 --- /dev/null +++ b/src/libprojectM/Renderer/hlslparser/src/HLSLParser.h @@ -0,0 +1,145 @@ +//============================================================================= +// +// Render/HLSLParser.h +// +// Created by Max McGuire (max@unknownworlds.com) +// Copyright (c) 2013, Unknown Worlds Entertainment, Inc. +// +//============================================================================= + +#ifndef HLSL_PARSER_H +#define HLSL_PARSER_H + +//#include "Engine/StringPool.h" +//#include "Engine/Array.h" +#include "Engine.h" + +#include "HLSLTokenizer.h" +#include "HLSLTree.h" + +namespace M4 +{ + +struct EffectState; + +class HLSLParser +{ + +public: + + HLSLParser(Allocator* allocator, const char* fileName, const char* buffer, size_t length); + + bool Parse(HLSLTree* tree); + +private: + + bool Accept(int token); + bool Expect(int token); + + /** + * Special form of Accept for accepting a word that is not actually a token + * but will be treated like one. This is useful for HLSL keywords that are + * only tokens in specific contexts (like in/inout in parameter lists). + */ + bool Accept(const char* token); + bool Expect(const char* token); + + bool AcceptIdentifier(const char*& identifier); + bool ExpectIdentifier(const char*& identifier); + bool AcceptFloat(float& value); + bool AcceptHalf( float& value ); + bool AcceptInt(int& value); + bool AcceptType(bool allowVoid, HLSLType& type); + bool ExpectType(bool allowVoid, HLSLType& type); + bool AcceptBinaryOperator(int priority, HLSLBinaryOp& binaryOp); + bool AcceptUnaryOperator(bool pre, HLSLUnaryOp& unaryOp); + bool AcceptAssign(HLSLBinaryOp& binaryOp); + bool AcceptTypeModifier(int & typeFlags); + bool AcceptInterpolationModifier(int& flags); + + /** + * Handles a declaration like: "float2 name[5]". If allowUnsizedArray is true, it is + * is acceptable for the declaration to not specify the bounds of the array (i.e. name[]). + */ + bool AcceptDeclaration(bool allowUnsizedArray, HLSLType& type, const char*& name); + bool ExpectDeclaration(bool allowUnsizedArray, HLSLType& type, const char*& name); + + bool ParseTopLevel(HLSLStatement*& statement); + bool ParseBlock(HLSLStatement*& firstStatement, const HLSLType& returnType); + bool ParseStatementOrBlock(HLSLStatement*& firstStatement, const HLSLType& returnType, bool scoped = true); + bool ParseStatement(HLSLStatement*& statement, const HLSLType& returnType); + bool ParseDeclaration(HLSLDeclaration*& declaration); + bool ParseFieldDeclaration(HLSLStructField*& field); + //bool ParseBufferFieldDeclaration(HLSLBufferField*& field); + bool ParseExpression(HLSLExpression*& expression); + bool ParseBinaryExpression(int priority, HLSLExpression*& expression); + bool ParseTerminalExpression(HLSLExpression*& expression, bool& needsEndParen); + bool ParseExpressionList(int endToken, bool allowEmptyEnd, HLSLExpression*& firstExpression, int& numExpressions); + bool ParseArgumentList(HLSLArgument*& firstArgument, int& numArguments, int& numOutputArguments); + bool ParseDeclarationAssignment(HLSLDeclaration* declaration); + bool ParsePartialConstructor(HLSLExpression*& expression, HLSLBaseType type, const char* typeName); + + bool ParseStateName(bool isSamplerState, bool isPipelineState, const char*& name, const EffectState *& state); + bool ParseColorMask(int& mask); + bool ParseStateValue(const EffectState * state, HLSLStateAssignment* stateAssignment); + bool ParseStateAssignment(HLSLStateAssignment*& stateAssignment, bool isSamplerState, bool isPipelineState); + bool ParseSamplerState(HLSLExpression*& expression); + bool ParseTechnique(HLSLStatement*& statement); + bool ParsePass(HLSLPass*& pass); + bool ParsePipeline(HLSLStatement*& pipeline); + bool ParseStage(HLSLStatement*& stage); + + bool ParseAttributeList(HLSLAttribute*& attribute); + bool ParseAttributeBlock(HLSLAttribute*& attribute); + + bool CheckForUnexpectedEndOfStream(int endToken); + + const HLSLStruct* FindUserDefinedType(const char* name) const; + + void BeginScope(); + void EndScope(); + + void DeclareVariable(const char* name, const HLSLType& type); + + /** Returned pointer is only valid until Declare or Begin/EndScope is called. */ + const HLSLType* FindVariable(const char* name, bool& global) const; + + const HLSLFunction* FindFunction(const char* name) const; + const HLSLFunction* FindFunction(const HLSLFunction* fun) const; + + bool GetIsFunction(const char* name) const; + + /** Finds the overloaded function that matches the specified call. */ + const HLSLFunction* MatchFunctionCall(const HLSLFunctionCall* functionCall, const char* name); + + /** Gets the type of the named field on the specified object type (fieldName can also specify a swizzle. ) */ + bool GetMemberType(const HLSLType& objectType, HLSLMemberAccess * memberAccess); + + bool CheckTypeCast(const HLSLType& srcType, const HLSLType& dstType); + + const char* GetFileName(); + int GetLineNumber() const; + +private: + + struct Variable + { + const char* name; + HLSLType type; + }; + + HLSLTokenizer m_tokenizer; + Array m_userTypes; + Array m_variables; + Array m_functions; + int m_numGlobals; + + HLSLTree* m_tree; + + bool m_allowUndeclaredIdentifiers = false; + bool m_disableSemanticValidation = false; +}; + +} + +#endif diff --git a/src/libprojectM/Renderer/hlslparser/src/HLSLTokenizer.cpp b/src/libprojectM/Renderer/hlslparser/src/HLSLTokenizer.cpp new file mode 100755 index 000000000..826810d12 --- /dev/null +++ b/src/libprojectM/Renderer/hlslparser/src/HLSLTokenizer.cpp @@ -0,0 +1,635 @@ +//#include "Engine/Log.h" +//#include "Engine/String.h" +#include "Engine.h" + +#include "HLSLTokenizer.h" + +#include +#include +#include +#include +#include + +namespace M4 +{ + +// The order here must match the order in the Token enum. +static const char* _reservedWords[] = + { + "float", + "float2", + "float3", + "float4", + "float2x2", + "float3x3", + "float4x4", + "float4x3", + "float4x2", + "half", + "half2", + "half3", + "half4", + "half2x2", + "half3x3", + "half4x4", + "half4x3", + "half4x2", + "bool", + "bool2", + "bool3", + "bool4", + "int", + "int2", + "int3", + "int4", + "uint", + "uint2", + "uint3", + "uint4", + "texture", + "sampler", + "sampler2D", + "sampler3D", + "samplerCUBE", + "sampler2DShadow", + "sampler2DMS", + "sampler2DArray", + "if", + "else", + "for", + "while", + "break", + "true", + "false", + "void", + "struct", + "cbuffer", + "tbuffer", + "register", + "return", + "continue", + "discard", + "const", + "static", + "inline", + "uniform", + "in", + "out", + "inout", + "sampler_state", + "technique", + "pass", + }; + +static bool GetIsSymbol(char c) +{ + switch (c) + { + case ';': + case ':': + case '(': case ')': + case '[': case ']': + case '{': case '}': + case '-': case '+': + case '*': case '/': + case '?': + case '!': + case ',': + case '=': + case '.': + case '<': case '>': + case '|': case '&': case '^': case '~': + case '@': + return true; + } + return false; +} + +/** Returns true if the character is a valid token separator at the end of a number type token */ +static bool GetIsNumberSeparator(char c) +{ + return c == 0 || isspace(c) || GetIsSymbol(c); +} + +HLSLTokenizer::HLSLTokenizer(const char* fileName, const char* buffer, size_t length) +{ + m_buffer = buffer; + m_bufferEnd = buffer + length; + m_fileName = fileName; + m_lineNumber = 1; + m_tokenLineNumber = 1; + m_error = false; + Next(); +} + +void HLSLTokenizer::Next() +{ + + while( SkipWhitespace() || SkipComment() || ScanLineDirective() || SkipPragmaDirective() ) + { + } + + if (m_error) + { + m_token = HLSLToken_EndOfStream; + return; + } + + m_tokenLineNumber = m_lineNumber; + + if (m_buffer >= m_bufferEnd || *m_buffer == '\0') + { + m_token = HLSLToken_EndOfStream; + return; + } + + const char* start = m_buffer; + + // +=, -=, *=, /=, ==, <=, >= + if (m_buffer[0] == '+' && m_buffer[1] == '=') + { + m_token = HLSLToken_PlusEqual; + m_buffer += 2; + return; + } + else if (m_buffer[0] == '-' && m_buffer[1] == '=') + { + m_token = HLSLToken_MinusEqual; + m_buffer += 2; + return; + } + else if (m_buffer[0] == '*' && m_buffer[1] == '=') + { + m_token = HLSLToken_TimesEqual; + m_buffer += 2; + return; + } + else if (m_buffer[0] == '/' && m_buffer[1] == '=') + { + m_token = HLSLToken_DivideEqual; + m_buffer += 2; + return; + } + else if (m_buffer[0] == '=' && m_buffer[1] == '=') + { + m_token = HLSLToken_EqualEqual; + m_buffer += 2; + return; + } + else if (m_buffer[0] == '!' && m_buffer[1] == '=') + { + m_token = HLSLToken_NotEqual; + m_buffer += 2; + return; + } + else if (m_buffer[0] == '<' && m_buffer[1] == '=') + { + m_token = HLSLToken_LessEqual; + m_buffer += 2; + return; + } + else if (m_buffer[0] == '>' && m_buffer[1] == '=') + { + m_token = HLSLToken_GreaterEqual; + m_buffer += 2; + return; + } + else if (m_buffer[0] == '&' && m_buffer[1] == '&') + { + m_token = HLSLToken_AndAnd; + m_buffer += 2; + return; + } + else if (m_buffer[0] == '|' && m_buffer[1] == '|') + { + m_token = HLSLToken_BarBar; + m_buffer += 2; + return; + } + + // ++, -- + if ((m_buffer[0] == '-' || m_buffer[0] == '+') && (m_buffer[1] == m_buffer[0])) + { + m_token = (m_buffer[0] == '+') ? HLSLToken_PlusPlus : HLSLToken_MinusMinus; + m_buffer += 2; + return; + } + + // Check for the start of a number. + if (ScanNumber()) + { + return; + } + + if (GetIsSymbol(m_buffer[0])) + { + m_token = static_cast(m_buffer[0]); + ++m_buffer; + return; + } + + // Must be an identifier or a reserved word. + while (m_buffer < m_bufferEnd && m_buffer[0] != 0 && !GetIsSymbol(m_buffer[0]) && !isspace(m_buffer[0])) + { + ++m_buffer; + } + + size_t length = m_buffer - start; + memcpy(m_identifier, start, length); + m_identifier[length] = 0; + + const int numReservedWords = sizeof(_reservedWords) / sizeof(const char*); + for (int i = 0; i < numReservedWords; ++i) + { + if (strcmp(_reservedWords[i], m_identifier) == 0) + { + m_token = 256 + i; + return; + } + } + + m_token = HLSLToken_Identifier; + +} + +bool HLSLTokenizer::SkipWhitespace() +{ + bool result = false; + while (m_buffer < m_bufferEnd && isspace(m_buffer[0])) + { + result = true; + if (m_buffer[0] == '\n') + { + ++m_lineNumber; + } + ++m_buffer; + } + return result; +} + +bool HLSLTokenizer::SkipComment() +{ + bool result = false; + if (m_buffer[0] == '/') + { + if (m_buffer[1] == '/') + { + // Single line comment. + result = true; + m_buffer += 2; + while (m_buffer < m_bufferEnd) + { + if (*(m_buffer++) == '\n') + { + ++m_lineNumber; + break; + } + } + } + else if (m_buffer[1] == '*') + { + // Multi-line comment. + result = true; + m_buffer += 2; + while (m_buffer < m_bufferEnd) + { + if (m_buffer[0] == '\n') + { + ++m_lineNumber; + } + if (m_buffer[0] == '*' && m_buffer[1] == '/') + { + break; + } + ++m_buffer; + } + if (m_buffer < m_bufferEnd) + { + m_buffer += 2; + } + } + } + return result; +} + +bool HLSLTokenizer::SkipPragmaDirective() +{ + bool result = false; + if( m_bufferEnd - m_buffer > 7 && *m_buffer == '#' ) + { + const char* ptr = m_buffer + 1; + while( isspace( *ptr ) ) + ptr++; + + if( strncmp( ptr, "pragma", 6 ) == 0 && isspace( ptr[ 6 ] ) ) + { + m_buffer = ptr + 6; + result = true; + while( m_buffer < m_bufferEnd ) + { + if( *( m_buffer++ ) == '\n' ) + { + ++m_lineNumber; + break; + } + } + } + } + return result; +} + +bool HLSLTokenizer::ScanNumber() +{ + + // Don't treat the + or - as part of the number. + if (m_buffer[0] == '+' || m_buffer[0] == '-') + { + return false; + } + + // Parse hex literals. + if (m_bufferEnd - m_buffer > 2 && m_buffer[0] == '0' && m_buffer[1] == 'x') + { + char* hEnd = NULL; + int iValue = strtol(m_buffer+2, &hEnd, 16); + if (GetIsNumberSeparator(hEnd[0])) + { + m_buffer = hEnd; + m_token = HLSLToken_IntLiteral; + m_iValue = iValue; + return true; + } + } + + char* fEnd = NULL; + double fValue = String_ToDouble(m_buffer, &fEnd); + + if (fEnd == m_buffer) + { + return false; + } + + char* iEnd = NULL; + int iValue = String_ToInteger(m_buffer, &iEnd); + + // If the character after the number is an f then the f is treated as part + // of the number (to handle 1.0f syntax). + if( ( fEnd[ 0 ] == 'f' || fEnd[ 0 ] == 'h' ) && fEnd < m_bufferEnd ) + { + ++fEnd; + } + + if( fEnd > iEnd && GetIsNumberSeparator( fEnd[ 0 ] ) ) + { + m_buffer = fEnd; + m_token = fEnd[ 0 ] == 'f' ? HLSLToken_FloatLiteral : HLSLToken_HalfLiteral; + m_fValue = static_cast(fValue); + return true; + } + else if (iEnd > m_buffer && GetIsNumberSeparator(iEnd[0])) + { + m_buffer = iEnd; + m_token = HLSLToken_IntLiteral; + m_iValue = iValue; + return true; + } + + return false; +} + +bool HLSLTokenizer::ScanLineDirective() +{ + + if (m_bufferEnd - m_buffer > 5 && strncmp(m_buffer, "#line", 5) == 0 && isspace(m_buffer[5])) + { + + m_buffer += 5; + + while (m_buffer < m_bufferEnd && isspace(m_buffer[0])) + { + if (m_buffer[0] == '\n') + { + Error("Syntax error: expected line number after #line"); + return false; + } + ++m_buffer; + } + + char* iEnd = NULL; + int lineNumber = String_ToInteger(m_buffer, &iEnd); + + if (!isspace(*iEnd)) + { + Error("Syntax error: expected line number after #line"); + return false; + } + + m_buffer = iEnd; + while (m_buffer < m_bufferEnd && isspace(m_buffer[0])) + { + char c = m_buffer[0]; + ++m_buffer; + if (c == '\n') + { + m_lineNumber = lineNumber; + return true; + } + } + + if (m_buffer >= m_bufferEnd) + { + m_lineNumber = lineNumber; + return true; + } + + if (m_buffer[0] != '"') + { + Error("Syntax error: expected '\"' after line number near #line"); + return false; + } + + ++m_buffer; + + int i = 0; + while (i + 1 < s_maxIdentifier && m_buffer < m_bufferEnd && m_buffer[0] != '"') + { + if (m_buffer[0] == '\n') + { + Error("Syntax error: expected '\"' before end of line near #line"); + return false; + } + + m_lineDirectiveFileName[i] = *m_buffer; + ++m_buffer; + ++i; + } + + m_lineDirectiveFileName[i] = 0; + + if (m_buffer >= m_bufferEnd) + { + Error("Syntax error: expected '\"' before end of file near #line"); + return false; + } + + if (i + 1 >= s_maxIdentifier) + { + Error("Syntax error: file name too long near #line"); + return false; + } + + // Skip the closing quote + ++m_buffer; + + while (m_buffer < m_bufferEnd && m_buffer[0] != '\n') + { + if (!isspace(m_buffer[0])) + { + Error("Syntax error: unexpected input after file name near #line"); + return false; + } + ++m_buffer; + } + + // Skip new line + ++m_buffer; + + m_lineNumber = lineNumber; + m_fileName = m_lineDirectiveFileName; + + return true; + + } + + return false; + +} + +int HLSLTokenizer::GetToken() const +{ + return m_token; +} + +float HLSLTokenizer::GetFloat() const +{ + return m_fValue; +} + +int HLSLTokenizer::GetInt() const +{ + return m_iValue; +} + +const char* HLSLTokenizer::GetIdentifier() const +{ + return m_identifier; +} + +int HLSLTokenizer::GetLineNumber() const +{ + return m_tokenLineNumber; +} + +const char* HLSLTokenizer::GetFileName() const +{ + return m_fileName; +} + +void HLSLTokenizer::Error(const char* format, ...) +{ + // It's not always convenient to stop executing when an error occurs, + // so just track once we've hit an error and stop reporting them until + // we successfully bail out of execution. + if (m_error) + { + return; + } + m_error = true; + + + char buffer[1024]; + va_list args; + va_start(args, format); + int result = vsnprintf(buffer, sizeof(buffer) - 1, format, args); + va_end(args); + + Log_Error("%s(%d) : %s\n", m_fileName, m_lineNumber, buffer); +} + +void HLSLTokenizer::GetTokenName(char buffer[s_maxIdentifier]) const +{ + if (m_token == HLSLToken_FloatLiteral || m_token == HLSLToken_HalfLiteral ) + { + sprintf(buffer, "%f", m_fValue); + } + else if (m_token == HLSLToken_IntLiteral) + { + sprintf(buffer, "%d", m_iValue); + } + else if (m_token == HLSLToken_Identifier) + { + strcpy(buffer, m_identifier); + } + else + { + GetTokenName(m_token, buffer); + } +} + +void HLSLTokenizer::GetTokenName(int token, char buffer[s_maxIdentifier]) +{ + if (token < 256) + { + buffer[0] = (char)token; + buffer[1] = 0; + } + else if (token < HLSLToken_LessEqual) + { + strcpy(buffer, _reservedWords[token - 256]); + } + else + { + switch (token) + { + case HLSLToken_PlusPlus: + strcpy(buffer, "++"); + break; + case HLSLToken_MinusMinus: + strcpy(buffer, "--"); + break; + case HLSLToken_PlusEqual: + strcpy(buffer, "+="); + break; + case HLSLToken_MinusEqual: + strcpy(buffer, "-="); + break; + case HLSLToken_TimesEqual: + strcpy(buffer, "*="); + break; + case HLSLToken_DivideEqual: + strcpy(buffer, "/="); + break; + case HLSLToken_HalfLiteral: + strcpy( buffer, "half" ); + break; + case HLSLToken_FloatLiteral: + strcpy(buffer, "float"); + break; + case HLSLToken_IntLiteral: + strcpy(buffer, "int"); + break; + case HLSLToken_Identifier: + strcpy(buffer, "identifier"); + break; + case HLSLToken_EndOfStream: + strcpy(buffer, ""); + break; + default: + strcpy(buffer, "unknown"); + break; + } + } + +} + +} diff --git a/src/libprojectM/Renderer/hlslparser/src/HLSLTokenizer.h b/src/libprojectM/Renderer/hlslparser/src/HLSLTokenizer.h new file mode 100755 index 000000000..2e1642ddc --- /dev/null +++ b/src/libprojectM/Renderer/hlslparser/src/HLSLTokenizer.h @@ -0,0 +1,172 @@ +#ifndef HLSL_TOKENIZER_H +#define HLSL_TOKENIZER_H + +namespace M4 +{ + +/** In addition to the values in this enum, all of the ASCII characters are +valid tokens. */ +enum HLSLToken +{ + // Built-in types. + HLSLToken_Float = 256, + HLSLToken_Float2, + HLSLToken_Float3, + HLSLToken_Float4, + HLSLToken_Float2x2, + HLSLToken_Float3x3, + HLSLToken_Float4x4, + HLSLToken_Float4x3, + HLSLToken_Float4x2, + HLSLToken_Half, + HLSLToken_Half2, + HLSLToken_Half3, + HLSLToken_Half4, + HLSLToken_Half2x2, + HLSLToken_Half3x3, + HLSLToken_Half4x4, + HLSLToken_Half4x3, + HLSLToken_Half4x2, + HLSLToken_Bool, + HLSLToken_Bool2, + HLSLToken_Bool3, + HLSLToken_Bool4, + HLSLToken_Int, + HLSLToken_Int2, + HLSLToken_Int3, + HLSLToken_Int4, + HLSLToken_Uint, + HLSLToken_Uint2, + HLSLToken_Uint3, + HLSLToken_Uint4, + HLSLToken_Texture, + HLSLToken_Sampler, + HLSLToken_Sampler2D, + HLSLToken_Sampler3D, + HLSLToken_SamplerCube, + HLSLToken_Sampler2DShadow, + HLSLToken_Sampler2DMS, + HLSLToken_Sampler2DArray, + + // Reserved words. + HLSLToken_If, + HLSLToken_Else, + HLSLToken_For, + HLSLToken_While, + HLSLToken_Break, + HLSLToken_True, + HLSLToken_False, + HLSLToken_Void, + HLSLToken_Struct, + HLSLToken_CBuffer, + HLSLToken_TBuffer, + HLSLToken_Register, + HLSLToken_Return, + HLSLToken_Continue, + HLSLToken_Discard, + HLSLToken_Const, + HLSLToken_Static, + HLSLToken_Inline, + + // Input modifiers. + HLSLToken_Uniform, + HLSLToken_In, + HLSLToken_Out, + HLSLToken_InOut, + + // Effect keywords. + HLSLToken_SamplerState, + HLSLToken_Technique, + HLSLToken_Pass, + + // Multi-character symbols. + HLSLToken_LessEqual, + HLSLToken_GreaterEqual, + HLSLToken_EqualEqual, + HLSLToken_NotEqual, + HLSLToken_PlusPlus, + HLSLToken_MinusMinus, + HLSLToken_PlusEqual, + HLSLToken_MinusEqual, + HLSLToken_TimesEqual, + HLSLToken_DivideEqual, + HLSLToken_AndAnd, // && + HLSLToken_BarBar, // || + + // Other token types. + HLSLToken_FloatLiteral, + HLSLToken_HalfLiteral, + HLSLToken_IntLiteral, + HLSLToken_Identifier, + + HLSLToken_EndOfStream, +}; + +class HLSLTokenizer +{ + +public: + + /// Maximum string length of an identifier. + static const int s_maxIdentifier = 255 + 1; + + /** The file name is only used for error reporting. */ + HLSLTokenizer(const char* fileName, const char* buffer, size_t length); + + /** Advances to the next token in the stream. */ + void Next(); + + /** Returns the current token in the stream. */ + int GetToken() const; + + /** Returns the number of the current token. */ + float GetFloat() const; + int GetInt() const; + + /** Returns the identifier for the current token. */ + const char* GetIdentifier() const; + + /** Returns the line number where the current token began. */ + int GetLineNumber() const; + + /** Returns the file name where the current token began. */ + const char* GetFileName() const; + + /** Gets a human readable text description of the current token. */ + void GetTokenName(char buffer[s_maxIdentifier]) const; + + /** Reports an error using printf style formatting. The current line number + is included. Only the first error reported will be output. */ + void Error(const char* format, ...); + + /** Gets a human readable text description of the specified token. */ + static void GetTokenName(int token, char buffer[s_maxIdentifier]); + +private: + + bool SkipWhitespace(); + bool SkipComment(); + bool SkipPragmaDirective(); + bool ScanNumber(); + bool ScanLineDirective(); + +private: + + const char* m_fileName; + const char* m_buffer; + const char* m_bufferEnd; + int m_lineNumber; + bool m_error; + + int m_token; + float m_fValue; + int m_iValue; + char m_identifier[s_maxIdentifier]; + char m_lineDirectiveFileName[s_maxIdentifier]; + int m_tokenLineNumber; + +}; + +} + +#endif diff --git a/src/libprojectM/Renderer/hlslparser/src/HLSLTree.cpp b/src/libprojectM/Renderer/hlslparser/src/HLSLTree.cpp new file mode 100755 index 000000000..3571dfdfe --- /dev/null +++ b/src/libprojectM/Renderer/hlslparser/src/HLSLTree.cpp @@ -0,0 +1,1964 @@ +//#include "Engine/Assert.h" +#include "Engine.h" + +#include "HLSLTree.h" +#include + +namespace M4 +{ + +const HLSLTypeDimension BaseTypeDimension[HLSLBaseType_Count] = +{ + HLSLTypeDimension_None, // HLSLBaseType_Unknown, + HLSLTypeDimension_None, // HLSLBaseType_Void, + HLSLTypeDimension_Scalar, // HLSLBaseType_Float, + HLSLTypeDimension_Vector2, // HLSLBaseType_Float2, + HLSLTypeDimension_Vector3, // HLSLBaseType_Float3, + HLSLTypeDimension_Vector4, // HLSLBaseType_Float4, + HLSLTypeDimension_Matrix2x2,// HLSLBaseType_Float2x2, + HLSLTypeDimension_Matrix3x3,// HLSLBaseType_Float3x3, + HLSLTypeDimension_Matrix4x4,// HLSLBaseType_Float4x4, + HLSLTypeDimension_Matrix4x3,// HLSLBaseType_Float4x3, + HLSLTypeDimension_Matrix4x2,// HLSLBaseType_Float4x2, + HLSLTypeDimension_Scalar, // HLSLBaseType_Half, + HLSLTypeDimension_Vector2, // HLSLBaseType_Half2, + HLSLTypeDimension_Vector3, // HLSLBaseType_Half3, + HLSLTypeDimension_Vector4, // HLSLBaseType_Half4, + HLSLTypeDimension_Matrix2x2,// HLSLBaseType_Half2x2, + HLSLTypeDimension_Matrix3x3,// HLSLBaseType_Half3x3, + HLSLTypeDimension_Matrix4x4,// HLSLBaseType_Half4x4, + HLSLTypeDimension_Matrix4x3,// HLSLBaseType_Half4x3, + HLSLTypeDimension_Matrix4x2,// HLSLBaseType_Half4x2, + HLSLTypeDimension_Scalar, // HLSLBaseType_Bool, + HLSLTypeDimension_Vector2, // HLSLBaseType_Bool2, + HLSLTypeDimension_Vector3, // HLSLBaseType_Bool3, + HLSLTypeDimension_Vector4, // HLSLBaseType_Bool4, + HLSLTypeDimension_Scalar, // HLSLBaseType_Int, + HLSLTypeDimension_Vector2, // HLSLBaseType_Int2, + HLSLTypeDimension_Vector3, // HLSLBaseType_Int3, + HLSLTypeDimension_Vector4, // HLSLBaseType_Int4, + HLSLTypeDimension_Scalar, // HLSLBaseType_Uint, + HLSLTypeDimension_Vector2, // HLSLBaseType_Uint2, + HLSLTypeDimension_Vector3, // HLSLBaseType_Uint3, + HLSLTypeDimension_Vector4, // HLSLBaseType_Uint4, + HLSLTypeDimension_None, // HLSLBaseType_Texture, + HLSLTypeDimension_None, // HLSLBaseType_Sampler, // @@ use type inference to determine sampler type. + HLSLTypeDimension_None, // HLSLBaseType_Sampler2D, + HLSLTypeDimension_None, // HLSLBaseType_Sampler3D, + HLSLTypeDimension_None, // HLSLBaseType_SamplerCube, + HLSLTypeDimension_None, // HLSLBaseType_Sampler2DShadow, + HLSLTypeDimension_None, // HLSLBaseType_Sampler2DMS, + HLSLTypeDimension_None, // HLSLBaseType_Sampler2DArray, + HLSLTypeDimension_None, // HLSLBaseType_UserDefined, // struct + HLSLTypeDimension_None, // HLSLBaseType_Expression, // type argument for defined() sizeof() and typeof(). + HLSLTypeDimension_None, // HLSLBaseType_Auto, +}; + +const HLSLBaseType ScalarBaseType[HLSLBaseType_Count] = { + HLSLBaseType_Unknown, // HLSLBaseType_Unknown, + HLSLBaseType_Void, // HLSLBaseType_Void, + HLSLBaseType_Float, // HLSLBaseType_Float, + HLSLBaseType_Float, // HLSLBaseType_Float2, + HLSLBaseType_Float, // HLSLBaseType_Float3, + HLSLBaseType_Float, // HLSLBaseType_Float4, + HLSLBaseType_Float, // HLSLBaseType_Float2x2, + HLSLBaseType_Float, // HLSLBaseType_Float3x3, + HLSLBaseType_Float, // HLSLBaseType_Float4x4, + HLSLBaseType_Float, // HLSLBaseType_Float4x3, + HLSLBaseType_Float, // HLSLBaseType_Float4x2, + HLSLBaseType_Half, // HLSLBaseType_Half, + HLSLBaseType_Half, // HLSLBaseType_Half2, + HLSLBaseType_Half, // HLSLBaseType_Half3, + HLSLBaseType_Half, // HLSLBaseType_Half4, + HLSLBaseType_Half, // HLSLBaseType_Half2x2, + HLSLBaseType_Half, // HLSLBaseType_Half3x3, + HLSLBaseType_Half, // HLSLBaseType_Half4x4, + HLSLBaseType_Half, // HLSLBaseType_Half4x3, + HLSLBaseType_Half, // HLSLBaseType_Half4x2, + HLSLBaseType_Bool, // HLSLBaseType_Bool, + HLSLBaseType_Bool, // HLSLBaseType_Bool2, + HLSLBaseType_Bool, // HLSLBaseType_Bool3, + HLSLBaseType_Bool, // HLSLBaseType_Bool4, + HLSLBaseType_Int, // HLSLBaseType_Int, + HLSLBaseType_Int, // HLSLBaseType_Int2, + HLSLBaseType_Int, // HLSLBaseType_Int3, + HLSLBaseType_Int, // HLSLBaseType_Int4, + HLSLBaseType_Uint, // HLSLBaseType_Uint, + HLSLBaseType_Uint, // HLSLBaseType_Uint2, + HLSLBaseType_Uint, // HLSLBaseType_Uint3, + HLSLBaseType_Uint, // HLSLBaseType_Uint4, + HLSLBaseType_Unknown, // HLSLBaseType_Texture, + HLSLBaseType_Unknown, // HLSLBaseType_Sampler, // @@ use type inference to determine sampler type. + HLSLBaseType_Unknown, // HLSLBaseType_Sampler2D, + HLSLBaseType_Unknown, // HLSLBaseType_Sampler3D, + HLSLBaseType_Unknown, // HLSLBaseType_SamplerCube, + HLSLBaseType_Unknown, // HLSLBaseType_Sampler2DShadow, + HLSLBaseType_Unknown, // HLSLBaseType_Sampler2DMS, + HLSLBaseType_Unknown, // HLSLBaseType_Sampler2DArray, + HLSLBaseType_Unknown, // HLSLBaseType_UserDefined, // struct + HLSLBaseType_Unknown, // HLSLBaseType_Expression, // type argument for defined() sizeof() and typeof(). + HLSLBaseType_Unknown, // HLSLBaseType_Auto, +}; + + +HLSLTree::HLSLTree(Allocator* allocator) : + m_allocator(allocator), m_stringPool(allocator) +{ + m_firstPage = m_allocator->New(); + m_firstPage->next = NULL; + + m_currentPage = m_firstPage; + m_currentPageOffset = 0; + + m_root = AddNode(NULL, 1); +} + +HLSLTree::~HLSLTree() +{ + NodePage* page = m_firstPage; + while (page != NULL) + { + NodePage* next = page->next; + m_allocator->Delete(page); + page = next; + } +} + +void HLSLTree::AllocatePage() +{ + NodePage* newPage = m_allocator->New(); + newPage->next = NULL; + m_currentPage->next = newPage; + m_currentPageOffset = 0; + m_currentPage = newPage; +} + +const char* HLSLTree::AddString(const char* string) +{ + return m_stringPool.AddString(string); +} + +const char* HLSLTree::AddStringFormat(const char* format, ...) +{ + va_list args; + va_start(args, format); + const char * string = m_stringPool.AddStringFormatList(format, args); + va_end(args); + return string; +} + +bool HLSLTree::GetContainsString(const char* string) const +{ + return m_stringPool.GetContainsString(string); +} + +HLSLRoot* HLSLTree::GetRoot() const +{ + return m_root; +} + +void* HLSLTree::AllocateMemory(size_t size) +{ + if (m_currentPageOffset + size > s_nodePageSize) + { + AllocatePage(); + } + void* buffer = m_currentPage->buffer + m_currentPageOffset; + m_currentPageOffset += size; + return buffer; +} + +// @@ This doesn't do any parameter matching. Simply returns the first function with that name. +HLSLFunction * HLSLTree::FindFunction(const char * name) +{ + HLSLStatement * statement = m_root->statement; + while (statement != NULL) + { + if (statement->nodeType == HLSLNodeType_Function) + { + HLSLFunction * function = (HLSLFunction *)statement; + if (String_Equal(name, function->name)) + { + return function; + } + } + + statement = statement->nextStatement; + } + + return NULL; +} + +HLSLDeclaration * HLSLTree::FindGlobalDeclaration(const char * name, HLSLBuffer ** buffer_out/*=NULL*/) +{ + HLSLStatement * statement = m_root->statement; + while (statement != NULL) + { + if (statement->nodeType == HLSLNodeType_Declaration) + { + HLSLDeclaration * declaration = (HLSLDeclaration *)statement; + if (String_Equal(name, declaration->name)) + { + if (buffer_out) *buffer_out = NULL; + return declaration; + } + } + else if (statement->nodeType == HLSLNodeType_Buffer) + { + HLSLBuffer* buffer = (HLSLBuffer*)statement; + + HLSLDeclaration* field = buffer->field; + while (field != NULL) + { + ASSERT(field->nodeType == HLSLNodeType_Declaration); + if (String_Equal(name, field->name)) + { + if (buffer_out) *buffer_out = buffer; + return field; + } + field = (HLSLDeclaration*)field->nextStatement; + } + } + + statement = statement->nextStatement; + } + + if (buffer_out) *buffer_out = NULL; + return NULL; +} + +HLSLStruct * HLSLTree::FindGlobalStruct(const char * name) +{ + HLSLStatement * statement = m_root->statement; + while (statement != NULL) + { + if (statement->nodeType == HLSLNodeType_Struct) + { + HLSLStruct * declaration = (HLSLStruct *)statement; + if (String_Equal(name, declaration->name)) + { + return declaration; + } + } + + statement = statement->nextStatement; + } + + return NULL; +} + +HLSLTechnique * HLSLTree::FindTechnique(const char * name) +{ + HLSLStatement * statement = m_root->statement; + while (statement != NULL) + { + if (statement->nodeType == HLSLNodeType_Technique) + { + HLSLTechnique * technique = (HLSLTechnique *)statement; + if (String_Equal(name, technique->name)) + { + return technique; + } + } + + statement = statement->nextStatement; + } + + return NULL; +} + +HLSLPipeline * HLSLTree::FindFirstPipeline() +{ + return FindNextPipeline(NULL); +} + +HLSLPipeline * HLSLTree::FindNextPipeline(HLSLPipeline * current) +{ + HLSLStatement * statement = current ? current : m_root->statement; + while (statement != NULL) + { + if (statement->nodeType == HLSLNodeType_Pipeline) + { + return (HLSLPipeline *)statement; + } + + statement = statement->nextStatement; + } + + return NULL; +} + +HLSLPipeline * HLSLTree::FindPipeline(const char * name) +{ + HLSLStatement * statement = m_root->statement; + while (statement != NULL) + { + if (statement->nodeType == HLSLNodeType_Pipeline) + { + HLSLPipeline * pipeline = (HLSLPipeline *)statement; + if (String_Equal(name, pipeline->name)) + { + return pipeline; + } + } + + statement = statement->nextStatement; + } + + return NULL; +} + +HLSLBuffer * HLSLTree::FindBuffer(const char * name) +{ + HLSLStatement * statement = m_root->statement; + while (statement != NULL) + { + if (statement->nodeType == HLSLNodeType_Buffer) + { + HLSLBuffer * buffer = (HLSLBuffer *)statement; + if (String_Equal(name, buffer->name)) + { + return buffer; + } + } + + statement = statement->nextStatement; + } + + return NULL; +} + + + +bool HLSLTree::GetExpressionValue(HLSLExpression * expression, int & value) +{ + ASSERT (expression != NULL); + + // Expression must be constant. + if ((expression->expressionType.flags & HLSLTypeFlag_Const) == 0) + { + return false; + } + + // We are expecting an integer scalar. @@ Add support for type conversion from other scalar types. + if (expression->expressionType.baseType != HLSLBaseType_Int && + expression->expressionType.baseType != HLSLBaseType_Bool) + { + return false; + } + + if (expression->expressionType.array) + { + return false; + } + + if (expression->nodeType == HLSLNodeType_BinaryExpression) + { + HLSLBinaryExpression * binaryExpression = (HLSLBinaryExpression *)expression; + + int value1, value2; + if (!GetExpressionValue(binaryExpression->expression1, value1) || + !GetExpressionValue(binaryExpression->expression2, value2)) + { + return false; + } + + switch(binaryExpression->binaryOp) + { + case HLSLBinaryOp_And: + value = value1 && value2; + return true; + case HLSLBinaryOp_Or: + value = value1 || value2; + return true; + case HLSLBinaryOp_Add: + value = value1 + value2; + return true; + case HLSLBinaryOp_Sub: + value = value1 - value2; + return true; + case HLSLBinaryOp_Mul: + value = value1 * value2; + return true; + case HLSLBinaryOp_Div: + value = value1 / value2; + return true; + case HLSLBinaryOp_Less: + value = value1 < value2; + return true; + case HLSLBinaryOp_Greater: + value = value1 > value2; + return true; + case HLSLBinaryOp_LessEqual: + value = value1 <= value2; + return true; + case HLSLBinaryOp_GreaterEqual: + value = value1 >= value2; + return true; + case HLSLBinaryOp_Equal: + value = value1 == value2; + return true; + case HLSLBinaryOp_NotEqual: + value = value1 != value2; + return true; + case HLSLBinaryOp_BitAnd: + value = value1 & value2; + return true; + case HLSLBinaryOp_BitOr: + value = value1 | value2; + return true; + case HLSLBinaryOp_BitXor: + value = value1 ^ value2; + return true; + case HLSLBinaryOp_Assign: + case HLSLBinaryOp_AddAssign: + case HLSLBinaryOp_SubAssign: + case HLSLBinaryOp_MulAssign: + case HLSLBinaryOp_DivAssign: + // IC: These are not valid on non-constant expressions and should fail earlier when querying expression value. + return false; + } + } + else if (expression->nodeType == HLSLNodeType_UnaryExpression) + { + HLSLUnaryExpression * unaryExpression = (HLSLUnaryExpression *)expression; + + if (!GetExpressionValue(unaryExpression->expression, value)) + { + return false; + } + + switch(unaryExpression->unaryOp) + { + case HLSLUnaryOp_Negative: + value = -value; + return true; + case HLSLUnaryOp_Positive: + // nop. + return true; + case HLSLUnaryOp_Not: + value = !value; + return true; + case HLSLUnaryOp_BitNot: + value = ~value; + return true; + case HLSLUnaryOp_PostDecrement: + case HLSLUnaryOp_PostIncrement: + case HLSLUnaryOp_PreDecrement: + case HLSLUnaryOp_PreIncrement: + // IC: These are not valid on non-constant expressions and should fail earlier when querying expression value. + return false; + } + } + else if (expression->nodeType == HLSLNodeType_IdentifierExpression) + { + HLSLIdentifierExpression * identifier = (HLSLIdentifierExpression *)expression; + + HLSLDeclaration * declaration = FindGlobalDeclaration(identifier->name); + if (declaration == NULL) + { + return false; + } + if ((declaration->type.flags & HLSLTypeFlag_Const) == 0) + { + return false; + } + + return GetExpressionValue(declaration->assignment, value); + } + else if (expression->nodeType == HLSLNodeType_LiteralExpression) + { + HLSLLiteralExpression * literal = (HLSLLiteralExpression *)expression; + + if (literal->expressionType.baseType == HLSLBaseType_Int) value = literal->iValue; + else if (literal->expressionType.baseType == HLSLBaseType_Bool) value = (int)literal->bValue; + else return false; + + return true; + } + + return false; +} + +bool HLSLTree::NeedsFunction(const char* name) +{ + // Early out + if (!GetContainsString(name)) + return false; + + struct NeedsFunctionVisitor: HLSLTreeVisitor + { + const char* name; + bool result; + + virtual void VisitTopLevelStatement(HLSLStatement * node) + { + if (!node->hidden) + HLSLTreeVisitor::VisitTopLevelStatement(node); + } + + virtual void VisitFunctionCall(HLSLFunctionCall * node) + { + result = result || String_Equal(name, node->function->name); + + HLSLTreeVisitor::VisitFunctionCall(node); + } + }; + + NeedsFunctionVisitor visitor; + visitor.name = name; + visitor.result = false; + + visitor.VisitRoot(m_root); + + return visitor.result; +} + +int GetVectorDimension(HLSLType & type) +{ + if (type.baseType >= HLSLBaseType_FirstNumeric && + type.baseType <= HLSLBaseType_LastNumeric) + { + if (type.baseType == HLSLBaseType_Float || type.baseType == HLSLBaseType_Half) return 1; + if (type.baseType == HLSLBaseType_Float2 || type.baseType == HLSLBaseType_Half2) return 2; + if (type.baseType == HLSLBaseType_Float3 || type.baseType == HLSLBaseType_Half3) return 3; + if (type.baseType == HLSLBaseType_Float4 || type.baseType == HLSLBaseType_Half4) return 4; + + } + return 0; +} + +// Returns dimension, 0 if invalid. +int HLSLTree::GetExpressionValue(HLSLExpression * expression, float values[4]) +{ + ASSERT (expression != NULL); + + // Expression must be constant. + if ((expression->expressionType.flags & HLSLTypeFlag_Const) == 0) + { + return 0; + } + + if (expression->expressionType.baseType == HLSLBaseType_Int || + expression->expressionType.baseType == HLSLBaseType_Bool) + { + int int_value; + if (GetExpressionValue(expression, int_value)) { + for (int i = 0; i < 4; i++) values[i] = (float)int_value; // @@ Warn if conversion is not exact. + return 1; + } + + return 0; + } + if (expression->expressionType.baseType >= HLSLBaseType_FirstInteger && expression->expressionType.baseType <= HLSLBaseType_LastInteger) + { + // @@ Add support for uints? + // @@ Add support for int vectors? + return 0; + } + if (expression->expressionType.baseType > HLSLBaseType_LastNumeric) + { + return 0; + } + + // @@ Not supported yet, but we may need it? + if (expression->expressionType.array) + { + return false; + } + + if (expression->nodeType == HLSLNodeType_BinaryExpression) + { + HLSLBinaryExpression * binaryExpression = (HLSLBinaryExpression *)expression; + int dim = GetVectorDimension(binaryExpression->expressionType); + + float values1[4], values2[4]; + int dim1 = GetExpressionValue(binaryExpression->expression1, values1); + int dim2 = GetExpressionValue(binaryExpression->expression2, values2); + + if (dim1 == 0 || dim2 == 0) + { + return 0; + } + + if (dim1 != dim2) + { + // Brodacast scalar to vector size. + if (dim1 == 1) + { + for (int i = 1; i < dim2; i++) values1[i] = values1[0]; + dim1 = dim2; + } + else if (dim2 == 1) + { + for (int i = 1; i < dim1; i++) values2[i] = values2[0]; + dim2 = dim1; + } + else + { + return 0; + } + } + ASSERT(dim == dim1); + + switch(binaryExpression->binaryOp) + { + case HLSLBinaryOp_Add: + for (int i = 0; i < dim; i++) values[i] = values1[i] + values2[i]; + return dim; + case HLSLBinaryOp_Sub: + for (int i = 0; i < dim; i++) values[i] = values1[i] - values2[i]; + return dim; + case HLSLBinaryOp_Mul: + for (int i = 0; i < dim; i++) values[i] = values1[i] * values2[i]; + return dim; + case HLSLBinaryOp_Div: + for (int i = 0; i < dim; i++) values[i] = values1[i] / values2[i]; + return dim; + default: + return 0; + } + } + else if (expression->nodeType == HLSLNodeType_UnaryExpression) + { + HLSLUnaryExpression * unaryExpression = (HLSLUnaryExpression *)expression; + int dim = GetVectorDimension(unaryExpression->expressionType); + + int dim1 = GetExpressionValue(unaryExpression->expression, values); + if (dim1 == 0) + { + return 0; + } + ASSERT(dim == dim1); + + switch(unaryExpression->unaryOp) + { + case HLSLUnaryOp_Negative: + for (int i = 0; i < dim; i++) values[i] = -values[i]; + return dim; + case HLSLUnaryOp_Positive: + // nop. + return dim; + default: + return 0; + } + } + else if (expression->nodeType == HLSLNodeType_ConstructorExpression) + { + HLSLConstructorExpression * constructor = (HLSLConstructorExpression *)expression; + + int dim = GetVectorDimension(constructor->expressionType); + + int idx = 0; + HLSLExpression * arg = constructor->argument; + while (arg != NULL) + { + float tmp[4]; + int n = GetExpressionValue(arg, tmp); + for (int i = 0; i < n; i++) values[idx + i] = tmp[i]; + idx += n; + + arg = arg->nextExpression; + } + ASSERT(dim == idx); + + return dim; + } + else if (expression->nodeType == HLSLNodeType_IdentifierExpression) + { + HLSLIdentifierExpression * identifier = (HLSLIdentifierExpression *)expression; + + HLSLDeclaration * declaration = FindGlobalDeclaration(identifier->name); + if (declaration == NULL) + { + return 0; + } + if ((declaration->type.flags & HLSLTypeFlag_Const) == 0) + { + return 0; + } + + return GetExpressionValue(declaration->assignment, values); + } + else if (expression->nodeType == HLSLNodeType_LiteralExpression) + { + HLSLLiteralExpression * literal = (HLSLLiteralExpression *)expression; + + if (literal->expressionType.baseType == HLSLBaseType_Float) values[0] = literal->fValue; + else if (literal->expressionType.baseType == HLSLBaseType_Half) values[0] = literal->fValue; + else if (literal->expressionType.baseType == HLSLBaseType_Bool) values[0] = literal->bValue; + else if (literal->expressionType.baseType == HLSLBaseType_Int) values[0] = (float)literal->iValue; // @@ Warn if conversion is not exact. + else return 0; + + return 1; + } + + return 0; +} + + + + +void HLSLTreeVisitor::VisitType(HLSLType & type) +{ +} + +void HLSLTreeVisitor::VisitRoot(HLSLRoot * root) +{ + HLSLStatement * statement = root->statement; + while (statement != NULL) { + VisitTopLevelStatement(statement); + statement = statement->nextStatement; + } +} + +void HLSLTreeVisitor::VisitTopLevelStatement(HLSLStatement * node) +{ + if (node->nodeType == HLSLNodeType_Declaration) { + VisitDeclaration((HLSLDeclaration *)node); + } + else if (node->nodeType == HLSLNodeType_Struct) { + VisitStruct((HLSLStruct *)node); + } + else if (node->nodeType == HLSLNodeType_Buffer) { + VisitBuffer((HLSLBuffer *)node); + } + else if (node->nodeType == HLSLNodeType_Function) { + VisitFunction((HLSLFunction *)node); + } + else if (node->nodeType == HLSLNodeType_Technique) { + VisitTechnique((HLSLTechnique *)node); + } + else if (node->nodeType == HLSLNodeType_Pipeline) { + VisitPipeline((HLSLPipeline *)node); + } + else { + ASSERT(0); + } +} + +void HLSLTreeVisitor::VisitStatements(HLSLStatement * statement) +{ + while (statement != NULL) { + VisitStatement(statement); + statement = statement->nextStatement; + } +} + +void HLSLTreeVisitor::VisitStatement(HLSLStatement * node) +{ + // Function statements + if (node->nodeType == HLSLNodeType_Declaration) { + VisitDeclaration((HLSLDeclaration *)node); + } + else if (node->nodeType == HLSLNodeType_ExpressionStatement) { + VisitExpressionStatement((HLSLExpressionStatement *)node); + } + else if (node->nodeType == HLSLNodeType_ReturnStatement) { + VisitReturnStatement((HLSLReturnStatement *)node); + } + else if (node->nodeType == HLSLNodeType_DiscardStatement) { + VisitDiscardStatement((HLSLDiscardStatement *)node); + } + else if (node->nodeType == HLSLNodeType_BreakStatement) { + VisitBreakStatement((HLSLBreakStatement *)node); + } + else if (node->nodeType == HLSLNodeType_ContinueStatement) { + VisitContinueStatement((HLSLContinueStatement *)node); + } + else if (node->nodeType == HLSLNodeType_IfStatement) { + VisitIfStatement((HLSLIfStatement *)node); + } + else if (node->nodeType == HLSLNodeType_ForStatement) { + VisitForStatement((HLSLForStatement *)node); + } + else if (node->nodeType == HLSLNodeType_BlockStatement) { + VisitBlockStatement((HLSLBlockStatement *)node); + } + else { + ASSERT(0); + } +} + +void HLSLTreeVisitor::VisitDeclaration(HLSLDeclaration * node) +{ + VisitType(node->type); + /*do { + VisitExpression(node->assignment); + node = node->nextDeclaration; + } while (node);*/ + if (node->assignment != NULL) { + VisitExpression(node->assignment); + } + if (node->nextDeclaration != NULL) { + VisitDeclaration(node->nextDeclaration); + } +} + +void HLSLTreeVisitor::VisitStruct(HLSLStruct * node) +{ + HLSLStructField * field = node->field; + while (field != NULL) { + VisitStructField(field); + field = field->nextField; + } +} + +void HLSLTreeVisitor::VisitStructField(HLSLStructField * node) +{ + VisitType(node->type); +} + +void HLSLTreeVisitor::VisitBuffer(HLSLBuffer * node) +{ + HLSLDeclaration * field = node->field; + while (field != NULL) { + ASSERT(field->nodeType == HLSLNodeType_Declaration); + VisitDeclaration(field); + ASSERT(field->nextDeclaration == NULL); + field = (HLSLDeclaration *)field->nextStatement; + } +} + +/*void HLSLTreeVisitor::VisitBufferField(HLSLBufferField * node) +{ + VisitType(node->type); +}*/ + +void HLSLTreeVisitor::VisitFunction(HLSLFunction * node) +{ + VisitType(node->returnType); + + HLSLArgument * argument = node->argument; + while (argument != NULL) { + VisitArgument(argument); + argument = argument->nextArgument; + } + + VisitStatements(node->statement); +} + +void HLSLTreeVisitor::VisitArgument(HLSLArgument * node) +{ + VisitType(node->type); + if (node->defaultValue != NULL) { + VisitExpression(node->defaultValue); + } +} + +void HLSLTreeVisitor::VisitExpressionStatement(HLSLExpressionStatement * node) +{ + VisitExpression(node->expression); +} + +void HLSLTreeVisitor::VisitExpression(HLSLExpression * node) +{ + VisitType(node->expressionType); + + if (node->nodeType == HLSLNodeType_UnaryExpression) { + VisitUnaryExpression((HLSLUnaryExpression *)node); + } + else if (node->nodeType == HLSLNodeType_BinaryExpression) { + VisitBinaryExpression((HLSLBinaryExpression *)node); + } + else if (node->nodeType == HLSLNodeType_ConditionalExpression) { + VisitConditionalExpression((HLSLConditionalExpression *)node); + } + else if (node->nodeType == HLSLNodeType_CastingExpression) { + VisitCastingExpression((HLSLCastingExpression *)node); + } + else if (node->nodeType == HLSLNodeType_LiteralExpression) { + VisitLiteralExpression((HLSLLiteralExpression *)node); + } + else if (node->nodeType == HLSLNodeType_IdentifierExpression) { + VisitIdentifierExpression((HLSLIdentifierExpression *)node); + } + else if (node->nodeType == HLSLNodeType_ConstructorExpression) { + VisitConstructorExpression((HLSLConstructorExpression *)node); + } + else if (node->nodeType == HLSLNodeType_MemberAccess) { + VisitMemberAccess((HLSLMemberAccess *)node); + } + else if (node->nodeType == HLSLNodeType_ArrayAccess) { + VisitArrayAccess((HLSLArrayAccess *)node); + } + else if (node->nodeType == HLSLNodeType_FunctionCall) { + VisitFunctionCall((HLSLFunctionCall *)node); + } + // Acoget-TODO: This was missing. Did adding it break anything? + else if (node->nodeType == HLSLNodeType_SamplerState) { + VisitSamplerState((HLSLSamplerState *)node); + } + else { + ASSERT(0); + } +} + +void HLSLTreeVisitor::VisitReturnStatement(HLSLReturnStatement * node) +{ + VisitExpression(node->expression); +} + +void HLSLTreeVisitor::VisitDiscardStatement(HLSLDiscardStatement * node) {} +void HLSLTreeVisitor::VisitBreakStatement(HLSLBreakStatement * node) {} +void HLSLTreeVisitor::VisitContinueStatement(HLSLContinueStatement * node) {} + +void HLSLTreeVisitor::VisitIfStatement(HLSLIfStatement * node) +{ + VisitExpression(node->condition); + VisitStatements(node->statement); + if (node->elseStatement) { + VisitStatements(node->elseStatement); + } +} + +void HLSLTreeVisitor::VisitForStatement(HLSLForStatement * node) +{ + if (node->initialization) { + VisitDeclaration(node->initialization); + } + if (node->condition) { + VisitExpression(node->condition); + } + if (node->increment) { + VisitExpression(node->increment); + } + VisitStatements(node->statement); +} + +void HLSLTreeVisitor::VisitBlockStatement(HLSLBlockStatement * node) +{ + VisitStatements(node->statement); +} + +void HLSLTreeVisitor::VisitUnaryExpression(HLSLUnaryExpression * node) +{ + VisitExpression(node->expression); +} + +void HLSLTreeVisitor::VisitBinaryExpression(HLSLBinaryExpression * node) +{ + VisitExpression(node->expression1); + VisitExpression(node->expression2); +} + +void HLSLTreeVisitor::VisitConditionalExpression(HLSLConditionalExpression * node) +{ + VisitExpression(node->condition); + VisitExpression(node->falseExpression); + VisitExpression(node->trueExpression); +} + +void HLSLTreeVisitor::VisitCastingExpression(HLSLCastingExpression * node) +{ + VisitType(node->type); + VisitExpression(node->expression); +} + +void HLSLTreeVisitor::VisitLiteralExpression(HLSLLiteralExpression * node) {} +void HLSLTreeVisitor::VisitIdentifierExpression(HLSLIdentifierExpression * node) {} + +void HLSLTreeVisitor::VisitConstructorExpression(HLSLConstructorExpression * node) +{ + HLSLExpression * argument = node->argument; + while (argument != NULL) { + VisitExpression(argument); + argument = argument->nextExpression; + } +} + +void HLSLTreeVisitor::VisitMemberAccess(HLSLMemberAccess * node) +{ + VisitExpression(node->object); +} + +void HLSLTreeVisitor::VisitArrayAccess(HLSLArrayAccess * node) +{ + VisitExpression(node->array); + VisitExpression(node->index); +} + +void HLSLTreeVisitor::VisitFunctionCall(HLSLFunctionCall * node) +{ + HLSLExpression * argument = node->argument; + while (argument != NULL) { + VisitExpression(argument); + argument = argument->nextExpression; + } +} + +void HLSLTreeVisitor::VisitStateAssignment(HLSLStateAssignment * node) {} + +void HLSLTreeVisitor::VisitSamplerState(HLSLSamplerState * node) +{ + HLSLStateAssignment * stateAssignment = node->stateAssignments; + while (stateAssignment != NULL) { + VisitStateAssignment(stateAssignment); + stateAssignment = stateAssignment->nextStateAssignment; + } +} + +void HLSLTreeVisitor::VisitPass(HLSLPass * node) +{ + HLSLStateAssignment * stateAssignment = node->stateAssignments; + while (stateAssignment != NULL) { + VisitStateAssignment(stateAssignment); + stateAssignment = stateAssignment->nextStateAssignment; + } +} + +void HLSLTreeVisitor::VisitTechnique(HLSLTechnique * node) +{ + HLSLPass * pass = node->passes; + while (pass != NULL) { + VisitPass(pass); + pass = pass->nextPass; + } +} + +void HLSLTreeVisitor::VisitPipeline(HLSLPipeline * node) +{ + // @@ ? +} + +void HLSLTreeVisitor::VisitFunctions(HLSLRoot * root) +{ + HLSLStatement * statement = root->statement; + while (statement != NULL) { + if (statement->nodeType == HLSLNodeType_Function) { + VisitFunction((HLSLFunction *)statement); + } + + statement = statement->nextStatement; + } +} + +void HLSLTreeVisitor::VisitParameters(HLSLRoot * root) +{ + HLSLStatement * statement = root->statement; + while (statement != NULL) { + if (statement->nodeType == HLSLNodeType_Declaration) { + VisitDeclaration((HLSLDeclaration *)statement); + } + + statement = statement->nextStatement; + } +} + + +class ResetHiddenFlagVisitor : public HLSLTreeVisitor +{ +public: + virtual void VisitTopLevelStatement(HLSLStatement * statement) + { + statement->hidden = true; + + if (statement->nodeType == HLSLNodeType_Buffer) + { + VisitBuffer((HLSLBuffer*)statement); + } + } + + // Hide buffer fields. + virtual void VisitDeclaration(HLSLDeclaration * node) + { + node->hidden = true; + } + + virtual void VisitArgument(HLSLArgument * node) + { + node->hidden = false; // Arguments are visible by default. + } +}; + +class MarkVisibleStatementsVisitor : public HLSLTreeVisitor +{ +public: + HLSLTree * tree; + MarkVisibleStatementsVisitor(HLSLTree * tree) : tree(tree) {} + + virtual void VisitFunction(HLSLFunction * node) + { + node->hidden = false; + HLSLTreeVisitor::VisitFunction(node); + + if (node->forward) + VisitFunction(node->forward); + } + + virtual void VisitFunctionCall(HLSLFunctionCall * node) + { + HLSLTreeVisitor::VisitFunctionCall(node); + + if (node->function->hidden) + { + VisitFunction(const_cast(node->function)); + } + } + + virtual void VisitIdentifierExpression(HLSLIdentifierExpression * node) + { + HLSLTreeVisitor::VisitIdentifierExpression(node); + + if (node->global) + { + HLSLDeclaration * declaration = tree->FindGlobalDeclaration(node->name); + if (declaration != NULL && declaration->hidden) + { + declaration->hidden = false; + VisitDeclaration(declaration); + } + } + } + + virtual void VisitType(HLSLType & type) + { + if (type.baseType == HLSLBaseType_UserDefined) + { + HLSLStruct * globalStruct = tree->FindGlobalStruct(type.typeName); + if (globalStruct != NULL) + { + globalStruct->hidden = false; + VisitStruct(globalStruct); + } + } + } + +}; + + +void PruneTree(HLSLTree* tree, const char* entryName0, const char* entryName1/*=NULL*/) +{ + HLSLRoot* root = tree->GetRoot(); + + // Reset all flags. + ResetHiddenFlagVisitor reset; + reset.VisitRoot(root); + + // Mark all the statements necessary for these entrypoints. + HLSLFunction* entry = tree->FindFunction(entryName0); + if (entry != NULL) + { + MarkVisibleStatementsVisitor mark(tree); + mark.VisitFunction(entry); + } + + if (entryName1 != NULL) + { + entry = tree->FindFunction(entryName1); + if (entry != NULL) + { + MarkVisibleStatementsVisitor mark(tree); + mark.VisitFunction(entry); + } + } + + // Mark buffers visible, if any of their fields is visible. + HLSLStatement * statement = root->statement; + while (statement != NULL) + { + if (statement->nodeType == HLSLNodeType_Buffer) + { + HLSLBuffer* buffer = (HLSLBuffer*)statement; + + HLSLDeclaration* field = buffer->field; + while (field != NULL) + { + ASSERT(field->nodeType == HLSLNodeType_Declaration); + if (!field->hidden) + { + buffer->hidden = false; + break; + } + field = (HLSLDeclaration*)field->nextStatement; + } + } + + statement = statement->nextStatement; + } +} + + +void SortTree(HLSLTree * tree) +{ + // Stable sort so that statements are in this order: + // structs, declarations, functions, techniques. + // but their relative order is preserved. + + HLSLRoot* root = tree->GetRoot(); + + HLSLStatement* structs = NULL; + HLSLStatement* lastStruct = NULL; + HLSLStatement* constDeclarations = NULL; + HLSLStatement* lastConstDeclaration = NULL; + HLSLStatement* declarations = NULL; + HLSLStatement* lastDeclaration = NULL; + HLSLStatement* functions = NULL; + HLSLStatement* lastFunction = NULL; + HLSLStatement* other = NULL; + HLSLStatement* lastOther = NULL; + + HLSLStatement* statement = root->statement; + while (statement != NULL) { + HLSLStatement* nextStatement = statement->nextStatement; + statement->nextStatement = NULL; + + if (statement->nodeType == HLSLNodeType_Struct) { + if (structs == NULL) structs = statement; + if (lastStruct != NULL) lastStruct->nextStatement = statement; + lastStruct = statement; + } + else if (statement->nodeType == HLSLNodeType_Declaration || statement->nodeType == HLSLNodeType_Buffer) { + if (statement->nodeType == HLSLNodeType_Declaration && (((HLSLDeclaration *)statement)->type.flags & HLSLTypeFlag_Const)) { + if (constDeclarations == NULL) constDeclarations = statement; + if (lastConstDeclaration != NULL) lastConstDeclaration->nextStatement = statement; + lastConstDeclaration = statement; + } + else { + if (declarations == NULL) declarations = statement; + if (lastDeclaration != NULL) lastDeclaration->nextStatement = statement; + lastDeclaration = statement; + } + } + else if (statement->nodeType == HLSLNodeType_Function) { + if (functions == NULL) functions = statement; + if (lastFunction != NULL) lastFunction->nextStatement = statement; + lastFunction = statement; + } + else { + if (other == NULL) other = statement; + if (lastOther != NULL) lastOther->nextStatement = statement; + lastOther = statement; + } + + statement = nextStatement; + } + + // Chain all the statements in the order that we want. + HLSLStatement * firstStatement = structs; + HLSLStatement * lastStatement = lastStruct; + + if (constDeclarations != NULL) { + if (firstStatement == NULL) firstStatement = constDeclarations; + else lastStatement->nextStatement = constDeclarations; + lastStatement = lastConstDeclaration; + } + + if (declarations != NULL) { + if (firstStatement == NULL) firstStatement = declarations; + else lastStatement->nextStatement = declarations; + lastStatement = lastDeclaration; + } + + if (functions != NULL) { + if (firstStatement == NULL) firstStatement = functions; + else lastStatement->nextStatement = functions; + lastStatement = lastFunction; + } + + if (other != NULL) { + if (firstStatement == NULL) firstStatement = other; + else lastStatement->nextStatement = other; + lastStatement = lastOther; + } + + root->statement = firstStatement; +} + + + + + +// First and last can be the same. +void AddStatements(HLSLRoot * root, HLSLStatement * before, HLSLStatement * first, HLSLStatement * last) +{ + if (before == NULL) { + last->nextStatement = root->statement; + root->statement = first; + } + else { + last->nextStatement = before->nextStatement; + before->nextStatement = first; + } +} + +void AddSingleStatement(HLSLRoot * root, HLSLStatement * before, HLSLStatement * statement) +{ + AddStatements(root, before, statement, statement); +} + + + +// @@ This is very game-specific. Should be moved to pipeline_parser or somewhere else. +void GroupParameters(HLSLTree * tree) +{ + // Sort parameters based on semantic and group them in cbuffers. + + HLSLRoot* root = tree->GetRoot(); + + HLSLDeclaration * firstPerItemDeclaration = NULL; + HLSLDeclaration * lastPerItemDeclaration = NULL; + + HLSLDeclaration * instanceDataDeclaration = NULL; + + HLSLDeclaration * firstPerPassDeclaration = NULL; + HLSLDeclaration * lastPerPassDeclaration = NULL; + + HLSLDeclaration * firstPerItemSampler = NULL; + HLSLDeclaration * lastPerItemSampler = NULL; + + HLSLDeclaration * firstPerPassSampler = NULL; + HLSLDeclaration * lastPerPassSampler = NULL; + + HLSLStatement * statementBeforeBuffers = NULL; + + HLSLStatement* previousStatement = NULL; + HLSLStatement* statement = root->statement; + while (statement != NULL) + { + HLSLStatement* nextStatement = statement->nextStatement; + + if (statement->nodeType == HLSLNodeType_Struct) // Do not remove this, or it will mess the else clause below. + { + statementBeforeBuffers = statement; + } + else if (statement->nodeType == HLSLNodeType_Declaration) + { + HLSLDeclaration* declaration = (HLSLDeclaration*)statement; + + // We insert buffers after the last const declaration. + if ((declaration->type.flags & HLSLTypeFlag_Const) != 0) + { + statementBeforeBuffers = statement; + } + + // Do not move samplers or static/const parameters. + if ((declaration->type.flags & (HLSLTypeFlag_Static|HLSLTypeFlag_Const)) == 0) + { + // Unlink statement. + statement->nextStatement = NULL; + if (previousStatement != NULL) previousStatement->nextStatement = nextStatement; + else root->statement = nextStatement; + + while(declaration != NULL) + { + HLSLDeclaration* nextDeclaration = declaration->nextDeclaration; + + if (declaration->semantic != NULL && String_EqualNoCase(declaration->semantic, "PER_INSTANCED_ITEM")) + { + ASSERT(instanceDataDeclaration == NULL); + instanceDataDeclaration = declaration; + } + else + { + // Select group based on type and semantic. + HLSLDeclaration ** first, ** last; + if (declaration->semantic == NULL || String_EqualNoCase(declaration->semantic, "PER_ITEM") || String_EqualNoCase(declaration->semantic, "PER_MATERIAL")) + { + if (IsSamplerType(declaration->type)) + { + first = &firstPerItemSampler; + last = &lastPerItemSampler; + } + else + { + first = &firstPerItemDeclaration; + last = &lastPerItemDeclaration; + } + } + else + { + if (IsSamplerType(declaration->type)) + { + first = &firstPerPassSampler; + last = &lastPerPassSampler; + } + else + { + first = &firstPerPassDeclaration; + last = &lastPerPassDeclaration; + } + } + + // Add declaration to new list. + if (*first == NULL) *first = declaration; + else (*last)->nextStatement = declaration; + *last = declaration; + } + + // Unlink from declaration list. + declaration->nextDeclaration = NULL; + + // Reset attributes. + declaration->registerName = NULL; + //declaration->semantic = NULL; // @@ Don't do this! + + declaration = nextDeclaration; + } + } + } + /*else + { + if (statementBeforeBuffers == NULL) { + // This is the location where we will insert our buffers. + statementBeforeBuffers = previousStatement; + } + }*/ + + if (statement->nextStatement == nextStatement) { + previousStatement = statement; + } + statement = nextStatement; + } + + + // Add instance data declaration at the end of the per_item buffer. + if (instanceDataDeclaration != NULL) + { + if (firstPerItemDeclaration == NULL) firstPerItemDeclaration = instanceDataDeclaration; + else lastPerItemDeclaration->nextStatement = instanceDataDeclaration; + } + + + // Add samplers. + if (firstPerItemSampler != NULL) { + AddStatements(root, statementBeforeBuffers, firstPerItemSampler, lastPerItemSampler); + statementBeforeBuffers = lastPerItemSampler; + } + if (firstPerPassSampler != NULL) { + AddStatements(root, statementBeforeBuffers, firstPerPassSampler, lastPerPassSampler); + statementBeforeBuffers = lastPerPassSampler; + } + + + // @@ We are assuming per_item and per_pass buffers don't already exist. @@ We should assert on that. + + if (firstPerItemDeclaration != NULL) + { + // Create buffer statement. + HLSLBuffer * perItemBuffer = tree->AddNode(firstPerItemDeclaration->fileName, firstPerItemDeclaration->line-1); + perItemBuffer->name = tree->AddString("per_item"); + perItemBuffer->registerName = tree->AddString("b0"); + perItemBuffer->field = firstPerItemDeclaration; + + // Set declaration buffer pointers. + HLSLDeclaration * field = perItemBuffer->field; + while (field != NULL) + { + field->buffer = perItemBuffer; + field = (HLSLDeclaration *)field->nextStatement; + } + + // Add buffer to statements. + AddSingleStatement(root, statementBeforeBuffers, perItemBuffer); + statementBeforeBuffers = perItemBuffer; + } + + if (firstPerPassDeclaration != NULL) + { + // Create buffer statement. + HLSLBuffer * perPassBuffer = tree->AddNode(firstPerPassDeclaration->fileName, firstPerPassDeclaration->line-1); + perPassBuffer->name = tree->AddString("per_pass"); + perPassBuffer->registerName = tree->AddString("b1"); + perPassBuffer->field = firstPerPassDeclaration; + + // Set declaration buffer pointers. + HLSLDeclaration * field = perPassBuffer->field; + while (field != NULL) + { + field->buffer = perPassBuffer; + field = (HLSLDeclaration *)field->nextStatement; + } + + // Add buffer to statements. + AddSingleStatement(root, statementBeforeBuffers, perPassBuffer); + } +} + + +class FindArgumentVisitor : public HLSLTreeVisitor +{ +public: + bool found; + const char * name; + + FindArgumentVisitor() + { + found = false; + name = NULL; + } + + bool FindArgument(const char * name, HLSLFunction * function) + { + this->found = false; + this->name = name; + VisitStatements(function->statement); + return found; + } + + virtual void VisitStatements(HLSLStatement * statement) override + { + while (statement != NULL && !found) + { + VisitStatement(statement); + statement = statement->nextStatement; + } + } + + virtual void VisitIdentifierExpression(HLSLIdentifierExpression * node) override + { + if (node->name == name) + { + found = true; + } + } +}; + + +void HideUnusedArguments(HLSLFunction * function) +{ + FindArgumentVisitor visitor; + + // For each argument. + HLSLArgument * arg = function->argument; + while (arg != NULL) + { + if (!visitor.FindArgument(arg->name, function)) + { + arg->hidden = true; + } + + arg = arg->nextArgument; + } +} + +bool EmulateAlphaTest(HLSLTree* tree, const char* entryName, float alphaRef/*=0.5*/) +{ + // Find all return statements of this entry point. + HLSLFunction* entry = tree->FindFunction(entryName); + if (entry != NULL) + { + HLSLStatement ** ptr = &entry->statement; + HLSLStatement * statement = entry->statement; + while (statement != NULL) + { + if (statement->nodeType == HLSLNodeType_ReturnStatement) + { + HLSLReturnStatement * returnStatement = (HLSLReturnStatement *)statement; + HLSLBaseType returnType = returnStatement->expression->expressionType.baseType; + + // Build statement: "if (%s.a < 0.5) discard;" + + HLSLDiscardStatement * discard = tree->AddNode(statement->fileName, statement->line); + + HLSLExpression * alpha = NULL; + if (returnType == HLSLBaseType_Float4 || returnType == HLSLBaseType_Half4) + { + // @@ If return expression is a constructor, grab 4th argument. + // That's not as easy, since we support 'float4(float3, float)' or 'float4(float, float3)', extracting + // the latter is not that easy. + /*if (returnStatement->expression->nodeType == HLSLNodeType_ConstructorExpression) { + HLSLConstructorExpression * constructor = (HLSLConstructorExpression *)returnStatement->expression; + //constructor-> + } + */ + + if (alpha == NULL) { + HLSLMemberAccess * access = tree->AddNode(statement->fileName, statement->line); + access->expressionType = HLSLType(HLSLBaseType_Float); + access->object = returnStatement->expression; // @@ Is reference OK? Or should we clone expression? + access->field = tree->AddString("a"); + access->swizzle = true; + + alpha = access; + } + } + else if (returnType == HLSLBaseType_Float || returnType == HLSLBaseType_Half) + { + alpha = returnStatement->expression; // @@ Is reference OK? Or should we clone expression? + } + else + { + return false; + } + + HLSLLiteralExpression * threshold = tree->AddNode(statement->fileName, statement->line); + threshold->expressionType = HLSLType(HLSLBaseType_Float); + threshold->fValue = alphaRef; + threshold->type = HLSLBaseType_Float; + + HLSLBinaryExpression * condition = tree->AddNode(statement->fileName, statement->line); + condition->expressionType = HLSLType(HLSLBaseType_Bool); + condition->binaryOp = HLSLBinaryOp_Less; + condition->expression1 = alpha; + condition->expression2 = threshold; + + // Insert statement. + HLSLIfStatement * st = tree->AddNode(statement->fileName, statement->line); + st->condition = condition; + st->statement = discard; + st->nextStatement = statement; + *ptr = st; + } + + ptr = &statement->nextStatement; + statement = statement->nextStatement; + } + } + + return true; +} + +bool NeedsFlattening(HLSLExpression * expr, int level = 0) { + if (expr == NULL) { + return false; + } + if (expr->nodeType == HLSLNodeType_UnaryExpression) { + HLSLUnaryExpression * unaryExpr = (HLSLUnaryExpression *)expr; + return NeedsFlattening(unaryExpr->expression, level+1) || NeedsFlattening(expr->nextExpression, level); + } + else if (expr->nodeType == HLSLNodeType_BinaryExpression) { + HLSLBinaryExpression * binaryExpr = (HLSLBinaryExpression *)expr; + if (IsAssignOp(binaryExpr->binaryOp)) { + return NeedsFlattening(binaryExpr->expression2, level+1) || NeedsFlattening(expr->nextExpression, level); + } + else { + return NeedsFlattening(binaryExpr->expression1, level+1) || NeedsFlattening(binaryExpr->expression2, level+1) || NeedsFlattening(expr->nextExpression, level); + } + } + else if (expr->nodeType == HLSLNodeType_ConditionalExpression) { + HLSLConditionalExpression * conditionalExpr = (HLSLConditionalExpression *)expr; + return NeedsFlattening(conditionalExpr->condition, level+1) || NeedsFlattening(conditionalExpr->trueExpression, level+1) || NeedsFlattening(conditionalExpr->falseExpression, level+1) || NeedsFlattening(expr->nextExpression, level); + } + else if (expr->nodeType == HLSLNodeType_CastingExpression) { + HLSLCastingExpression * castingExpr = (HLSLCastingExpression *)expr; + return NeedsFlattening(castingExpr->expression, level+1) || NeedsFlattening(expr->nextExpression, level); + } + else if (expr->nodeType == HLSLNodeType_LiteralExpression) { + return NeedsFlattening(expr->nextExpression, level); + } + else if (expr->nodeType == HLSLNodeType_IdentifierExpression) { + return NeedsFlattening(expr->nextExpression, level); + } + else if (expr->nodeType == HLSLNodeType_ConstructorExpression) { + HLSLConstructorExpression * constructorExpr = (HLSLConstructorExpression *)expr; + return NeedsFlattening(constructorExpr->argument, level+1) || NeedsFlattening(expr->nextExpression, level); + } + else if (expr->nodeType == HLSLNodeType_MemberAccess) { + return NeedsFlattening(expr->nextExpression, level+1); + } + else if (expr->nodeType == HLSLNodeType_ArrayAccess) { + HLSLArrayAccess * arrayAccess = (HLSLArrayAccess *)expr; + return NeedsFlattening(arrayAccess->array, level+1) || NeedsFlattening(arrayAccess->index, level+1) || NeedsFlattening(expr->nextExpression, level); + } + else if (expr->nodeType == HLSLNodeType_FunctionCall) { + HLSLFunctionCall * functionCall = (HLSLFunctionCall *)expr; + if (functionCall->function->numOutputArguments > 0) { + if (level > 0) { + return true; + } + } + return NeedsFlattening(functionCall->argument, level+1) || NeedsFlattening(expr->nextExpression, level); + } + else { + //assert(false); + return false; + } +} + + +struct StatementList { + HLSLStatement * head = NULL; + HLSLStatement * tail = NULL; + void append(HLSLStatement * st) { + if (head == NULL) { + tail = head = st; + } + tail->nextStatement = st; + tail = st; + } +}; + + + class ExpressionFlattener : public HLSLTreeVisitor + { + public: + HLSLTree * m_tree; + int tmp_index; + HLSLStatement ** statement_pointer; + HLSLFunction * current_function; + + ExpressionFlattener() + { + m_tree = NULL; + tmp_index = 0; + statement_pointer = NULL; + current_function = NULL; + } + + void FlattenExpressions(HLSLTree * tree) + { + m_tree = tree; + VisitRoot(tree->GetRoot()); + } + + // Visit all statements updating the statement_pointer so that we can insert and replace statements. @@ Add this to the default visitor? + virtual void VisitFunction(HLSLFunction * node) override + { + current_function = node; + statement_pointer = &node->statement; + VisitStatements(node->statement); + statement_pointer = NULL; + current_function = NULL; + } + + virtual void VisitIfStatement(HLSLIfStatement * node) override + { + if (NeedsFlattening(node->condition, 1)) { + assert(false); // @@ Add statements before if statement. + } + + statement_pointer = &node->statement; + VisitStatements(node->statement); + if (node->elseStatement) { + statement_pointer = &node->elseStatement; + VisitStatements(node->elseStatement); + } + } + + virtual void VisitForStatement(HLSLForStatement * node) override + { + if (NeedsFlattening(node->initialization->assignment, 1)) { + assert(false); // @@ Add statements before for statement. + } + if (NeedsFlattening(node->condition, 1) || NeedsFlattening(node->increment, 1)) { + assert(false); // @@ These are tricky to implement. Need to handle all loop exits. + } + + statement_pointer = &node->statement; + VisitStatements(node->statement); + } + + virtual void VisitBlockStatement(HLSLBlockStatement * node) override + { + statement_pointer = &node->statement; + VisitStatements(node->statement); + } + + virtual void VisitStatements(HLSLStatement * statement) override + { + while (statement != NULL) { + VisitStatement(statement); + statement_pointer = &statement->nextStatement; + statement = statement->nextStatement; + } + } + + // This is usually a function call or assignment. + virtual void VisitExpressionStatement(HLSLExpressionStatement * node) override + { + if (NeedsFlattening(node->expression, 0)) + { + StatementList statements; + Flatten(node->expression, statements, false); + + // Link beginning of statement list. + *statement_pointer = statements.head; + + // Link end of statement list. + HLSLStatement * tail = statements.tail; + tail->nextStatement = node->nextStatement; + + // Update statement pointer. + statement_pointer = &tail->nextStatement; + + // @@ Delete node? + } + } + + virtual void VisitDeclaration(HLSLDeclaration * node) override + { + // Skip global declarations. + if (statement_pointer == NULL) return; + + if (NeedsFlattening(node->assignment, 1)) + { + StatementList statements; + HLSLIdentifierExpression * ident = Flatten(node->assignment, statements, true); + + // @@ Delete node->assignment? + + node->assignment = ident; + statements.append(node); + + // Link beginning of statement list. + *statement_pointer = statements.head; + + // Link end of statement list. + HLSLStatement * tail = statements.tail; + tail->nextStatement = node->nextStatement; + + // Update statement pointer. + statement_pointer = &tail->nextStatement; + } + } + + virtual void VisitReturnStatement(HLSLReturnStatement * node) override + { + if (NeedsFlattening(node->expression, 1)) + { + StatementList statements; + HLSLIdentifierExpression * ident = Flatten(node->expression, statements, true); + + // @@ Delete node->expression? + + node->expression = ident; + statements.append(node); + + // Link beginning of statement list. + *statement_pointer = statements.head; + + // Link end of statement list. + HLSLStatement * tail = statements.tail; + tail->nextStatement = node->nextStatement; + + // Update statement pointer. + statement_pointer = &tail->nextStatement; + } + } + + + HLSLDeclaration * BuildTemporaryDeclaration(HLSLExpression * expr) + { + assert(expr->expressionType.baseType != HLSLBaseType_Void); + + HLSLDeclaration * declaration = m_tree->AddNode(expr->fileName, expr->line); + declaration->name = m_tree->AddStringFormat("tmp%d", tmp_index++); + declaration->type = expr->expressionType; + declaration->assignment = expr; + + HLSLIdentifierExpression * ident = (HLSLIdentifierExpression *)expr; + + return declaration; + } + + HLSLExpressionStatement * BuildExpressionStatement(HLSLExpression * expr) + { + HLSLExpressionStatement * statement = m_tree->AddNode(expr->fileName, expr->line); + statement->expression = expr; + return statement; + } + + HLSLIdentifierExpression * AddExpressionStatement(HLSLExpression * expr, StatementList & statements, bool wantIdent) + { + if (wantIdent) { + HLSLDeclaration * declaration = BuildTemporaryDeclaration(expr); + statements.append(declaration); + + HLSLIdentifierExpression * ident = m_tree->AddNode(expr->fileName, expr->line); + ident->name = declaration->name; + ident->expressionType = declaration->type; + return ident; + } + else { + HLSLExpressionStatement * statement = BuildExpressionStatement(expr); + statements.append(statement); + return NULL; + } + } + + HLSLIdentifierExpression * Flatten(HLSLExpression * expr, StatementList & statements, bool wantIdent = true) + { + if (!NeedsFlattening(expr, wantIdent)) { + return AddExpressionStatement(expr, statements, wantIdent); + } + + if (expr->nodeType == HLSLNodeType_UnaryExpression) { + assert(expr->nextExpression == NULL); + + HLSLUnaryExpression * unaryExpr = (HLSLUnaryExpression *)expr; + + HLSLIdentifierExpression * tmp = Flatten(unaryExpr->expression, statements, true); + + HLSLUnaryExpression * newUnaryExpr = m_tree->AddNode(unaryExpr->fileName, unaryExpr->line); + newUnaryExpr->unaryOp = unaryExpr->unaryOp; + newUnaryExpr->expression = tmp; + newUnaryExpr->expressionType = unaryExpr->expressionType; + + return AddExpressionStatement(newUnaryExpr, statements, wantIdent); + } + else if (expr->nodeType == HLSLNodeType_BinaryExpression) { + assert(expr->nextExpression == NULL); + + HLSLBinaryExpression * binaryExpr = (HLSLBinaryExpression *)expr; + + if (IsAssignOp(binaryExpr->binaryOp)) { + // Flatten right hand side only. + HLSLIdentifierExpression * tmp2 = Flatten(binaryExpr->expression2, statements, true); + + HLSLBinaryExpression * newBinaryExpr = m_tree->AddNode(binaryExpr->fileName, binaryExpr->line); + newBinaryExpr->binaryOp = binaryExpr->binaryOp; + newBinaryExpr->expression1 = binaryExpr->expression1; + newBinaryExpr->expression2 = tmp2; + newBinaryExpr->expressionType = binaryExpr->expressionType; + + return AddExpressionStatement(newBinaryExpr, statements, wantIdent); + } + else { + HLSLIdentifierExpression * tmp1 = Flatten(binaryExpr->expression1, statements, true); + HLSLIdentifierExpression * tmp2 = Flatten(binaryExpr->expression2, statements, true); + + HLSLBinaryExpression * newBinaryExpr = m_tree->AddNode(binaryExpr->fileName, binaryExpr->line); + newBinaryExpr->binaryOp = binaryExpr->binaryOp; + newBinaryExpr->expression1 = tmp1; + newBinaryExpr->expression2 = tmp2; + newBinaryExpr->expressionType = binaryExpr->expressionType; + + return AddExpressionStatement(newBinaryExpr, statements, wantIdent); + } + } + else if (expr->nodeType == HLSLNodeType_ConditionalExpression) { + assert(false); + } + else if (expr->nodeType == HLSLNodeType_CastingExpression) { + assert(false); + } + else if (expr->nodeType == HLSLNodeType_LiteralExpression) { + assert(false); + } + else if (expr->nodeType == HLSLNodeType_IdentifierExpression) { + assert(false); + } + else if (expr->nodeType == HLSLNodeType_ConstructorExpression) { + assert(false); + } + else if (expr->nodeType == HLSLNodeType_MemberAccess) { + assert(false); + } + else if (expr->nodeType == HLSLNodeType_ArrayAccess) { + assert(false); + } + else if (expr->nodeType == HLSLNodeType_FunctionCall) { + HLSLFunctionCall * functionCall = (HLSLFunctionCall *)expr; + + // @@ Output function as is? + // @@ We have to flatten function arguments! This is tricky, need to handle input/output arguments. + assert(!NeedsFlattening(functionCall->argument)); + + return AddExpressionStatement(expr, statements, wantIdent); + } + else { + assert(false); + } + return NULL; + } + }; + + +void FlattenExpressions(HLSLTree* tree) { + ExpressionFlattener flattener; + flattener.FlattenExpressions(tree); +} + +} // M4 + diff --git a/src/libprojectM/Renderer/hlslparser/src/HLSLTree.h b/src/libprojectM/Renderer/hlslparser/src/HLSLTree.h new file mode 100755 index 000000000..73c812e3c --- /dev/null +++ b/src/libprojectM/Renderer/hlslparser/src/HLSLTree.h @@ -0,0 +1,969 @@ +#ifndef HLSL_TREE_H +#define HLSL_TREE_H + +//#include "Engine/StringPool.h" +#include "Engine.h" + +#include + +namespace M4 +{ + +enum HLSLNodeType +{ + HLSLNodeType_Root, + HLSLNodeType_Declaration, + HLSLNodeType_Struct, + HLSLNodeType_StructField, + HLSLNodeType_Buffer, + HLSLNodeType_BufferField, + HLSLNodeType_Function, + HLSLNodeType_Argument, + HLSLNodeType_ExpressionStatement, + HLSLNodeType_Expression, + HLSLNodeType_ReturnStatement, + HLSLNodeType_DiscardStatement, + HLSLNodeType_BreakStatement, + HLSLNodeType_ContinueStatement, + HLSLNodeType_IfStatement, + HLSLNodeType_ForStatement, + HLSLNodeType_BlockStatement, + HLSLNodeType_UnaryExpression, + HLSLNodeType_BinaryExpression, + HLSLNodeType_ConditionalExpression, + HLSLNodeType_CastingExpression, + HLSLNodeType_LiteralExpression, + HLSLNodeType_IdentifierExpression, + HLSLNodeType_ConstructorExpression, + HLSLNodeType_MemberAccess, + HLSLNodeType_ArrayAccess, + HLSLNodeType_FunctionCall, + HLSLNodeType_StateAssignment, + HLSLNodeType_SamplerState, + HLSLNodeType_Pass, + HLSLNodeType_Technique, + HLSLNodeType_Attribute, + HLSLNodeType_Pipeline, + HLSLNodeType_Stage, +}; + +enum HLSLTypeDimension +{ + HLSLTypeDimension_None, + HLSLTypeDimension_Scalar, + HLSLTypeDimension_Vector2, + HLSLTypeDimension_Vector3, + HLSLTypeDimension_Vector4, + HLSLTypeDimension_Matrix2x2, + HLSLTypeDimension_Matrix3x3, + HLSLTypeDimension_Matrix4x4, + HLSLTypeDimension_Matrix4x3, + HLSLTypeDimension_Matrix4x2 +}; + +enum HLSLBaseType +{ + HLSLBaseType_Unknown, + HLSLBaseType_Void, + HLSLBaseType_Float, + HLSLBaseType_FirstNumeric = HLSLBaseType_Float, + HLSLBaseType_Float2, + HLSLBaseType_Float3, + HLSLBaseType_Float4, + HLSLBaseType_Float2x2, + HLSLBaseType_Float3x3, + HLSLBaseType_Float4x4, + HLSLBaseType_Float4x3, + HLSLBaseType_Float4x2, + HLSLBaseType_Half, + HLSLBaseType_Half2, + HLSLBaseType_Half3, + HLSLBaseType_Half4, + HLSLBaseType_Half2x2, + HLSLBaseType_Half3x3, + HLSLBaseType_Half4x4, + HLSLBaseType_Half4x3, + HLSLBaseType_Half4x2, + HLSLBaseType_Bool, + HLSLBaseType_FirstInteger = HLSLBaseType_Bool, + HLSLBaseType_Bool2, + HLSLBaseType_Bool3, + HLSLBaseType_Bool4, + HLSLBaseType_Int, + HLSLBaseType_Int2, + HLSLBaseType_Int3, + HLSLBaseType_Int4, + HLSLBaseType_Uint, + HLSLBaseType_Uint2, + HLSLBaseType_Uint3, + HLSLBaseType_Uint4, + /*HLSLBaseType_Short, // @@ Separate dimension from Base type, this is getting out of control. + HLSLBaseType_Short2, + HLSLBaseType_Short3, + HLSLBaseType_Short4, + HLSLBaseType_Ushort, + HLSLBaseType_Ushort2, + HLSLBaseType_Ushort3, + HLSLBaseType_Ushort4,*/ + HLSLBaseType_LastInteger = HLSLBaseType_Uint4, + HLSLBaseType_LastNumeric = HLSLBaseType_Uint4, + HLSLBaseType_Texture, + HLSLBaseType_Sampler, // @@ use type inference to determine sampler type. + HLSLBaseType_Sampler2D, + HLSLBaseType_Sampler3D, + HLSLBaseType_SamplerCube, + HLSLBaseType_Sampler2DShadow, + HLSLBaseType_Sampler2DMS, + HLSLBaseType_Sampler2DArray, + HLSLBaseType_UserDefined, // struct + HLSLBaseType_Expression, // type argument for defined() sizeof() and typeof(). + HLSLBaseType_Auto, + + HLSLBaseType_Count, + HLSLBaseType_NumericCount = HLSLBaseType_LastNumeric - HLSLBaseType_FirstNumeric + 1 +}; + +extern const HLSLTypeDimension BaseTypeDimension[HLSLBaseType_Count]; +extern const HLSLBaseType ScalarBaseType[HLSLBaseType_Count]; + +inline bool IsSamplerType(HLSLBaseType baseType) +{ + return baseType == HLSLBaseType_Sampler || + baseType == HLSLBaseType_Sampler2D || + baseType == HLSLBaseType_Sampler3D || + baseType == HLSLBaseType_SamplerCube || + baseType == HLSLBaseType_Sampler2DShadow || + baseType == HLSLBaseType_Sampler2DMS || + baseType == HLSLBaseType_Sampler2DArray; +} + +inline bool IsMatrixType(HLSLBaseType baseType) +{ + return baseType == HLSLBaseType_Float3x3 || baseType == HLSLBaseType_Float4x4 || baseType == HLSLBaseType_Float4x3 || baseType == HLSLBaseType_Float4x2 || + baseType == HLSLBaseType_Half3x3 || baseType == HLSLBaseType_Half4x4 || baseType == HLSLBaseType_Half4x3 || baseType == HLSLBaseType_Half4x2; +} + +inline bool IsScalarType( HLSLBaseType baseType ) +{ + return baseType == HLSLBaseType_Float || + baseType == HLSLBaseType_Half || + baseType == HLSLBaseType_Bool || + baseType == HLSLBaseType_Int || + baseType == HLSLBaseType_Uint; +} + +inline bool IsVectorType( HLSLBaseType baseType ) +{ + return baseType == HLSLBaseType_Float2 || + baseType == HLSLBaseType_Float3 || + baseType == HLSLBaseType_Float4 || + baseType == HLSLBaseType_Half2 || + baseType == HLSLBaseType_Half3 || + baseType == HLSLBaseType_Half4 || + baseType == HLSLBaseType_Bool2 || + baseType == HLSLBaseType_Bool3 || + baseType == HLSLBaseType_Bool4 || + baseType == HLSLBaseType_Int2 || + baseType == HLSLBaseType_Int3 || + baseType == HLSLBaseType_Int4 || + baseType == HLSLBaseType_Uint2 || + baseType == HLSLBaseType_Uint3 || + baseType == HLSLBaseType_Uint4; +} + + +enum HLSLBinaryOp +{ + HLSLBinaryOp_And, + HLSLBinaryOp_Or, + HLSLBinaryOp_Add, + HLSLBinaryOp_Sub, + HLSLBinaryOp_Mul, + HLSLBinaryOp_Div, + HLSLBinaryOp_Less, + HLSLBinaryOp_Greater, + HLSLBinaryOp_LessEqual, + HLSLBinaryOp_GreaterEqual, + HLSLBinaryOp_Equal, + HLSLBinaryOp_NotEqual, + HLSLBinaryOp_BitAnd, + HLSLBinaryOp_BitOr, + HLSLBinaryOp_BitXor, + HLSLBinaryOp_Assign, + HLSLBinaryOp_AddAssign, + HLSLBinaryOp_SubAssign, + HLSLBinaryOp_MulAssign, + HLSLBinaryOp_DivAssign, +}; + +inline bool IsCompareOp( HLSLBinaryOp op ) +{ + return op == HLSLBinaryOp_Less || + op == HLSLBinaryOp_Greater || + op == HLSLBinaryOp_LessEqual || + op == HLSLBinaryOp_GreaterEqual || + op == HLSLBinaryOp_Equal || + op == HLSLBinaryOp_NotEqual; +} + +inline bool IsArithmeticOp( HLSLBinaryOp op ) +{ + return op == HLSLBinaryOp_Add || + op == HLSLBinaryOp_Sub || + op == HLSLBinaryOp_Mul || + op == HLSLBinaryOp_Div; +} + +inline bool IsLogicOp( HLSLBinaryOp op ) +{ + return op == HLSLBinaryOp_And || + op == HLSLBinaryOp_Or; +} + +inline bool IsAssignOp( HLSLBinaryOp op ) +{ + return op == HLSLBinaryOp_Assign || + op == HLSLBinaryOp_AddAssign || + op == HLSLBinaryOp_SubAssign || + op == HLSLBinaryOp_MulAssign || + op == HLSLBinaryOp_DivAssign; +} + + +enum HLSLUnaryOp +{ + HLSLUnaryOp_Negative, // -x + HLSLUnaryOp_Positive, // +x + HLSLUnaryOp_Not, // !x + HLSLUnaryOp_PreIncrement, // ++x + HLSLUnaryOp_PreDecrement, // --x + HLSLUnaryOp_PostIncrement, // x++ + HLSLUnaryOp_PostDecrement, // x++ + HLSLUnaryOp_BitNot, // ~x +}; + +enum HLSLArgumentModifier +{ + HLSLArgumentModifier_None, + HLSLArgumentModifier_In, + HLSLArgumentModifier_Out, + HLSLArgumentModifier_Inout, + HLSLArgumentModifier_Uniform, + HLSLArgumentModifier_Const, +}; + +enum HLSLTypeFlags +{ + HLSLTypeFlag_None = 0, + HLSLTypeFlag_Const = 0x01, + HLSLTypeFlag_Static = 0x02, + //HLSLTypeFlag_Uniform = 0x04, + //HLSLTypeFlag_Extern = 0x10, + //HLSLTypeFlag_Volatile = 0x20, + //HLSLTypeFlag_Shared = 0x40, + //HLSLTypeFlag_Precise = 0x80, + + HLSLTypeFlag_Input = 0x100, + HLSLTypeFlag_Output = 0x200, + + // Interpolation modifiers. + HLSLTypeFlag_Linear = 0x10000, + HLSLTypeFlag_Centroid = 0x20000, + HLSLTypeFlag_NoInterpolation = 0x40000, + HLSLTypeFlag_NoPerspective = 0x80000, + HLSLTypeFlag_Sample = 0x100000, + + // Misc. + HLSLTypeFlag_NoPromote = 0x200000, +}; + +enum HLSLAttributeType +{ + HLSLAttributeType_Unknown, + HLSLAttributeType_Unroll, + HLSLAttributeType_Branch, + HLSLAttributeType_Flatten, + HLSLAttributeType_NoFastMath, +}; + +enum HLSLAddressSpace +{ + HLSLAddressSpace_Undefined, + HLSLAddressSpace_Constant, + HLSLAddressSpace_Device, + HLSLAddressSpace_Thread, + HLSLAddressSpace_Shared, +}; + + +struct HLSLNode; +struct HLSLRoot; +struct HLSLStatement; +struct HLSLAttribute; +struct HLSLDeclaration; +struct HLSLStruct; +struct HLSLStructField; +struct HLSLBuffer; +struct HLSLFunction; +struct HLSLArgument; +struct HLSLExpressionStatement; +struct HLSLExpression; +struct HLSLBinaryExpression; +struct HLSLLiteralExpression; +struct HLSLIdentifierExpression; +struct HLSLConstructorExpression; +struct HLSLFunctionCall; +struct HLSLArrayAccess; +struct HLSLAttribute; + +struct HLSLType +{ + explicit HLSLType(HLSLBaseType _baseType = HLSLBaseType_Unknown) + { + baseType = _baseType; + samplerType = HLSLBaseType_Float; + typeName = NULL; + array = false; + arraySize = NULL; + flags = 0; + addressSpace = HLSLAddressSpace_Undefined; + } + HLSLBaseType baseType; + HLSLBaseType samplerType; // Half or Float + const char* typeName; // For user defined types. + bool array; + HLSLExpression* arraySize; + int flags; + HLSLAddressSpace addressSpace; +}; + +inline bool IsSamplerType(const HLSLType & type) +{ + return IsSamplerType(type.baseType); +} + +inline bool IsScalarType(const HLSLType & type) +{ + return IsScalarType(type.baseType); +} + +inline bool IsVectorType(const HLSLType & type) +{ + return IsVectorType(type.baseType); +} + + +/** Base class for all nodes in the HLSL AST */ +struct HLSLNode +{ + HLSLNodeType nodeType; + const char* fileName; + int line; +}; + +struct HLSLRoot : public HLSLNode +{ + static const HLSLNodeType s_type = HLSLNodeType_Root; + HLSLRoot() { statement = NULL; } + HLSLStatement* statement; // First statement. +}; + +struct HLSLStatement : public HLSLNode +{ + HLSLStatement() + { + nextStatement = NULL; + attributes = NULL; + hidden = false; + } + HLSLStatement* nextStatement; // Next statement in the block. + HLSLAttribute* attributes; + mutable bool hidden; +}; + +struct HLSLAttribute : public HLSLNode +{ + static const HLSLNodeType s_type = HLSLNodeType_Attribute; + HLSLAttribute() + { + attributeType = HLSLAttributeType_Unknown; + argument = NULL; + nextAttribute = NULL; + } + HLSLAttributeType attributeType; + HLSLExpression* argument; + HLSLAttribute* nextAttribute; +}; + +struct HLSLDeclaration : public HLSLStatement +{ + static const HLSLNodeType s_type = HLSLNodeType_Declaration; + HLSLDeclaration() + { + name = NULL; + registerName = NULL; + semantic = NULL; + nextDeclaration = NULL; + assignment = NULL; + buffer = NULL; + } + const char* name; + HLSLType type; + const char* registerName; // @@ Store register index? + const char* semantic; + HLSLDeclaration* nextDeclaration; // If multiple variables declared on a line. + HLSLExpression* assignment; + HLSLBuffer* buffer; +}; + +struct HLSLStruct : public HLSLStatement +{ + static const HLSLNodeType s_type = HLSLNodeType_Struct; + HLSLStruct() + { + name = NULL; + field = NULL; + } + const char* name; + HLSLStructField* field; // First field in the structure. +}; + +struct HLSLStructField : public HLSLNode +{ + static const HLSLNodeType s_type = HLSLNodeType_StructField; + HLSLStructField() + { + name = NULL; + semantic = NULL; + sv_semantic = NULL; + nextField = NULL; + hidden = false; + } + const char* name; + HLSLType type; + const char* semantic; + const char* sv_semantic; + HLSLStructField* nextField; // Next field in the structure. + bool hidden; +}; + +/** A cbuffer or tbuffer declaration. */ +struct HLSLBuffer : public HLSLStatement +{ + static const HLSLNodeType s_type = HLSLNodeType_Buffer; + HLSLBuffer() + { + name = NULL; + registerName = NULL; + field = NULL; + } + const char* name; + const char* registerName; + HLSLDeclaration* field; +}; + + +/** Function declaration */ +struct HLSLFunction : public HLSLStatement +{ + static const HLSLNodeType s_type = HLSLNodeType_Function; + HLSLFunction() + { + name = NULL; + semantic = NULL; + sv_semantic = NULL; + statement = NULL; + argument = NULL; + numArguments = 0; + numOutputArguments = 0; + forward = NULL; + } + const char* name; + HLSLType returnType; + const char* semantic; + const char* sv_semantic; + int numArguments; + int numOutputArguments; // Includes out and inout arguments. + HLSLArgument* argument; + HLSLStatement* statement; + HLSLFunction* forward; // Which HLSLFunction this one forward-declares +}; + +/** Declaration of an argument to a function. */ +struct HLSLArgument : public HLSLNode +{ + static const HLSLNodeType s_type = HLSLNodeType_Argument; + HLSLArgument() + { + name = NULL; + modifier = HLSLArgumentModifier_None; + semantic = NULL; + sv_semantic = NULL; + defaultValue = NULL; + nextArgument = NULL; + hidden = false; + } + const char* name; + HLSLArgumentModifier modifier; + HLSLType type; + const char* semantic; + const char* sv_semantic; + HLSLExpression* defaultValue; + HLSLArgument* nextArgument; + bool hidden; +}; + +/** A expression which forms a complete statement. */ +struct HLSLExpressionStatement : public HLSLStatement +{ + static const HLSLNodeType s_type = HLSLNodeType_ExpressionStatement; + HLSLExpressionStatement() + { + expression = NULL; + } + HLSLExpression* expression; +}; + +struct HLSLReturnStatement : public HLSLStatement +{ + static const HLSLNodeType s_type = HLSLNodeType_ReturnStatement; + HLSLReturnStatement() + { + expression = NULL; + } + HLSLExpression* expression; +}; + +struct HLSLDiscardStatement : public HLSLStatement +{ + static const HLSLNodeType s_type = HLSLNodeType_DiscardStatement; +}; + +struct HLSLBreakStatement : public HLSLStatement +{ + static const HLSLNodeType s_type = HLSLNodeType_BreakStatement; +}; + +struct HLSLContinueStatement : public HLSLStatement +{ + static const HLSLNodeType s_type = HLSLNodeType_ContinueStatement; +}; + +struct HLSLIfStatement : public HLSLStatement +{ + static const HLSLNodeType s_type = HLSLNodeType_IfStatement; + HLSLIfStatement() + { + condition = NULL; + statement = NULL; + elseStatement = NULL; + isStatic = false; + } + HLSLExpression* condition; + HLSLStatement* statement; + HLSLStatement* elseStatement; + bool isStatic; +}; + +struct HLSLForStatement : public HLSLStatement +{ + static const HLSLNodeType s_type = HLSLNodeType_ForStatement; + HLSLForStatement() + { + initialization = NULL; + condition = NULL; + increment = NULL; + statement = NULL; + } + HLSLDeclaration* initialization; + HLSLExpression* condition; + HLSLExpression* increment; + HLSLStatement* statement; +}; + +struct HLSLBlockStatement : public HLSLStatement +{ + static const HLSLNodeType s_type = HLSLNodeType_BlockStatement; + HLSLBlockStatement() + { + statement = NULL; + } + HLSLStatement* statement; +}; + + +/** Base type for all types of expressions. */ +struct HLSLExpression : public HLSLNode +{ + static const HLSLNodeType s_type = HLSLNodeType_Expression; + HLSLExpression() + { + nextExpression = NULL; + } + HLSLType expressionType; + HLSLExpression* nextExpression; // Used when the expression is part of a list, like in a function call. +}; + +struct HLSLUnaryExpression : public HLSLExpression +{ + static const HLSLNodeType s_type = HLSLNodeType_UnaryExpression; + HLSLUnaryExpression() + { + expression = NULL; + } + HLSLUnaryOp unaryOp; + HLSLExpression* expression; +}; + +struct HLSLBinaryExpression : public HLSLExpression +{ + static const HLSLNodeType s_type = HLSLNodeType_BinaryExpression; + HLSLBinaryExpression() + { + expression1 = NULL; + expression2 = NULL; + } + HLSLBinaryOp binaryOp; + HLSLExpression* expression1; + HLSLExpression* expression2; +}; + +/** ? : construct */ +struct HLSLConditionalExpression : public HLSLExpression +{ + static const HLSLNodeType s_type = HLSLNodeType_ConditionalExpression; + HLSLConditionalExpression() + { + condition = NULL; + trueExpression = NULL; + falseExpression = NULL; + } + HLSLExpression* condition; + HLSLExpression* trueExpression; + HLSLExpression* falseExpression; +}; + +struct HLSLCastingExpression : public HLSLExpression +{ + static const HLSLNodeType s_type = HLSLNodeType_CastingExpression; + HLSLCastingExpression() + { + expression = NULL; + } + HLSLType type; + HLSLExpression* expression; +}; + +/** Float, integer, boolean, etc. literal constant. */ +struct HLSLLiteralExpression : public HLSLExpression +{ + static const HLSLNodeType s_type = HLSLNodeType_LiteralExpression; + HLSLBaseType type; // Note, not all types can be literals. + union + { + bool bValue; + float fValue; + int iValue; + }; +}; + +/** An identifier, typically a variable name or structure field name. */ +struct HLSLIdentifierExpression : public HLSLExpression +{ + static const HLSLNodeType s_type = HLSLNodeType_IdentifierExpression; + HLSLIdentifierExpression() + { + name = NULL; + global = false; + } + const char* name; + bool global; // This is a global variable. +}; + +/** float2(1, 2) */ +struct HLSLConstructorExpression : public HLSLExpression +{ + static const HLSLNodeType s_type = HLSLNodeType_ConstructorExpression; + HLSLConstructorExpression() + { + argument = NULL; + } + HLSLType type; + HLSLExpression* argument; +}; + +/** object.member **/ +struct HLSLMemberAccess : public HLSLExpression +{ + static const HLSLNodeType s_type = HLSLNodeType_MemberAccess; + HLSLMemberAccess() + { + object = NULL; + field = NULL; + swizzle = false; + } + HLSLExpression* object; + const char* field; + bool swizzle; +}; + +/** array[index] **/ +struct HLSLArrayAccess : public HLSLExpression +{ + static const HLSLNodeType s_type = HLSLNodeType_ArrayAccess; + HLSLArrayAccess() + { + array = NULL; + index = NULL; + } + HLSLExpression* array; + HLSLExpression* index; +}; + +struct HLSLFunctionCall : public HLSLExpression +{ + static const HLSLNodeType s_type = HLSLNodeType_FunctionCall; + HLSLFunctionCall() + { + function = NULL; + argument = NULL; + numArguments = 0; + } + const HLSLFunction* function; + HLSLExpression* argument; + int numArguments; +}; + +struct HLSLStateAssignment : public HLSLNode +{ + static const HLSLNodeType s_type = HLSLNodeType_StateAssignment; + HLSLStateAssignment() + { + stateName = NULL; + sValue = NULL; + nextStateAssignment = NULL; + } + + const char* stateName; + int d3dRenderState; + union { + int iValue; + float fValue; + const char * sValue; + }; + HLSLStateAssignment* nextStateAssignment; +}; + +struct HLSLSamplerState : public HLSLExpression // @@ Does this need to be an expression? Does it have a type? I guess type is useful. +{ + static const HLSLNodeType s_type = HLSLNodeType_SamplerState; + HLSLSamplerState() + { + numStateAssignments = 0; + stateAssignments = NULL; + } + + int numStateAssignments; + HLSLStateAssignment* stateAssignments; +}; + +struct HLSLPass : public HLSLNode +{ + static const HLSLNodeType s_type = HLSLNodeType_Pass; + HLSLPass() + { + name = NULL; + numStateAssignments = 0; + stateAssignments = NULL; + nextPass = NULL; + } + + const char* name; + int numStateAssignments; + HLSLStateAssignment* stateAssignments; + HLSLPass* nextPass; +}; + +struct HLSLTechnique : public HLSLStatement +{ + static const HLSLNodeType s_type = HLSLNodeType_Technique; + HLSLTechnique() + { + name = NULL; + numPasses = 0; + passes = NULL; + } + + const char* name; + int numPasses; + HLSLPass* passes; +}; + +struct HLSLPipeline : public HLSLStatement +{ + static const HLSLNodeType s_type = HLSLNodeType_Pipeline; + HLSLPipeline() + { + name = NULL; + numStateAssignments = 0; + stateAssignments = NULL; + } + + const char* name; + int numStateAssignments; + HLSLStateAssignment* stateAssignments; +}; + +struct HLSLStage : public HLSLStatement +{ + static const HLSLNodeType s_type = HLSLNodeType_Stage; + HLSLStage() + { + name = NULL; + statement = NULL; + inputs = NULL; + outputs = NULL; + } + + const char* name; + HLSLStatement* statement; + HLSLDeclaration* inputs; + HLSLDeclaration* outputs; +}; + + +/** + * Abstract syntax tree for parsed HLSL code. + */ +class HLSLTree +{ + +public: + + explicit HLSLTree(Allocator* allocator); + ~HLSLTree(); + + /** Adds a string to the string pool used by the tree. */ + const char* AddString(const char* string); + const char* AddStringFormat(const char* string, ...); + + /** Returns true if the string is contained within the tree. */ + bool GetContainsString(const char* string) const; + + /** Returns the root block in the tree */ + HLSLRoot* GetRoot() const; + + /** Adds a new node to the tree with the specified type. */ + template + T* AddNode(const char* fileName, int line) + { + HLSLNode* node = new (AllocateMemory(sizeof(T))) T(); + node->nodeType = T::s_type; + node->fileName = fileName; + node->line = line; + return static_cast(node); + } + + HLSLFunction * FindFunction(const char * name); + HLSLDeclaration * FindGlobalDeclaration(const char * name, HLSLBuffer ** buffer_out = NULL); + HLSLStruct * FindGlobalStruct(const char * name); + HLSLTechnique * FindTechnique(const char * name); + HLSLPipeline * FindFirstPipeline(); + HLSLPipeline * FindNextPipeline(HLSLPipeline * current); + HLSLPipeline * FindPipeline(const char * name); + HLSLBuffer * FindBuffer(const char * name); + + bool GetExpressionValue(HLSLExpression * expression, int & value); + int GetExpressionValue(HLSLExpression * expression, float values[4]); + + bool NeedsFunction(const char * name); + +private: + + void* AllocateMemory(size_t size); + void AllocatePage(); + +private: + + static const size_t s_nodePageSize = 1024 * 4; + + struct NodePage + { + NodePage* next; + char buffer[s_nodePageSize]; + }; + + Allocator* m_allocator; + StringPool m_stringPool; + HLSLRoot* m_root; + + NodePage* m_firstPage; + NodePage* m_currentPage; + size_t m_currentPageOffset; + +}; + + + +class HLSLTreeVisitor +{ +public: + virtual void VisitType(HLSLType & type); + + virtual void VisitRoot(HLSLRoot * node); + virtual void VisitTopLevelStatement(HLSLStatement * node); + virtual void VisitStatements(HLSLStatement * statement); + virtual void VisitStatement(HLSLStatement * node); + virtual void VisitDeclaration(HLSLDeclaration * node); + virtual void VisitStruct(HLSLStruct * node); + virtual void VisitStructField(HLSLStructField * node); + virtual void VisitBuffer(HLSLBuffer * node); + //virtual void VisitBufferField(HLSLBufferField * node); + virtual void VisitFunction(HLSLFunction * node); + virtual void VisitArgument(HLSLArgument * node); + virtual void VisitExpressionStatement(HLSLExpressionStatement * node); + virtual void VisitExpression(HLSLExpression * node); + virtual void VisitReturnStatement(HLSLReturnStatement * node); + virtual void VisitDiscardStatement(HLSLDiscardStatement * node); + virtual void VisitBreakStatement(HLSLBreakStatement * node); + virtual void VisitContinueStatement(HLSLContinueStatement * node); + virtual void VisitIfStatement(HLSLIfStatement * node); + virtual void VisitForStatement(HLSLForStatement * node); + virtual void VisitBlockStatement(HLSLBlockStatement * node); + virtual void VisitUnaryExpression(HLSLUnaryExpression * node); + virtual void VisitBinaryExpression(HLSLBinaryExpression * node); + virtual void VisitConditionalExpression(HLSLConditionalExpression * node); + virtual void VisitCastingExpression(HLSLCastingExpression * node); + virtual void VisitLiteralExpression(HLSLLiteralExpression * node); + virtual void VisitIdentifierExpression(HLSLIdentifierExpression * node); + virtual void VisitConstructorExpression(HLSLConstructorExpression * node); + virtual void VisitMemberAccess(HLSLMemberAccess * node); + virtual void VisitArrayAccess(HLSLArrayAccess * node); + virtual void VisitFunctionCall(HLSLFunctionCall * node); + virtual void VisitStateAssignment(HLSLStateAssignment * node); + virtual void VisitSamplerState(HLSLSamplerState * node); + virtual void VisitPass(HLSLPass * node); + virtual void VisitTechnique(HLSLTechnique * node); + virtual void VisitPipeline(HLSLPipeline * node); + + + virtual void VisitFunctions(HLSLRoot * root); + virtual void VisitParameters(HLSLRoot * root); + + HLSLFunction * FindFunction(HLSLRoot * root, const char * name); + HLSLDeclaration * FindGlobalDeclaration(HLSLRoot * root, const char * name); + HLSLStruct * FindGlobalStruct(HLSLRoot * root, const char * name); +}; + + +// Tree transformations: +extern void PruneTree(HLSLTree* tree, const char* entryName0, const char* entryName1 = NULL); +extern void SortTree(HLSLTree* tree); +extern void GroupParameters(HLSLTree* tree); +extern void HideUnusedArguments(HLSLFunction * function); +extern bool EmulateAlphaTest(HLSLTree* tree, const char* entryName, float alphaRef = 0.5f); +extern void FlattenExpressions(HLSLTree* tree); + +} // M4 + +#endif diff --git a/src/libprojectM/Renderer/hlslparser/src/MSLGenerator.cpp b/src/libprojectM/Renderer/hlslparser/src/MSLGenerator.cpp new file mode 100755 index 000000000..1c6d5967c --- /dev/null +++ b/src/libprojectM/Renderer/hlslparser/src/MSLGenerator.cpp @@ -0,0 +1,2302 @@ +//============================================================================= +// +// Render/MSLGenerator.cpp +// +// Created by Max McGuire (max@unknownworlds.com) +// Copyright (c) 2013, Unknown Worlds Entertainment, Inc. +// +//============================================================================= + +//#include "Engine/String.h" +//#include "Engine/Log.h" +#include "Engine.h" + +#include "MSLGenerator.h" +#include "HLSLParser.h" +#include "HLSLTree.h" + +#include + +// MSL limitations: +// - Some type conversions and constructors don't work exactly the same way. For example, casts to smaller size vectors are not alloweed in C++. @@ Add more details... +// - Swizzles on scalar types, whether or not it expands them. a_float.x, a_float.xxxx both cause compile errors. +// - Using ints as floats without the trailing .0 makes the compiler sad. +// Unsupported by this generator: +// - Matrix [] access is implemented as a function call, so result cannot be passed as out/inout argument. +// - Matrix [] access is not supported in all l-value expressions. Only simple assignments. +// - No support for boolean vectors and logical operators involving vectors. This is not just in metal. +// - No support for non-float texture types + +namespace M4 +{ + +static void ParseSemantic(const char* semantic, unsigned int* outputLength, unsigned int* outputIndex) +{ + const char* semanticIndex = semantic; + + while (*semanticIndex && !isdigit(*semanticIndex)) + { + semanticIndex++; + } + + *outputLength = semanticIndex - semantic; + *outputIndex = atoi(semanticIndex); +} + +// Parse register name and advance next register index. +static int ParseRegister(const char* registerName, int& nextRegister) +{ + if (!registerName) + { + return nextRegister++; + } + + while (*registerName && !isdigit(*registerName)) + { + registerName++; + } + + if (!*registerName) + { + return nextRegister++; + } + + int result = atoi(registerName); + + if (nextRegister <= result) + { + nextRegister = result + 1; + } + + return result; +} + +inline bool IsHalf(HLSLBaseType type) +{ + return type >= HLSLBaseType_Half && type <= HLSLBaseType_Half4x2; +} + +inline bool IsFloat(HLSLBaseType type) +{ + return type >= HLSLBaseType_Float && type <= HLSLBaseType_Float4x2; +} + + +MSLGenerator::MSLGenerator() +{ + m_tree = NULL; + m_entryName = NULL; + m_target = Target_VertexShader; + m_error = false; + + m_firstClassArgument = NULL; + m_lastClassArgument = NULL; + + m_currentFunction = NULL; +} + +// Copied from GLSLGenerator +void MSLGenerator::Error(const char* format, ...) +{ + // It's not always convenient to stop executing when an error occurs, + // so just track once we've hit an error and stop reporting them until + // we successfully bail out of execution. + if (m_error) + { + return; + } + m_error = true; + + va_list arg; + va_start(arg, format); + Log_ErrorArgList(format, arg); + va_end(arg); +} + +inline void MSLGenerator::AddClassArgument(ClassArgument* arg) +{ + if (m_firstClassArgument == NULL) + { + m_firstClassArgument = arg; + } + else + { + m_lastClassArgument->nextArg = arg; + } + m_lastClassArgument = arg; +} + + +void MSLGenerator::Prepass(HLSLTree* tree, Target target, HLSLFunction* entryFunction) +{ + // Hide unused arguments. @@ It would be good to do this in the other generators too. + HideUnusedArguments(entryFunction); + + HLSLRoot* root = tree->GetRoot(); + HLSLStatement* statement = root->statement; + ASSERT(m_firstClassArgument == NULL); + + HLSLType samplerType(HLSLBaseType_Sampler); + + int nextTextureRegister = 0; + int nextBufferRegister = 0; + + while (statement != NULL) + { + if (statement->nodeType == HLSLNodeType_Declaration) + { + HLSLDeclaration* declaration = (HLSLDeclaration*)statement; + + if (!declaration->hidden && IsSamplerType(declaration->type)) + { + int textureRegister = ParseRegister(declaration->registerName, nextTextureRegister); + + const char * textureName = m_tree->AddStringFormat("%s_texture", declaration->name); + const char * textureRegisterName = m_tree->AddStringFormat("texture(%d)", textureRegister); + AddClassArgument(new ClassArgument(textureName, declaration->type, textureRegisterName)); + + if (declaration->type.baseType != HLSLBaseType_Sampler2DMS) + { + const char * samplerName = m_tree->AddStringFormat("%s_sampler", declaration->name); + const char * samplerRegisterName = m_tree->AddStringFormat("sampler(%d)", textureRegister); + AddClassArgument(new ClassArgument(samplerName, samplerType, samplerRegisterName)); + } + } + } + else if (statement->nodeType == HLSLNodeType_Buffer) + { + HLSLBuffer * buffer = (HLSLBuffer *)statement; + + if (!buffer->hidden) + { + HLSLType type(HLSLBaseType_UserDefined); + type.addressSpace = HLSLAddressSpace_Constant; + type.typeName = m_tree->AddStringFormat("Uniforms_%s", buffer->name); + + int bufferRegister = ParseRegister(buffer->registerName, nextBufferRegister) + m_options.bufferRegisterOffset; + + const char * bufferRegisterName = m_tree->AddStringFormat("buffer(%d)", bufferRegister); + + AddClassArgument(new ClassArgument(buffer->name, type, bufferRegisterName)); + } + } + + statement = statement->nextStatement; + } + + // @@ IC: instance_id parameter must be a function argument. If we find it inside a struct we must move it to the function arguments + // and patch all the references to it! + + // Translate semantics. + HLSLArgument* argument = entryFunction->argument; + while (argument != NULL) + { + if (argument->hidden) + { + argument = argument->nextArgument; + continue; + } + + if (argument->modifier == HLSLArgumentModifier_Out) + { + // Translate output arguments semantics. + if (argument->type.baseType == HLSLBaseType_UserDefined) + { + // Our vertex input is a struct and its fields need to be tagged when we generate that + HLSLStruct* structure = tree->FindGlobalStruct(argument->type.typeName); + if (structure == NULL) + { + Error("Vertex shader output struct '%s' not found in shader\n", argument->type.typeName); + } + + HLSLStructField* field = structure->field; + while (field != NULL) + { + if (!field->hidden) + { + field->sv_semantic = TranslateOutputSemantic(field->semantic); + } + field = field->nextField; + } + } + else + { + argument->sv_semantic = TranslateOutputSemantic(argument->semantic); + } + } + else + { + // Translate input arguments semantics. + if (argument->type.baseType == HLSLBaseType_UserDefined) + { + // Our vertex input is a struct and its fields need to be tagged when we generate that + HLSLStruct* structure = tree->FindGlobalStruct(argument->type.typeName); + if (structure == NULL) + { + Error("Vertex shader input struct '%s' not found in shader\n", argument->type.typeName); + } + + HLSLStructField* field = structure->field; + while (field != NULL) + { + // Hide vertex shader output position from fragment shader. @@ This is messing up the runtime compiler... + /*if (target == Target_FragmentShader && is_semantic(field->semantic, "POSITION")) + { + field->hidden = true; + }*/ + + if (!field->hidden) + { + field->sv_semantic = TranslateInputSemantic(field->semantic); + + // Force type to uint. + if (field->sv_semantic && strcmp(field->sv_semantic, "sample_id") == 0) { + field->type.baseType = HLSLBaseType_Uint; + field->type.flags |= HLSLTypeFlag_NoPromote; + } + + /*if (target == Target_VertexShader && is_semantic(field->semantic, "COLOR")) + { + field->type.flags |= HLSLTypeFlag_Swizzle_BGRA; + }*/ + } + field = field->nextField; + } + } + else + { + argument->sv_semantic = TranslateInputSemantic(argument->semantic); + + // Force type to uint. + if (argument->sv_semantic && strcmp(argument->sv_semantic, "sample_id") == 0) { + argument->type.baseType = HLSLBaseType_Uint; + argument->type.flags |= HLSLTypeFlag_NoPromote; + } + } + } + + argument = argument->nextArgument; + } + + // Translate return value semantic. + if (entryFunction->returnType.baseType != HLSLBaseType_Void) + { + if (entryFunction->returnType.baseType == HLSLBaseType_UserDefined) + { + // Our vertex input is a struct and its fields need to be tagged when we generate that + HLSLStruct* structure = tree->FindGlobalStruct(entryFunction->returnType.typeName); + if (structure == NULL) + { + Error("Vertex shader output struct '%s' not found in shader\n", entryFunction->returnType.typeName); + } + + HLSLStructField* field = structure->field; + while (field != NULL) + { + if (!field->hidden) + { + field->sv_semantic = TranslateOutputSemantic(field->semantic); + } + field = field->nextField; + } + } + else + { + entryFunction->sv_semantic = TranslateOutputSemantic(entryFunction->semantic); + + //Error("MSL only supports COLOR semantic in return \n", entryFunction->returnType.typeName); + } + } +} + +void MSLGenerator::CleanPrepass() +{ + ClassArgument* currentArg = m_firstClassArgument; + while (currentArg != NULL) + { + ClassArgument* nextArg = currentArg->nextArg; + delete currentArg; + currentArg = nextArg; + } + delete currentArg; + m_firstClassArgument = NULL; + m_lastClassArgument = NULL; +} + +void MSLGenerator::PrependDeclarations() +{ + // Any special function stubs we need go here + // That includes special constructors to emulate HLSL not being strict + + + m_writer.WriteLine(0, "#include "); + m_writer.WriteLine(0, "using namespace metal;"); + m_writer.WriteLine(0, ""); + + if (m_tree->NeedsFunction("mad")) + { + if (m_options.usePreciseFma) + { + m_writer.WriteLine(0, "#define mad precise::fma"); + } + else + { + m_writer.WriteLine(0, "inline float mad(float a, float b, float c) {"); + m_writer.WriteLine(1, "return a * b + c;"); + m_writer.WriteLine(0, "}"); + m_writer.WriteLine(0, "inline float2 mad(float2 a, float2 b, float2 c) {"); + m_writer.WriteLine(1, "return a * b + c;"); + m_writer.WriteLine(0, "}"); + m_writer.WriteLine(0, "inline float3 mad(float3 a, float3 b, float3 c) {"); + m_writer.WriteLine(1, "return a * b + c;"); + m_writer.WriteLine(0, "}"); + m_writer.WriteLine(0, "inline float4 mad(float4 a, float4 b, float4 c) {"); + m_writer.WriteLine(1, "return a * b + c;"); + m_writer.WriteLine(0, "}"); + + if (!m_options.treatHalfAsFloat) + { + m_writer.WriteLine(0, "inline half mad(half a, half b, half c) {"); + m_writer.WriteLine(1, "return a * b + c;"); + m_writer.WriteLine(0, "}"); + m_writer.WriteLine(0, "inline half2 mad(half2 a, half2 b, half2 c) {"); + m_writer.WriteLine(1, "return a * b + c;"); + m_writer.WriteLine(0, "}"); + m_writer.WriteLine(0, "inline half3 mad(half3 a, half3 b, half3 c) {"); + m_writer.WriteLine(1, "return a * b + c;"); + m_writer.WriteLine(0, "}"); + m_writer.WriteLine(0, "inline half4 mad(half4 a, half4 b, half4 c) {"); + m_writer.WriteLine(1, "return a * b + c;"); + m_writer.WriteLine(0, "}"); + } + } + } + + // @@ These should not be needed anymore. + /*if (m_tree->NeedsFunction("max")) + { + m_writer.WriteLine(0, "inline float max(int a, float b) {"); + m_writer.WriteLine(1, "return max((float)a, b);"); + m_writer.WriteLine(0, "}"); + m_writer.WriteLine(0, "inline float max(float a, int b) {"); + m_writer.WriteLine(1, "return max(a, (float)b);"); + m_writer.WriteLine(0, "}"); + } + if (m_tree->NeedsFunction("min")) + { + m_writer.WriteLine(0, "inline float min(int a, float b) {"); + m_writer.WriteLine(1, "return min((float)a, b);"); + m_writer.WriteLine(0, "}"); + m_writer.WriteLine(0, "inline float min(float a, int b) {"); + m_writer.WriteLine(1, "return min(a, (float)b);"); + m_writer.WriteLine(0, "}"); + }*/ + + if (m_tree->NeedsFunction("lerp")) + { + //m_writer.WriteLine(0, "template inline T mix(T a, T b, X x) {"); + //m_writer.WriteLine(1, "return mix(a, b, (float)x);"); + //m_writer.WriteLine(0, "}"); + m_writer.WriteLine(0, "#define lerp mix"); + } + + if (m_tree->NeedsFunction("mul")) + { + const char* am = (m_options.flags & Flag_PackMatrixRowMajor) ? "m * a" : "a * m"; + const char* ma = (m_options.flags & Flag_PackMatrixRowMajor) ? "a * m" : "m * a"; + + m_writer.WriteLine(0, "inline float2 mul(float2 a, float2x2 m) { return %s; }", am); + m_writer.WriteLine(0, "inline float3 mul(float3 a, float3x3 m) { return %s; }", am); + m_writer.WriteLine(0, "inline float4 mul(float4 a, float4x4 m) { return %s; }", am); + + m_writer.WriteLine(0, "inline float2 mul(float2x2 m, float2 a) { return %s; }", ma); + m_writer.WriteLine(0, "inline float3 mul(float3x3 m, float3 a) { return %s; }", ma); + m_writer.WriteLine(0, "inline float4 mul(float4x4 m, float4 a) { return %s; }", ma); + + // TODO: Support PackMatrixRowMajor for float3x4/float4x3 + m_writer.WriteLine(0, "inline float3 mul(float4 a, float3x4 m) { return a * m; }"); + m_writer.WriteLine(0, "inline float2 mul(float4 a, float2x4 m) { return a * m; }"); + + if (!m_options.treatHalfAsFloat) + { + m_writer.WriteLine(0, "inline half2 mul(half2 a, half2x2 m) { return %s; }", am); + m_writer.WriteLine(0, "inline half3 mul(half3 a, half3x3 m) { return %s; }", am); + m_writer.WriteLine(0, "inline half4 mul(half4 a, half4x4 m) { return %s; }", am); + + m_writer.WriteLine(0, "inline half2 mul(half2x2 m, half2 a) { return %s; }", ma); + m_writer.WriteLine(0, "inline half3 mul(half3x3 m, half3 a) { return %s; }", ma); + m_writer.WriteLine(0, "inline half4 mul(half4x4 m, half4 a) { return %s; }", ma); + + // TODO: Support PackMatrixRowMajor for half3x4/half4x3 + m_writer.WriteLine(0, "inline half3 mul(half4 a, half3x4 m) { return a * m; }"); + m_writer.WriteLine(0, "inline half2 mul(half4 a, half2x4 m) { return a * m; }"); + } + + } + + // @@ How do we know if these will be needed? We could write these after parsing the whole file and prepend them. + m_writer.WriteLine(0, "inline float4 column(float4x4 m, int i) {"); + m_writer.WriteLine(1, "return float4(m[0][i], m[1][i], m[2][i], m[3][i]);"); + m_writer.WriteLine(0, "}"); + + m_writer.WriteLine(0, "inline float3 column(float3x4 m, int i) {"); + m_writer.WriteLine(1, "return float3(m[0][i], m[1][i], m[2][i]);"); + m_writer.WriteLine(0, "}"); + + m_writer.WriteLine(0, "inline float2 column(float2x4 m, int i) {"); + m_writer.WriteLine(1, "return float2(m[0][i], m[1][i]);"); + m_writer.WriteLine(0, "}"); + + m_writer.WriteLine(0, "inline float4 set_column(thread float4x4& m, int i, float4 v) {"); + m_writer.WriteLine(1, " m[0][i] = v.x; m[1][i] = v.y; m[2][i] = v.z; m[3][i] = v.w; return v;"); + m_writer.WriteLine(0, "}"); + + m_writer.WriteLine(0, "inline float3 set_column(thread float3x4& m, int i, float3 v) {"); + m_writer.WriteLine(1, " m[0][i] = v.x; m[1][i] = v.y; m[2][i] = v.z; return v;"); + m_writer.WriteLine(0, "}"); + + m_writer.WriteLine(0, "inline float2 set_column(thread float2x4& m, int i, float2 v) {"); + m_writer.WriteLine(1, " m[0][i] = v.x; m[1][i] = v.y; return v;"); + m_writer.WriteLine(0, "}"); + + m_writer.WriteLine(0, "inline float3x3 matrix_ctor(float4x4 m) {"); + m_writer.WriteLine(1, " return float3x3(m[0].xyz, m[1].xyz, m[2].xyz);"); + m_writer.WriteLine(0, "}"); + + + if (m_tree->NeedsFunction("clip")) + { + m_writer.WriteLine(0, "inline void clip(float x) {"); + m_writer.WriteLine(1, "if (x < 0.0) discard_fragment();"); + m_writer.WriteLine(0, "}"); + } + if (m_tree->NeedsFunction("rcp")) + { + m_writer.WriteLine(0, "inline float rcp(float x) {"); + m_writer.WriteLine(1, "return 1.0f / x;"); + m_writer.WriteLine(0, "}"); + + m_writer.WriteLine(0, "inline half rcp(half x) {"); + m_writer.WriteLine(1, "return 1.0h / x;"); + m_writer.WriteLine(0, "}"); + } + + if (m_tree->NeedsFunction("ddx")) m_writer.WriteLine(0, "#define ddx dfdx"); + if (m_tree->NeedsFunction("ddy")) m_writer.WriteLine(0, "#define ddy dfdy"); + if (m_tree->NeedsFunction("frac")) m_writer.WriteLine(0, "#define frac fract"); + + //m_writer.WriteLine(0, "#define mad fma"); // @@ This doesn't seem to work. + + const char * samplerType = "float"; + /*if (m_options.halfTextureSamplers) + { + samplerType = "half"; + }*/ + const char * intType = "int"; + const char * uintType = "uint"; + if (m_options.use16BitIntegers) { + intType = "short"; + uintType = "ushort"; + } + + if (m_tree->NeedsFunction("tex2D") || + m_tree->NeedsFunction("tex2Dlod") || + m_tree->NeedsFunction("tex2Dgrad") || + m_tree->NeedsFunction("tex2Dbias") || + m_tree->NeedsFunction("tex2Dfetch")) + { + m_writer.WriteLine(0, "struct Texture2DSampler {"); + m_writer.WriteLine(1, "Texture2DSampler(thread const texture2d& t, thread const sampler& s) : t(t), s(s) {};"); + m_writer.WriteLine(1, "const thread texture2d& t;"); + m_writer.WriteLine(1, "const thread sampler& s;"); + m_writer.WriteLine(0, "};"); + + if (!m_options.treatHalfAsFloat) { + m_writer.WriteLine(0, "struct Texture2DHalfSampler {"); + m_writer.WriteLine(1, "Texture2DHalfSampler(thread const texture2d& t, thread const sampler& s) : t(t), s(s) {};"); + m_writer.WriteLine(1, "const thread texture2d& t;"); + m_writer.WriteLine(1, "const thread sampler& s;"); + m_writer.WriteLine(0, "};"); + } + } + + if (m_tree->NeedsFunction("tex2D")) + { + m_writer.WriteLine(0, "inline float4 tex2D(Texture2DSampler ts, float2 texCoord) {"); + m_writer.WriteLine(1, "return ts.t.sample(ts.s, texCoord);"); + m_writer.WriteLine(0, "}"); + + if (!m_options.treatHalfAsFloat) + { + m_writer.WriteLine(0, "inline half4 tex2D(Texture2DHalfSampler ts, float2 texCoord) {"); + m_writer.WriteLine(1, "return ts.t.sample(ts.s, texCoord);"); + m_writer.WriteLine(0, "}"); + } + } + if (m_tree->NeedsFunction("tex2Dlod")) + { + m_writer.WriteLine(0, "inline float4 tex2Dlod(Texture2DSampler ts, float4 texCoordMip) {"); + m_writer.WriteLine(1, "return ts.t.sample(ts.s, texCoordMip.xy, level(texCoordMip.w));"); + m_writer.WriteLine(0, "}"); + + if (!m_options.treatHalfAsFloat) + { + m_writer.WriteLine(0, "inline half4 tex2Dlod(Texture2DHalfSampler ts, float4 texCoordMip) {"); + m_writer.WriteLine(1, "return ts.t.sample(ts.s, texCoordMip.xy, level(texCoordMip.w));"); + m_writer.WriteLine(0, "}"); + } + } + if (m_tree->NeedsFunction("tex2Dgrad")) + { + m_writer.WriteLine(0, "inline float4 tex2Dgrad(Texture2DSampler ts, float2 texCoord, float2 gradx, float2 grady) {"); + m_writer.WriteLine(1, "return ts.t.sample(ts.s, texCoord.xy, gradient2d(gradx, grady));"); + m_writer.WriteLine(0, "}"); + } + if (m_tree->NeedsFunction("tex2Dbias")) + { + m_writer.WriteLine(0, "inline float4 tex2Dbias(Texture2DSampler ts, float4 texCoordBias) {"); + m_writer.WriteLine(1, "return ts.t.sample(ts.s, texCoordBias.xy, bias(texCoordBias.w));"); + m_writer.WriteLine(0, "}"); + + if (!m_options.treatHalfAsFloat) + { + m_writer.WriteLine(0, "inline half4 tex2Dbias(Texture2DHalfSampler ts, float4 texCoordBias) {"); + m_writer.WriteLine(1, "return ts.t.sample(ts.s, texCoordBias.xy, bias(texCoordBias.w));"); + m_writer.WriteLine(0, "}"); + } + } + if (m_tree->NeedsFunction("tex2Dfetch")) + { + // @@ not used? not tested? + m_writer.WriteLine(0, "inline float4 tex2Dfetch(Texture2DSampler ts, %s2 texCoord) {", intType); + m_writer.WriteLine(1, "return ts.t.read((uint2)texCoord);"); + m_writer.WriteLine(0, "}"); + } + + if (m_tree->NeedsFunction("tex3D") || + m_tree->NeedsFunction("tex3Dlod")) + { + m_writer.WriteLine(0, "struct Texture3DSampler {"); + m_writer.WriteLine(1, "Texture3DSampler(thread const texture3d& t, thread const sampler& s) : t(t), s(s) {};"); + m_writer.WriteLine(1, "const thread texture3d& t;"); + m_writer.WriteLine(1, "const thread sampler& s;"); + m_writer.WriteLine(0, "};"); + } + + if (m_tree->NeedsFunction("tex3D")) + { + m_writer.WriteLine(0, "inline float4 tex3D(Texture3DSampler ts, float3 texCoord) {"); + m_writer.WriteLine(1, "return ts.t.sample(ts.s, texCoord);"); + m_writer.WriteLine(0, "}"); + } + if (m_tree->NeedsFunction("tex3Dlod")) + { + m_writer.WriteLine(0, "inline float4 tex3Dlod(Texture3DSampler ts, float4 texCoordMip) {"); + m_writer.WriteLine(1, "return ts.t.sample(ts.s, texCoordMip.xyz, level(texCoordMip.w));"); + m_writer.WriteLine(0, "}"); + } + + if (m_tree->NeedsFunction("texCUBE") || + m_tree->NeedsFunction("texCUBElod") || + m_tree->NeedsFunction("texCUBEbias")) + { + m_writer.WriteLine(0, "struct TextureCubeSampler {"); + m_writer.WriteLine(1, "TextureCubeSampler(thread const texturecube& t, thread const sampler& s) : t(t), s(s) {};"); + m_writer.WriteLine(1, "const thread texturecube& t;"); + m_writer.WriteLine(1, "const thread sampler& s;"); + m_writer.WriteLine(0, "};"); + } + + if (m_tree->NeedsFunction("texCUBE")) + { + m_writer.WriteLine(0, "inline float4 texCUBE(TextureCubeSampler ts, float3 texCoord) {"); + m_writer.WriteLine(1, "return ts.t.sample(ts.s, texCoord);"); + m_writer.WriteLine(0, "}"); + } + + if (m_tree->NeedsFunction("texCUBElod")) + { + m_writer.WriteLine(0, "inline float4 texCUBElod(TextureCubeSampler ts, float4 texCoordMip) {"); + m_writer.WriteLine(1, "return ts.t.sample(ts.s, texCoordMip.xyz, level(texCoordMip.w));"); + m_writer.WriteLine(0, "}"); + } + + if (m_tree->NeedsFunction("texCUBEbias")) + { + m_writer.WriteLine(0, "inline float4 texCUBEbias(TextureCubeSampler ts, float4 texCoordBias) {"); + m_writer.WriteLine(1, "return ts.t.sample(ts.s, texCoordBias.xyz, bias(texCoordBias.w));"); + m_writer.WriteLine(0, "}"); + } + + if (m_tree->NeedsFunction("tex2Dcmp")) + { + m_writer.WriteLine(0, "struct Texture2DShadowSampler {"); + m_writer.WriteLine(1, "Texture2DShadowSampler(thread const depth2d& t, thread const sampler& s) : t(t), s(s) {};"); + m_writer.WriteLine(1, "const thread depth2d& t;"); + m_writer.WriteLine(1, "const thread sampler& s;"); + m_writer.WriteLine(0, "};"); + + m_writer.WriteLine(0, "inline float4 tex2Dcmp(Texture2DShadowSampler ts, float4 texCoordCompare) {"); + if (m_options.flags & Flag_ConstShadowSampler) + { + // iOS Metal requires that the sampler in sample_compare is a compile-time constant + m_writer.WriteLine(1, "constexpr sampler shadow_constant_sampler(mip_filter::none, min_filter::linear, mag_filter::linear, address::clamp_to_edge, compare_func::less);"); + m_writer.WriteLine(1, "return ts.t.sample_compare(shadow_constant_sampler, texCoordCompare.xy, texCoordCompare.z);"); + } + else + { + m_writer.WriteLine(1, "return ts.t.sample_compare(ts.s, texCoordCompare.xy, texCoordCompare.z);"); + } + m_writer.WriteLine(0, "}"); + } + + if (m_tree->NeedsFunction("tex2DMSfetch")) + { + m_writer.WriteLine(0, "inline float4 tex2DMSfetch(texture2d_ms t, %s2 texCoord, %s sample) {", intType, intType); + m_writer.WriteLine(1, "return t.read((uint2)texCoord, (uint)sample);"); + m_writer.WriteLine(0, "}"); + } + + if (m_tree->NeedsFunction("tex2DArray")) + { + m_writer.WriteLine(0, "struct Texture2DArraySampler {"); + m_writer.WriteLine(1, "const thread texture2d_array& t;"); + m_writer.WriteLine(1, "const thread sampler& s;"); + m_writer.WriteLine(1, "Texture2DArraySampler(thread const texture2d_array& t, thread const sampler& s) : t(t), s(s) {};"); + m_writer.WriteLine(0, "};"); + + m_writer.WriteLine(0, "inline float4 tex2DArray(Texture2DArraySampler ts, float3 texCoord) {"); + m_writer.WriteLine(1, "return ts.t.sample(ts.s, texCoord.xy, texCoord.z + 0.5);"); // 0.5 offset needed on nvidia gpus + m_writer.WriteLine(0, "}"); + } +} + +bool MSLGenerator::Generate(HLSLTree* tree, Target target, const char* entryName, const Options& options) +{ + m_firstClassArgument = NULL; + m_lastClassArgument = NULL; + + m_tree = tree; + m_target = target; + ASSERT(m_target == Target_VertexShader || m_target == Target_FragmentShader); + m_entryName = entryName; + m_options = options; + + m_writer.Reset(); + + // Find entry point function + HLSLFunction* entryFunction = tree->FindFunction(entryName); + if (entryFunction == NULL) + { + Error("Entry point '%s' doesn't exist\n", entryName); + return false; + } + + Prepass(tree, target, entryFunction); + + PrependDeclarations(); + + // In MSL, uniforms are parameters for the entry point, not globals: + // to limit code rewriting, we wrap the entire original shader into a class. + // Uniforms are then passed to the constructor and copied to member variables. + const char* shaderClassName = (target == MSLGenerator::Target_VertexShader) ? "Vertex_Shader" : "Pixel_Shader"; + m_writer.WriteLine(0, "struct %s {", shaderClassName); + + HLSLRoot* root = m_tree->GetRoot(); + OutputStatements(1, root->statement); + + // Generate constructor + m_writer.WriteLine(0, ""); + m_writer.BeginLine(1); + + m_writer.Write("%s(", shaderClassName); + const ClassArgument* currentArg = m_firstClassArgument; + while (currentArg != NULL) + { + if (currentArg->type.addressSpace == HLSLAddressSpace_Constant) m_writer.Write("constant "); + else m_writer.Write("thread "); + + m_writer.Write("%s & %s", GetTypeName(currentArg->type), currentArg->name); + + currentArg = currentArg->nextArg; + if (currentArg) m_writer.Write(", "); + } + m_writer.Write(")"); + + currentArg = m_firstClassArgument; + if (currentArg) m_writer.Write(" : "); + while (currentArg != NULL) + { + m_writer.Write("%s(%s)", currentArg->name, currentArg->name); + currentArg = currentArg->nextArg; + if (currentArg) m_writer.Write(", "); + } + m_writer.EndLine(" {}"); + + m_writer.WriteLine(0, "};"); // Class + + + // Generate real entry point, the one called by Metal + m_writer.WriteLine(0, ""); + + // If function return value has a non-color output semantic, declare a temporary struct for the output. + bool wrapReturnType = false; + if (entryFunction->sv_semantic != NULL && strcmp(entryFunction->sv_semantic, "color(0)") != 0) + { + wrapReturnType = true; + + m_writer.WriteLine(0, "struct %s_output { %s tmp [[%s]]; };", entryName, GetTypeName(entryFunction->returnType), entryFunction->sv_semantic); + + m_writer.WriteLine(0, ""); + } + + + m_writer.BeginLine(0); + + // @@ Add/Translate function attributes. + // entryFunction->attributes + + if (m_target == Target_VertexShader) + { + m_writer.Write("vertex "); + } + else + { + m_writer.Write("fragment "); + } + + // Return type. + if (wrapReturnType) + { + m_writer.Write("%s_output", entryName); + } + else + { + if (entryFunction->returnType.baseType == HLSLBaseType_UserDefined) + { + m_writer.Write("%s::", shaderClassName); + } + m_writer.Write("%s", GetTypeName(entryFunction->returnType)); + } + + m_writer.Write(" %s(", entryName); + + int argumentCount = 0; + HLSLArgument* argument = entryFunction->argument; + while (argument != NULL) + { + if (!argument->hidden) + { + if (argument->type.baseType == HLSLBaseType_UserDefined) + { + m_writer.Write("%s::", shaderClassName); + } + m_writer.Write("%s %s", GetTypeName(argument->type), argument->name); + + // @@ IC: We are assuming that the first argument is the 'stage_in'. + if (argument->type.baseType == HLSLBaseType_UserDefined && argument == entryFunction->argument) + { + m_writer.Write(" [[stage_in]]"); + } + else if (argument->sv_semantic) + { + m_writer.Write(" [[%s]]", argument->sv_semantic); + } + argumentCount++; + } + argument = argument->nextArgument; + if (argument && !argument->hidden) + { + m_writer.Write(", "); + } + } + + currentArg = m_firstClassArgument; + if (argumentCount && currentArg != NULL) + { + m_writer.Write(", "); + } + while (currentArg != NULL) + { + //if (currentArg->type.addressSpace == HLSLAddressSpace_Constant) m_writer.Write("constant "); + //else m_writer.Write("thread "); + + if (currentArg->type.baseType == HLSLBaseType_UserDefined) + { + m_writer.Write("constant %s::%s & %s [[%s]]", shaderClassName, currentArg->type.typeName, currentArg->name, currentArg->registerName); + } + else + { + m_writer.Write("%s %s [[%s]]", GetTypeName(currentArg->type), currentArg->name, currentArg->registerName); + } + + currentArg = currentArg->nextArg; + if (currentArg) + { + m_writer.Write(", "); + } + } + m_writer.EndLine(") {"); + + // Create the helper class instance and call the entry point from the original shader + m_writer.BeginLine(1); + m_writer.Write("%s %s", shaderClassName, entryName); + + currentArg = m_firstClassArgument; + if (currentArg) + { + m_writer.Write("("); + + while (currentArg != NULL) + { + m_writer.Write("%s", currentArg->name); + currentArg = currentArg->nextArg; + if (currentArg) + { + m_writer.Write(", "); + } + } + + m_writer.Write(")"); + } + m_writer.EndLine(";"); + + m_writer.BeginLine(1); + + if (wrapReturnType) + { + m_writer.Write("%s_output output; output.tmp = %s.%s(", entryName, entryName, entryName); + } + else + { + m_writer.Write("return %s.%s(", entryName, entryName); + } + + argument = entryFunction->argument; + while (argument != NULL) + { + if (!argument->hidden) + { + m_writer.Write("%s", argument->name); + } + argument = argument->nextArgument; + if (argument && !argument->hidden) + { + m_writer.Write(", "); + } + } + + m_writer.EndLine(");"); + + if (wrapReturnType) + { + m_writer.WriteLine(1, "return output;"); + } + + m_writer.WriteLine(0, "}"); + + CleanPrepass(); + m_tree = NULL; + + // Any final check goes here, but shouldn't be needed as the Metal compiler is solid + + return !m_error; +} + +const char* MSLGenerator::GetResult() const +{ + return m_writer.GetResult(); +} + +void MSLGenerator::OutputStatements(int indent, HLSLStatement* statement) +{ + // Main generator loop: called recursively + while (statement != NULL) + { + if (statement->hidden) + { + statement = statement->nextStatement; + continue; + } + + OutputAttributes(indent, statement->attributes); + + if (statement->nodeType == HLSLNodeType_Declaration) + { + HLSLDeclaration* declaration = static_cast(statement); + + if (declaration->assignment && declaration->assignment->nodeType == HLSLNodeType_FunctionCall) + { + OutputFunctionCallStatement(indent, (HLSLFunctionCall*)declaration->assignment, declaration); + } + else + { + m_writer.BeginLine(indent, declaration->fileName, declaration->line); + OutputDeclaration(declaration); + m_writer.EndLine(";"); + } + } + else if (statement->nodeType == HLSLNodeType_Struct) + { + HLSLStruct* structure = static_cast(statement); + OutputStruct(indent, structure); + } + else if (statement->nodeType == HLSLNodeType_Buffer) + { + HLSLBuffer* buffer = static_cast(statement); + OutputBuffer(indent, buffer); + } + else if (statement->nodeType == HLSLNodeType_Function) + { + HLSLFunction* function = static_cast(statement); + + if (!function->forward) + { + OutputFunction(indent, function); + } + } + else if (statement->nodeType == HLSLNodeType_ExpressionStatement) + { + HLSLExpressionStatement* expressionStatement = static_cast(statement); + HLSLExpression* expression = expressionStatement->expression; + + if (expression->nodeType == HLSLNodeType_FunctionCall) + { + OutputFunctionCallStatement(indent, (HLSLFunctionCall*)expression, NULL); + } + else + { + m_writer.BeginLine(indent, statement->fileName, statement->line); + OutputExpression(expressionStatement->expression, NULL); + m_writer.EndLine(";"); + } + } + else if (statement->nodeType == HLSLNodeType_ReturnStatement) + { + HLSLReturnStatement* returnStatement = static_cast(statement); + if (m_currentFunction->numOutputArguments > 0) + { + m_writer.BeginLine(indent, returnStatement->fileName, returnStatement->line); + m_writer.Write("return { "); + + int numArguments = 0; + if (returnStatement->expression != NULL) + { + OutputTypedExpression(m_currentFunction->returnType, returnStatement->expression, NULL); + numArguments++; + } + + HLSLArgument * argument = m_currentFunction->argument; + while (argument != NULL) + { + if (argument->modifier == HLSLArgumentModifier_Out || argument->modifier == HLSLArgumentModifier_Inout) + { + if (numArguments) m_writer.Write(", "); + m_writer.Write("%s", argument->name); + numArguments++; + } + argument = argument->nextArgument; + } + + m_writer.EndLine(" };"); + } + else if (returnStatement->expression != NULL) + { + m_writer.BeginLine(indent, returnStatement->fileName, returnStatement->line); + m_writer.Write("return "); + OutputTypedExpression(m_currentFunction->returnType, returnStatement->expression, NULL); + m_writer.EndLine(";"); + } + else + { + m_writer.WriteLineTagged(indent, returnStatement->fileName, returnStatement->line, "return;"); + } + } + else if (statement->nodeType == HLSLNodeType_DiscardStatement) + { + HLSLDiscardStatement* discardStatement = static_cast(statement); + m_writer.WriteLineTagged(indent, discardStatement->fileName, discardStatement->line, "discard_fragment();"); + } + else if (statement->nodeType == HLSLNodeType_BreakStatement) + { + HLSLBreakStatement* breakStatement = static_cast(statement); + m_writer.WriteLineTagged(indent, breakStatement->fileName, breakStatement->line, "break;"); + } + else if (statement->nodeType == HLSLNodeType_ContinueStatement) + { + HLSLContinueStatement* continueStatement = static_cast(statement); + m_writer.WriteLineTagged(indent, continueStatement->fileName, continueStatement->line, "continue;"); + } + else if (statement->nodeType == HLSLNodeType_IfStatement) + { + HLSLIfStatement* ifStatement = static_cast(statement); + + if (ifStatement->isStatic) { + int value; + if (!m_tree->GetExpressionValue(ifStatement->condition, value)) { + Error("@if condition could not be evaluated.\n"); + } + if (value != 0) { + OutputStatements(indent + 1, ifStatement->statement); + } + else if (ifStatement->elseStatement != NULL) { + OutputStatements(indent + 1, ifStatement->elseStatement); + } + } + else { + m_writer.BeginLine(indent, ifStatement->fileName, ifStatement->line); + m_writer.Write("if ("); + OutputExpression(ifStatement->condition, NULL); + m_writer.Write(") {"); + m_writer.EndLine(); + OutputStatements(indent + 1, ifStatement->statement); + m_writer.WriteLine(indent, "}"); + if (ifStatement->elseStatement != NULL) + { + m_writer.WriteLine(indent, "else {"); + OutputStatements(indent + 1, ifStatement->elseStatement); + m_writer.WriteLine(indent, "}"); + } + } + } + else if (statement->nodeType == HLSLNodeType_ForStatement) + { + HLSLForStatement* forStatement = static_cast(statement); + m_writer.BeginLine(indent, forStatement->fileName, forStatement->line); + m_writer.Write("for ("); + OutputDeclaration(forStatement->initialization); + m_writer.Write("; "); + OutputExpression(forStatement->condition, NULL); + m_writer.Write("; "); + OutputExpression(forStatement->increment, NULL); + m_writer.Write(") {"); + m_writer.EndLine(); + OutputStatements(indent + 1, forStatement->statement); + m_writer.WriteLine(indent, "}"); + } + else if (statement->nodeType == HLSLNodeType_BlockStatement) + { + HLSLBlockStatement* blockStatement = static_cast(statement); + m_writer.WriteLineTagged(indent, blockStatement->fileName, blockStatement->line, "{"); + OutputStatements(indent + 1, blockStatement->statement); + m_writer.WriteLine(indent, "}"); + } + else if (statement->nodeType == HLSLNodeType_Technique) + { + // Techniques are ignored. + } + else if (statement->nodeType == HLSLNodeType_Pipeline) + { + // Pipelines are ignored. + } + else + { + // Unhandled statement type. + ASSERT(0); + } + + statement = statement->nextStatement; + } +} + +// Called by OutputStatements + +void MSLGenerator::OutputAttributes(int indent, HLSLAttribute* attribute) +{ + // IC: These do not appear to exist in MSL. + while (attribute != NULL) { + if (attribute->attributeType == HLSLAttributeType_Unroll) + { + // @@ Do any of these work? + //m_writer.WriteLine(indent, attribute->fileName, attribute->line, "#pragma unroll"); + //m_writer.WriteLine(indent, attribute->fileName, attribute->line, "[[unroll]]"); + } + else if (attribute->attributeType == HLSLAttributeType_Flatten) + { + // @@ + } + else if (attribute->attributeType == HLSLAttributeType_Branch) + { + // @@ + } + + attribute = attribute->nextAttribute; + } +} + +void MSLGenerator::OutputDeclaration(HLSLDeclaration* declaration) +{ + if (IsSamplerType(declaration->type)) + { + // Declare a texture and a sampler instead + // We do not handle multiple textures on the same line + ASSERT(declaration->nextDeclaration == NULL); + const char * typeName = "float"; + if (declaration->type.samplerType == HLSLBaseType_Half && !m_options.treatHalfAsFloat) + { + typeName = "half"; + } + + if (declaration->type.baseType == HLSLBaseType_Sampler2D) + { + m_writer.Write("thread texture2d<%s>& %s_texture; thread sampler& %s_sampler", typeName, declaration->name, declaration->name); + } + else if (declaration->type.baseType == HLSLBaseType_Sampler3D) + { + m_writer.Write("thread texture3d<%s>& %s_texture; thread sampler& %s_sampler", typeName, declaration->name, declaration->name); + } + else if (declaration->type.baseType == HLSLBaseType_SamplerCube) + { + m_writer.Write("thread texturecube<%s>& %s_texture; thread sampler& %s_sampler", typeName, declaration->name, declaration->name); + } + else if (declaration->type.baseType == HLSLBaseType_Sampler2DShadow) + { + m_writer.Write("thread depth2d& %s_texture; thread sampler& %s_sampler", declaration->name, declaration->name); + } + else if (declaration->type.baseType == HLSLBaseType_Sampler2DMS) + { + m_writer.Write("thread texture2d_ms<%s>& %s_texture", typeName, declaration->name); + } + else if (declaration->type.baseType == HLSLBaseType_Sampler2DArray) + { + m_writer.Write("thread texture2d_array<%s>& %s_texture; thread sampler& %s_sampler", typeName, declaration->name, declaration->name); + } + else + { + m_writer.Write(""); + } + } + else + { + OutputDeclaration(declaration->type, declaration->name, declaration->assignment); + + declaration = declaration->nextDeclaration; + while(declaration != NULL) + { + m_writer.Write(","); + OutputDeclarationBody(declaration->type, declaration->name, declaration->assignment); + declaration = declaration->nextDeclaration; + }; + } +} + +void MSLGenerator::OutputStruct(int indent, HLSLStruct* structure) +{ + m_writer.WriteLineTagged(indent, structure->fileName, structure->line, "struct %s {", structure->name); + HLSLStructField* field = structure->field; + while (field != NULL) + { + if (!field->hidden) + { + m_writer.BeginLine(indent + 1, field->fileName, field->line); + OutputDeclaration(field->type, field->name, NULL); + if (field->sv_semantic) + { + m_writer.Write(" [[%s]]", field->sv_semantic); + } + + m_writer.Write(";"); + m_writer.EndLine(); + } + field = field->nextField; + } + m_writer.WriteLine(indent, "};"); +} + +void MSLGenerator::OutputBuffer(int indent, HLSLBuffer* buffer) +{ + HLSLDeclaration* field = buffer->field; + + m_writer.BeginLine(indent, buffer->fileName, buffer->line); + m_writer.Write("struct Uniforms_%s", buffer->name); + m_writer.EndLine(" {"); + while (field != NULL) + { + if (!field->hidden) + { + m_writer.BeginLine(indent + 1, field->fileName, field->line); + OutputDeclaration(field->type, field->name, field->assignment, false, false, /*alignment=*/16); + m_writer.EndLine(";"); + } + field = (HLSLDeclaration*)field->nextStatement; + } + m_writer.WriteLine(indent, "};"); + + m_writer.WriteLine(indent, "constant Uniforms_%s & %s;", buffer->name, buffer->name); +} + +void MSLGenerator::OutputFunction(int indent, HLSLFunction* function) +{ + const char* functionName = function->name; + const char* returnTypeName = GetTypeName(function->returnType); + + // Declare output tuple. + if (function->numOutputArguments > 0) + { + returnTypeName = m_tree->AddStringFormat("%s_out%d", functionName, function->line); // @@ Find a better way to generate unique name. + + m_writer.BeginLine(indent, function->fileName, function->line); + m_writer.Write("struct %s { ", returnTypeName); + m_writer.EndLine(); + + if (function->returnType.baseType != HLSLBaseType_Void) + { + m_writer.BeginLine(indent+1, function->fileName, function->line); + OutputDeclaration(function->returnType, "__result", /*defaultValue=*/NULL, /*isRef=*/false, /*isConst=*/false); + m_writer.EndLine(";"); + } + + HLSLArgument * argument = function->argument; + while (argument != NULL) + { + if (argument->modifier == HLSLArgumentModifier_Out || argument->modifier == HLSLArgumentModifier_Inout) + { + m_writer.BeginLine(indent+1, function->fileName, function->line); + OutputDeclaration(argument->type, argument->name, /*defaultValue=*/NULL, /*isRef=*/false, /*isConst=*/false); + m_writer.EndLine(";"); + } + argument = argument->nextArgument; + } + + m_writer.WriteLine(indent, "};"); + + // Create unique function name to avoid collision with overloads and different return types. + m_writer.BeginLine(indent, function->fileName, function->line); + m_writer.Write("%s %s_%d(", returnTypeName, functionName, function->line); + } + else + { + m_writer.BeginLine(indent, function->fileName, function->line); + m_writer.Write("%s %s(", returnTypeName, functionName); + } + + OutputArguments(function->argument); + + m_writer.EndLine(") {"); + m_currentFunction = function; + + // Local declarations for output arguments. + HLSLArgument * argument = function->argument; + while (argument != NULL) + { + if (argument->modifier == HLSLArgumentModifier_Out) + { + m_writer.BeginLine(indent + 1, function->fileName, function->line); + OutputDeclaration(argument->type, argument->name, /*defaultValue=*/NULL, /*isRef=*/false, /*isConst=*/false); + m_writer.EndLine(";"); + } + argument = argument->nextArgument; + } + + OutputStatements(indent + 1, function->statement); // @@ Modify return statements if function has multiple output arguments! + + // Output implicit return. + if (function->numOutputArguments > 0) + { + bool needsImplicitReturn = true; + HLSLStatement * statement = function->statement; + if (statement != NULL) + { + while (statement->nextStatement != NULL) + { + statement = statement->nextStatement; + } + needsImplicitReturn = (statement->nodeType != HLSLNodeType_ReturnStatement) && function->returnType.baseType == HLSLBaseType_Void; + } + + if (needsImplicitReturn) + { + m_writer.BeginLine(indent+1); + m_writer.Write("return { "); + + int numArguments = 0; + HLSLArgument * argument = m_currentFunction->argument; + while (argument != NULL) + { + if (argument->modifier == HLSLArgumentModifier_Out || argument->modifier == HLSLArgumentModifier_Inout) + { + if (numArguments) m_writer.Write(", "); + m_writer.Write("%s ", argument->name); + numArguments++; + } + argument = argument->nextArgument; + } + + m_writer.EndLine(" };"); + } + } + + m_writer.WriteLine(indent, "};"); + m_currentFunction = NULL; +} + + +// @@ We could be a lot smarter removing parenthesis based on the operator precedence of the parent expression. +static bool NeedsParenthesis(HLSLExpression* expression, HLSLExpression* parentExpression) { + + // For now we just omit the parenthesis if there's no parent expression. + if (parentExpression == NULL) + { + return false; + } + + // One more special case that's pretty common. + if (parentExpression->nodeType == HLSLNodeType_MemberAccess) + { + if (expression->nodeType == HLSLNodeType_IdentifierExpression || + expression->nodeType == HLSLNodeType_ArrayAccess || + expression->nodeType == HLSLNodeType_MemberAccess) + { + return false; + } + } + + return true; +} + +bool MSLGenerator::NeedsCast(const HLSLType & target, const HLSLType & source) +{ + HLSLBaseType targetType = target.baseType; + HLSLBaseType sourceType = source.baseType; + + if (sourceType == HLSLBaseType_Int) { + int k = 1; + } + + /*if (IsScalarType(target)) + { + // Scalar types do not need casting. + return false; + }*/ + + if (m_options.treatHalfAsFloat) + { + if (IsHalf(targetType)) targetType = HLSLBaseType(targetType + HLSLBaseType_Float - HLSLBaseType_Half); + if (IsHalf(sourceType)) sourceType = HLSLBaseType(sourceType + HLSLBaseType_Float - HLSLBaseType_Half); + } + + return targetType != sourceType && (BaseTypeDimension[targetType] == BaseTypeDimension[sourceType] || BaseTypeDimension[sourceType] == HLSLTypeDimension_Scalar); +} + + +void MSLGenerator::OutputTypedExpression(const HLSLType& type, HLSLExpression* expression, HLSLExpression* parentExpression) +{ + // If base types are not exactly the same, do explicit cast. + bool closeCastExpression = false; + if (NeedsCast(type, expression->expressionType)) + { + OutputCast(type); + m_writer.Write("("); + closeCastExpression = true; + } + + OutputExpression(expression, parentExpression); + + if (closeCastExpression) + { + m_writer.Write(")"); + } +} + +void MSLGenerator::OutputExpression(HLSLExpression* expression, HLSLExpression* parentExpression) +{ + if (expression->nodeType == HLSLNodeType_IdentifierExpression) + { + HLSLIdentifierExpression* identifierExpression = static_cast(expression); + const char* name = identifierExpression->name; + // For texture declaration, generate a struct instead + if (identifierExpression->global && IsSamplerType(identifierExpression->expressionType)) + { + if (identifierExpression->expressionType.baseType == HLSLBaseType_Sampler2D) + { + if (identifierExpression->expressionType.samplerType == HLSLBaseType_Half && !m_options.treatHalfAsFloat) + { + m_writer.Write("Texture2DHalfSampler(%s_texture, %s_sampler)", name, name); + } + else + { + m_writer.Write("Texture2DSampler(%s_texture, %s_sampler)", name, name); + } + } + else if (identifierExpression->expressionType.baseType == HLSLBaseType_Sampler3D) + m_writer.Write("Texture3DSampler(%s_texture, %s_sampler)", name, name); + else if (identifierExpression->expressionType.baseType == HLSLBaseType_SamplerCube) + m_writer.Write("TextureCubeSampler(%s_texture, %s_sampler)", name, name); + else if (identifierExpression->expressionType.baseType == HLSLBaseType_Sampler2DShadow) + m_writer.Write("Texture2DShadowSampler(%s_texture, %s_sampler)", name, name); + else if (identifierExpression->expressionType.baseType == HLSLBaseType_Sampler2DMS) + m_writer.Write("%s_texture", name); + else if (identifierExpression->expressionType.baseType == HLSLBaseType_Sampler2DArray) + m_writer.Write("Texture2DArraySampler(%s_texture, %s_sampler)", name, name); + else + m_writer.Write(""); + } + else + { + if (identifierExpression->global) + { + HLSLBuffer * buffer; + HLSLDeclaration * declaration = m_tree->FindGlobalDeclaration(identifierExpression->name, &buffer); + + if (declaration && declaration->buffer) + { + ASSERT(buffer == declaration->buffer); + m_writer.Write("%s.", declaration->buffer->name); + } + } + m_writer.Write("%s", name); + + // IC: Add swizzle if this is a member access of a field that has the swizzle flag. + /*if (parentExpression->nodeType == HLSLNodeType_MemberAccess) + { + HLSLMemberAccess* memberAccess = (HLSLMemberAccess*)parentExpression; + const HLSLType & objectType = memberAccess->object->expressionType; + const HLSLStruct* structure = m_tree->FindGlobalStruct(objectType.typeName); + if (structure != NULL) + { + const HLSLStructField* field = structure->field; + while (field != NULL) + { + if (field->name == name) + { + if (field->type.flags & HLSLTypeFlag_Swizzle_BGRA) + { + m_writer.Write(".bgra", name); + } + } + } + } + }*/ + } + } + else if (expression->nodeType == HLSLNodeType_CastingExpression) + { + HLSLCastingExpression* castingExpression = static_cast(expression); + OutputCast(castingExpression->type); + m_writer.Write("("); + OutputExpression(castingExpression->expression, castingExpression); + m_writer.Write(")"); + } + else if (expression->nodeType == HLSLNodeType_ConstructorExpression) + { + HLSLConstructorExpression* constructorExpression = static_cast(expression); + m_writer.Write("%s(", GetTypeName(constructorExpression->type)); + //OutputExpressionList(constructorExpression->type, constructorExpression->argument); // @@ Get element type. + OutputExpressionList(constructorExpression->argument); + m_writer.Write(")"); + } + else if (expression->nodeType == HLSLNodeType_LiteralExpression) + { + HLSLLiteralExpression* literalExpression = static_cast(expression); + switch (literalExpression->type) + { + case HLSLBaseType_Half: + if (m_options.treatHalfAsFloat) { + m_writer.Write("%ff", literalExpression->fValue); + } + else { + m_writer.Write("%fh", literalExpression->fValue); + } + break; + case HLSLBaseType_Float: + m_writer.Write("%ff", literalExpression->fValue); + break; + case HLSLBaseType_Int: + m_writer.Write("%d", literalExpression->iValue); + break; + case HLSLBaseType_Bool: + m_writer.Write("%s", literalExpression->bValue ? "true" : "false"); + break; + default: + ASSERT(0); + } + } + else if (expression->nodeType == HLSLNodeType_UnaryExpression) + { + HLSLUnaryExpression* unaryExpression = static_cast(expression); + const char* op = "?"; + bool pre = true; + switch (unaryExpression->unaryOp) + { + case HLSLUnaryOp_Negative: op = "-"; break; + case HLSLUnaryOp_Positive: op = "+"; break; + case HLSLUnaryOp_Not: op = "!"; break; + case HLSLUnaryOp_BitNot: op = "~"; break; + case HLSLUnaryOp_PreIncrement: op = "++"; break; + case HLSLUnaryOp_PreDecrement: op = "--"; break; + case HLSLUnaryOp_PostIncrement: op = "++"; pre = false; break; + case HLSLUnaryOp_PostDecrement: op = "--"; pre = false; break; + } + bool addParenthesis = NeedsParenthesis(unaryExpression->expression, expression); + if (addParenthesis) m_writer.Write("("); + if (pre) + { + m_writer.Write("%s", op); + OutputExpression(unaryExpression->expression, unaryExpression); + } + else + { + OutputExpression(unaryExpression->expression, unaryExpression); + m_writer.Write("%s", op); + } + if (addParenthesis) m_writer.Write(")"); + } + else if (expression->nodeType == HLSLNodeType_BinaryExpression) + { + HLSLBinaryExpression* binaryExpression = static_cast(expression); + + bool addParenthesis = NeedsParenthesis(expression, parentExpression); + if (addParenthesis) m_writer.Write("("); + + bool rewrite_assign = false; + if (binaryExpression->binaryOp == HLSLBinaryOp_Assign && binaryExpression->expression1->nodeType == HLSLNodeType_ArrayAccess) + { + HLSLArrayAccess* arrayAccess = static_cast(binaryExpression->expression1); + if (!arrayAccess->array->expressionType.array && IsMatrixType(arrayAccess->array->expressionType.baseType)) + { + rewrite_assign = true; + + m_writer.Write("set_column("); + OutputExpression(arrayAccess->array, NULL); + m_writer.Write(", "); + OutputExpression(arrayAccess->index, NULL); + m_writer.Write(", "); + OutputExpression(binaryExpression->expression2, NULL); + m_writer.Write(")"); + } + } + + if (!rewrite_assign) + { + if (IsArithmeticOp(binaryExpression->binaryOp) || IsLogicOp(binaryExpression->binaryOp)) + { + // Do intermediate type promotion, without changing dimension: + HLSLType promotedType = binaryExpression->expression1->expressionType; + + if (ScalarBaseType[binaryExpression->expressionType.baseType] != ScalarBaseType[promotedType.baseType]) + { + promotedType.baseType = HLSLBaseType(ScalarBaseType[binaryExpression->expressionType.baseType] + BaseTypeDimension[promotedType.baseType] - 1); + } + + OutputTypedExpression(promotedType, binaryExpression->expression1, binaryExpression); + } + else + { + OutputExpression(binaryExpression->expression1, binaryExpression); + } + + const char* op = "?"; + switch (binaryExpression->binaryOp) + { + case HLSLBinaryOp_Add: op = " + "; break; + case HLSLBinaryOp_Sub: op = " - "; break; + case HLSLBinaryOp_Mul: op = " * "; break; + case HLSLBinaryOp_Div: op = " / "; break; + case HLSLBinaryOp_Less: op = " < "; break; + case HLSLBinaryOp_Greater: op = " > "; break; + case HLSLBinaryOp_LessEqual: op = " <= "; break; + case HLSLBinaryOp_GreaterEqual: op = " >= "; break; + case HLSLBinaryOp_Equal: op = " == "; break; + case HLSLBinaryOp_NotEqual: op = " != "; break; + case HLSLBinaryOp_Assign: op = " = "; break; + case HLSLBinaryOp_AddAssign: op = " += "; break; + case HLSLBinaryOp_SubAssign: op = " -= "; break; + case HLSLBinaryOp_MulAssign: op = " *= "; break; + case HLSLBinaryOp_DivAssign: op = " /= "; break; + case HLSLBinaryOp_And: op = " && "; break; + case HLSLBinaryOp_Or: op = " || "; break; + case HLSLBinaryOp_BitAnd: op = " & "; break; + case HLSLBinaryOp_BitOr: op = " | "; break; + case HLSLBinaryOp_BitXor: op = " ^ "; break; + default: + ASSERT(0); + } + m_writer.Write("%s", op); + + if (binaryExpression->binaryOp == HLSLBinaryOp_MulAssign || + binaryExpression->binaryOp == HLSLBinaryOp_DivAssign || + IsArithmeticOp(binaryExpression->binaryOp) || + IsLogicOp(binaryExpression->binaryOp)) + { + // Do intermediate type promotion, without changing dimension: + HLSLType promotedType = binaryExpression->expression2->expressionType; + + if (ScalarBaseType[binaryExpression->expressionType.baseType] != ScalarBaseType[promotedType.baseType]) + { + promotedType.baseType = HLSLBaseType(ScalarBaseType[binaryExpression->expressionType.baseType] + BaseTypeDimension[promotedType.baseType] - 1); + } + + OutputTypedExpression(promotedType, binaryExpression->expression2, binaryExpression); + } + else if (IsAssignOp(binaryExpression->binaryOp)) + { + OutputTypedExpression(binaryExpression->expressionType, binaryExpression->expression2, binaryExpression); + } + else + { + OutputExpression(binaryExpression->expression2, binaryExpression); + } + } + if (addParenthesis) m_writer.Write(")"); + } + else if (expression->nodeType == HLSLNodeType_ConditionalExpression) + { + HLSLConditionalExpression* conditionalExpression = static_cast(expression); + // @@ Remove parenthesis. + m_writer.Write("(("); + OutputExpression(conditionalExpression->condition, NULL); + m_writer.Write(")?("); + OutputExpression(conditionalExpression->trueExpression, NULL); + m_writer.Write("):("); + OutputExpression(conditionalExpression->falseExpression, NULL); + m_writer.Write("))"); + } + else if (expression->nodeType == HLSLNodeType_MemberAccess) + { + HLSLMemberAccess* memberAccess = static_cast(expression); + bool addParenthesis = NeedsParenthesis(memberAccess->object, expression); + + if (addParenthesis) + { + m_writer.Write("("); + } + OutputExpression(memberAccess->object, NULL); + if (addParenthesis) + { + m_writer.Write(")"); + } + + m_writer.Write(".%s", memberAccess->field); + } + else if (expression->nodeType == HLSLNodeType_ArrayAccess) + { + HLSLArrayAccess* arrayAccess = static_cast(expression); + if (arrayAccess->array->expressionType.array || !IsMatrixType(arrayAccess->array->expressionType.baseType)) + { + OutputExpression(arrayAccess->array, expression); + m_writer.Write("["); + OutputExpression(arrayAccess->index, NULL); + m_writer.Write("]"); + } + else + { + // @@ This doesn't work for l-values! + m_writer.Write("column("); + OutputExpression(arrayAccess->array, NULL); + m_writer.Write(", "); + OutputExpression(arrayAccess->index, NULL); + m_writer.Write(")"); + } + } + else if (expression->nodeType == HLSLNodeType_FunctionCall) + { + HLSLFunctionCall* functionCall = static_cast(expression); + OutputFunctionCall(functionCall, parentExpression); + } + else + { + m_writer.Write(""); + } +} + +void MSLGenerator::OutputCast(const HLSLType& type) +{ + if (type.baseType == HLSLBaseType_Float3x3) + { + m_writer.Write("matrix_ctor"); + } + else + { + m_writer.Write("("); + OutputDeclarationType(type, /*isConst=*/false, /*isRef=*/false, /*alignment=*/0, /*isTypeCast=*/true); + m_writer.Write(")"); + } +} + +// Called by the various Output functions +void MSLGenerator::OutputArguments(HLSLArgument* argument) +{ + int numArgs = 0; + while (argument != NULL) + { + // Skip hidden and output arguments. + if (argument->hidden || argument->modifier == HLSLArgumentModifier_Out) + { + argument = argument->nextArgument; + continue; + } + + if (numArgs > 0) + { + m_writer.Write(", "); + } + + //bool isRef = false; + bool isConst = false; + /*if (argument->modifier == HLSLArgumentModifier_Out || argument->modifier == HLSLArgumentModifier_Inout) + { + isRef = true; + }*/ + if (argument->modifier == HLSLArgumentModifier_In || argument->modifier == HLSLArgumentModifier_Const) + { + isConst = true; + } + + OutputDeclaration(argument->type, argument->name, argument->defaultValue, /*isRef=*/false, isConst); + argument = argument->nextArgument; + ++numArgs; + } +} + +void MSLGenerator::OutputDeclaration(const HLSLType& type, const char* name, HLSLExpression* assignment, bool isRef, bool isConst, int alignment) +{ + OutputDeclarationType(type, isRef, isConst, alignment); + OutputDeclarationBody(type, name, assignment, isRef); +} + +void MSLGenerator::OutputDeclarationType(const HLSLType& type, bool isRef, bool isConst, int alignment, bool isTypeCast) +{ + const char* typeName = GetTypeName(type); + + /*if (!isTypeCast)*/ + { + if (isRef && !isTypeCast) + { + m_writer.Write("thread "); + } + if (isConst || type.flags & HLSLTypeFlag_Const) + { + m_writer.Write("const "); + + if ((type.flags & HLSLTypeFlag_Static) != 0 && !isTypeCast) + { + m_writer.Write("static constant constexpr "); + } + } + } + if (IsSamplerType(type)) + { + if (type.baseType == HLSLBaseType_Sampler2D) { + if (type.samplerType == HLSLBaseType_Half && !m_options.treatHalfAsFloat) { + typeName = "Texture2DHalfSampler"; + } + else { + typeName = "Texture2DSampler"; + } + } + else if (type.baseType == HLSLBaseType_Sampler3D) + typeName = "Texture3DSampler"; + else if (type.baseType == HLSLBaseType_SamplerCube) + typeName = "TextureCubeSampler"; + else if (type.baseType == HLSLBaseType_Sampler2DShadow) + typeName = "Texture2DShadowSampler"; + else if (type.baseType == HLSLBaseType_Sampler2DMS) + typeName = "Texture2DMSSampler"; + else if (type.baseType == HLSLBaseType_Sampler2DArray) + typeName = "Texture2DArraySampler"; + else + typeName = ""; + } + else if (alignment != 0 && !isTypeCast) + { + m_writer.Write("alignas(%d) ", alignment); + } + + m_writer.Write("%s", typeName); + + if (isTypeCast) + { + // Do not output modifiers inside type cast expressions. + return; + } + + // Interpolation modifiers. + if (type.flags & HLSLTypeFlag_NoInterpolation) + { + m_writer.Write(" [[flat]]"); + } + else + { + if (type.flags & HLSLTypeFlag_NoPerspective) + { + if (type.flags & HLSLTypeFlag_Centroid) + { + m_writer.Write(" [[centroid_no_perspective]]"); + } + else if (type.flags & HLSLTypeFlag_Sample) + { + m_writer.Write(" [[sample_no_perspective]]"); + } + else + { + m_writer.Write(" [[center_no_perspective]]"); + } + } + else + { + if (type.flags & HLSLTypeFlag_Centroid) + { + m_writer.Write(" [[centroid_perspective]]"); + } + else if (type.flags & HLSLTypeFlag_Sample) + { + m_writer.Write(" [[sample_perspective]]"); + } + else + { + // Default. + //m_writer.Write(" [[center_perspective]]"); + } + } + } +} + +void MSLGenerator::OutputDeclarationBody(const HLSLType& type, const char* name, HLSLExpression* assignment, bool isRef) +{ + if (isRef) + { + // Arrays of refs are illegal in C++ and hence MSL, need to "link" the & to the var name + m_writer.Write("(&"); + } + + // Then name + m_writer.Write(" %s", name); + + if (isRef) + { + m_writer.Write(")"); + } + + // Add brackets for arrays + if (type.array) + { + m_writer.Write("["); + if (type.arraySize != NULL) + { + OutputExpression(type.arraySize, NULL); + } + m_writer.Write("]"); + } + + // Semantics and registers unhandled for now + + // Assignment handling + if (assignment != NULL) + { + m_writer.Write(" = "); + if (type.array) + { + m_writer.Write("{ "); + OutputExpressionList(assignment); + m_writer.Write(" }"); + } + else + { + OutputTypedExpression(type, assignment, NULL); + } + } +} + +void MSLGenerator::OutputExpressionList(HLSLExpression* expression) +{ + int numExpressions = 0; + while (expression != NULL) + { + if (numExpressions > 0) + { + m_writer.Write(", "); + } + OutputExpression(expression, NULL); + expression = expression->nextExpression; + ++numExpressions; + } +} + +// Cast all expressions to given type. +void MSLGenerator::OutputExpressionList(const HLSLType & type, HLSLExpression* expression) +{ + int numExpressions = 0; + while (expression != NULL) + { + if (numExpressions > 0) + { + m_writer.Write(", "); + } + + OutputTypedExpression(type, expression, NULL); + expression = expression->nextExpression; + ++numExpressions; + } +} + +// Cast each expression to corresponding argument type. +void MSLGenerator::OutputExpressionList(HLSLArgument* argument, HLSLExpression* expression) +{ + int numExpressions = 0; + while (expression != NULL) + { + ASSERT(argument != NULL); + if (argument->modifier != HLSLArgumentModifier_Out) + { + if (numExpressions > 0) + { + m_writer.Write(", "); + } + + OutputTypedExpression(argument->type, expression, NULL); + ++numExpressions; + } + + expression = expression->nextExpression; + argument = argument->nextArgument; + } +} + + + +inline bool isAddressable(HLSLExpression* expression) +{ + if (expression->nodeType == HLSLNodeType_IdentifierExpression) + { + return true; + } + if (expression->nodeType == HLSLNodeType_ArrayAccess) + { + return true; + } + if (expression->nodeType == HLSLNodeType_MemberAccess) + { + HLSLMemberAccess* memberAccess = (HLSLMemberAccess*)expression; + return !memberAccess->swizzle; + } + return false; +} + + +void MSLGenerator::OutputFunctionCallStatement(int indent, HLSLFunctionCall* functionCall, HLSLDeclaration* declaration) +{ + // Nothing special about these cases: + if (functionCall->function->numOutputArguments == 0) + { + m_writer.BeginLine(indent, functionCall->fileName, functionCall->line); + if (declaration) + { + OutputDeclaration(declaration); + } + else + { + OutputExpression(functionCall, NULL); + } + m_writer.EndLine(";"); + return; + } + + + // Transform this: + // float foo = functionCall(bah, poo); + + // Into: + // auto tmp = functionCall(bah, poo); + // bah = tmp.bah; + // poo = tmp.poo; + // float foo = tmp.__result; + + const char* functionName = functionCall->function->name; + + m_writer.BeginLine(indent, functionCall->fileName, functionCall->line); + m_writer.Write("auto out%d = %s_%d(", functionCall->line, functionName, functionCall->function->line); + OutputExpressionList(functionCall->function->argument, functionCall->argument); + m_writer.EndLine(");"); + + HLSLExpression * expression = functionCall->argument; + HLSLArgument * argument = functionCall->function->argument; + while (argument != NULL) + { + if (argument->modifier == HLSLArgumentModifier_Out || argument->modifier == HLSLArgumentModifier_Inout) + { + m_writer.BeginLine(indent); + OutputExpression(expression, NULL); + // @@ This assignment may need a cast. + m_writer.Write(" = "); + if (NeedsCast(expression->expressionType, argument->type)) { + m_writer.Write("(%s)", GetTypeName(expression->expressionType)); + } + m_writer.Write("out%d.%s;", functionCall->line, argument->name); + m_writer.EndLine(); + } + + expression = expression->nextExpression; + argument = argument->nextArgument; + } + + if (declaration) + { + m_writer.BeginLine(indent); + OutputDeclarationType(declaration->type); + m_writer.Write(" %s = out%d.__result;", declaration->name, functionCall->line); + m_writer.EndLine(); + } + + +#if 0 + int argumentIndex = 0; + HLSLArgument* argument = functionCall->function->argument; + HLSLExpression* expression = functionCall->argument; + while (argument != NULL) + { + if (!isAddressable(expression)) + { + if (argument->modifier == HLSLArgumentModifier_Out) + { + m_writer.BeginLine(indent, functionCall->fileName, functionCall->line); + OutputDeclarationType(argument->type); + m_writer.Write("tmp%d;", argumentIndex); + m_writer.EndLine(); + } + else if (argument->modifier == HLSLArgumentModifier_Inout) + { + m_writer.BeginLine(indent, functionCall->fileName, functionCall->line); + OutputDeclarationType(argument->type); + m_writer.Write("tmp%d = ", argumentIndex); + OutputExpression(expression, NULL); + m_writer.EndLine(";"); + } + } + argument = argument->nextArgument; + expression = expression->nextExpression; + argumentIndex++; + } + + m_writer.BeginLine(indent, functionCall->fileName, functionCall->line); + const char* name = functionCall->function->name; + m_writer.Write("%s(", name); + //OutputExpressionList(functionCall->argument); + + // Output expression list with temporary substitution. + argumentIndex = 0; + argument = functionCall->function->argument; + expression = functionCall->argument; + while (expression != NULL) + { + if (!isAddressable(expression) && (argument->modifier == HLSLArgumentModifier_Out || argument->modifier == HLSLArgumentModifier_Inout)) + { + m_writer.Write("tmp%d", argumentIndex); + } + else + { + OutputExpression(expression, NULL); + } + + argument = argument->nextArgument; + expression = expression->nextExpression; + argumentIndex++; + if (expression) + { + m_writer.Write(", "); + } + } + m_writer.EndLine(");"); + + argumentIndex = 0; + argument = functionCall->function->argument; + expression = functionCall->argument; + while (expression != NULL) + { + if (!isAddressable(expression) && (argument->modifier == HLSLArgumentModifier_Out || argument->modifier == HLSLArgumentModifier_Inout)) + { + m_writer.BeginLine(indent, functionCall->fileName, functionCall->line); + OutputExpression(expression, NULL); + m_writer.Write(" = tmp%d", argumentIndex); + m_writer.EndLine(";"); + } + + argument = argument->nextArgument; + expression = expression->nextExpression; + argumentIndex++; + } +#endif // 0 +} + +void MSLGenerator::OutputFunctionCall(HLSLFunctionCall* functionCall, HLSLExpression * parentExpression) +{ + if (functionCall->function->numOutputArguments > 0) + { + ASSERT(false); + } + + const char* functionName = functionCall->function->name; + + // If function begins with tex, then it returns float4 or half4 depending on options.halfTextureSamplers + /*if (strncmp(functionName, "tex", 3) == 0) + { + if (parentExpression && IsFloat(parentExpression->expressionType.baseType)) + { + if (m_options.halfTextureSamplers) + { + OutputCast(functionCall->expressionType); + } + } + }*/ + + { + m_writer.Write("%s(", functionName); + OutputExpressionList(functionCall->function->argument, functionCall->argument); + //OutputExpressionList(functionCall->argument); + m_writer.Write(")"); + } +} + +const char* MSLGenerator::TranslateInputSemantic(const char * semantic) +{ + if (semantic == NULL) + return NULL; + + unsigned int length, index; + ParseSemantic(semantic, &length, &index); + + if (m_target == MSLGenerator::Target_VertexShader) + { + if (String_Equal(semantic, "INSTANCE_ID")) return "instance_id"; + if (String_Equal(semantic, "SV_InstanceID")) return "instance_id"; + if (String_Equal(semantic, "VERTEX_ID")) return "vertex_id"; + if (String_Equal(semantic, "SV_VertexID")) return "vertex_id"; + + if (m_options.attributeCallback) + { + char name[64]; + ASSERT(length < sizeof(name)); + + strncpy(name, semantic, length); + name[length] = 0; + + int attribute = m_options.attributeCallback(name, index); + + if (attribute >= 0) + { + return m_tree->AddStringFormat("attribute(%d)", attribute); + } + } + } + else if (m_target == MSLGenerator::Target_FragmentShader) + { + if (String_Equal(semantic, "POSITION")) return "position"; + if (String_Equal(semantic, "SV_Position")) return "position"; + if (String_Equal(semantic, "VFACE")) return "front_facing"; + if (String_Equal(semantic, "TARGET_INDEX")) return "render_target_array_index"; + if (String_Equal(semantic, "DST_COLOR")) return "color(0)"; + if (String_Equal(semantic, "SAMPLE_ID")) return "sample_id"; + //if (String_Equal(semantic, "SAMPLE_MASK")) return "sample_mask"; + //if (String_Equal(semantic, "SAMPLE_MASK")) return "sample_mask,post_depth_coverage"; + } + + return NULL; +} + +const char* MSLGenerator::TranslateOutputSemantic(const char * semantic) +{ + if (semantic == NULL) + return NULL; + + unsigned int length, index; + ParseSemantic(semantic, &length, &index); + + if (m_target == MSLGenerator::Target_VertexShader) + { + if (String_Equal(semantic, "POSITION")) return "position"; + if (String_Equal(semantic, "SV_Position")) return "position"; + if (String_Equal(semantic, "PSIZE")) return "point_size"; + if (String_Equal(semantic, "POINT_SIZE")) return "point_size"; + if (String_Equal(semantic, "TARGET_INDEX")) return "render_target_array_index"; + } + else if (m_target == MSLGenerator::Target_FragmentShader) + { + if (m_options.flags & MSLGenerator::Flag_NoIndexAttribute) + { + // No dual-source blending on iOS, and no index() attribute + if (String_Equal(semantic, "COLOR0_1")) return NULL; + } + else + { + // @@ IC: Hardcoded for this specific case, extend ParseSemantic? + if (String_Equal(semantic, "COLOR0_1")) return "color(0), index(1)"; + } + + if (strncmp(semantic, "SV_Target", length) == 0) + { + return m_tree->AddStringFormat("color(%d)", index); + } + if (strncmp(semantic, "COLOR", length) == 0) + { + return m_tree->AddStringFormat("color(%d)", index); + } + + if (String_Equal(semantic, "DEPTH")) return "depth(any)"; + if (String_Equal(semantic, "DEPTH_GT")) return "depth(greater)"; + if (String_Equal(semantic, "DEPTH_LT")) return "depth(less)"; + if (String_Equal(semantic, "SAMPLE_MASK")) return "sample_mask"; + } + + return NULL; +} + +const char* MSLGenerator::GetTypeName(const HLSLType& type) +{ + bool promote = ((type.flags & HLSLTypeFlag_NoPromote) == 0); + + bool float_to_half = false; + bool half_to_float = promote && m_options.treatHalfAsFloat; + bool int_to_short = promote && m_options.use16BitIntegers; + bool half_samplers = promote && type.samplerType == HLSLBaseType_Half && !m_options.treatHalfAsFloat; + + + switch (type.baseType) + { + case HLSLBaseType_Void: return "void"; + case HLSLBaseType_Float: return float_to_half ? "half" : "float"; + case HLSLBaseType_Float2: return float_to_half ? "half2" : "float2"; + case HLSLBaseType_Float3: return float_to_half ? "half3" : "float3"; + case HLSLBaseType_Float4: return float_to_half ? "half4" : "float4"; + case HLSLBaseType_Float2x2: return float_to_half ? "half2x2" : "float2x2"; + case HLSLBaseType_Float3x3: return float_to_half ? "half3x3" : "float3x3"; + case HLSLBaseType_Float4x4: return float_to_half ? "half4x4" : "float4x4"; + case HLSLBaseType_Float4x3: return float_to_half ? "half3x4" : "float3x4"; + case HLSLBaseType_Float4x2: return float_to_half ? "half2x4" : "float2x4"; + case HLSLBaseType_Half: return half_to_float ? "float" : "half"; + case HLSLBaseType_Half2: return half_to_float ? "float2" : "half2"; + case HLSLBaseType_Half3: return half_to_float ? "float3" : "half3"; + case HLSLBaseType_Half4: return half_to_float ? "float4" : "half4"; + case HLSLBaseType_Half2x2: return half_to_float ? "float2x2" : "half2x2"; + case HLSLBaseType_Half3x3: return half_to_float ? "float3x3" : "half3x3"; + case HLSLBaseType_Half4x4: return half_to_float ? "float4x4" : "half4x4"; + case HLSLBaseType_Half4x3: return half_to_float ? "float3x4" : "half3x4"; + case HLSLBaseType_Half4x2: return half_to_float ? "float2x4" : "half2x4"; + case HLSLBaseType_Bool: return "bool"; + case HLSLBaseType_Bool2: return "bool2"; + case HLSLBaseType_Bool3: return "bool3"; + case HLSLBaseType_Bool4: return "bool4"; + case HLSLBaseType_Int: return int_to_short ? "short" : "int"; + case HLSLBaseType_Int2: return int_to_short ? "short2" : "int2"; + case HLSLBaseType_Int3: return int_to_short ? "short3" : "int3"; + case HLSLBaseType_Int4: return int_to_short ? "short4" : "int4"; + case HLSLBaseType_Uint: return int_to_short ? "ushort" : "uint"; + case HLSLBaseType_Uint2: return int_to_short ? "ushort2" : "uint2"; + case HLSLBaseType_Uint3: return int_to_short ? "ushort3" : "uint3"; + case HLSLBaseType_Uint4: return int_to_short ? "ushort4" : "uint4"; + case HLSLBaseType_Texture: return "texture"; + case HLSLBaseType_Sampler: return "sampler"; + case HLSLBaseType_Sampler2D: return half_samplers ? "texture2d" : "texture2d"; + case HLSLBaseType_Sampler3D: return half_samplers ? "texture3d" : "texture3d"; + case HLSLBaseType_SamplerCube: return half_samplers ? "texturecube" : "texturecube"; + case HLSLBaseType_Sampler2DShadow: return "depth2d"; + case HLSLBaseType_Sampler2DMS: return half_samplers ? "texture2d_ms" : "texture2d_ms"; + case HLSLBaseType_Sampler2DArray: return half_samplers ? "texture2d_array" : "texture2d_array"; + case HLSLBaseType_UserDefined: return type.typeName; + default: + ASSERT(0); + return ""; + } +} + + + +} // M4 diff --git a/src/libprojectM/Renderer/hlslparser/src/MSLGenerator.h b/src/libprojectM/Renderer/hlslparser/src/MSLGenerator.h new file mode 100755 index 000000000..e56ec250e --- /dev/null +++ b/src/libprojectM/Renderer/hlslparser/src/MSLGenerator.h @@ -0,0 +1,132 @@ +#ifndef MSL_GENERATOR_H +#define MSL_GENERATOR_H + +#include "CodeWriter.h" +#include "HLSLTree.h" + +namespace M4 +{ + +class HLSLTree; +struct HLSLFunction; +struct HLSLStruct; + +/** + * This class is used to generate MSL shaders. + */ +class MSLGenerator +{ +public: + enum Target + { + Target_VertexShader, + Target_FragmentShader, + }; + + enum Flags + { + Flag_ConstShadowSampler = 1 << 0, + Flag_PackMatrixRowMajor = 1 << 1, + Flag_NoIndexAttribute = 1 << 2, + }; + + struct Options + { + unsigned int flags; + unsigned int bufferRegisterOffset; + int (*attributeCallback)(const char* name, unsigned int index); + bool treatHalfAsFloat; + bool usePreciseFma; + bool use16BitIntegers; + + Options() + { + flags = 0; + bufferRegisterOffset = 0; + attributeCallback = NULL; + treatHalfAsFloat = true; + usePreciseFma = false; + use16BitIntegers = false; + } + }; + + MSLGenerator(); + + bool Generate(HLSLTree* tree, Target target, const char* entryName, const Options& options = Options()); + const char* GetResult() const; + +private: + + // @@ Rename class argument. Add buffers & textures. + struct ClassArgument + { + const char* name; + HLSLType type; + //const char* typeName; // @@ Do we need more than the type name? + const char* registerName; + + ClassArgument * nextArg; + + ClassArgument(const char* name, HLSLType type, const char * registerName) : + name(name), type(type), registerName(registerName) + { + nextArg = NULL; + } + }; + + void AddClassArgument(ClassArgument * arg); + + void Prepass(HLSLTree* tree, Target target, HLSLFunction* entryFunction); + void CleanPrepass(); + + void PrependDeclarations(); + + void OutputStatements(int indent, HLSLStatement* statement); + void OutputAttributes(int indent, HLSLAttribute* attribute); + void OutputDeclaration(HLSLDeclaration* declaration); + void OutputStruct(int indent, HLSLStruct* structure); + void OutputBuffer(int indent, HLSLBuffer* buffer); + void OutputFunction(int indent, HLSLFunction* function); + void OutputExpression(HLSLExpression* expression, HLSLExpression* parentExpression); + void OutputTypedExpression(const HLSLType& type, HLSLExpression* expression, HLSLExpression* parentExpression); + bool NeedsCast(const HLSLType & target, const HLSLType & source); + void OutputCast(const HLSLType& type); + + void OutputArguments(HLSLArgument* argument); + void OutputDeclaration(const HLSLType& type, const char* name, HLSLExpression* assignment, bool isRef = false, bool isConst = false, int alignment = 0); + void OutputDeclarationType(const HLSLType& type, bool isConst = false, bool isRef = false, int alignment = 0, bool isTypeCast = false); + void OutputDeclarationBody(const HLSLType& type, const char* name, HLSLExpression* assignment, bool isRef = false); + void OutputExpressionList(HLSLExpression* expression); + void OutputExpressionList(const HLSLType& type, HLSLExpression* expression); + void OutputExpressionList(HLSLArgument* argument, HLSLExpression* expression); + + void OutputFunctionCallStatement(int indent, HLSLFunctionCall* functionCall, HLSLDeclaration* assingmentExpression); + void OutputFunctionCall(HLSLFunctionCall* functionCall, HLSLExpression * parentExpression); + + const char* TranslateInputSemantic(const char* semantic); + const char* TranslateOutputSemantic(const char* semantic); + + const char* GetTypeName(const HLSLType& type); + + void Error(const char* format, ...); + +private: + + CodeWriter m_writer; + + HLSLTree* m_tree; + const char* m_entryName; + Target m_target; + Options m_options; + + bool m_error; + + ClassArgument * m_firstClassArgument; + ClassArgument * m_lastClassArgument; + + HLSLFunction * m_currentFunction; +}; + +} // M4 + +#endif diff --git a/src/libprojectM/Renderer/hlslparser/src/Main.cpp b/src/libprojectM/Renderer/hlslparser/src/Main.cpp new file mode 100755 index 000000000..2de6228ef --- /dev/null +++ b/src/libprojectM/Renderer/hlslparser/src/Main.cpp @@ -0,0 +1,169 @@ +#include "HLSLParser.h" + +#include "GLSLGenerator.h" +#include "HLSLGenerator.h" +#include "MSLGenerator.h" + +#include +#include +#include + +enum Target +{ + Target_VertexShader, + Target_FragmentShader, +}; + +enum Language +{ + Language_GLSL, + Language_HLSL, + Language_LegacyHLSL, + Language_Metal, +}; + +std::string ReadFile( const char* fileName ) +{ + std::ifstream ifs( fileName ); + std::stringstream buffer; + buffer << ifs.rdbuf(); + return buffer.str(); +} + +void PrintUsage() +{ + std::cerr << "usage: hlslparser [-h] [-fs | -vs] FILENAME ENTRYNAME\n" + << "\n" + << "Translate HLSL shader to GLSL shader.\n" + << "\n" + << "positional arguments:\n" + << " FILENAME input file name\n" + << " ENTRYNAME entry point of the shader\n" + << "\n" + << "optional arguments:\n" + << " -h, --help show this help message and exit\n" + << " -fs generate fragment shader (default)\n" + << " -vs generate vertex shader\n" + << " -glsl generate GLSL (default)\n" + << " -hlsl generate HLSL\n" + << " -legacyhlsl generate legacy HLSL\n" + << " -metal generate MSL\n"; +} + +int main( int argc, char* argv[] ) +{ + using namespace M4; + + // Parse arguments + const char* fileName = NULL; + const char* entryName = NULL; + + Target target = Target_FragmentShader; + Language language = Language_GLSL; + + for( int argn = 1; argn < argc; ++argn ) + { + const char* const arg = argv[ argn ]; + + if( String_Equal( arg, "-h" ) || String_Equal( arg, "--help" ) ) + { + PrintUsage(); + return 0; + } + else if( String_Equal( arg, "-fs" ) ) + { + target = Target_FragmentShader; + } + else if( String_Equal( arg, "-vs" ) ) + { + target = Target_VertexShader; + } + else if( String_Equal( arg, "-glsl" ) ) + { + language = Language_GLSL; + } + else if( String_Equal( arg, "-hlsl" ) ) + { + language = Language_HLSL; + } + else if( String_Equal( arg, "-legacyhlsl" ) ) + { + language = Language_LegacyHLSL; + } + else if( String_Equal( arg, "-metal" ) ) + { + language = Language_Metal; + } + else if( fileName == NULL ) + { + fileName = arg; + } + else if( entryName == NULL ) + { + entryName = arg; + } + else + { + Log_Error( "Too many arguments\n" ); + PrintUsage(); + return 1; + } + } + + if( fileName == NULL || entryName == NULL ) + { + Log_Error( "Missing arguments\n" ); + PrintUsage(); + return 1; + } + + // Read input file + const std::string source = ReadFile( fileName ); + + // Parse input file + Allocator allocator; + HLSLParser parser( &allocator, fileName, source.data(), source.size() ); + HLSLTree tree( &allocator ); + if( !parser.Parse( &tree ) ) + { + Log_Error( "Parsing failed, aborting\n" ); + return 1; + } + + // Generate output + if (language == Language_GLSL) + { + GLSLGenerator generator; + if (!generator.Generate( &tree, GLSLGenerator::Target(target), GLSLGenerator::Version_140, entryName )) + { + Log_Error( "Translation failed, aborting\n" ); + return 1; + } + + std::cout << generator.GetResult(); + } + else if (language == Language_HLSL) + { + HLSLGenerator generator; + if (!generator.Generate( &tree, HLSLGenerator::Target(target), entryName, language == Language_LegacyHLSL )) + { + Log_Error( "Translation failed, aborting\n" ); + return 1; + } + + std::cout << generator.GetResult(); + } + else if (language == Language_Metal) + { + MSLGenerator generator; + if (!generator.Generate( &tree, MSLGenerator::Target(target), entryName )) + { + Log_Error( "Translation failed, aborting\n" ); + return 1; + } + + std::cout << generator.GetResult(); + } + + return 0; +}