diff --git a/hlslparser/outshaders/ShaderHLSL.h b/hlslparser/outshaders/ShaderHLSL.h index 46a8fdf0..97a1e402 100644 --- a/hlslparser/outshaders/ShaderHLSL.h +++ b/hlslparser/outshaders/ShaderHLSL.h @@ -39,30 +39,44 @@ // no using, so do typedef // this is ugly syntax +//typedef int16_t short; typedef int16_t2 short2; typedef int16_t3 short3; typedef int16_t4 short4; +typedef uint16_t ushort; typedef uint16_t2 ushort2; typedef uint16_t3 ushort3; typedef uint16_t4 ushort4; -// TODO: double, u/char -// TODO: add double, but won't work on mobile. +//typedef int64_t long; +typedef int64_t2 long2; +typedef int64_t3 long3; +typedef int64_t4 long4; + +typedef uint64_t ulong; +typedef uint64_t2 ulong2; +typedef uint64_t3 ulong3; +typedef uint64_t4 ulong4; + +//typedef float64_t double; +typedef float64_t2 double2; +typedef float64_t3 double3; +typedef float64_t4 double4; + +typedef float64_t2x2 double2x2; +typedef float64_t3x3 double3x3; +typedef float64_t4x4 double4x4; + + +// Note: no u/char +// Note: add double, but won't work on mobile (Android/MSL). // also Intel removed fp64 GPU support. Often runs 1/64th speed. // But may be needed for ray-tracing large worlds. Metal doesn't have double. -// -//typedef int64_t2 long2; -//typedef int64_t3 long3; -//typedef int64_t4 long4; -// -//typedef uint64_t2 ulong2; -//typedef uint64_t3 ulong3; -//typedef uint64_t4 ulong4; -// -//typedef float64_t2 double2; -//typedef float64_t3 double3; -//typedef float64_t4 double4; + +// TODO: add Atomics, more atomic u/long and float in SM 6.6 +// otherwise it's most atomic_u/int that is portable. +// Apple Metal 3 added atomic_float. // 6.6 is cutting edge, want to target 6.2 for now #define SM66 1 diff --git a/hlslparser/shaders/Sample.hlsl b/hlslparser/shaders/Sample.hlsl index 53c1453c..a3356162 100644 --- a/hlslparser/shaders/Sample.hlsl +++ b/hlslparser/shaders/Sample.hlsl @@ -31,17 +31,6 @@ struct LightState float4x4 viewProj; }; -/* -cbuffer SceneConstantBuffer : register(b0) -{ - float4x4 model; - float4x4 viewProj; - float4 ambientColor; - bool sampleShadowMap; - LightState lights[NUM_LIGHTS]; -}; -*/ - struct SceneConstantBuffer { float4x4 model; diff --git a/hlslparser/shaders/Skinning.hlsl b/hlslparser/shaders/Skinning.hlsl index 0ff5515d..6e86327b 100644 --- a/hlslparser/shaders/Skinning.hlsl +++ b/hlslparser/shaders/Skinning.hlsl @@ -8,30 +8,31 @@ // // https://github.com/microsoft/DirectXShaderCompiler/blob/main/docs/SPIR-V.rst -// setup specialization -// HLSL: at beginning +// setup variants +// HLSL: specialization constants marked at beginning // [[vk::constant_id(0)]] const int specConstInt = 1; // [[vk::constant_id(1)]] const bool specConstBool = true; // -// MSL: at end +// MSL: function constants marked at end // constant bool a [[function_constant(0)]]; // constant int a [[function_constant(1)]]; // 0.. 64K-1 +// This is for tile shaders // subpass input, and SubpassLoad() calls // [[vk::input_attachment_index(i)]] SubpassInput input; // class SubpassInput { T SubpassLoad(); }; // class SubpassInputMS { T SubpassLoad(int sampleIndex); }; -// push constants +// push constants (DONE) // [[vk::push_constant]] // descriptors and arg buffers // [[vk::binding(X[, Y])]] and [[vk::counter_binding(X)]] +// tagging the format of buffers/textures, since HLSL can't represent // [[vk::image_format("rgba8")]] // RWBuffer Buf; - - +// // [[vk::image_format("rg16f")]] // RWTexture2D Tex; diff --git a/hlslparser/src/HLSLGenerator.cpp b/hlslparser/src/HLSLGenerator.cpp index c7e5d6eb..b9752ca3 100644 --- a/hlslparser/src/HLSLGenerator.cpp +++ b/hlslparser/src/HLSLGenerator.cpp @@ -19,54 +19,11 @@ namespace M4 const char* HLSLGenerator::GetTypeName(const HLSLType& type) { HLSLBaseType baseType = type.baseType; - - if (baseType == HLSLBaseType_UserDefined) - return type.typeName; - - // TODO: these can all just use a table entry, have another slot for MSL - const char* name = GetNumericTypeName(baseType); - if (name) - return name; - + + // DONE: these can all just use a table entry, have another slot for MSL // Functions can return void, especially with compute - if (baseType == HLSLBaseType_Void) - return "void"; - - // TODO: pull names from table, they should be same - if (IsSamplerType(baseType)) - { - switch (baseType) - { - // samplers - case HLSLBaseType_SamplerState: return "SamplerState"; - - // can only pair this with depth texture to match Metal - case HLSLBaseType_SamplerComparisonState: return "SamplerComparisonState"; - default: break; - } - } - else if (IsTextureType(baseType)) - { - switch (baseType) - { - // depth textures just use Texture2D typedef - // TODO: add ms, others - case HLSLBaseType_Texture2D: return "Texture2D"; - case HLSLBaseType_Texture2DArray: return "Texture2DArray"; - case HLSLBaseType_Texture3D: return "Texture3D"; - case HLSLBaseType_TextureCube: return "TextureCube"; - case HLSLBaseType_TextureCubeArray: return "TextureCubeArray"; - case HLSLBaseType_Texture2DMS: return "Texture2DMS"; - - case HLSLBaseType_Depth2D: return "Depth2D"; - case HLSLBaseType_Depth2DArray: return "Depth2DArray"; - case HLSLBaseType_DepthCube: return "DepthCube"; - - case HLSLBaseType_RWTexture2D: return "RWTexture2D"; - - default: break; - } - } + if (IsTextureType(baseType) || IsSamplerType(baseType) || IsNumericType(baseType) || baseType == HLSLBaseType_Void || baseType == HLSLBaseType_UserDefined) + return GetTypeNameHLSL(type); Error("Unknown type"); return NULL; diff --git a/hlslparser/src/HLSLParser.cpp b/hlslparser/src/HLSLParser.cpp index 6df37a8a..4a050d1e 100644 --- a/hlslparser/src/HLSLParser.cpp +++ b/hlslparser/src/HLSLParser.cpp @@ -154,6 +154,7 @@ bool IsNumericTypeEqual(HLSLBaseType lhsType, HLSLBaseType rhsType) baseTypeDescriptions[rhsType].numericType; } +// TODO: with so many types, should just request the numeric type bool IsHalf(HLSLBaseType type) { return baseTypeDescriptions[type].numericType == NumericType_Half; @@ -164,6 +165,14 @@ bool IsFloat(HLSLBaseType type) return baseTypeDescriptions[type].numericType == NumericType_Float; } +bool IsDouble(HLSLBaseType type) +{ + return baseTypeDescriptions[type].numericType == NumericType_Double; +} + +// TODO: IsUint, IsInt, ... or just get type. + + bool IsSamplerType(const HLSLType & type) { return IsSamplerType(type.baseType); @@ -266,8 +275,6 @@ HLSLBaseType HalfToFloatBaseType(HLSLBaseType type) case HLSLBaseType_Half4: return HLSLBaseType_Float4; case HLSLBaseType_Half2x2: return HLSLBaseType_Float2x2; case HLSLBaseType_Half3x3: return HLSLBaseType_Float3x3; - // case HLSLBaseType_Half4x2: return HLSLBaseType_Float4x2; - // case HLSLBaseType_Half4x3: return HLSLBaseType_Float4x3; case HLSLBaseType_Half4x4: return HLSLBaseType_Float4x4; default: @@ -278,6 +285,27 @@ HLSLBaseType HalfToFloatBaseType(HLSLBaseType type) return type; } +HLSLBaseType DoubleToFloatBaseType(HLSLBaseType type) +{ + switch(type) + { + case HLSLBaseType_Double: return HLSLBaseType_Float; + case HLSLBaseType_Double2: return HLSLBaseType_Float2; + case HLSLBaseType_Double3: return HLSLBaseType_Float3; + case HLSLBaseType_Double4: return HLSLBaseType_Float4; + case HLSLBaseType_Double2x2: return HLSLBaseType_Float2x2; + case HLSLBaseType_Double3x3: return HLSLBaseType_Float3x3; + case HLSLBaseType_Double4x4: return HLSLBaseType_Float4x4; + + default: + // do nothing; + break; + } + + return type; +} + + static HLSLBaseType ArithmeticOpResultType(HLSLBinaryOp binaryOp, HLSLBaseType t1, HLSLBaseType t2); const char* GetNumericTypeName(HLSLBaseType type) @@ -971,6 +999,7 @@ void RegisterMatrix(HLSLBaseType type, uint32_t typeOffset, NumericType numericT BaseTypeDescription& desc = baseTypeDescriptions[baseType]; desc.typeName = name; + desc.typeNameMetal = name; desc.baseType = baseType; desc.coreType = CoreType_Matrix; @@ -1228,7 +1257,7 @@ HLSLBaseType ArithmeticOpResultType(HLSLBinaryOp binaryOp, HLSLBaseType t1, HLSL // Priority of the ? : operator. const int _conditionalOpPriority = 1; -const char* GetTypeName(const HLSLType& type) +const char* GetTypeNameHLSL(const HLSLType& type) { if (type.baseType == HLSLBaseType_UserDefined) { @@ -1328,6 +1357,8 @@ static int GetTypeCastRank(HLSLTree * tree, const HLSLType& srcType, const HLSLT if (srcType.baseType == dstType.baseType) { + // This only works if textures are half or float, but not hwne + // there are more varied texture that can be cast. if (IsTextureType(srcType.baseType)) { return srcType.formatType == dstType.formatType ? 0 : -1; @@ -2411,8 +2442,8 @@ 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); + const char* srcTypeName = GetTypeNameHLSL(srcType); + const char* dstTypeName = GetTypeNameHLSL(dstType); m_tokenizer.Error("Cannot implicitly convert from '%s' to '%s'", srcTypeName, dstTypeName); return false; } @@ -2444,8 +2475,8 @@ bool HLSLParser::ParseExpression(HLSLExpression*& expression) if (!CheckTypeCast(expression2->expressionType, expression->expressionType)) { - const char* srcTypeName = GetTypeName(expression2->expressionType); - const char* dstTypeName = GetTypeName(expression->expressionType); + const char* srcTypeName = GetTypeNameHLSL(expression2->expressionType); + const char* dstTypeName = GetTypeNameHLSL(expression->expressionType); m_tokenizer.Error("Cannot implicitly convert from '%s' to '%s'", srcTypeName, dstTypeName); return false; } @@ -2585,8 +2616,8 @@ bool HLSLParser::ParseBinaryExpression(int priority, HLSLExpression*& 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 ); + const char* typeName1 = GetTypeNameHLSL( binaryExpression->expression1->expressionType ); + const char* typeName2 = GetTypeNameHLSL( 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); @@ -2614,8 +2645,8 @@ bool HLSLParser::ParseBinaryExpression(int priority, HLSLExpression*& expression // 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); + const char* srcTypeName = GetTypeNameHLSL(expression2->expressionType); + const char* dstTypeName = GetTypeNameHLSL(expression1->expressionType); m_tokenizer.Error("':' no possible conversion from from '%s' to '%s'", srcTypeName, dstTypeName); return false; } @@ -2682,7 +2713,7 @@ bool HLSLParser::ParseTerminalExpression(HLSLExpression*& expression, bool& need if (unaryExpression->expression->expressionType.baseType < HLSLBaseType_FirstInteger || unaryExpression->expression->expressionType.baseType > HLSLBaseType_LastInteger) { - const char * typeName = GetTypeName(unaryExpression->expression->expressionType); + const char * typeName = GetTypeNameHLSL(unaryExpression->expression->expressionType); m_tokenizer.Error("unary '~' : no global operator found which takes type '%s' (or there is no acceptable conversion)", typeName); return false; } @@ -3017,11 +3048,6 @@ bool HLSLParser::ParseTerminalExpression(HLSLExpression*& expression, bool& need return false; } -// if (String_Equal(function->name, "mul")) -// { -// int bp = 0; -// bp = bp; -// } functionCall->function = function; functionCall->expressionType = function->returnType; expression = functionCall; @@ -3592,10 +3618,14 @@ bool HLSLParser::ParseAttributeList(HLSLAttribute*& firstAttribute) HLSLAttribute * attribute = m_tree->AddNode(fileName, line); - if (String_Equal(identifier, "unroll")) attribute->attributeType = HLSLAttributeType_Unroll; - else if (String_Equal(identifier, "flatten")) attribute->attributeType = HLSLAttributeType_Flatten; - else if (String_Equal(identifier, "branch")) attribute->attributeType = HLSLAttributeType_Branch; - else if (String_Equal(identifier, "nofastmath")) attribute->attributeType = HLSLAttributeType_NoFastMath; + if (String_Equal(identifier, "unroll")) + attribute->attributeType = HLSLAttributeType_Unroll; + else if (String_Equal(identifier, "flatten")) + attribute->attributeType = HLSLAttributeType_Flatten; + else if (String_Equal(identifier, "branch")) + attribute->attributeType = HLSLAttributeType_Branch; + else if (String_Equal(identifier, "nofastmath")) + attribute->attributeType = HLSLAttributeType_NoFastMath; // @@ parse arguments, () not required if attribute constructor has no arguments. @@ -3967,6 +3997,7 @@ bool HLSLParser::AcceptType(bool allowVoid, HLSLType& type/*, bool acceptFlags*/ type.baseType = HLSLBaseType_RWTexture2D; break; + // samplers case HLSLToken_SamplerState: type.baseType = HLSLBaseType_SamplerState; break; @@ -3974,12 +4005,14 @@ bool HLSLParser::AcceptType(bool allowVoid, HLSLType& type/*, bool acceptFlags*/ type.baseType = HLSLBaseType_SamplerComparisonState; break; + // older constants case HLSLToken_CBuffer: case HLSLToken_TBuffer: // might make these BufferGlobals? type.baseType = HLSLBaseType_Buffer; break; + // SSBO case HLSLToken_StructuredBuffer: case HLSLToken_RWStructuredBuffer: case HLSLToken_ByteAddressBuffer: @@ -4000,6 +4033,7 @@ bool HLSLParser::AcceptType(bool allowVoid, HLSLType& type/*, bool acceptFlags*/ int token = m_tokenizer.GetToken(); // TODO: need more format types + // TODO: double, and other types if (token >= HLSLToken_Float && token <= HLSLToken_Float4) { // TODO: code only tests if texture formatType exactly matches @@ -4016,7 +4050,7 @@ bool HLSLParser::AcceptType(bool allowVoid, HLSLType& type/*, bool acceptFlags*/ } else { - m_tokenizer.Error("Expected half or float."); + m_tokenizer.Error("Expected half or float format type on texture."); return false; } m_tokenizer.Next(); @@ -4286,15 +4320,10 @@ const HLSLFunction* HLSLParser::MatchFunctionCall(const HLSLFunctionCall* functi const HLSLFunction* function = &_intrinsic[i].function; // TODO: this is doing another O(n) search of intrinsic names + // and there are a lot of them with all the input/output variants if (String_Equal(function->name, name)) { nameMatches = true; - - if (String_Equal(name, "SampleH")) - { - int bp = 0; - bp = bp; - } CompareFunctionsResult result = CompareFunctions( m_tree, functionCall, function, matchedFunction ); if (result == Function1Better) diff --git a/hlslparser/src/HLSLParser.h b/hlslparser/src/HLSLParser.h index 40aa655c..a14720bd 100644 --- a/hlslparser/src/HLSLParser.h +++ b/hlslparser/src/HLSLParser.h @@ -167,6 +167,7 @@ enum NumericType bool IsHalf(HLSLBaseType type); bool IsFloat(HLSLBaseType type); +bool IsDouble(HLSLBaseType type); bool IsSamplerType(HLSLBaseType baseType); bool IsMatrixType(HLSLBaseType baseType); @@ -190,10 +191,11 @@ bool IsTextureType(const HLSLType& type); HLSLBaseType PromoteType(HLSLBaseType toType, HLSLBaseType type); HLSLBaseType HalfToFloatBaseType(HLSLBaseType type); +HLSLBaseType DoubleToFloatBaseType(HLSLBaseType type); const char* GetNumericTypeName(HLSLBaseType type); -const char* GetTypeName(const HLSLType& type); +const char* GetTypeNameHLSL(const HLSLType& type); const char* GetTypeNameMetal(const HLSLType& type); } diff --git a/hlslparser/src/HLSLTokenizer.cpp b/hlslparser/src/HLSLTokenizer.cpp index b078ad5f..227615bb 100644 --- a/hlslparser/src/HLSLTokenizer.cpp +++ b/hlslparser/src/HLSLTokenizer.cpp @@ -330,6 +330,7 @@ void HLSLTokenizer::Next() const int numReservedWords = sizeof(_reservedWords) / sizeof(const char*); for (int i = 0; i < numReservedWords; ++i) { + // TODO: remove O(N) search of strings, need unordered_map if (String_Equal(_reservedWords[i], m_identifier)) { m_token = 256 + i; diff --git a/hlslparser/src/HLSLTree.h b/hlslparser/src/HLSLTree.h index 55f9e42f..17f157e0 100644 --- a/hlslparser/src/HLSLTree.h +++ b/hlslparser/src/HLSLTree.h @@ -161,7 +161,7 @@ enum HLSLBaseType // counts HLSLBaseType_FirstNumeric = HLSLBaseType_Float, - HLSLBaseType_LastNumeric = HLSLBaseType_Ushort4, + HLSLBaseType_LastNumeric = HLSLBaseType_Ulong4, HLSLBaseType_FirstInteger = HLSLBaseType_Bool, HLSLBaseType_LastInteger = HLSLBaseType_LastNumeric, diff --git a/hlslparser/src/MSLGenerator.cpp b/hlslparser/src/MSLGenerator.cpp index cc6cf556..cafed104 100644 --- a/hlslparser/src/MSLGenerator.cpp +++ b/hlslparser/src/MSLGenerator.cpp @@ -2116,24 +2116,21 @@ namespace M4 // number bool isHalfNumerics = promote && !m_options.treatHalfAsFloat; auto baseType = type.baseType; + + // Note: these conversions should really be done during parsing + // so that casting gets applied. if (!isHalfNumerics) baseType = HalfToFloatBaseType(baseType); - const char* name = GetNumericTypeName(baseType); - if (name) - return name; - - // struct - if (baseType == HLSLBaseType_UserDefined) - return type.typeName; + // MSL doesn't support double + if (IsDouble(baseType)) + baseType = DoubleToFloatBaseType(baseType); - // Functions can return void, especially with compute - if (baseType == HLSLBaseType_Void) - return "void"; + HLSLType remappedType(baseType); + remappedType.typeName = type.typeName; // in case it's a struct - // sampler - if (IsSamplerType(baseType)) - return "sampler"; + if (IsSamplerType(baseType) || IsNumericType(baseType) || baseType == HLSLBaseType_Void || baseType == HLSLBaseType_UserDefined) + return GetTypeNameMetal(remappedType); // texture if (IsTextureType(baseType)) @@ -2146,6 +2143,14 @@ namespace M4 if (IsDepthTextureType(baseType)) isHalfTexture = false; + // TODO: could use GetTypeNameMetal() but it doesn't include <> portion + // so would have to pool and then return the result. + + // This would allow more formats + // const char* textureTypeName = GetTypeNameMetal(baseType); + // const char* formatTypeName = GetFormatTypeName(baseType, formatType); + // snprintf(buf, sizeof(buf), "%s<%s>", textureTypeName, formatTypeName); + switch (baseType) { case HLSLBaseType_Depth2D: @@ -2154,7 +2159,8 @@ namespace M4 return isHalfTexture ? "depth2d_array" : "depth2d_array"; case HLSLBaseType_DepthCube: return isHalfTexture ? "depthcube" : "depthcube"; - /* TODO: add, also depth_ms_array, but no HLSL equivalent + + /* TODO: also depth_ms_array, but HLSL6.6 equivalent case HLSLBaseType_Depth2DMS: return isHalfTexture ? "depth2d_ms" : "depth2d_ms"; */