-
-
Notifications
You must be signed in to change notification settings - Fork 21.3k
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
GDExtension library cannot be reloaded while editor is running #66231
Comments
This can likely be achieved by having a list of library path remaps passed as a CLI argument to the project run from the editor, with new paths being temporary locations that will never be overwritten. |
The hardest part of this seems to be invalidating all of the pointers the library hands to the engine, like Edit: Actually my first examples aren't that hard to deal with, it's mostly about objects like nodes or custom servers. |
Very rough idea, how the new DLL could at least be used for a game's launch (1st problem):
Main takeaway: Godot never directly loads Other ideas:
Is something like that realistic? |
I think it's very important for the loaded file to be debuggable in the same way as the compiled one. Not sure if this change might interfere with that. |
@EngineGuy That's a very good point. 👍 The cleanest solution would probably be to release the lock (either manually triggered, or on FocusLost/Minimized events). |
I tried to create a plugin for Godot that uses NativeExtensionManager class in GDScript to manually control loaded extensions. You can unload the extension, compile new dll, replace the old one and then load the extension again. Now this does really work but it breaks the engine. It either
After these I just gave up. Unfortunately I need native extensions due to performance (and somewhat disliking GDScript) and restarting engine each time is annoying workflow for rapid development and IMO somewhat undermines the idea of dynamic GDExtensions. I understand "hot reload" is hard to get right (UE as an example) and I don't think it even needs to be pursued but replacing libraries between running the game from editor should be doable without the "hot" magic? edit: typos |
Now that we have https://github.com/godot-rust/gdextension/ I was able to play around a bit with the new system and, I don't want to sound too harsh, but GDExtension feels like a substantial usability downgrade for me as a game developer due to this issue. Let me elaborate: The way I develop games with Godot 3.x using a native language (Rust in my case, but this applies just as well to C++ devs) is pretty much the same as one would do in regular Now, I know I'm not the only one using Godot like this, but I can only speak for myself. My issue with the transition to GDNative to GDExtension is that it takes away value from me as a user without really giving me much in return. Let me explain with an example: In Godot 3.x, this is how I work when I want to create some new behavior:
This all works wonderfully and makes me feel as productive as it gets! But now, let's take a look at my pain points in Godot 4 via GDExtension:
I really don't mean any of this in a bad way. My hope is that, by explaining my workflow in detail, I will help developers understand why so many people seem to care about this issue (I mean, look at the upvotes!). There is a niche (yes, we're not that many, but we exist) of developers that has seen a considerable degradation in their workflow when transitioning from Godot 3 -> 4. Basically, I just came to suggest that, even if the DLL locking bug is solved, what lies underneath is not the productive experience developers used to have in 3.x. I understand that might not be a priority, and I know I'm hardly Godot's target user by any means, but I'd like to raise awareness about why this is an issue and how it breaks developer's workflows exactly. |
I would like to have a Button in the Editor to compile (like C#?) which triggers
To work Godot needs some Information
Maybe a special resouces with the information is enough. The import process would trigger the workflow. sava solution sounds similar, so I have no idea if this can work. |
This isn't exactly true (in my experience on Linux, without the file lock, at least). The editor doesn't reload the extension, but the game loads the updated one each time you launch it. Only if you need to create or modify those classes/properties in the editor do you need to restart (which admittedly does suck). I have been using That being said, I think the experience on Windows should be at least as good as the current one on Linux. Side note: Personally, I have found GDExtension most useful for code that needs to be fast and changes frequently, but isn't directly tied to the node system (i.e. classes that extend |
Ah, sorry about that. I should've made it more clear in my wording. Yes, this matches my experience on Linux too with Rust. My main concern here is that for a workflow that is tied to the node system (the mainstream way to develop things in Godot), it gets much more difficult because creating a new script-like class requires an editor restart. Otherwise, there's no way to put that node on the scene. Well, technically there is, but it's not a comfortable way to do it.
Yes, I totally agree. This seems to be the main use case behind its design. |
Might this be of interest? https://github.com/fungos/cr/ 🤔 |
Just want to echo and really emphasize this comment, as I think this is the main use case for extending the engine for use with other languages, which is primarily at the scripting layer not necessarily core engine layer. |
Personally I'm not even sure if I ran into someone who tried to extend the core engine this way. Maybe I'm biased since I personally also used it for "scripting" (or specifically replacing GDScript with Rust to get both performance and more developer friendly environment), but it does seem like this is the actual use case? Having released one non-trivially sized game with this in 3.x and been looking forward to 4.x for a while, I don't want to be overly dramatic, but this issue is enough of a reason for me to not consider using Godot 4.x for any future projects. At least for me the main reason to consider Godot is getting an editor that's integrated into the dev workflow. One already has to make a sacrifice compared to Unity and UE4 where inspecting the scene during play mode is much more interactive, while in Godot it's much more detached. But this is a sacrifice that is worth making for some other benefits, namely the nice integration with a native language 3.x provided. But if this relative interactivity with the editor is taken away with 4.x, it creates another point of friction between development and using the editor., making the editor even less useful for people who use native scripts. Maybe some can develop without using the editor and only using Godot as a renderer, but at that point I'd question the benefits of using Godot at all, as many viable alternatives exist. To me the main benefit of Godot and GDNative was that I could get both simple scripting, as well as tight integration with Rust. It wasn't perfect, but it worked well enough where after the initial setup I could just build my game without thinking about it too much. Being simply a consumer of the engine and being 100% focused on making games I don't see inside the engine and don't understand the intricacies or needs for doing a big rewrite to GDExtension. But from purely a game developers perspective, I really don't understand why I'd even want GDExtension, and would 100% be fine with having "4.x features" with the same API as GDNative in 3.x. I understand there are probably internal reasons, and that this is really not what people want to hear after having spent months working on it, but for me as a simple consumer who had many issues with Godot 3.x, GDNative was really not one of them. If there was an option to get the nice things in 4.x (better 2d sprite batching, better tilemaps, etc.) while having the same GDNative extension, I'd take it over any extra power from GDExtension, simply because as a small game developer I don't imagine ever needing to extend the engine over just building things as a feature. Maybe this enables certain class of more ambitious games that were harder to make with engine mods previously, though if that's the case I'd have to question if this is actually Godot's target audience. |
In my opinion this is not being dramatic, this is a valid point and I'm at the same boat. Having just prototyped an idea with Unity I have been eyeing Godot 4.x as the main platform. This unfortunately might be big enough of an issue for me to pass Godot for that idea and wait if this side of the engine matures better with time. Like @setzer22 wrote well, having a mature static analysis tooling makes all the difference when writing game logic/systems. GDScript (without going into detail here) has too many flaws for me to enjoy writing large parts of the game in - smaller bits are fine though. And on top of that the prototype in question has liquid dynamics associated with the gameplay and GDScript performance is not suited for such a thing.
Hmm, quite an interesting library. Maybe writing an engine module for Godot that acts as a host for a plugin might work but that could be quite hacky, require lots of macrofoo (for proxying) and maybe - as this is quite large of an issue - this would be better to fix at engine level rather than bubblegum fix 😅 |
See also godotengine/godot-cpp#955 for @BastiaanOlij's take on (part of?) this problem. |
This comment @BastiaanOlij is a big concerning, and for me kinda misses a major goal of extension system's potential. I'm curious what the technical challenges/issues are compared to gdnative in Those 3 things (adding/modifying classes, properties, and methods) are pretty much the main use case for gdextention for a lot of developers no? The workflow goal would be to:
Ignoring and putting all other native problems aside (that can potentially totally break everything, like say storing memory addresses within your dynamic library across reloads, statics, symbol names, etc), |
Sorry though I was writing in my other thread :), Ok proper reaction... So as @akien-mga pointed to my PR, that is a workaround solution that would probably be the least painful in the short term but doesn't solve enough long term. Reading some of the other suggestions here they seem to be variations on the same deal, start up a copy of the DLL so the original one won't be locked and can be overwritten. The question then is, how to hot load the new DLL. First, to answer @EngineGuy concern, the debugger doesn't care where the DLL is, as long as it has the right debug symbols your debugger should be able to step through the code, so the copy approach should be fine. The real issue is the ability to reload DLLs and this is what @saviilsy kinda ran into. In the Godot 4 approach the extension fully updates the Now GDScript can handle this, and there may be an answer there, but just like GDNative, GDScript doesn't change the node, it just adds a script to it and if the script isn't there temporarily, the node just doesn't have the extra functionality. If this is solvable I'm not sure but that is the two steps forward, one step back issue GDExtension has atm. |
@BastiaanOlij In my opinion, the main appeal of GDExtension is for extension developers. Say you're developing a terrain system, or a new low-latency audio server for Godot. GDExtension is clearly better in those scenarios because you can build classes that feel just like regular godot nodes to their users. Everything is integrated, the class appears in the "Add node" menu, and you even get things like autocomplete in GDScript. This is great because more experienced developers can become almost-core engine developers and provide a lot of value to Godot's target audience. With this use case in mind, it makes sense that there will be a bit more pain involved (i.e. frequent editor restarts): Developing these extensions is a fundamentally different workflow than what regular GDScript users are expected, and the experience has to be optimized for the latter. But, speaking as someone who has done a lot of GDNative work, I find none of the extra features in GDExtension actually useful to my work (I don't mean that in a bad way: I completely understand the use case, I'm just not an engine developer, I am a game developer). IMHO, I would very much prefer if something like GDNative / Nativescript were brought back so that I could continue developing like before, with each "class" I create being equivalent to a script I can attach to nodes, and not a node type itself. I think enabling proper hot reloading only when certain criteria is met would be a reasonable compromise here. Implement reloading, but if Godot detects some DLL registered a new native class after a reload, simply crash (in a safe way, and not letting memory be corrupted). Then we can start thinking about a mechanism to enable iterative game development in native languages, which could be built on top of the current extension system, instead of around it. That's my idea anyway 😅 I'd be interested on hearing about other people's opinions. Would you be happy with a NativeScript-like approach? Or is real native classes, GDExtension-style, something you were looking forward to when in order to build games using native languages (C++, Rust...)? |
I don't think these have to be mutually exclusive! A 'NativeScript' (attaching a native compiled script to any generic node) has a really strong appeal as a 'component' based approach to game development that can be used in replacement of a GDScript attachment... i.e. really useful for implementing logic in another language, be for whatever other reason. (performance, static analysis, tooling, etc). GDExtension can be targeted for engine or module developers who are integrating into the engine tighter than at the game logic / scripting level. I think the later will be a use-case that is utilized to a lesser extent than something like 'NativeScript' attachment to any node, so the workflow around a native script attachment use-case imo should be prioritized. This also starts the scratch the surface of 'inheritance' vs 'composition', as the current behaviour of GDNative in Perhaps a middle ground can support both approaches? |
@jordo To clarify my comment above, this is also what I was suggesting. 😄 Not remove GDExtension, but introduce something else that works similar to the old GDNative for those wanting to program godot scripts in non-GDSCript languages. |
That's a really tricky one. Reading thought the feedback here I can totally see the root problem. GDNative was written as a solution to do what we can do with GDScript but through a plugin that uses some other language, so the way you attached a GDNative "Script" made reloading possible. The problem however was that GDNative did not go far enough for those who wish to make plugins that extent the capabilities of the engine in meaningful ways. GDExternal is an evolution that specifically addresses that problem, the much tighter integration allows for nearly any functionality that would normally be implemented as a module and compiled into the engine, to be moved into a plugin instead making the engine far more extendable. So to some extend they fullfill two different niches but it is not practical to maintain two systems here. The group of contributors that is invested in this is only a small group, kind of part and parcel of this being a niche feature of Godot. We're currently stretched out as it is to try and improve the system we have. Anyway, baby steps, it's clear there is a need for reloadability of the GDExternal plugins so I'm sure we'll find a way to do it in due time. |
@BastiaanOlij Thanks 🙂 I appreciate your effort looking into this! I was suggesting adding something else (possibly on top of GDExtension itself?) because I was kind of assuming a better solution isn't possible, but you're the expert here! If you think a way to reload GDExtension is possible, then that would be preferrable. |
Sorry if this is a dumb question as I don't have that much knowledge how gdextensions are handled inside the engine but As in the reload would be something akin to :
I'm sure InitObject could be extended to handle old -> new state transfers by introducting callbacks for it if necessary? I am more worried about the way how Godot handles "orphan" or unexisting classes as it just removes them! Hopefully this can be easily fixed by just showing errors and assigning existing classes to some kind of null/dummy object or similar that does not do anything until the actual implementation is found again. In any case removing the whole object and all things attached to it means lost work... For me the requirement of restarting to get new properties/methods is not that much of a problem. It is pain yes, but manageable pain :) I just want to iterate fast when coding and usually that iteration happens inside few known functions/methods. |
@saviilsy yes, the problem isn't that we can't unload the library, the problem is what happens when we do. As you say:
They don't just get orphaned, they get destroyed. So say your external implements a class that you've used for a bunch of nodes in the scene you're editing. When you unload that external, it will remove all those nodes from that scene, and once they are gone, they are gone. Whats worse, if you've just added those nodes to your scene, they're part of the undo/redo structure, which will become corrupted as we're now pointing to instances that no longer exist. One suggestion that was offered while we were discussing this in the contributors chat server yesterday is that we'd save and unload all scenes before reloading externals. It'll still be annoying that you need to re-open your scenes but you'll save some time in having to close and re-open the editor fully. That all said, I do believe that step one is to ensure the editor always makes copies of the DLLs it is about to open so the originals can be overwritten. All the different reload scenarios will require that as a base. |
Hey im fairly new to Godot and started with my first Godot 4 project just recently, but im pretty firm with C++ etc. For reloading a Library, wouldnt it be an easy thing to distinguish between reloadable and non-reloadable libraries, by simply having 2 mandatory functions for all realodable libraries, which are a serialize and deserialize function. Sure you would need to make them compatible when making iterations, which creates some extra work for the dev, but overall that would make reloading any custom extension easy and achievable. When ever a certain extension is realoded:
This would bring some caveats with it, like what to do if the dev breaks his serializer? In that case the editor should shutdown gracefully and tell the dev he did an oops, or reload the old extension (because there the deserializer should 100% work). A requirement would be to basically wrap all custom nodes into a container that has the extension origin and a data block for serializing. Just throwing in some thoughs. |
I just encountered this issue. that's a big issue because it makes godot even LESS viable for c++ games when godot 4 was supposed to be a game changer for perf intensive 3d games to me it makes no sense |
May i suggest interim solution while hot-reloading is not supported?
|
What's going on currently with this issue? |
@rayanmargham It looks like they're discussing use-cases, possible approaches, and solutions. |
… and prompt user to restart.
…editor restart and experimental work on dynamic reload
@TheMasterofBlubb I was able to get godot to the point that it detects the gdextension library change after a recompile. It then automatically recommends a restart. You can check out my fork linked above. @MachineMakesNoise sounds like you have some experience with this. I made an attempt and it doesn't crash on macos. The library loads, but at some point I believe the For godot-cpp I think this would involve having the Compile with my partially working code using Also see this regarding compiling dynamic libraries(at least on macos) godotengine/godot-docs#7284 |
I join the problem. It is impossible to work without auto-reload of compiled code. Also GODOT is great engine and i really love it, but this is serious issue |
This is certainly a big issue for me as well, but I've noticed something after reading all of this. One of the major points of issue for the engine developers seems to be that GDExtensions can register new types that would break the engine if they were removed at runtime. But as @Faless and @setzer22 talked about, that's not a requirement. My suggestion is to have a set of registration operations that are considered "reload-safe". This would include registering new scripts and new resource types, since both are serializable. Any classes registered that are more complex would mark the extension as "reload-unsafe". The extension could include in its metadata if it's trying to be "reload-safe" and an error could pop up if that contract is violated. Implementation-wise any non "reload-safe" ClassDB registrations set a boolean flag for the GDExtension that called them, and when loading is done the editor checks that flag against the extension's expected "safe-ness". Combine that with the above recommendations of copying the dll/so/dylib into the .godot folder with a unique hash and, it seems to me, that you have the best of both worlds. TL;DR: GDExtensions that ONLY register script classes and fully-serializable resources should be considered "hot-reload safe". Anything else should require an editor restart. |
I don't think there's anything that can be considered safe to reload, not anything useful at least. One of the main problems is inherent to the fact that native extension classes are treated the same as engine classes by ClassDB. And any one can grab a pointer to a method of one of those classes and cache it. That's what language implementations do, otherwise calls would be too slow. Fixing this could require changing native extension classes to be treated differently, which kind of defeats the point. Another option could be to make the methods (and other stuff) refcounted, to avoid references to freed memory after a reload. Making a call on one of these references should either work or result in a non-fatal error message if a method with that signature is no longer present after reloading. Additionally, ClassDB should notify anyone interested when something is reloaded, to allow them to adjust (their bindings, for example). Even then, I don't know if that would work in practice, and it's only one of the problems. This is the main reason I only treat native extensions as library dependencies. For code I need to iterate on a lot, there's only the script system for now. Unfortunately many language bindings (like cpp and rust I think?) that were using the scripting system in the GDNative days, are now using native extensions classes only. |
I feel like from the moment gdextension was planned as a *replacement* to
gdnative, it had to account for reload. Because at this point even if there
are some improvements in features. It is a HUGE downgrade in actual
usability.
Like because of it, i can't even consider full c++ games on godot 4,
reopening the editor all the time is a pain, and on godot 3 i had made a
class factory anyway that autogenerated the gdns files so the workflow was
waaaay faster.
I feel like while gdextension didn't have feature parity with gdnative,
gdnative should not have been deprecated and should have been another
option alongside gdextension
Le jeu. 25 mai 2023 à 11:23, Ignacio Roldán Etcheverry <
***@***.***> a écrit :
… I don't think there's anything that can be considered safe to reload, not
anything useful at least. One of the main problems is inherent to the fact
that native extension classes are treated the same as engine classes by
ClassDB. And any one can grab a pointer to a method of one of those classes
and cache it. That's what language implementations do, otherwise calls
would be too slow.
Fixing this could require changing native extension classes to be treated
differently, which kind of defeats the point. Another option could be to
make the methods (and other stuff) refcounted, to avoid references to freed
memory after a reload. Making a call one of these references should either
work or result in a non-fatal error message if a method with that signature
is no longer present after reloading. Additionally, ClassDB should notify
anyone interested when something is reloaded, to allow them to adjust
(their bindings, for example).
Even then, I don't know if that would work in practice, and it's only one
of the problems.
This is the main reason I only treat native extensions as library
dependencies. For code I need to iterate on a lot, there's only the script
system for now. Unfortunately many language bindings (like cpp and rust I
think?) that were using the scripting system in the GDNative days, are now
using native extensions classes only.
—
Reply to this email directly, view it on GitHub
<#66231 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AD4UF4K5B5LQCB5KQW3YYQDXH4QHTANCNFSM6AAAAAAQSNTYVY>
.
You are receiving this because you commented.Message ID:
***@***.***>
|
That's why I'm making a differentiation between extension classes and extension scripts. Scripts are a subclass of Resource, and all Resources MUST be serializable according to the documentation. If you look at the Script Class you'll notice it has three sub-types: EDIT: That all being said I suspect it would be possible to implement a GDExtension to add this functionality, but the extension would have to provide its own API which feels like the fast-path to ecosystem fracturing. It would likely be good as a proof-of-concept. Unfortunately I don't currently have enough time for my own projects, much less this one. |
Hi, I'm adding myself to the discussion as this is an important issue for me too. I'm currently evaluating (through prototypes) using Godot with GDExtension so that I have most logic and "model" of my game in C++ while using Godot to implement the "view" (including input handling) of the game. In my case, I need both kinds of reloading, script and class, I think, but it's not completely clear to me yet how different these are. Also I think it's ok if I have to explicitly reload for class changes and hot-reload scripts. Would that means we would ideally I have to separate the scripts code into a separate extension? I also intend to add a Godot Editor GDExtension because I use an unusual build-system ( I have some experience in implementing hot-reloading on Windows (long ago, in C++) but I am not familiar with the codebase of Godot and I guess my time is too limited for diving in this big thing, but if I can help in some limited ways (testing? debugging?) feel free to ask 👍🏽 |
FYI, PR #80284 (which attempts to implement hot reloading for GDExtension) is ready for testing. It doesn't do everything described in this issue, and there may still be a use case for something like cppscript even if/when GDExtension supports hot reloading, but it's a start! If we can get the PR tested, reviewed and merged in the next ~2-3 weeks, then it'll be included in Godot 4.2. (If not, that just means it'll have to wait until Godot 4.3. :-)) |
Back to the good old times when it took quite an effort (and 10+ floppy swaps) to compile my Amiga games :) I would love to see this hot swap feature. Perhaps, have a toggle button which when toggled off would unload all libraries (release locks), let the user recompile, then user could toggle it back on. Editor could automagically re-lock when it detects file changes after the library was built. Of course, it would be best to find solution that doesn't require any button push :) You guys are awesome, ty for Godot! |
Very happy to see this addressed! Great work everyone 🚀 |
works nicely! ty! |
I can't believe this has been solved, I was expecting this to take a couple of years, it's really a pleasant surprise |
I just started learning godot, and pairing it with godot-rust for similar reasons that others have mentioned - to get a more pleasant coding experience. |
Godot version
4.0.dev (b770fa2)
System information
Windows 10
Issue description
On Windows, the Godot editor "locks" a DLL containing a GDExtension library and releases it only after shutdown. Native code can thus not be recompiled as long as the editor remains open.
I'm not exactly sure about the current behavior on Linux and Mac. A user reported the editor would immediately crash upon swapping a .so file (see below), but I already heard that people managed to get the launched game running with an updated library. What is the state here?
This is a considerable limitation for game developers actively using GDExtension. Unlike add-ons/tools that exist during the whole lifetime of the editor, GDExtension for games lives from being frequently updated. Requiring the user to reload the editor on every change not only makes a very common workflow impossible, but is also a regression from GDNative, where reloading (for non-tool classes) could be achieved while the editor was out of focus -- even if it had its bugs.
Now, it isn't my style to complain without trying to find a common solution 😉
I think this problem can be split into two parts, of which the first is likely easier to achieve and might already mitigate some problems:
Do you think it's feasible to enable a "library swap" feature that would at least let the launched game make use of recompiled native code? The extension library loaded by the editor itself could remain the same.
In the original GDExtension PR, hot reloading was listed under TODO. This feature has the potential to make GDExtension a true first-class citizen, and would likely encourage a huge amount of plugin and game development in the Godot ecosystem. In particular, it would make Godot much more attractive to C++ developers.
I'm fully aware that true hot-reloading is very difficult to achieve, as such I'd appreciate some insights on the topic from Godot developers. Have there already been (rough) plans or ideas how such a feature might play along with the
ClassDB
? If not, would it be appreciated if I offered my help in working out a high-level design together?Some challenges I see:
Define their behavior when referenced from GDScript code, scenes or Godot caches.
User callbacks to invoke for cleanup code.
This has been brought up before, but so far without a more detailed discussion. I'll try to use this issue also to summarize the efforts (but can also switch to
godot-proposals
in case we go into design).Godot 3 (GDNative):
Godot 4 (GDExtension):
Steps to reproduce
Recompile a GDExtension native library while the Godot 4 editor is open.
Minimal reproduction project
No response
The text was updated successfully, but these errors were encountered: