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

[X11] Add support for using EGL/GLES instead of GLX. #82101

Merged
merged 1 commit into from
Oct 4, 2023

Conversation

bruvzg
Copy link
Member

@bruvzg bruvzg commented Sep 22, 2023

Follow up to #72831

Allow using EGL/GLES on X11 (using project setting or --rendering-driver opengl3_es argument). GLES should have better compatibility with various single board computers (see godotengine/godot-proposals#988).

Note:
Currently, it's still using GLX for desktop GL and PRIME detection, but it might be better to switch it to EGL as well (but I'm not sure if EGL is always available).

Copy link
Contributor

@Riteo Riteo left a comment

Choose a reason for hiding this comment

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

Looks fine!

@akien-mga
Copy link
Member

Tested locally on Mageia 9, it seems to work, though I'm getting a weird crash.

Works fine:

  • Project manager
  • Running a project

Crashes:

  • Opening a project in the editor

Steps to reproduce:

  • godot-git --rendering-driver opengl3_es --print-fps -e
  • Create new project using GL Compatibility
  • Edit it
Editing project: /home/akien/TestGLES
Godot Engine v4.2.dev.custom_build.9180f4703 - https://godotengine.org
[akien@cauldron godot.git (x11_gles)]$ OpenGL API OpenGL ES 3.2 Mesa 23.1.5 - Compatibility - Using Device: AMD - AMD Radeon RX Vega M GL Graphics (vegam, LLVM 15.0.6, DRM 3.52, 6.4.9-desktop-4.mga9)
 

================================================================
handle_crash: Program crashed with signal 11
Engine version: Godot Engine v4.2.dev.custom_build (9180f4703249b6f9e5c78e338d4ef8eced2fc757)
Dumping the backtrace. Please include this when reporting the bug to the project developer.
[1] /lib64/libc.so.6(+0x36980) [0x7fbd0b32b980] (??:0)
[2] Image::get_size() const (/home/akien/Projects/godot/godot.git/./core/io/image.cpp:453)
[3] AnimationPlayerEditor::_notification(int) (/home/akien/Projects/godot/godot.git/./editor/plugins/animation_player_editor_plugin.cpp:143)
[4] AnimationPlayerEditor::_notificationv(int, bool) (/home/akien/Projects/godot/godot.git/./editor/plugins/animation_player_editor_plugin.h:48 (discriminator 14))
[5] Object::notification(int, bool) (/home/akien/Projects/godot/godot.git/./core/object/object.cpp:831)
[6] ThemeOwner::_owner_context_changed() (/home/akien/Projects/godot/godot.git/scene/theme/theme_owner.cpp:101)
[7] ThemeOwner::set_owner_context(ThemeContext*, bool) (/home/akien/Projects/godot/godot.git/scene/theme/theme_owner.cpp:89)
[8] Control::set_theme_context(ThemeContext*, bool) (/home/akien/Projects/godot/godot.git/./scene/gui/control.cpp:2472)
[9] Control::_notification(int) (/home/akien/Projects/godot/godot.git/./scene/gui/control.cpp:3157)
[10] Control::_notificationv(int, bool) (/home/akien/Projects/godot/godot.git/./scene/gui/control.h:48 (discriminator 14))
[11] Container::_notificationv(int, bool) (/home/akien/Projects/godot/godot.git/./scene/gui/container.h:37 (discriminator 3))
[12] BoxContainer::_notificationv(int, bool) (/home/akien/Projects/godot/godot.git/./scene/gui/box_container.h:37 (discriminator 3))
[13] VBoxContainer::_notificationv(int, bool) (/home/akien/Projects/godot/godot.git/./scene/gui/box_container.h:90 (discriminator 3))
[14] AnimationPlayerEditor::_notificationv(int, bool) (/home/akien/Projects/godot/godot.git/./editor/plugins/animation_player_editor_plugin.h:48 (discriminator 3))
[15] Object::notification(int, bool) (/home/akien/Projects/godot/godot.git/./core/object/object.cpp:831)
[16] Node::_propagate_enter_tree() (/home/akien/Projects/godot/godot.git/./scene/main/node.cpp:259)
[17] Node::_propagate_enter_tree() (/home/akien/Projects/godot/godot.git/./scene/main/node.cpp:274 (discriminator 2))
[18] Node::_propagate_enter_tree() (/home/akien/Projects/godot/godot.git/./scene/main/node.cpp:274 (discriminator 2))
[19] Node::_propagate_enter_tree() (/home/akien/Projects/godot/godot.git/./scene/main/node.cpp:274 (discriminator 2))
[20] Node::_propagate_enter_tree() (/home/akien/Projects/godot/godot.git/./scene/main/node.cpp:274 (discriminator 2))
[21] Node::_propagate_enter_tree() (/home/akien/Projects/godot/godot.git/./scene/main/node.cpp:274 (discriminator 2))
[22] Node::_propagate_enter_tree() (/home/akien/Projects/godot/godot.git/./scene/main/node.cpp:274 (discriminator 2))
[23] Node::_propagate_enter_tree() (/home/akien/Projects/godot/godot.git/./scene/main/node.cpp:274 (discriminator 2))
[24] Node::_propagate_enter_tree() (/home/akien/Projects/godot/godot.git/./scene/main/node.cpp:274 (discriminator 2))
[25] Node::_propagate_enter_tree() (/home/akien/Projects/godot/godot.git/./scene/main/node.cpp:274 (discriminator 2))
[26] Node::_propagate_enter_tree() (/home/akien/Projects/godot/godot.git/./scene/main/node.cpp:274 (discriminator 2))
[27] Node::_propagate_enter_tree() (/home/akien/Projects/godot/godot.git/./scene/main/node.cpp:274 (discriminator 2))
[28] Node::_set_tree(SceneTree*) (/home/akien/Projects/godot/godot.git/./scene/main/node.cpp:2919)
[29] SceneTree::initialize() (/home/akien/Projects/godot/godot.git/./scene/main/scene_tree.cpp:450)
[30] OS_LinuxBSD::run() (/home/akien/Projects/godot/godot.git/platform/linuxbsd/os_linuxbsd.cpp:929)
[31] /home/akien/Projects/godot/godot.git/bin/godot.linuxbsd.editor.dev.x86_64(main+0x15a) [0x5410f40] (/home/akien/Projects/godot/godot.git/platform/linuxbsd/godot_linuxbsd.cpp:76)
[32] /lib64/libc.so.6(+0x236b7) [0x7fbd0b3186b7] (??:0)
[33] /lib64/libc.so.6(__libc_start_main+0x85) [0x7fbd0b318775] (??:0)
[34] /home/akien/Projects/godot/godot.git/bin/godot.linuxbsd.editor.dev.x86_64(_start+0x21) [0x5410d21] (??:?)
-- END OF BACKTRACE --
================================================================

@dsnopek
Copy link
Contributor

dsnopek commented Sep 22, 2023

I think we need to make some changes to openxr_opengl_extension.cpp to allow it to work with EGL on Linux. We're currently using XrGraphicsBindingOpenGLXlibKHR to initialize OpenXR, which requires GLX specific handles. I didn't test, so I'm not sure what'll happen if we try to use that when OpenGL was loaded via EGL (maybe crash? maybe error?). There is an OpenXR EGL extension from Monado that needs to be used instead.

@bruvzg
Copy link
Member Author

bruvzg commented Sep 22, 2023

I think we need to make some changes to openxr_opengl_extension.cpp to allow it to work with EGL on Linux.

I do not see any options for GLES except for Android, so I guess it's not supported by OpenXR (but devices like Raspberry Pi are likely not XR capable anyway).

@dsnopek
Copy link
Contributor

dsnopek commented Sep 22, 2023

I do not see any options for GLES except for Android, so I guess it's not supported by OpenXR

There's an OpenXR extension for it from Monado (XR_MNDX_egl_enable) which I think SteamVR might support too. So, I think we just need to be able to detect that OpenGL was loaded via EGL and then use that extension.

See: https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#XR_MNDX_egl_enable

@bruvzg
Copy link
Member Author

bruvzg commented Sep 22, 2023

I think we need to make some changes to openxr_opengl_extension.cpp to allow it to work with EGL on Linux.

Probably the same is true for ANGLE on Windows, and we should use underlying D3D11 device instead as well.

@akien-mga akien-mga modified the milestones: 4.x, 4.2 Oct 3, 2023
@akien-mga
Copy link
Member

I'd like to get this merged for 4.2 if possible, but I'm puzzled by the crash I ran into. Can anyone reproduce it?

@dsnopek
Copy link
Contributor

dsnopek commented Oct 3, 2023

I just tested this PR on Ubuntu 22.04, opening the editor for an existing Godot project using --rendering-driver opengl3_es and got the same crash as @akien-mga:

