Skip to content
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

Merged
merged 1 commit into from
Oct 17, 2017
Merged

Conversation

touilleMan
Copy link
Member

@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.

@neikeq
Copy link
Contributor

neikeq commented Oct 8, 2017

Wow, is this that DLScript thing everyone is talking about?

Copy link
Contributor

@karroffel karroffel left a 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;
Copy link
Contributor

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.

Copy link
Member Author

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
Copy link
Contributor

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();
Copy link
Contributor

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.

Copy link
Member Author

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 ?
Copy link
Contributor

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.

Copy link
Member Author

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...
Copy link
Contributor

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();
Copy link
Contributor

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.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.


// --- Language ---

typedef struct {
Copy link
Contributor

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?

Copy link
Member Author

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
Copy link
Contributor

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

Copy link
Member Author

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();
Copy link
Contributor

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?

Copy link
Member Author

@touilleMan touilleMan Oct 9, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

perhaps

But in release reload is called once at load time, so I guess performance is not really a concern (beside GDscript doesn't remove this code in release).

So if you're sure about this removal, I'll do it ;-)

Copy link
Contributor

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 ?
Copy link
Contributor

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.

Copy link
Member Author

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

Copy link
Contributor

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

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

@HummusSamurai
Copy link
Contributor

Closes #8505 ?

@touilleMan
Copy link
Member Author

@neikeq yep, now nothing can stop Brainfuck to be the number 1 language for Godot !

@groud
Copy link
Member

groud commented Oct 12, 2017

Can someone explain me what is PR this about ? I don't understand what is it for.

@27thLiz
Copy link
Contributor

27thLiz commented Oct 12, 2017

@groud this is for adding "plug & play" script languages via GDNative.
It abstracts away some of the inconviences of GDNative.
If a language is exposed as a PluginScript it will benefit from the same editor integration as GDScript/C#.
So you can download the plugin from the assetlib and are able to choose that language directly in the "Add script" dialog, have the buildsystem set up, use the godot script editor, able to use templates, build automatically when running the game, etc.. all the things you'd have to do manually when using GDNative bindings directly.

// self_time: <int:microseconds>
// }
// }
int (*profiling_get_accumulated_data)(godot_pluginscript_language_data *p_data, godot_dictionary *r_info, int p_info_max);
Copy link
Member Author

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:

  1. replace the godot dict returned by the function by an godot array
  2. create a new C struct equivalent ProfilingInfo and allocate an array of them to be filled by the profiling_get_frame_data
  3. pass the actual C array of ProfilingInfo class instances to profiling_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 ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like 2.

Copy link
Member Author

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);
Copy link
Member Author

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

Copy link
Contributor

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. ^^

Copy link
Member

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.

Copy link
Member Author

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 :trollface:

@touilleMan touilleMan force-pushed the pluginscript branch 2 times, most recently from 97ca109 to 4546385 Compare October 12, 2017 18:52
@@ -0,0 +1,151 @@
// Godot imports
Copy link
Member

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.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

@HummusSamurai
Copy link
Contributor

@groud

Can someone explain me what is PR this about ? I don't understand what is it for.

I believe it's an implementation of #8505

@touilleMan
Copy link
Member Author

touilleMan commented Oct 14, 2017

@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

@touilleMan
Copy link
Member Author

Build system is corrected to work like arvrnative one

@touilleMan
Copy link
Member Author

@akien-mga @karroffel #11953 (comment) and #11937 (comment) are done now, it's up to you now ;-)

@akien-mga
Copy link
Member

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?)

@touilleMan
Copy link
Member Author

@akien-mga squashed and style corrected ;-)

@@ -208,6 +210,7 @@ void unregister_gdnative_types() {
singleton_gdnatives.clear();

unregister_nativearvr_types();
unregister_pluginscript_types();
unregister_nativescript_types();
Copy link
Contributor

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 😛

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

@karroffel karroffel merged commit 8c50bc3 into godotengine:master Oct 17, 2017
@reduz
Copy link
Member

reduz commented Oct 17, 2017

🎉 🎉 🎉 🎈 🍾

@touilleMan
Copy link
Member Author

it's over

@vnen
Copy link
Member

vnen commented Nov 19, 2017

I have missed this merge. Is there any documentation about how to use this?

@karroffel
Copy link
Contributor

@vnen not yet, but all that this PR does is provide one header with data structures and only one C function, godot_pluginscript_register_language. If that function gets called from a GDNative singleton (a library that gets loaded at startup and stays loaded all the time) then you can provide a new scripting language, as if it was done via a module.

In order to implement the scripting language you need to fill out the struct you pass to godot_pluginscript_register_language accordingly with function pointers and other information.

@touilleMan
Copy link
Member Author

@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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants