Added 'fma' function to shader language

This commit is contained in:
Yuri Roubinsky 2020-02-14 23:09:53 +03:00
parent 70e21154f0
commit ecb5f7ea23
8 changed files with 199 additions and 0 deletions

View file

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VisualShaderNodeMultiplyAdd" inherits="VisualShaderNode" version="4.0">
<brief_description>
Performs a fused multiply-add operation within the visual shader graph.
</brief_description>
<description>
Uses three operands to compute [code](a * b + c)[/code] expression.
</description>
<tutorials>
</tutorials>
<methods>
</methods>
<members>
<member name="type" type="int" setter="set_type" getter="get_type" enum="VisualShaderNodeMultiplyAdd.Type" default="0">
A type of operands and returned value.
</member>
</members>
<constants>
<constant name="TYPE_SCALAR" value="0" enum="Type">
A scalar type.
</constant>
<constant name="TYPE_VECTOR" value="1" enum="Type">
A vector type.
</constant>
<constant name="TYPE_MAX" value="2" enum="Type">
Represents the size of the [enum Type] enum.
</constant>
</constants>
</class>

View file

@ -1016,6 +1016,7 @@ ShaderCompilerGLES2::ShaderCompilerGLES2() {
actions[RS::SHADER_CANVAS_ITEM].usage_defines["isinf"] = "#define IS_INF_USED\n";
actions[RS::SHADER_CANVAS_ITEM].usage_defines["isnan"] = "#define IS_NAN_USED\n";
actions[RS::SHADER_CANVAS_ITEM].usage_defines["trunc"] = "#define TRUNC_USED\n";
actions[RS::SHADER_CANVAS_ITEM].usage_defines["fma"] = "#define FMA_USED\n";
/** SPATIAL SHADER **/
@ -1126,6 +1127,7 @@ ShaderCompilerGLES2::ShaderCompilerGLES2() {
actions[RS::SHADER_SPATIAL].usage_defines["isinf"] = "#define IS_INF_USED\n";
actions[RS::SHADER_SPATIAL].usage_defines["isnan"] = "#define IS_NAN_USED\n";
actions[RS::SHADER_SPATIAL].usage_defines["trunc"] = "#define TRUNC_USED\n";
actions[RS::SHADER_SPATIAL].usage_defines["fma"] = "#define FMA_USED\n";
actions[RS::SHADER_SPATIAL].render_mode_defines["skip_vertex_transform"] = "#define SKIP_TRANSFORM_USED\n";
actions[RS::SHADER_SPATIAL].render_mode_defines["world_vertex_coords"] = "#define VERTEX_WORLD_COORDS_USED\n";

View file

@ -417,4 +417,24 @@ highp mat4 outerProduct(highp vec4 c, highp vec4 r) {
#endif
#if defined(FMA_USED)
highp float fma(highp float a, highp float b, highp float c) {
return a * b + c;
}
highp vec2 fma(highp vec2 a, highp vec2 b, highp vec2 c) {
return a * b + c;
}
highp vec3 fma(highp vec3 a, highp vec3 b, highp vec3 c) {
return a * b + c;
}
highp vec4 fma(highp vec4 a, highp vec4 b, highp vec4 c) {
return a * b + c;
}
#endif
#endif

View file

@ -1392,6 +1392,12 @@ VisualShaderNode *VisualShaderEditor::_add_node(int p_idx, int p_op_idx) {
if (vderFunc) {
vderFunc->set_function((VisualShaderNodeVectorDerivativeFunc::Function)p_op_idx);
}
VisualShaderNodeMultiplyAdd *fmaFunc = Object::cast_to<VisualShaderNodeMultiplyAdd>(vsn);
if (fmaFunc) {
fmaFunc->set_type((VisualShaderNodeMultiplyAdd::Type)p_op_idx);
}
}
vsnode = Ref<VisualShaderNode>(vsn);
@ -2711,6 +2717,7 @@ VisualShaderEditor::VisualShaderEditor() {
add_options.push_back(AddOption("Max", "Scalar", "Functions", "VisualShaderNodeFloatOp", TTR("Returns the greater of two values."), VisualShaderNodeFloatOp::OP_MAX, VisualShaderNode::PORT_TYPE_SCALAR));
add_options.push_back(AddOption("Min", "Scalar", "Functions", "VisualShaderNodeFloatOp", TTR("Returns the lesser of two values."), VisualShaderNodeFloatOp::OP_MIN, VisualShaderNode::PORT_TYPE_SCALAR));
add_options.push_back(AddOption("Mix", "Scalar", "Functions", "VisualShaderNodeScalarInterp", TTR("Linear interpolation between two scalars."), -1, VisualShaderNode::PORT_TYPE_SCALAR));
add_options.push_back(AddOption("MultiplyAdd", "Scalar", "Functions", "VisualShaderNodeMultiplyAdd", TTR("Performs a fused multiply-add operation (a * b + c) on scalars."), VisualShaderNodeMultiplyAdd::TYPE_SCALAR, VisualShaderNode::PORT_TYPE_SCALAR));
add_options.push_back(AddOption("Negate", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Returns the opposite value of the parameter."), VisualShaderNodeFloatFunc::FUNC_NEGATE, VisualShaderNode::PORT_TYPE_SCALAR));
add_options.push_back(AddOption("Negate", "Scalar", "Functions", "VisualShaderNodeIntFunc", TTR("Returns the opposite value of the parameter."), VisualShaderNodeIntFunc::FUNC_NEGATE, VisualShaderNode::PORT_TYPE_SCALAR_INT));
add_options.push_back(AddOption("OneMinus", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("1.0 - scalar"), VisualShaderNodeFloatFunc::FUNC_ONEMINUS, VisualShaderNode::PORT_TYPE_SCALAR));
@ -2813,6 +2820,7 @@ VisualShaderEditor::VisualShaderEditor() {
add_options.push_back(AddOption("Min", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Returns the lesser of two values."), VisualShaderNodeVectorOp::OP_MIN, VisualShaderNode::PORT_TYPE_VECTOR));
add_options.push_back(AddOption("Mix", "Vector", "Functions", "VisualShaderNodeVectorInterp", TTR("Linear interpolation between two vectors."), -1, VisualShaderNode::PORT_TYPE_VECTOR));
add_options.push_back(AddOption("MixS", "Vector", "Functions", "VisualShaderNodeVectorScalarMix", TTR("Linear interpolation between two vectors using scalar."), -1, VisualShaderNode::PORT_TYPE_VECTOR));
add_options.push_back(AddOption("MultiplyAdd", "Vector", "Functions", "VisualShaderNodeMultiplyAdd", TTR("Performs a fused multiply-add operation (a * b + c) on vectors."), VisualShaderNodeMultiplyAdd::TYPE_VECTOR, VisualShaderNode::PORT_TYPE_VECTOR));
add_options.push_back(AddOption("Negate", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the opposite value of the parameter."), VisualShaderNodeVectorFunc::FUNC_NEGATE, VisualShaderNode::PORT_TYPE_VECTOR));
add_options.push_back(AddOption("Normalize", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Calculates the normalize product of vector."), VisualShaderNodeVectorFunc::FUNC_NORMALIZE, VisualShaderNode::PORT_TYPE_VECTOR));
add_options.push_back(AddOption("OneMinus", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("1.0 - vector"), VisualShaderNodeVectorFunc::FUNC_ONEMINUS, VisualShaderNode::PORT_TYPE_VECTOR));

View file

@ -562,6 +562,7 @@ void register_scene_types() {
ClassDB::register_class<VisualShaderNodeGlobalExpression>();
ClassDB::register_class<VisualShaderNodeIs>();
ClassDB::register_class<VisualShaderNodeCompare>();
ClassDB::register_class<VisualShaderNodeMultiplyAdd>();
ClassDB::register_class<ShaderMaterial>();
ClassDB::register_virtual_class<CanvasItem>();

View file

@ -4794,3 +4794,96 @@ VisualShaderNodeCompare::VisualShaderNodeCompare() {
set_input_port_default_value(1, 0.0);
set_input_port_default_value(2, CMP_EPSILON);
}
////////////// Fma
String VisualShaderNodeMultiplyAdd::get_caption() const {
return "MultiplyAdd";
}
int VisualShaderNodeMultiplyAdd::get_input_port_count() const {
return 3;
}
VisualShaderNodeMultiplyAdd::PortType VisualShaderNodeMultiplyAdd::get_input_port_type(int p_port) const {
if (type == TYPE_SCALAR) {
return PORT_TYPE_SCALAR;
}
return PORT_TYPE_VECTOR;
}
String VisualShaderNodeMultiplyAdd::get_input_port_name(int p_port) const {
if (p_port == 0) {
return "a";
} else if (p_port == 1) {
return "b(*)";
} else if (p_port == 2) {
return "c(+)";
}
return "";
}
int VisualShaderNodeMultiplyAdd::get_output_port_count() const {
return 1;
}
VisualShaderNodeMultiplyAdd::PortType VisualShaderNodeMultiplyAdd::get_output_port_type(int p_port) const {
if (type == TYPE_SCALAR) {
return PORT_TYPE_SCALAR;
} else {
return PORT_TYPE_VECTOR;
}
}
String VisualShaderNodeMultiplyAdd::get_output_port_name(int p_port) const {
return "";
}
String VisualShaderNodeMultiplyAdd::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
return "\t" + p_output_vars[0] + " = fma(" + p_input_vars[0] + ", " + p_input_vars[1] + ", " + p_input_vars[2] + ");\n";
}
void VisualShaderNodeMultiplyAdd::set_type(Type p_type) {
ERR_FAIL_INDEX((int)p_type, TYPE_MAX);
if (p_type != type) {
if (p_type == TYPE_SCALAR) {
set_input_port_default_value(0, 0.0);
set_input_port_default_value(1, 0.0);
set_input_port_default_value(2, 0.0);
} else {
set_input_port_default_value(0, Vector3(0.0, 0.0, 0.0));
set_input_port_default_value(1, Vector3(0.0, 0.0, 0.0));
set_input_port_default_value(2, Vector3(0.0, 0.0, 0.0));
}
}
type = p_type;
emit_changed();
}
VisualShaderNodeMultiplyAdd::Type VisualShaderNodeMultiplyAdd::get_type() const {
return type;
}
Vector<StringName> VisualShaderNodeMultiplyAdd::get_editable_properties() const {
Vector<StringName> props;
props.push_back("type");
return props;
}
void VisualShaderNodeMultiplyAdd::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_type", "type"), &VisualShaderNodeMultiplyAdd::set_type);
ClassDB::bind_method(D_METHOD("get_type"), &VisualShaderNodeMultiplyAdd::get_type);
ADD_PROPERTY(PropertyInfo(Variant::INT, "type", PROPERTY_HINT_ENUM, "Scalar,Vector"), "set_type", "get_type");
BIND_ENUM_CONSTANT(TYPE_SCALAR);
BIND_ENUM_CONSTANT(TYPE_VECTOR);
BIND_ENUM_CONSTANT(TYPE_MAX);
}
VisualShaderNodeMultiplyAdd::VisualShaderNodeMultiplyAdd() {
type = TYPE_SCALAR;
set_input_port_default_value(0, 0.0);
set_input_port_default_value(1, 0.0);
set_input_port_default_value(2, 0.0);
}

View file

@ -1995,4 +1995,43 @@ VARIANT_ENUM_CAST(VisualShaderNodeCompare::ComparisonType)
VARIANT_ENUM_CAST(VisualShaderNodeCompare::Function)
VARIANT_ENUM_CAST(VisualShaderNodeCompare::Condition)
class VisualShaderNodeMultiplyAdd : public VisualShaderNode {
GDCLASS(VisualShaderNodeMultiplyAdd, VisualShaderNode);
public:
enum Type {
TYPE_SCALAR,
TYPE_VECTOR,
TYPE_MAX,
};
protected:
Type type;
protected:
static void _bind_methods();
public:
virtual String get_caption() const;
virtual int get_input_port_count() const;
virtual PortType get_input_port_type(int p_port) const;
virtual String get_input_port_name(int p_port) const;
virtual int get_output_port_count() const;
virtual PortType get_output_port_type(int p_port) const;
virtual String get_output_port_name(int p_port) const;
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
void set_type(Type p_type);
Type get_type() const;
virtual Vector<StringName> get_editable_properties() const;
VisualShaderNodeMultiplyAdd();
};
VARIANT_ENUM_CAST(VisualShaderNodeMultiplyAdd::Type)
#endif // VISUAL_SHADER_NODES_H

View file

@ -2135,6 +2135,13 @@ const ShaderLanguage::BuiltinFuncDef ShaderLanguage::builtin_func_defs[] = {
//array
{ "length", TYPE_INT, { TYPE_VOID }, TAG_ARRAY, true },
// modern functions
{ "fma", TYPE_FLOAT, { TYPE_FLOAT, TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true },
{ "fma", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, true },
{ "fma", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, true },
{ "fma", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, true },
{ nullptr, TYPE_VOID, { TYPE_VOID }, TAG_GLOBAL, false }
};