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

Discern between VIRTUAL and ABSTRACT class bindings #58972

Merged
merged 1 commit into from
Mar 10, 2022

Conversation

reduz
Copy link
Member

@reduz reduz commented Mar 10, 2022

  • Previous "virtual" classes (which can't be instantiated) are now correctly named "abstract" (GDREGISTER_ABSTRACT_CLASS).
  • Added a new "virtual" category for classes, they can't be instantiated from the editor, but can be inherited from script and extensions (GDREGISTER_VIRTUAL_CLASS).
  • Converted a large amount of classes from "abstract" to "virtual" where it makes sense, and added the relevant GDVIRTUAL bindings so they can be extended via extension.

Most classes (that make sense) have been converted. Missing:

  • Physics servers
  • VideoStream
  • Script* classes.

which will go in separate PRs due to the complexity involved.

@reduz reduz requested review from a team as code owners March 10, 2022 07:35
@touilleMan
Copy link
Member

Wow this is super cool ! 😃

Script* classes.

Does it means the plan is to expose ScriptServer/Script/ScriptInstance to ClassDB and hence to allow them to be used from GDExtension ? (this is what point 1 of godotengine/godot-proposals#3927 was about, so I'm super happy if this is happening ^^)

@reduz
Copy link
Member Author

reduz commented Mar 10, 2022

@touilleMan I think ScriptInstance will be exposed via a native extension API (Plain C), since its more efficient if it does not use the binder, but Script and ScriptLanguage will be exposed as regular ClassDB classes. Likely as ScriptExtension and ScriptLanguageExtension.

Copy link
Member

@akien-mga akien-mga left a comment

Choose a reason for hiding this comment

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

Looks pretty good to me, some comments and nitpicks.

MethodInfo get_rid_bind("_get_rid");
get_rid_bind.return_val.type = Variant::RID;

::ClassDB::add_virtual_method(get_class_static(), get_rid_bind, true, Vector<String>(), true);
Copy link
Member

Choose a reason for hiding this comment

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

Just curious, does it actually need the :: scope here? Just above there's ClassDB::bind_method without extra scoping.

return false;
}
#endif
return (!ti->disabled && ti->creation_func != nullptr && !(ti->native_extension && !ti->native_extension->create_instance) && ti->is_virtual);
Copy link
Member

Choose a reason for hiding this comment

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

I see you adapted this from can_instantiate() so this is basically can_instantiate() && is_virtual.

Just wondering if all checks from can_instantiate() are indeed wanted - e.g. do we want to exclude classes without creation func or disabled from being qualified virtual? Just a theoretical question.

Copy link
Member Author

Choose a reason for hiding this comment

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

Good point, I am not sure. For the time being I only made this changes to affect the editor, but not more. We can improve it towards the future I suppose.

Comment on lines +173 to 174
GDREGISTER_ABSTRACT_CLASS(StreamPeer);
GDREGISTER_CLASS(StreamPeerExtension);
Copy link
Member

Choose a reason for hiding this comment

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

StreamPeer, PacketPeer and MultiplayerPeer all have an *Extension wrapper which AFAIU was specifically added to workaround the limitation that this PR lifts. So these could likely be registered as virtual instead, and the *Extension wrappers dropped.

But that's probably stuff for a future PR so it might be good to keep it as is in this one. CC @Faless

Copy link
Member Author

Choose a reason for hiding this comment

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

Extension makes sense if its something very large, so I guess it is up to Fabio to change.

GDREGISTER_CLASS(AStar);
GDREGISTER_CLASS(AStar2D);
GDREGISTER_CLASS(EncodedObjectAsID);
GDREGISTER_CLASS(RandomNumberGenerator);

GDREGISTER_VIRTUAL_CLASS(ResourceImporter);
GDREGISTER_ABSTRACT_CLASS(ResourceImporter);
Copy link
Member

Choose a reason for hiding this comment

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

Isn't that something that might typically make sense to implement as virtual?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, I completely forgot about these, I will add them.

Copy link
Member Author

Choose a reason for hiding this comment

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

Looking at them, this will require likely another PR because its a lot of code.

Comment on lines +15 to +16
<method name="_draw" qualifiers="virtual const">
<return type="void" />
Copy link
Member

Choose a reason for hiding this comment

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

Just a note in passing but as we recently moved the docs for constructors and operators to dedicated <constructors> and <operators> sections, it might make sense to do the same (in a follow-up) with a <virtuals> or something like this.

So we can make the docs a bit clearer with a separation between the API users can use, and the one users can extend/reimplement.

scene/resources/mesh.cpp Outdated Show resolved Hide resolved
scene/resources/texture.cpp Outdated Show resolved Hide resolved
virtual int get_depth() const = 0;
virtual bool has_mipmaps() const = 0;
virtual Vector<Ref<Image>> get_data() const = 0;
TypedArray<Image> _get_datai() const;
Copy link
Member

Choose a reason for hiding this comment

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

What does i stand for here?

Copy link
Member Author

Choose a reason for hiding this comment

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

internal I suppose, its not user exposed and _get_data is virtual already.

servers/audio/audio_stream.cpp Outdated Show resolved Hide resolved
Comment on lines +113 to 114
GDREGISTER_ABSTRACT_CLASS(TextServer);
GDREGISTER_CLASS(TextServerExtension);
Copy link
Member

Choose a reason for hiding this comment

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

Another candidate for merging TextServerExtension into a virtual TextServer I guess @bruvzg.

Copy link
Member

Choose a reason for hiding this comment

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

This make sense, but last time I have tried to do so, every GDVIRTUAL binding which is using enums for the TextServer stopped working, seems like registering enums and binding virtual methods should have a certain order. So it might need some extra changes.

Copy link
Member

Choose a reason for hiding this comment

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

It also sounds like @reduz suggests keeping *Extension classes for classes where a lot of the API is virtual, so this might be the case here.

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 think for the large ones (like script, physics server, etc. Using Extension makes more sense because otherwise the code gets to be a mess.

@akien-mga
Copy link
Member

Looking at the kind of GDVIRTUAL_CALL + ERR_FAIL_MSG construct which happens a lot for virtual methods which must be implemented, I was thinking that it could make sense to add a new GDVIRTUAL_CALL_REQUIRED that would handle this automatically with an explicit error message.

So this:

Ref<Material> Mesh::surface_get_material(int p_idx) const {
	Ref<Material> ret;
	if (GDVIRTUAL_CALL(_surface_get_material, p_idx, ret)) {
		return ret;
	}

	ERR_FAIL_V_MSG(Ref<Material>(), "Unimplemented function");
}

Would become this:

Ref<Material> Mesh::surface_get_material(int p_idx) const {
	Ref<Material> ret;
	if (GDVIRTUAL_CALL_REQUIRED(_surface_get_material, p_idx, ret)) {
		return ret;
	}
	return Ref<Material>();
}

And GDVIRTUAL_CALL_REQUIRED would use ##m_name## and get_class() to compose a nice error message when it's not implemented, referring to the name of the virtual methods that must be implemented.
So you'd get an error like this:

Missing implementation for required virtual method '_surface_get_material' in Mesh.

@akien-mga
Copy link
Member

For the record, godot-cpp fails building after this PR with:

g++ -o src/register_types.os -c -std=c++17 -fPIC -Wwrite-strings -Og -g -m64 -fPIC -DDEBUG_ENABLED -DDEBUG_METHODS_ENABLED -I. -Igodot-headers -Iinclude -Igen/include -I/home/runner/work/godot/godot/godot-cpp/gen/include -I/home/runner/work/godot/godot/godot-cpp/include -I/home/runner/work/godot/godot/godot-cpp/godot-headers -Isrc src/register_types.cpp
In file included from src/register_types.cpp:35:
/home/runner/work/godot/godot/godot-cpp/include/godot_cpp/core/class_db.hpp: In instantiation of ‘static void godot::ClassDB::register_class() [with T = ExampleRef]’:
src/register_types.cpp:44:38:   required from here
/home/runner/work/godot/godot/godot-cpp/include/godot_cpp/core/class_db.hpp:162:14: error: invalid conversion from ‘void*’ to ‘GDNativeExtensionClassGetRID’ {aka ‘long unsigned int (*)(void*)’} [-fpermissive]
  162 |   (void *)cl.name, // void *class_userdata;
      |           ~~~^~~~
      |              |
      |              void*
/home/runner/work/godot/godot/godot-cpp/include/godot_cpp/core/class_db.hpp: In instantiation of ‘static void godot::ClassDB::register_class() [with T = Example]’:
src/register_types.cpp:45:35:   required from here
/home/runner/work/godot/godot/godot-cpp/include/godot_cpp/core/class_db.hpp:162:14: error: invalid conversion from ‘void*’ to ‘GDNativeExtensionClassGetRID’ {aka ‘long unsigned int (*)(void*)’} [-fpermissive]

We need to disable the godot-cpp test in this PR temporarily until this is merged and a matching counterpart is merged in godot-cpp.

@akien-mga
Copy link
Member

For the record, here's the list of classes currently exposed as virtual as of this PR (it's not visible in the diff due to the name swap with virtual -> abstract so those didn't get a diff):

$ rg GDREGISTER_VIRTUAL_CLASS
core/object/class_db.h
442:#define GDREGISTER_VIRTUAL_CLASS(m_class)            \

servers/register_server_types.cpp
142:    GDREGISTER_VIRTUAL_CLASS(AudioStreamPlaybackResampled);
145:    GDREGISTER_VIRTUAL_CLASS(AudioEffect);
146:    GDREGISTER_VIRTUAL_CLASS(AudioEffectInstance);

scene/register_scene_types.cpp
320:    GDREGISTER_VIRTUAL_CLASS(BaseButton);
341:    GDREGISTER_VIRTUAL_CLASS(Range);
462:    GDREGISTER_VIRTUAL_CLASS(VisualInstance3D);
463:    GDREGISTER_VIRTUAL_CLASS(GeometryInstance3D);
748:    GDREGISTER_VIRTUAL_CLASS(Mesh);
756:    GDREGISTER_VIRTUAL_CLASS(PrimitiveMesh);
767:    GDREGISTER_VIRTUAL_CLASS(Material);
808:    GDREGISTER_VIRTUAL_CLASS(Texture);
809:    GDREGISTER_VIRTUAL_CLASS(Texture2D);
822:    GDREGISTER_VIRTUAL_CLASS(TextureLayered);
824:    GDREGISTER_VIRTUAL_CLASS(Texture3D);
845:    GDREGISTER_VIRTUAL_CLASS(StyleBox);

@reduz reduz force-pushed the expose-more-gdextension branch from 038ea7c to f3b3c1e Compare March 10, 2022 10:17
@reduz reduz requested a review from a team as a code owner March 10, 2022 10:17
* Previous "virtual" classes (which can't be instantiated) are not corretly named "abstract".
* Added a new "virtual" category for classes, they can't be instantiated from the editor, but can be inherited from script and extensions.
* Converted a large amount of classes from "abstract" to "virtual" where it makes sense.

Most classes that make sense have been converted. Missing:

* Physics servers
* VideoStream
* Script* classes.

which will go in a separate PR due to the complexity involved.
@reduz reduz force-pushed the expose-more-gdextension branch from f3b3c1e to 6f51eca Compare March 10, 2022 11:28
@akien-mga akien-mga merged commit 9b59736 into godotengine:master Mar 10, 2022
@akien-mga
Copy link
Member

Thanks!

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.

4 participants