Added few more built-ins to shader language

This commit is contained in:
Yuri Roubinsky 2021-10-13 12:49:57 +03:00
parent 74a87fe98b
commit e9b7ffd1fa
3 changed files with 202 additions and 81 deletions

View file

@ -1463,6 +1463,7 @@ void ShaderCompilerRD::initialize(DefaultIdentifierActions p_actions) {
texture_functions.insert("textureLod"); texture_functions.insert("textureLod");
texture_functions.insert("textureProjLod"); texture_functions.insert("textureProjLod");
texture_functions.insert("textureGrad"); texture_functions.insert("textureGrad");
texture_functions.insert("textureGather");
texture_functions.insert("textureSize"); texture_functions.insert("textureSize");
texture_functions.insert("texelFetch"); texture_functions.insert("texelFetch");
} }

View file

@ -2456,6 +2456,23 @@ const ShaderLanguage::BuiltinFuncDef ShaderLanguage::builtin_func_defs[] = {
{ "textureGrad", TYPE_VEC4, { TYPE_SAMPLERCUBE, TYPE_VEC3, TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "sampler", "coords", "dPdx", "dPdy" }, TAG_GLOBAL, true }, { "textureGrad", TYPE_VEC4, { TYPE_SAMPLERCUBE, TYPE_VEC3, TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "sampler", "coords", "dPdx", "dPdy" }, TAG_GLOBAL, true },
{ "textureGrad", TYPE_VEC4, { TYPE_SAMPLERCUBEARRAY, TYPE_VEC4, TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "sampler", "coords", "dPdx", "dPdy" }, TAG_GLOBAL, true }, { "textureGrad", TYPE_VEC4, { TYPE_SAMPLERCUBEARRAY, TYPE_VEC4, TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "sampler", "coords", "dPdx", "dPdy" }, TAG_GLOBAL, true },
// textureGather
{ "textureGather", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_VEC2, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, true },
{ "textureGather", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_VEC2, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, true },
{ "textureGather", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_VEC2, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, true },
{ "textureGather", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_VEC2, TYPE_INT, TYPE_VOID }, { "sampler", "coords", "comp" }, TAG_GLOBAL, true },
{ "textureGather", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_VEC2, TYPE_INT, TYPE_VOID }, { "sampler", "coords", "comp" }, TAG_GLOBAL, true },
{ "textureGather", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_VEC2, TYPE_INT, TYPE_VOID }, { "sampler", "coords", "comp" }, TAG_GLOBAL, true },
{ "textureGather", TYPE_VEC4, { TYPE_SAMPLER2DARRAY, TYPE_VEC3, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, true },
{ "textureGather", TYPE_IVEC4, { TYPE_ISAMPLER2DARRAY, TYPE_VEC3, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, true },
{ "textureGather", TYPE_UVEC4, { TYPE_USAMPLER2DARRAY, TYPE_VEC3, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, true },
{ "textureGather", TYPE_VEC4, { TYPE_SAMPLER2DARRAY, TYPE_VEC3, TYPE_INT, TYPE_VOID }, { "sampler", "coords", "comp" }, TAG_GLOBAL, true },
{ "textureGather", TYPE_IVEC4, { TYPE_ISAMPLER2DARRAY, TYPE_VEC3, TYPE_INT, TYPE_VOID }, { "sampler", "coords", "comp" }, TAG_GLOBAL, true },
{ "textureGather", TYPE_UVEC4, { TYPE_USAMPLER2DARRAY, TYPE_VEC3, TYPE_INT, TYPE_VOID }, { "sampler", "coords", "comp" }, TAG_GLOBAL, true },
{ "textureGather", TYPE_VEC4, { TYPE_SAMPLERCUBE, TYPE_VEC3, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, true },
{ "textureGather", TYPE_VEC4, { TYPE_SAMPLERCUBE, TYPE_VEC3, TYPE_INT, TYPE_VOID }, { "sampler", "coords", "comp" }, TAG_GLOBAL, true },
// dFdx // dFdx
{ "dFdx", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, { "dFdx", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "p" }, TAG_GLOBAL, true },
@ -2576,6 +2593,20 @@ const ShaderLanguage::BuiltinFuncDef ShaderLanguage::builtin_func_defs[] = {
{ "findMSB", TYPE_UVEC3, { TYPE_UVEC3, TYPE_VOID }, { "value" }, TAG_GLOBAL, true }, { "findMSB", TYPE_UVEC3, { TYPE_UVEC3, TYPE_VOID }, { "value" }, TAG_GLOBAL, true },
{ "findMSB", TYPE_UVEC4, { TYPE_UVEC4, TYPE_VOID }, { "value" }, TAG_GLOBAL, true }, { "findMSB", TYPE_UVEC4, { TYPE_UVEC4, TYPE_VOID }, { "value" }, TAG_GLOBAL, true },
// umulExtended
{ "umulExtended", TYPE_VOID, { TYPE_UINT, TYPE_UINT, TYPE_UINT, TYPE_UINT, TYPE_VOID }, { "x", "y", "msb", "lsb" }, TAG_GLOBAL, true },
{ "umulExtended", TYPE_VOID, { TYPE_UVEC2, TYPE_UVEC2, TYPE_UVEC2, TYPE_UVEC2, TYPE_VOID }, { "x", "y", "msb", "lsb" }, TAG_GLOBAL, true },
{ "umulExtended", TYPE_VOID, { TYPE_UVEC3, TYPE_UVEC3, TYPE_UVEC3, TYPE_UVEC3, TYPE_VOID }, { "x", "y", "msb", "lsb" }, TAG_GLOBAL, true },
{ "umulExtended", TYPE_VOID, { TYPE_UVEC4, TYPE_UVEC4, TYPE_UVEC4, TYPE_UVEC4, TYPE_VOID }, { "x", "y", "msb", "lsb" }, TAG_GLOBAL, true },
// imulExtended
{ "imulExtended", TYPE_VOID, { TYPE_INT, TYPE_INT, TYPE_INT, TYPE_INT, TYPE_VOID }, { "x", "y", "msb", "lsb" }, TAG_GLOBAL, true },
{ "imulExtended", TYPE_VOID, { TYPE_IVEC2, TYPE_IVEC2, TYPE_IVEC2, TYPE_IVEC2, TYPE_VOID }, { "x", "y", "msb", "lsb" }, TAG_GLOBAL, true },
{ "imulExtended", TYPE_VOID, { TYPE_IVEC3, TYPE_IVEC3, TYPE_IVEC3, TYPE_IVEC3, TYPE_VOID }, { "x", "y", "msb", "lsb" }, TAG_GLOBAL, true },
{ "imulExtended", TYPE_VOID, { TYPE_IVEC4, TYPE_IVEC4, TYPE_IVEC4, TYPE_IVEC4, TYPE_VOID }, { "x", "y", "msb", "lsb" }, TAG_GLOBAL, true },
// uaddCarry // uaddCarry
{ "uaddCarry", TYPE_UINT, { TYPE_UINT, TYPE_UINT, TYPE_UINT, TYPE_VOID }, { "x", "y", "carry" }, TAG_GLOBAL, true }, { "uaddCarry", TYPE_UINT, { TYPE_UINT, TYPE_UINT, TYPE_UINT, TYPE_VOID }, { "x", "y", "carry" }, TAG_GLOBAL, true },
@ -2590,15 +2621,37 @@ const ShaderLanguage::BuiltinFuncDef ShaderLanguage::builtin_func_defs[] = {
{ "usubBorrow", TYPE_UVEC3, { TYPE_UVEC3, TYPE_UVEC3, TYPE_UVEC3, TYPE_VOID }, { "x", "y", "borrow" }, TAG_GLOBAL, true }, { "usubBorrow", TYPE_UVEC3, { TYPE_UVEC3, TYPE_UVEC3, TYPE_UVEC3, TYPE_VOID }, { "x", "y", "borrow" }, TAG_GLOBAL, true },
{ "usubBorrow", TYPE_UVEC4, { TYPE_UVEC4, TYPE_UVEC4, TYPE_UVEC4, TYPE_VOID }, { "x", "y", "borrow" }, TAG_GLOBAL, true }, { "usubBorrow", TYPE_UVEC4, { TYPE_UVEC4, TYPE_UVEC4, TYPE_UVEC4, TYPE_VOID }, { "x", "y", "borrow" }, TAG_GLOBAL, true },
// ldexp
{ "ldexp", TYPE_FLOAT, { TYPE_FLOAT, TYPE_INT, TYPE_VOID }, { "x", "exp" }, TAG_GLOBAL, true },
{ "ldexp", TYPE_VEC2, { TYPE_VEC2, TYPE_IVEC2, TYPE_VOID }, { "x", "exp" }, TAG_GLOBAL, true },
{ "ldexp", TYPE_VEC3, { TYPE_VEC3, TYPE_IVEC3, TYPE_VOID }, { "x", "exp" }, TAG_GLOBAL, true },
{ "ldexp", TYPE_VEC4, { TYPE_VEC4, TYPE_IVEC4, TYPE_VOID }, { "x", "exp" }, TAG_GLOBAL, true },
// frexp
{ "frexp", TYPE_FLOAT, { TYPE_FLOAT, TYPE_INT, TYPE_VOID }, { "x", "exp" }, TAG_GLOBAL, true },
{ "frexp", TYPE_VEC2, { TYPE_VEC2, TYPE_IVEC2, TYPE_VOID }, { "x", "exp" }, TAG_GLOBAL, true },
{ "frexp", TYPE_VEC3, { TYPE_VEC3, TYPE_IVEC3, TYPE_VOID }, { "x", "exp" }, TAG_GLOBAL, true },
{ "frexp", TYPE_VEC4, { TYPE_VEC4, TYPE_IVEC4, TYPE_VOID }, { "x", "exp" }, TAG_GLOBAL, true },
{ nullptr, TYPE_VOID, { TYPE_VOID }, { "" }, TAG_GLOBAL, false } { nullptr, TYPE_VOID, { TYPE_VOID }, { "" }, TAG_GLOBAL, false }
}; };
const ShaderLanguage::BuiltinFuncOutArgs ShaderLanguage::builtin_func_out_args[] = { const ShaderLanguage::BuiltinFuncOutArgs ShaderLanguage::builtin_func_out_args[] = {
//constructors { "modf", { 1, -1 } },
{ "modf", 1 }, { "umulExtended", { 2, 3 } },
{ "uaddCarry", 2 }, { "imulExtended", { 2, 3 } },
{ "usubBorrow", 2 }, { "uaddCarry", { 2, -1 } },
{ nullptr, 0 } { "usubBorrow", { 2, -1 } },
{ "ldexp", { 1, -1 } },
{ "frexp", { 1, -1 } },
{ nullptr, { 0, -1 } }
};
const ShaderLanguage::BuiltinFuncConstArgs ShaderLanguage::builtin_func_const_args[] = {
{ "textureGather", 2, 0, 3 },
{ nullptr, 0, 0, 0 }
}; };
bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionInfo &p_function_info, OperatorNode *p_func, DataType *r_ret_type, StringName *r_ret_type_str) { bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionInfo &p_function_info, OperatorNode *p_func, DataType *r_ret_type, StringName *r_ret_type_str) {
@ -2692,100 +2745,152 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI
} }
if (!fail) { if (!fail) {
{
int constarg_idx = 0;
while (builtin_func_const_args[constarg_idx].name) {
if (String(name) == builtin_func_const_args[constarg_idx].name) {
int arg = builtin_func_const_args[constarg_idx].arg + 1;
if (p_func->arguments.size() <= arg) {
break;
}
int min = builtin_func_const_args[constarg_idx].min;
int max = builtin_func_const_args[constarg_idx].max;
bool error = false;
if (p_func->arguments[arg]->type == Node::TYPE_VARIABLE) {
const VariableNode *vn = (VariableNode *)p_func->arguments[arg];
bool is_const = false;
ConstantNode::Value value;
_find_identifier(p_block, false, p_function_info, vn->name, nullptr, nullptr, &is_const, nullptr, nullptr, &value);
if (!is_const || value.sint < min || value.sint > max) {
error = true;
}
} else {
if (p_func->arguments[arg]->type == Node::TYPE_CONSTANT) {
ConstantNode *cn = (ConstantNode *)p_func->arguments[arg];
if (cn->get_datatype() == TYPE_INT && cn->values.size() == 1) {
int value = cn->values[0].sint;
if (value < min || value > max) {
error = true;
}
} else {
error = true;
}
} else {
error = true;
}
}
if (error) {
_set_error(vformat("Expected integer constant within %s..%s range.", min, max));
return false;
}
}
constarg_idx++;
}
}
//make sure its not an out argument used in the wrong way //make sure its not an out argument used in the wrong way
int outarg_idx = 0; int outarg_idx = 0;
while (builtin_func_out_args[outarg_idx].name) { while (builtin_func_out_args[outarg_idx].name) {
if (String(name) == builtin_func_out_args[outarg_idx].name) { if (String(name) == builtin_func_out_args[outarg_idx].name) {
int arg_idx = builtin_func_out_args[outarg_idx].argument; for (int arg = 0; arg < BuiltinFuncOutArgs::MAX_ARGS; arg++) {
int arg_idx = builtin_func_out_args[outarg_idx].arguments[arg];
if (arg_idx < argcount) { if (arg_idx == -1) {
if (p_func->arguments[arg_idx + 1]->type != Node::TYPE_VARIABLE && p_func->arguments[arg_idx + 1]->type != Node::TYPE_MEMBER && p_func->arguments[arg_idx + 1]->type != Node::TYPE_ARRAY) { break;
_set_error("Argument " + itos(arg_idx + 1) + " of function '" + String(name) + "' is not a variable, array or member.");
return false;
} }
if (arg_idx < argcount) {
if (p_func->arguments[arg_idx + 1]->type != Node::TYPE_VARIABLE && p_func->arguments[arg_idx + 1]->type != Node::TYPE_MEMBER && p_func->arguments[arg_idx + 1]->type != Node::TYPE_ARRAY) {
_set_error("Argument " + itos(arg_idx + 1) + " of function '" + String(name) + "' is not a variable, array or member.");
return false;
}
if (p_func->arguments[arg_idx + 1]->type == Node::TYPE_ARRAY) { if (p_func->arguments[arg_idx + 1]->type == Node::TYPE_ARRAY) {
ArrayNode *mn = static_cast<ArrayNode *>(p_func->arguments[arg_idx + 1]); ArrayNode *mn = static_cast<ArrayNode *>(p_func->arguments[arg_idx + 1]);
if (mn->is_const) { if (mn->is_const) {
fail = true; fail = true;
} }
} else if (p_func->arguments[arg_idx + 1]->type == Node::TYPE_MEMBER) { } else if (p_func->arguments[arg_idx + 1]->type == Node::TYPE_MEMBER) {
MemberNode *mn = static_cast<MemberNode *>(p_func->arguments[arg_idx + 1]); MemberNode *mn = static_cast<MemberNode *>(p_func->arguments[arg_idx + 1]);
if (mn->basetype_const) { if (mn->basetype_const) {
fail = true; fail = true;
} }
} else { // TYPE_VARIABLE } else { // TYPE_VARIABLE
VariableNode *vn = static_cast<VariableNode *>(p_func->arguments[arg_idx + 1]); VariableNode *vn = static_cast<VariableNode *>(p_func->arguments[arg_idx + 1]);
if (vn->is_const) { if (vn->is_const) {
fail = true;
} else {
StringName varname = vn->name;
if (shader->uniforms.has(varname)) {
fail = true; fail = true;
} else { } else {
if (shader->varyings.has(varname)) { StringName varname = vn->name;
_set_error(vformat("Varyings cannot be passed for '%s' parameter!", "out")); if (shader->uniforms.has(varname)) {
return false; fail = true;
} } else {
if (p_function_info.built_ins.has(varname)) { if (shader->varyings.has(varname)) {
BuiltInInfo info = p_function_info.built_ins[varname]; _set_error(vformat("Varyings cannot be passed for '%s' parameter!", "out"));
if (info.constant) { return false;
fail = true; }
if (p_function_info.built_ins.has(varname)) {
BuiltInInfo info = p_function_info.built_ins[varname];
if (info.constant) {
fail = true;
}
} }
} }
} }
} }
} if (fail) {
if (fail) { _set_error(vformat("Constant value cannot be passed for '%s' parameter!", "out"));
_set_error(vformat("Constant value cannot be passed for '%s' parameter!", "out"));
return false;
}
StringName var_name;
if (p_func->arguments[arg_idx + 1]->type == Node::TYPE_ARRAY) {
var_name = static_cast<const ArrayNode *>(p_func->arguments[arg_idx + 1])->name;
} else if (p_func->arguments[arg_idx + 1]->type == Node::TYPE_MEMBER) {
Node *n = static_cast<const MemberNode *>(p_func->arguments[arg_idx + 1])->owner;
while (n->type == Node::TYPE_MEMBER) {
n = static_cast<const MemberNode *>(n)->owner;
}
if (n->type != Node::TYPE_VARIABLE && n->type != Node::TYPE_ARRAY) {
_set_error("Argument " + itos(arg_idx + 1) + " of function '" + String(name) + "' is not a variable, array or member.");
return false; return false;
} }
if (n->type == Node::TYPE_VARIABLE) {
var_name = static_cast<const VariableNode *>(n)->name; StringName var_name;
} else { // TYPE_ARRAY if (p_func->arguments[arg_idx + 1]->type == Node::TYPE_ARRAY) {
var_name = static_cast<const ArrayNode *>(n)->name; var_name = static_cast<const ArrayNode *>(p_func->arguments[arg_idx + 1])->name;
} else if (p_func->arguments[arg_idx + 1]->type == Node::TYPE_MEMBER) {
Node *n = static_cast<const MemberNode *>(p_func->arguments[arg_idx + 1])->owner;
while (n->type == Node::TYPE_MEMBER) {
n = static_cast<const MemberNode *>(n)->owner;
}
if (n->type != Node::TYPE_VARIABLE && n->type != Node::TYPE_ARRAY) {
_set_error("Argument " + itos(arg_idx + 1) + " of function '" + String(name) + "' is not a variable, array or member.");
return false;
}
if (n->type == Node::TYPE_VARIABLE) {
var_name = static_cast<const VariableNode *>(n)->name;
} else { // TYPE_ARRAY
var_name = static_cast<const ArrayNode *>(n)->name;
}
} else { // TYPE_VARIABLE
var_name = static_cast<const VariableNode *>(p_func->arguments[arg_idx + 1])->name;
} }
} else { // TYPE_VARIABLE const BlockNode *b = p_block;
var_name = static_cast<const VariableNode *>(p_func->arguments[arg_idx + 1])->name; bool valid = false;
} while (b) {
const BlockNode *b = p_block; if (b->variables.has(var_name) || p_function_info.built_ins.has(var_name)) {
bool valid = false; valid = true;
while (b) { break;
if (b->variables.has(var_name) || p_function_info.built_ins.has(var_name)) { }
valid = true; if (b->parent_function) {
break; for (int i = 0; i < b->parent_function->arguments.size(); i++) {
} if (b->parent_function->arguments[i].name == var_name) {
if (b->parent_function) { valid = true;
for (int i = 0; i < b->parent_function->arguments.size(); i++) { break;
if (b->parent_function->arguments[i].name == var_name) { }
valid = true;
break;
} }
} }
b = b->parent_block;
} }
b = b->parent_block;
}
if (!valid) { if (!valid) {
_set_error("Argument " + itos(arg_idx + 1) + " of function '" + String(name) + "' can only take a local variable, array or member."); _set_error("Argument " + itos(arg_idx + 1) + " of function '" + String(name) + "' can only take a local variable, array or member.");
return false; return false;
}
} }
} }
} }
outarg_idx++; outarg_idx++;
} }
//implicitly convert values if possible //implicitly convert values if possible
@ -9157,10 +9262,16 @@ Error ShaderLanguage::complete(const String &p_code, const Map<StringName, Funct
} }
int idx2 = 0; int idx2 = 0;
int out_arg = -1; Set<int> out_args;
while (builtin_func_out_args[idx2].name != nullptr) { while (builtin_func_out_args[idx2].name != nullptr) {
if (builtin_func_out_args[idx2].name == builtin_func_defs[idx].name) { if (builtin_func_out_args[idx2].name == builtin_func_defs[idx].name) {
out_arg = builtin_func_out_args[idx2].argument; for (int i = 0; i < BuiltinFuncOutArgs::MAX_ARGS; i++) {
int arg = builtin_func_out_args[idx2].arguments[i];
if (arg == -1) {
break;
}
out_args.insert(arg);
}
break; break;
} }
idx2++; idx2++;
@ -9197,7 +9308,7 @@ Error ShaderLanguage::complete(const String &p_code, const Map<StringName, Funct
calltip += char32_t(0xFFFF); calltip += char32_t(0xFFFF);
} }
if (out_arg >= 0 && i == out_arg) { if (out_args.has(i)) {
calltip += "out "; calltip += "out ";
} }

View file

@ -949,8 +949,16 @@ private:
}; };
struct BuiltinFuncOutArgs { //arguments used as out in built in functions struct BuiltinFuncOutArgs { //arguments used as out in built in functions
enum { MAX_ARGS = 2 };
const char *name; const char *name;
int argument; const int arguments[MAX_ARGS];
};
struct BuiltinFuncConstArgs {
const char *name;
int arg;
int min;
int max;
}; };
CompletionType completion_type; CompletionType completion_type;
@ -966,6 +974,7 @@ private:
bool _get_completable_identifier(BlockNode *p_block, CompletionType p_type, StringName &identifier); bool _get_completable_identifier(BlockNode *p_block, CompletionType p_type, StringName &identifier);
static const BuiltinFuncDef builtin_func_defs[]; static const BuiltinFuncDef builtin_func_defs[];
static const BuiltinFuncOutArgs builtin_func_out_args[]; static const BuiltinFuncOutArgs builtin_func_out_args[];
static const BuiltinFuncConstArgs builtin_func_const_args[];
Error _validate_datatype(DataType p_type); Error _validate_datatype(DataType p_type);
bool _compare_datatypes(DataType p_datatype_a, String p_datatype_name_a, int p_array_size_a, DataType p_datatype_b, String p_datatype_name_b, int p_array_size_b); bool _compare_datatypes(DataType p_datatype_a, String p_datatype_name_a, int p_array_size_a, DataType p_datatype_b, String p_datatype_name_b, int p_array_size_b);