-
-
Notifications
You must be signed in to change notification settings - Fork 21.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[GDnative] add pluginscript \o/ #11953
Conversation
Wow, is this that DLScript thing everyone is talking about? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Really cool!
// flags: <int> | ||
// rpc_mode: <int:godot_method_rpc_mode> | ||
// } | ||
godot_array methods; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you know the format, why not use a struct godot_pluginscript_method_desc
with those fields, so you can have a godot_pluginscript_method_desc *methods; // pointer to array
and num of methods? should be less painfull than filling a dictionary.
But a dictionary works too of course.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems easier for me to build those fields with godot arrays&dict to avoid painful memory management (typically having to free a single array vs having to walk all the sub arrays and fields in each elements to free everything by hand...)
// rpc_mode: <int:godot_method_rpc_mode> | ||
// } | ||
godot_array methods; | ||
// Same format than for methods |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same format
as for methods
;)
} | ||
|
||
ScriptLanguage *PluginScriptInstance::get_language() { | ||
return this->_script.ptr()->get_language(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ref<T>
has an overloaded operator->
, you don't need that .ptr()->
, just use this->_script->get_language();
This happens in multiple places.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done.
} | ||
|
||
Variant PluginScriptInstance::call(const StringName &p_method, const Variant **p_args, int p_argcount, Variant::CallError &r_error) { | ||
// TODO: optimize when calling a Godot method from Godot to avoid param conversion ? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know it's in a TODO, but the conversion is really just an act of tricking the type system and shouldn't have any performance impacts.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not about Godot's String
-> godot_string
conversion I'm talking about but (example with godot-python) godot_string
-> Python str
-> godot_string
when you call from Godot a method of a Python Script
and this method is in fact defined in a parent implemented within Godot
|
||
Script *PluginScriptLanguage::create_script() const { | ||
PluginScript *script = memnew(PluginScript()); | ||
// I'm hurting kittens doing this I guess... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
:)
if (this->_desc.profiling_start) { | ||
this->_desc.profiling_start(); | ||
} | ||
this->unlock(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would move the locking inside the if. If the scripting language doesn't provide profiling methods then why even bother locking? There won't be any changes anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done.
|
||
// --- Language --- | ||
|
||
typedef struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why doesn't language have a void *data;
field?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done.
} | ||
|
||
void PluginScript::update_exports() { | ||
// TODO |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup, that's pretty important. :P
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure what should be done here, so I would prefer to keep it this way for the current PR ;-)
Error PluginScript::reload(bool p_keep_state) { | ||
this->_language->lock(); | ||
ERR_FAIL_COND_V(!p_keep_state && this->_instances.size(), ERR_ALREADY_IN_USE); | ||
this->_language->unlock(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Won't this be useless in release builds?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah you're right about that, no need to remove
if (ret) { | ||
ERR_FAIL(); | ||
} | ||
// TODO: @Karroffel how should we pass the langage_desc argument ? Should we only keep a pointer inside PluginScriptLanguage ? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a good question. It depends on if the struct's lifetime is greater than that of the PluginScriptLanguage object aka heap or static data. Should make up a convention for that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My current guess is it's pretty easy for user's library to have a static
struct of godot_pluginscript_language_desc
(or store the structure in the godot_pluginscript_language_data
structure that is currently missing as you pointed out ^^), so we should be able to use the passed pointer during the entire lifetime of the program. The thing is we should add a comment or use a convention to tell the user he should not free this thing until godot_pluginscript_language_desc.finish
is called
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment can be removed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done.
Closes #8505 ? |
@neikeq yep, now nothing can stop Brainfuck to be the number 1 language for Godot ! |
1251ead
to
3cb6af8
Compare
Can someone explain me what is PR this about ? I don't understand what is it for. |
@groud this is for adding "plug & play" script languages via GDNative. |
// self_time: <int:microseconds> | ||
// } | ||
// } | ||
int (*profiling_get_accumulated_data)(godot_pluginscript_language_data *p_data, godot_dictionary *r_info, int p_info_max); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure about how ProfilingInfo
is passed here:
In the end pluginscript will fill an a C array of ProfilingInfo
structs.
Currently the language will fill a godot dict, with function signature as key and a dict of ints as value.
So Pluginscript iterate over the dict to fill the final C array. Meaning:
- creating the dict with function signature as key takes time for each insertion for no benefit (given the dict is only iterated)
- creating all the subdicts is costly
- the iteration & recopy of the dicts into the
ProfilingInfo
class instances takes time
Given profiling_get_frame_data
is called for each frame during debug, I think we should try to lower the overhead, the possibilities I see:
- replace the godot dict returned by the function by an godot array
- create a new C struct equivalent
ProfilingInfo
and allocate an array of them to be filled by theprofiling_get_frame_data
- pass the actual C array of
ProfilingInfo
class instances toprofiling_get_frame_data
, and allow it to be filled with a custom function:
void godot_pluginscript_fill_profiling_entry(void *profiling_array, int index, godot_string_name *signature, godot_int call_count, godot_int total_time, godot_int self_time) {
ProfilingInfo *array = profiling_array;
array[index].signature = StringName(*(StringName*)signature);
array[index].call_count = call_count;
array[index].total_time = total_time;
array[index].self_time = self_time;
}
@karroffel any idea on this ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like 2.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done.
|
||
bool PluginScriptInstance::set(const StringName &p_name, const Variant &p_value) { | ||
String name = String(p_name); | ||
return this->_desc->set_prop(this->_data, (const godot_string *)&name, (const godot_variant *)&p_value); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As you may have noticed, I'm a python guy so I love to access my instance's attribute explicitly (e.g. this->_attribute) ^^
This is not what is done in the rest of the project so I can update this to a more traditional implicit this, just tell me if it hurts you ;p
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn't exactly hurt me, but I'd still prefer to remove the this
pointers. ^^
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, if only for consistency, it's better not to use this
pointers when not necessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you prefer I can also provide a PR to put this->
everywhere in Godot codebase
97ca109
to
4546385
Compare
@@ -0,0 +1,151 @@ | |||
// Godot imports |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't forget to add the Godot copyright header to all .cpp and .h files.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done.
@akien-mga @karroffel do you think we could have this merged for the next alpha ? Only thing on my todo list remaining are #11953 (comment) and #11937 (comment) which I should be able to do this weekend |
d96753c
to
dd84fc5
Compare
Build system is corrected to work like arvrnative one |
8f61184
to
dbbb5a0
Compare
@akien-mga @karroffel #11953 (comment) and #11937 (comment) are done now, it's up to you now ;-) |
Could you squash some of the commits together to merge the fixups in the commits introducing the code that had to be fixed up? 15 commits is a lot, but not all are relevant to the git history. (And I can't check the travis log, but it still seems to be failing, probably style issue?) |
dbbb5a0
to
cecd8af
Compare
@akien-mga squashed and style corrected ;-) |
modules/gdnative/register_types.cpp
Outdated
@@ -208,6 +210,7 @@ void unregister_gdnative_types() { | |||
singleton_gdnatives.clear(); | |||
|
|||
unregister_nativearvr_types(); | |||
unregister_pluginscript_types(); | |||
unregister_nativescript_types(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh while you're here, can you make the order of unregistering the opposite of registering? @BastiaanOlij put the nativearvr stuff in the wrong order 😛
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done.
cecd8af
to
e715be0
Compare
🎉 🎉 🎉 🎈 🍾 |
I have missed this merge. Is there any documentation about how to use this? |
@vnen not yet, but all that this PR does is provide one header with data structures and only one C function, In order to implement the scripting language you need to fill out the struct you pass to |
@vnen I guess best "documentation" so far for pluginscript is godot-python 😜 If you need more specific questions info, don't hesitate to ping me on the discord |
@karroffel This code is not final so don't merge it yet, I'm posting it to have more eyes on it ;-)
There is still some commented stuff and TODO that need to be discuss about but the code works fine with godot-python.