From c7fb6cea3d745c6d06d26b99e083f3fdf3cd7e8b Mon Sep 17 00:00:00 2001 From: Juan Linietsky Date: Thu, 20 Jul 2023 11:49:59 +0200 Subject: [PATCH] Add ability to call code on rendering thread As more users use compute in Godot 4, the way they do is most likely incompatible when running on separate threads and will start erroring soon as we improve the thread safety of the render thread. To properly run code on the render thread, this function was added. Use like this: ```GDScript func initialize_compute_code(): .... func update_compute_code(custom_data): ... func _ready(): RenderingServer.call_on_render_thread( initialize_compute_code ) func _process(): RenderingServer.call_on_render_thread( update_compute_code.bind(with_data) ) ``` --- doc/classes/RenderingServer.xml | 7 +++++++ servers/rendering/rendering_server_default.cpp | 6 ++++++ servers/rendering/rendering_server_default.h | 11 +++++++++++ servers/rendering_server.cpp | 2 ++ servers/rendering_server.h | 2 ++ 5 files changed, 28 insertions(+) diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml index 868079a51667..c0042b391801 100644 --- a/doc/classes/RenderingServer.xml +++ b/doc/classes/RenderingServer.xml @@ -27,6 +27,13 @@ Bakes the material data of the Mesh passed in the [param base] parameter with optional [param material_overrides] to a set of [Image]s of size [param image_size]. Returns an array of [Image]s containing material properties as specified in [enum BakeChannels]. + + + + + As the RenderingServer actual logic may run on an separate thread, accessing its internals from the main (or any other) thread will result in errors. To make it easier to run code that can safely access the rendering internals (such as [RenderingDevice] and similar RD classes), push a callable via this function so it will be executed on the render thread. + + diff --git a/servers/rendering/rendering_server_default.cpp b/servers/rendering/rendering_server_default.cpp index ff6bf3a6ba7e..c4464cb18062 100644 --- a/servers/rendering/rendering_server_default.cpp +++ b/servers/rendering/rendering_server_default.cpp @@ -386,6 +386,12 @@ void RenderingServerDefault::draw(bool p_swap_buffers, double frame_step) { } } +void RenderingServerDefault::_call_on_render_thread(const Callable &p_callable) { + Variant ret; + Callable::CallError ce; + p_callable.callp(nullptr, 0, ret, ce); +} + RenderingServerDefault::RenderingServerDefault(bool p_create_thread) : command_queue(p_create_thread) { RenderingServer::init(); diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h index 340eb7394b21..944dbce0d141 100644 --- a/servers/rendering/rendering_server_default.h +++ b/servers/rendering/rendering_server_default.h @@ -97,6 +97,8 @@ class RenderingServerDefault : public RenderingServer { void _free(RID p_rid); + void _call_on_render_thread(const Callable &p_callable); + public: //if editor is redrawing when it shouldn't, enable this and put a breakpoint in _changes_changed() //#define DEBUG_CHANGES @@ -987,6 +989,15 @@ public: virtual void init() override; virtual void finish() override; + virtual void call_on_render_thread(const Callable &p_callable) override { + if (Thread::get_caller_id() == server_thread) { + command_queue.flush_if_pending(); + _call_on_render_thread(p_callable); + } else { + command_queue.push(this, &RenderingServerDefault::_call_on_render_thread, p_callable); + } + } + /* TESTING */ virtual double get_frame_setup_time_cpu() const override; diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index 4c6b76515731..1c720ff1499e 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -2808,6 +2808,8 @@ void RenderingServer::_bind_methods() { ClassDB::bind_method(D_METHOD("force_draw", "swap_buffers", "frame_step"), &RenderingServer::draw, DEFVAL(true), DEFVAL(0.0)); ClassDB::bind_method(D_METHOD("get_rendering_device"), &RenderingServer::get_rendering_device); ClassDB::bind_method(D_METHOD("create_local_rendering_device"), &RenderingServer::create_local_rendering_device); + + ClassDB::bind_method(D_METHOD("call_on_render_thread", "callable"), &RenderingServer::call_on_render_thread); } void RenderingServer::mesh_add_surface_from_mesh_data(RID p_mesh, const Geometry3D::MeshData &p_mesh_data) { diff --git a/servers/rendering_server.h b/servers/rendering_server.h index deac2a59f92e..d7a14721d7e4 100644 --- a/servers/rendering_server.h +++ b/servers/rendering_server.h @@ -1599,6 +1599,8 @@ public: bool is_render_loop_enabled() const; void set_render_loop_enabled(bool p_enabled); + virtual void call_on_render_thread(const Callable &p_callable) = 0; + RenderingServer(); virtual ~RenderingServer();