Full stack trace
OpenGL API OpenGL ES 3.2 NVIDIA 535.113.01 - Compatibility - Using Device: NVIDIA - NVIDIA GeForce RTX 4070 Ti
 
WARNING: Blend file import is enabled in the project settings, but no Blender path is configured in the editor settings. Blend files will not be imported.
     at: _editor_init (modules/gltf/register_types.cpp:63)

================================================================
handle_crash: Program crashed with signal 11
Engine version: Godot Engine v4.2.dev.custom_build (9180f4703249b6f9e5c78e338d4ef8eced2fc757)
Dumping the backtrace. Please include this when reporting the bug to the project developer.
[1] /lib/x86_64-linux-gnu/libc.so.6(+0x42520) [0x7f2052842520] (??:0)
[2] Image::get_size() const (/home/dsnopek/Sync/Projects/default/godot-4/core/io/image.cpp:453)
[3] AnimationPlayerEditor::_notification(int) (/home/dsnopek/Sync/Projects/default/godot-4/editor/plugins/animation_player_editor_plugin.cpp:143)
[4] AnimationPlayerEditor::_notificationv(int, bool) (/home/dsnopek/Sync/Projects/default/godot-4/editor/plugins/animation_player_editor_plugin.h:48 (discriminator 14))
[5] Object::notification(int, bool) (/home/dsnopek/Sync/Projects/default/godot-4/core/object/object.cpp:831)
[6] ThemeOwner::_owner_context_changed() (/home/dsnopek/Sync/Projects/default/godot-4/scene/theme/theme_owner.cpp:101)
[7] ThemeOwner::set_owner_context(ThemeContext*, bool) (/home/dsnopek/Sync/Projects/default/godot-4/scene/theme/theme_owner.cpp:89)
[8] Control::set_theme_context(ThemeContext*, bool) (/home/dsnopek/Sync/Projects/default/godot-4/scene/gui/control.cpp:2472)
[9] Control::_notification(int) (/home/dsnopek/Sync/Projects/default/godot-4/scene/gui/control.cpp:3157)
[10] Control::_notificationv(int, bool) (/home/dsnopek/Sync/Projects/default/godot-4/./scene/gui/control.h:48 (discriminator 14))
[11] Container::_notificationv(int, bool) (/home/dsnopek/Sync/Projects/default/godot-4/./scene/gui/container.h:37 (discriminator 3))
[12] BoxContainer::_notificationv(int, bool) (/home/dsnopek/Sync/Projects/default/godot-4/./scene/gui/box_container.h:37 (discriminator 3))
[13] VBoxContainer::_notificationv(int, bool) (/home/dsnopek/Sync/Projects/default/godot-4/./scene/gui/box_container.h:90 (discriminator 3))
[14] AnimationPlayerEditor::_notificationv(int, bool) (/home/dsnopek/Sync/Projects/default/godot-4/editor/plugins/animation_player_editor_plugin.h:48 (discriminator 3))
[15] Object::notification(int, bool) (/home/dsnopek/Sync/Projects/default/godot-4/core/object/object.cpp:831)
[16] Node::_propagate_enter_tree() (/home/dsnopek/Sync/Projects/default/godot-4/scene/main/node.cpp:259)
[17] Node::_propagate_enter_tree() (/home/dsnopek/Sync/Projects/default/godot-4/scene/main/node.cpp:274 (discriminator 2))
[18] Node::_propagate_enter_tree() (/home/dsnopek/Sync/Projects/default/godot-4/scene/main/node.cpp:274 (discriminator 2))
[19] Node::_propagate_enter_tree() (/home/dsnopek/Sync/Projects/default/godot-4/scene/main/node.cpp:274 (discriminator 2))
[20] Node::_propagate_enter_tree() (/home/dsnopek/Sync/Projects/default/godot-4/scene/main/node.cpp:274 (discriminator 2))
[21] Node::_propagate_enter_tree() (/home/dsnopek/Sync/Projects/default/godot-4/scene/main/node.cpp:274 (discriminator 2))
[22] Node::_propagate_enter_tree() (/home/dsnopek/Sync/Projects/default/godot-4/scene/main/node.cpp:274 (discriminator 2))
[23] Node::_propagate_enter_tree() (/home/dsnopek/Sync/Projects/default/godot-4/scene/main/node.cpp:274 (discriminator 2))
[24] Node::_propagate_enter_tree() (/home/dsnopek/Sync/Projects/default/godot-4/scene/main/node.cpp:274 (discriminator 2))
[25] Node::_propagate_enter_tree() (/home/dsnopek/Sync/Projects/default/godot-4/scene/main/node.cpp:274 (discriminator 2))
[26] Node::_propagate_enter_tree() (/home/dsnopek/Sync/Projects/default/godot-4/scene/main/node.cpp:274 (discriminator 2))
[27] Node::_propagate_enter_tree() (/home/dsnopek/Sync/Projects/default/godot-4/scene/main/node.cpp:274 (discriminator 2))
[28] Node::_set_tree(SceneTree*) (/home/dsnopek/Sync/Projects/default/godot-4/scene/main/node.cpp:2919)
[29] SceneTree::initialize() (/home/dsnopek/Sync/Projects/default/godot-4/scene/main/scene_tree.cpp:450)
[30] OS_LinuxBSD::run() (/home/dsnopek/Sync/Projects/default/godot-4/platform/linuxbsd/os_linuxbsd.cpp:929)
[31] /home/dsnopek/prj/default/godot-4/bin/godot.linuxbsd.editor.dev.x86_64(main+0x19f) [0x55c746f13268] (/home/dsnopek/Sync/Projects/default/godot-4/platform/linuxbsd/godot_linuxbsd.cpp:76)
[32] /lib/x86_64-linux-gnu/libc.so.6(+0x29d90) [0x7f2052829d90] (??:0)
[33] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0x80) [0x7f2052829e40] (??:0)
[34] /home/dsnopek/prj/default/godot-4/bin/godot.linuxbsd.editor.dev.x86_64(_start+0x25) [0x55c746f13005] (??:?)
-- END OF BACKTRACE --

@Riteo
Copy link
Contributor

Riteo commented Oct 3, 2023

[2] Image::get_size() const (/home/dsnopek/Sync/Projects/default/godot-4/core/io/image.cpp:453)

I'd seriously like to see what that (GDB) frame looks like. The failing line of code is related to... Size calculation of the autoplay icon in the animation player editor.

The failing method in question is:

Size2i Image::get_size() const {
	return Size2i(width, height);
}

What on earth could be failing here?

Size2i's constructor doesn't help (typedefs to Vector2i):

inline Vector2i(const int32_t p_x, const int32_t p_y) { 
        x = p_x; 
        y = p_y; 
}

The rest of that class is some interesting union thing though:

union {
        struct {
                union {
                        int32_t x;
                        int32_t width;
                };
                union {
                        int32_t y;
                        int32_t height;
                };
        };

        int32_t coord[2] = { 0 };
};

It looks like Vector2i is implemented this way to allow reading/writing to both x/y or width/height.

I'm really baffled. The only things that come to mind to me is either some extremely obscure memory corruption (stack bit me once, but why would it now?) or some UB with unions.

Regarding the memory corruption, a memory sanitizer could help (although I don't recall it directly reporting a stack overflow as such)

Regarding the UB I'd like to point out that cppreference's union page says that "It is undefined behavior to read from the member of the union that wasn't most recently written. Many compilers implement, as a non-standard language extension, the ability to read inactive members of a union."

Perhaps this one might be biting us out of the blue?

Due to the randomness of this crash, honestly both cases look equally probable to the best of my knowledge, unless there's (hopefully) some way simpler explanation.

Again, I'm seriously baffled as, from what I can see, this PR is both touching stuff that has nothing to do with that code path and pretty much just plumbing the EGL driver, which works in each single supported desktop platform if we include the Wayland branch.

@dsnopek
Copy link
Contributor

dsnopek commented Oct 3, 2023

I'd seriously like to see what that (GDB) frame looks like. The failing line of code is related to... Size calculation of the autoplay icon in the animation player editor.

It's a segfault, so it probably isn't really about the size calculation. It's probably that the Image * that is having get_size() called on it is nullptr or just a random chunk of uninitialized memory.

It could be an issue in loading the icon? Perhaps it's failing to load and returning nullptr? It could also be something else further up the stack too.

EDIT: I just ran it in GDB, and assuming it's not lying to me (which it sometimes does, unfortunately), it looks like autoplay_img is, in fact, nullptr. So, it does appear that it's failing to load the image for some reason, and the code in animation_player_editor_plugin.cpp never checks if it's nullptr before calling get_size() on it.

@Riteo
Copy link
Contributor

Riteo commented Oct 3, 2023

It's a segfault, so it probably isn't really about the size calculation. It's probably that the Image * that is having get_size() called on it is nullptr or just a random chunk of uninitialized memory.

Oh god I completely missed that dereference operator :gdyeet:

EDIT: I just ran it in GDB, and assuming it's not lying to me (which it sometimes does, unfortunately), it looks like autoplay_img is, in fact, nullptr. So, it does appear that it's failing to load the image for some reason, and the code in animation_player_editor_plugin.cpp never checks if it's nullptr before calling get_size() on it.

There, much better than UB or stack overflows! Thanks for checking!

It could be an issue in loading the icon? Perhaps it's failing to load and returning nullptr?

Perhaps, but I don't think that a value like this is expected by the API. That's a Ref<Texture2D> which I don't think can be an actual plain nullptr at all (although it can point to one), so I suppose that it's just unitialized as in not having anything assigned.

It could also be something else further up the stack too.

Indeed that method has a lot of places where it can fetch the texture.

get_theme_icon method for reference:

Ref<Texture2D> Control::get_theme_icon(const StringName &p_name, const StringName &p_theme_type) const {
	ERR_READ_THREAD_GUARD_V(Ref<Texture2D>());
	if (!data.initialized) {
		WARN_PRINT_ONCE(vformat("Attempting to access theme items too early in %s; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED", this->get_description()));
	}

	if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) {
		const Ref<Texture2D> *tex = data.theme_icon_override.getptr(p_name);
		if (tex) {
			return *tex;
		}
	}

	if (data.theme_icon_cache.has(p_theme_type) && data.theme_icon_cache[p_theme_type].has(p_name)) {
		return data.theme_icon_cache[p_theme_type][p_name];
	}

	List<StringName> theme_types;
	data.theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types);
	Ref<Texture2D> icon = data.theme_owner->get_theme_item_in_types(Theme::DATA_TYPE_ICON, p_name, theme_types);
	data.theme_icon_cache[p_theme_type][p_name] = icon;
	return icon;
}

Honestly the weirdest of them all is that ERR_READ_THREAD_GUARD_V(Ref<Texture2D>());, as it's guaranteed that it will return an uninitialized Ref.

That said, this requires a thorough debug which I suppose would just be easier to do in loco instead than back and forth on a GitHub thread.

@bruvzg
Copy link
Member Author

bruvzg commented Oct 4, 2023

Crash should be fixed, I have missed a define and Texture2D was returning null Image.

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.

Tested locally, works great!

I could run https://github.com/team-godog/NGJ2023_Godog successfully with --rendering-driver opengl3_es.

With VSync disabled, I get in average (laptop on battery, not thorough test, high fluctuations):

  • OpenGL: 210-320 FPS
  • OpenGL ES: 140-190 FPS

@bruvzg
Copy link
Member Author

bruvzg commented Oct 4, 2023

OpenGL: 210-320 FPS
OpenGL ES: 140-190 FPS

I guess it's something rendering team might want to take a look at. I would expect almost the same performance on the same hardware, so something in the GLES pipeline might be less optimized.

main/main.cpp Outdated Show resolved Hide resolved
@akien-mga
Copy link
Member

OpenGL: 210-320 FPS
OpenGL ES: 140-190 FPS

I guess it's something rendering team might want to take a look at. I would expect almost the same performance on the same hardware, so something in the GLES pipeline might be less optimized.

Yeah, my test project isn't the best for proper benchmarking but now that we have GLES on desktop with this PR and with ANGLE for Windows/macOS, we should be able to do some comparisons and optimizations.

Much easier to figure out potential performance issues when comparing the two methods on the same hardware :)

@nilsnordmark
Copy link

Such great work! Really appreciated.
I would love to test this out my Arm-handheld consoles such as the RG351V, when I get hold of some arm64-export templates. Not sure how to compile those myself.

@capnm
Copy link
Contributor

capnm commented Oct 4, 2023

A quick test with an empty 3d scene 👍

  • godot4.2 --rendering-driver opengl3 --print-fps
    Project FPS: 498 (2.00 mspf)
    Project FPS: 473 (2.11 mspf)
    Project FPS: 502 (1.99 mspf)
    ...

  • godot4.2 --rendering-driver opengl3_es --print-fps
    Project FPS: 500 (2.00 mspf)
    Project FPS: 485 (2.06 mspf)
    Project FPS: 498 (2.00 mspf)
    ...

Btw. https://hackaday.com/2023/09/28/a-raspberry-pi-5-is-better-than-two-pi-4s/ :)

@akien-mga akien-mga merged commit 03ff9fe into godotengine:master Oct 4, 2023
15 checks passed
@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.

7 participants