diff --git a/include/Nazara/Shader/Ast/Enums.hpp b/include/Nazara/Shader/Ast/Enums.hpp index 7e64589a8..d87aae0b0 100644 --- a/include/Nazara/Shader/Ast/Enums.hpp +++ b/include/Nazara/Shader/Ast/Enums.hpp @@ -57,6 +57,8 @@ namespace Nz::ShaderAst CrossProduct = 0, DotProduct = 1, Length = 3, + Max = 4, + Min = 5, SampleTexture = 2, }; diff --git a/src/Nazara/Shader/Ast/SanitizeVisitor.cpp b/src/Nazara/Shader/Ast/SanitizeVisitor.cpp index 2eeba481d..44a7de4aa 100644 --- a/src/Nazara/Shader/Ast/SanitizeVisitor.cpp +++ b/src/Nazara/Shader/Ast/SanitizeVisitor.cpp @@ -448,6 +448,8 @@ namespace Nz::ShaderAst { case IntrinsicType::CrossProduct: case IntrinsicType::DotProduct: + case IntrinsicType::Max: + case IntrinsicType::Min: { if (clone->parameters.size() != 2) throw AstError { "Expected two parameters" }; @@ -523,6 +525,21 @@ namespace Nz::ShaderAst break; } + case IntrinsicType::Max: + case IntrinsicType::Min: + { + const ExpressionType& type = GetExpressionType(*clone->parameters.front()); + if (!IsPrimitiveType(type) && !IsVectorType(type)) + throw AstError{ "max and min only work with primitive and vector types" }; + + if ((IsPrimitiveType(type) && std::get(type) == PrimitiveType::Boolean) || + (IsVectorType(type) && std::get(type).type == PrimitiveType::Boolean)) + throw AstError{ "max and min do not work with booleans" }; + + clone->cachedExpressionType = type; + break; + } + case IntrinsicType::SampleTexture: { clone->cachedExpressionType = VectorType{ 4, std::get(GetExpressionType(*clone->parameters.front())).sampledType }; diff --git a/src/Nazara/Shader/GlslWriter.cpp b/src/Nazara/Shader/GlslWriter.cpp index 13b952818..82d787cb0 100644 --- a/src/Nazara/Shader/GlslWriter.cpp +++ b/src/Nazara/Shader/GlslWriter.cpp @@ -822,6 +822,14 @@ namespace Nz Append("length"); break; + case ShaderAst::IntrinsicType::Max: + Append("max"); + break; + + case ShaderAst::IntrinsicType::Min: + Append("min"); + break; + case ShaderAst::IntrinsicType::SampleTexture: Append("texture"); break; diff --git a/src/Nazara/Shader/LangWriter.cpp b/src/Nazara/Shader/LangWriter.cpp index a0e5eed6f..9460057b6 100644 --- a/src/Nazara/Shader/LangWriter.cpp +++ b/src/Nazara/Shader/LangWriter.cpp @@ -658,6 +658,14 @@ namespace Nz Append("length"); break; + case ShaderAst::IntrinsicType::Max: + Append("max"); + break; + + case ShaderAst::IntrinsicType::Min: + Append("min"); + break; + case ShaderAst::IntrinsicType::SampleTexture: Append("texture"); break; diff --git a/src/Nazara/Shader/ShaderLangParser.cpp b/src/Nazara/Shader/ShaderLangParser.cpp index 66cf22c3c..778c0428a 100644 --- a/src/Nazara/Shader/ShaderLangParser.cpp +++ b/src/Nazara/Shader/ShaderLangParser.cpp @@ -22,6 +22,8 @@ namespace Nz::ShaderLang std::unordered_map s_identifierToIntrinsic = { { "cross", ShaderAst::IntrinsicType::CrossProduct }, { "dot", ShaderAst::IntrinsicType::DotProduct }, + { "max", ShaderAst::IntrinsicType::Max }, + { "min", ShaderAst::IntrinsicType::Min }, { "length", ShaderAst::IntrinsicType::Length }, }; diff --git a/src/Nazara/Shader/SpirvAstVisitor.cpp b/src/Nazara/Shader/SpirvAstVisitor.cpp index 4b6f774af..53d8216bc 100644 --- a/src/Nazara/Shader/SpirvAstVisitor.cpp +++ b/src/Nazara/Shader/SpirvAstVisitor.cpp @@ -710,6 +710,51 @@ namespace Nz break; } + case ShaderAst::IntrinsicType::Max: + case ShaderAst::IntrinsicType::Min: + { + UInt32 glslInstructionSet = m_writer.GetExtendedInstructionSet("GLSL.std.450"); + + const ShaderAst::ExpressionType& parameterType = GetExpressionType(*node.parameters[0]); + assert(IsPrimitiveType(parameterType) || IsVectorType(parameterType)); + UInt32 typeId = m_writer.GetTypeId(parameterType); + + ShaderAst::PrimitiveType basicType; + if (IsPrimitiveType(parameterType)) + basicType = std::get(parameterType); + else if (IsVectorType(parameterType)) + basicType = std::get(parameterType).type; + else + throw std::runtime_error("unexpected expression type"); + + GLSLstd450 op; + switch (basicType) + { + case ShaderAst::PrimitiveType::Boolean: + throw std::runtime_error("unexpected boolean for max/min intrinsic"); + + case ShaderAst::PrimitiveType::Float32: + op = (node.intrinsic == ShaderAst::IntrinsicType::Max) ? GLSLstd450FMax : GLSLstd450FMin; + break; + + case ShaderAst::PrimitiveType::Int32: + op = (node.intrinsic == ShaderAst::IntrinsicType::Max) ? GLSLstd450SMax : GLSLstd450SMin; + break; + + case ShaderAst::PrimitiveType::UInt32: + op = (node.intrinsic == ShaderAst::IntrinsicType::Max) ? GLSLstd450UMax : GLSLstd450UMin; + break; + } + + UInt32 firstParam = EvaluateExpression(node.parameters[0]); + UInt32 secondParam = EvaluateExpression(node.parameters[1]); + UInt32 resultId = m_writer.AllocateResultId(); + + m_currentBlock->Append(SpirvOp::OpExtInst, typeId, resultId, glslInstructionSet, op, firstParam, secondParam); + PushResultId(resultId); + break; + } + case ShaderAst::IntrinsicType::SampleTexture: { UInt32 typeId = m_writer.GetTypeId(ShaderAst::VectorType{4, ShaderAst::PrimitiveType::Float32}); diff --git a/src/Nazara/Shader/SpirvWriter.cpp b/src/Nazara/Shader/SpirvWriter.cpp index 26d82c7d1..994949d2c 100644 --- a/src/Nazara/Shader/SpirvWriter.cpp +++ b/src/Nazara/Shader/SpirvWriter.cpp @@ -283,6 +283,8 @@ namespace Nz // Require GLSL.std.450 case ShaderAst::IntrinsicType::CrossProduct: case ShaderAst::IntrinsicType::Length: + case ShaderAst::IntrinsicType::Max: + case ShaderAst::IntrinsicType::Min: extInsts.emplace("GLSL.std.450"); break;