-
-
Notifications
You must be signed in to change notification settings - Fork 21.2k
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
Scripting: Fix script docs not being searchable without manually recompiling scripts #95821
base: master
Are you sure you want to change the base?
Conversation
58e4ff3
to
7fde3a4
Compare
7fde3a4
to
88966ec
Compare
59d1345
to
53cc97c
Compare
I think it's better to fix this by checking the tab type. Doc tabs should never truncate the name, only script tabs. |
53cc97c
to
eff26b9
Compare
Why the difference? You can still get the full path by hovering and it's at the top of the help document. This way it feels like behavior is at least consistent? |
873d775
to
67c22e6
Compare
That project would help - I could at least inspect where the threads are and figure out what's causing the deadlock. Turns out shaders are considered scripts for doc purposes and will have their documentation cached when they are compiled, e.g. when a scene with a shader is loaded in the editor. Might need to look into how to remove those docs if the shader is ever deleted. |
67c22e6
to
70edfc4
Compare
Latest update:
I noticed that |
70edfc4
to
65f33aa
Compare
New update:
The reason for the deadlock was that non-main threads like the EditorHelp worker thread will queue resource load tasks and wait for them to be dispatched (I assume) on the main thread and loaded from Godot's general worker threads. But, if the main thread waits on the EditorHelp worker thread by calling e.g. I added a verbose error message to catch when deadlocks might happen. There's a chance more of them happen in other projects with different load pathways that end up calling Also, force-reloading all the scripts from a worker thread in a larger project like @KoBeWi's has resulted in |
I'm going to create a new worker thread just for the |
65f33aa
to
0caaf73
Compare
Latest update:
|
Testing is showing docs are not when changed when Godot is closed, or when changed from outside Godot with it open. Looking into it. Could be either because something changed in EditorFileSystem which stopped Or, you know, some other skill issue of mine 🤣 |
0caaf73
to
3ebb4d3
Compare
void EditorHelp::regenerate_script_doc_cache() { | ||
if (EditorFileSystem::get_singleton()->is_scanning()) { | ||
// Wait until EditorFileSystem scanning is complete to use updated filesystem structure. | ||
EditorFileSystem::get_singleton()->connect(SNAME("sources_changed"), callable_mp_static(_regenerate_script_doc_cache), CONNECT_ONE_SHOT); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed to sources_changed
from filesystem_changed
. It looks like that might be a more reliable signal, since in some situations where no change has happened, filesystem_changed
may not emit. I also like that OS::get_singleton()->benchmark_end_measure("Editor", "First Scan");
happens in EditorNode::_sources_changed
, which is good evidence that it's reliable to detect the end of the first scan.
ERR_FAIL_COND_MSG(!ProjectSettings::get_singleton()->is_project_loaded(), "Error: cannot load script doc cache without a project."); | ||
ERR_FAIL_COND_MSG(!ResourceLoader::exists(get_script_doc_cache_full_path()), "Error: cannot load script doc cache from inexistent file."); | ||
|
||
Ref<Resource> script_doc_cache_res = ResourceLoader::load(get_script_doc_cache_full_path(), "", ResourceFormatLoader::CACHE_MODE_IGNORE); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added CACHE_MODE_IGNORE
here since it feels silly to cache something we're deleting anyway :)
Perhaps this is worth doing for the native docs as well?
Ran the following tests. With Godot open:
With Godot closed:
The extra scripts are 4096 extra scripts that exist just to make script regen take longer and hopefully bring concurrency issues to light. None did. Notes:
Again, bit thanks to @KoBeWi who provided a larger project to bugfix & test on. |
This PR adds a script documentation cache in the project folder. It is loaded at alongside native documentation caches. This makes scripts fully accessible through Search Help, including their members, etc, right from project start, without having to compile every single script. Co-authored-by: Hilderin <[email protected]>
3ebb4d3
to
3af34bf
Compare
I've been trying to diagnose the two issues related to
But I don't know how to reproduce them. I know no. 1 has been worked around, but I'd still like to see the deadlock happening in front of me with the version of the code prior to the workaround that I can retrieve from GitHub. Also, regarding no. 1, could you tell me what happens if instead of using the blocking |
Thank you for following up! <3 I'll try to find some time in the coming days to get back to a previous commit and try your suggestion! :) If @KoBeWi feels good about sharing the project with you, you should be able to replicate 2. by opening it when the script docs cache isn't present (because it was never generated it or you deleted it). |
This PR adds a script documentation cache in the project folder. It is loaded at alongside native documentation caches. This makes scripts fully accessible through Search Help, including their members, etc, right from project start, without having to compile every single script manually to access its docs.
Testing with GDScript but, in theory, should work for any scripting language that has documentation support. Might be worth splitting the cache into a per-language basis to reduce conflicts, though if there are conflicts with e.g. a MyClass in GDScript and MyClass in C#, that may already lead to problems with the current documentation system.
Ensuring file deletions when Godot closed trigger EditorFileSystem's documentation updates is done by #95965, until then the cache will persist docs from deleted scriptsDone.Diagram of call behavior that I needed to try to organize this in my brain:
Grey background runs on whatever thread called it. Orange background runs in
worker_thread
. Green background runs onloader_thread
, and can includeResourceLoader::load()
calls, orange cannot. Blue background represents waiting for one shotsources_updated
signals depending on whetherEditorFileSystem::is_scanning()
is true.Implementation details:
ResourceLoader::load()
to generate docs always happens inEditorHelp::worker_thread
.ResourceLoader::load()
happen inEditorHelp::loader_thread
. This prevents deadlocks where the main thread needs doc info and waits forworker_thread
, butworker_thread
is waiting for main thread to dispatch loading tasks.worker_thread
connects toEditorFileSystem::filesystem_changed
signal, indicating the end ofEditorFileSystem
's scan, and ends its execution. It spools back up once the signal is fired to regenerate the cache, this time usingEditorFileSystemDirectory
, therefore not accessing underlying OS filesystem calls.EditorFileSystem
catch changes like deleted/added files to ensure docs are kept up to date.DocTool
method forwarding toEditorHelp
to help maintain cache consistency. It is meant to track whether changes affect script docs or other docs. These forwarded methods should be used from now on instead of e.g.get_doc_data()->add_doc()
."new_script.gd"
becomesnew_script.gd
, since this resulted in messy truncations of paths (to test, createnew_folder/new_script.gd
andnew_script.gd
and open both their documentations on stable.Fixes #72406, fixes #86577, fixes #72952 (for GDScript, C# docs are not supported yet).
Added coauthorship with @Hilderin because of their wonderful and insightful help in designing and testing this <3