-
-
Notifications
You must be signed in to change notification settings - Fork 241
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
C++ Extension Generator Inheriting from VoxelGeneratorScript crashes when "reloadable = true" in .gdextension #605
Comments
I thought I would add some gdb data.
Here is the stack trace in case there are any hints. I'm still learning to debug the engine so I am not sure what to make of them as yet.
|
That's not how the module expects generators to be added currently. Not sure why it doesnt appear in the list, but even if it did it wouldn't work properly.
As far as I know this is not related to this module. The module calls extensions/scripts the same way other Godot classes do. If Godot crashes that's either a GDExtension problem or a problem in your code. Maybe the reloading feature wasn't properly tested in this kind of situation where heavy multi-threading is involved. From reading your call stack, the module tries to call your class, something happens in your code (which does not seem to have any debug symbols so it shows Another possibility is that something you're doing in your code somehow messes up with reloading. But I can't really tell what it is because I'm not familiar to how that feature works, and I'm not using it myself. It could help to see your code as well, and which line crashes. Yet another thing that could go south is if your generator is already being called in the editor while you trigger hot-reloading. That's something I already warn about with scripts, because I'm pretty sure Godot will crash if reloading is triggered at the same time threads are executing script/extension code. A lot of people dont use threads in their game so it's likely not being noticed, but this module uses threads a lot including its API (in case it matters, those are threads created with Godot's |
I'm unsure how to add the debug traces to the output in my extension. It is not compiled into the engine, rather loaded from a shared object in some way. The shared object is compiled separately using a godot-cpp extension. The code is mainly example type stuff that generates a flat plane in an SDF volume. It may be as you surmise that the generator is already being called in the editor. It is only when the generator is added to the VoxelTexture generator field does it crash. Note: I would expect the sdf_plane function in the generator could just be replaced with the value of "gy". Header for my plane generator. #ifndef SGVOXELGEN_H
#define SGVOXELGEN_H
#include <godot_cpp/classes/voxel_generator_script.hpp>
#include <godot_cpp/classes/voxel_buffer.hpp>
namespace godot {
class SgVoxelGen : public VoxelGeneratorScript {
GDCLASS(SgVoxelGen, VoxelGeneratorScript)
protected:
static void _bind_methods();
public:
SgVoxelGen();
~SgVoxelGen();
void
_generate_block(const Ref<VoxelBuffer> &out_buffer, const Vector3i &origin_in_voxels, int32_t lod) override;
int32_t _get_used_channels_mask() const override;
};
} //namespace godot
#endif
C++ of my generator. #include "sg_voxel_gen.h"
#include "sg_math_sdf.h"
#include <godot_cpp/core/class_db.hpp>
using namespace godot;
void SgVoxelGen::_bind_methods() {
}
SgVoxelGen::SgVoxelGen() {
// Initialize any variables here.
}
SgVoxelGen::~SgVoxelGen() {
// Add your cleanup here.
}
void SgVoxelGen::_generate_block(const Ref<VoxelBuffer> &out_buffer, const Vector3i &origin, int32_t lod) {
if (lod != 0)
return;
static const float QUANTIZED_SDF_16_BITS_SCALE = 0.002f;
static const int CHANNEL_SDF = 1;
static const int PLANE_Y = 0;
const Vector3i bs = out_buffer->get_size();
const int stride = 1 << lod;
int gz = origin.z;
for (int z = 0; z < bs.z; ++z, gz += stride) {
int gx = origin.x;
for (int x = 0; x < bs.x; ++x, gx += stride) {
int gy = origin.y;
for (int y = 0; y < bs.y; ++y, gy += stride) {
float sdf = sdf_plane(Vector3(gx, gy, gz), Vector3(0, 1, 0), 0.0);
out_buffer->set_voxel_f(sdf, x, y, z, CHANNEL_SDF);
}
} // for x
} // for z
}
int32_t SgVoxelGen::_get_used_channels_mask() const {
static const int CHANNEL_SDF = 1;
return CHANNEL_SDF;
// return VoxelGeneratorScript::_get_used_channels_mask();
} The remaining files are godot-cpp boiler plate. #include "register_types.h"
#include <gdextension_interface.h>
#include <godot_cpp/core/class_db.hpp>
#include <godot_cpp/core/defs.hpp>
#include <godot_cpp/godot.hpp>
#include "sg_voxel_gen.h"
#include "sg_voxel_simple.h"
#include "sg_gen_terrain_perlin.h"
using namespace godot;
void initialize_gdextension_types(ModuleInitializationLevel p_level)
{
if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
return;
}
ClassDB::register_class<SgVoxelGen>();
ClassDB::register_class<SgGenTerrainPerlin>();
}
void uninitialize_gdextension_types(ModuleInitializationLevel p_level) {
if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
return;
}
}
extern "C"
{
// Initialization
GDExtensionBool GDE_EXPORT sgtools_library_init(GDExtensionInterfaceGetProcAddress p_get_proc_address, GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization)
{
GDExtensionBinding::InitObject init_obj(p_get_proc_address, p_library, r_initialization);
init_obj.register_initializer(initialize_gdextension_types);
init_obj.register_terminator(uninitialize_gdextension_types);
init_obj.set_minimum_library_initialization_level(MODULE_INITIALIZATION_LEVEL_SCENE);
return init_obj.init();
}
} #ifndef EXAMPLE_REGISTER_TYPES_H
#define EXAMPLE_REGISTER_TYPES_H
void initialize_gdextension_types();
void uninitialize_gdextension_types();
#endif // EXAMPLE_REGISTER_TYPES_H My .gdextension file.
|
If you're using SCons there are options you can specify when building, After a quick glance I don't see what could be wrong in your code. So perhaps reloading is the culprit. Your code is stateless so I wouldn't expect it to be caused by something you are doing in code I had a quick look around Despite the feature being called "hot-reload" I think it has more to do with reloading the library while Godot is open, rather than being able to reload it while it runs. But I could be wrong, I dont use this feature. |
My extension is already built with dev_build=yes, so I am not sure how the loading is done such that it would apply the debug information. These are the same settings, minus the module params, I used to compile my version of Godot.
The whole hot-reloading thing is new, so I imagine there are likely to be bugs. I can work on it without the hot reload as I imagined it. I was just hoping I would be able to build my plugin and have the changes show up automatically in the editor after I compiled changes, without needing to restart Godot. Thanks for your help, I'm still determining how I want my process to work, while at the same time learning Godot. Thanks for all your work on the Voxel project, I look forward to using it. |
The absence of file locations suggests that somehow your backtrace is unable to pick up your debug symbols. If they are present then I don't know why. Also FYI the name |
It looks like the default target was template, compiling things with target=editor, changed the the errors. Every time I run I get different errors. Here is the longest one I can see, unfortunately I don't know who called propagate_notification. I may try a few breakpoints a little later.
|
Well, I spent some time rummaging about this rabbit hole. Stack traces seemed to lead me to "voxel_engine_updater" where I made a couple random changes to defer setting values, as some of the error messages indicated as a fix. It didn't appear to hurt in the editor, well not such I could notice, but all it did was push the crash to a new location. I don't really understand the code, or Godot for that matter, well enough to be of much use. So I'll leave this alone for now and go back to my project. Getting a closer look at some of this was a fun aside, but not critical to me by any means. Just for posterity, my hacks to VoxelEngineUpdater. void VoxelEngineUpdater::ensure_existence(SceneTree *st) {
if (st == nullptr) {
return;
}
if (g_updater_created) {
return;
}
Node *root = st->get_root();
for (int i = 0; i < root->get_child_count(); ++i) {
VoxelEngineUpdater *u = Object::cast_to<VoxelEngineUpdater>(root->get_child(i));
if (u != nullptr) {
return;
}
}
VoxelEngineUpdater *u = memnew(VoxelEngineUpdater);
//u->set_name("VoxelEngineUpdater_dont_touch_this");
u->set_deferred("name","VoxelEngineUpdater_dont_touch_this");
// TODO This can fail (for example if `Node::data.blocked > 0` while in `_ready()`) but Godot offers no API to check
// anything. So if this fail, the node will leak.
//root->add_child(u);
root->call_deferred(SNAME("add_child"), u);
} |
This change makes no sense. It should be possible to set the name right away.
If you hit this issue, I'd like to know in which circumstance you are getting it (as in, what's the call stack when the error occurs). Because that should be completely unrelated to your issue as well |
I was trying to isolate a crash that seemed to reoccur in propagate_notification and added code to print the stack-trace every time it entered that function. When it crashed at that function, giving a message indicating that the set_name function call should be deferred I followed the call stack back. Honestly I was grasping at straws. I'll try to reproduce what was happening again and post it. I don't get a lot of stack traces, it usually bombs out in a malloc or memory alignment error. I am not very experienced with multi-threaded code, so feel free to let me know if I do something stupid :) |
Ok ran it a couple times in GDB. In the second run (shown first), I got this, which traces back to to ensure_existence.
In the first run GDB actually produced a stack trace:
|
With my changes in place this is where it dies, but also doesn't want to give me a stack trace.
|
That failure is nonsensical. It rather sounds like the memory allocator got corrupted by your hot-reloading issue, because that piece of code is completely unrelated. I'm not even sure why that code runs when you hot-reload because if you had a terrain in the open scene, that code will have already run once and would never need to run again. |
No problem, it was just a shot in the dark. Gdb, wasn't giving me much to go on so I tried a few print statements. But it is at the very least consistent, it creates the same traces then dies in a memory routine. I'm not sure what the hot reload does or even how it loads the extensions. I read in blog post that it runs when the window focus changes. I'm wondering if it just keeps reloading things, or what it is really doing in the editor. A long time ago I loaded shared objects programmatically, but I only vaguely remember the APIs. If I have time in the future I may browse the code in depth to at least see how the loader works. |
Either way at this point this should be reported somehow to GDExtension devs, not the module, but I'm not even sure how that report should look like, because right now a repro would pretty much require my module and your extension. |
I could probably write something up and submit to them. It is getting a little late for me today, so I can do it tomorrow. I could also bounce it off the thread discussing hot-reloading as well and get their thoughts. |
Hi just getting started with this tool.
My system:
I am testing out a custom generator inheriting from VoxelGeneratorScript.
I have my example generating a voxel flat terrain and it works within VoxelTerrain when normally used.
I am trying to see if I could get it to work with hot pluggable C++.
If I set "reloadable = true", within .gdextension, the godot engine crashes out.
I understand the "reloadable" flag enables hot plugging for C++ (feel free to correct me if I miss-understand).
I am still learning how to debug C++ in godot, but thought I would check and see if there is something special I need to
do to my extension so that it can handle the reload.
I've tried the flag with the built in generators and it seems to work fine. I also tried to implement a generator deriving from VoxelGenerator directly, but the versions I've made do not show up in the VoxelTexture's generators list.
Is there something special I need to add to my generator that derives from VoxelGeneratorScript to enable hot reloading?
Thank you,
Steve Green
The text was updated successfully, but these errors were encountered: