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

Add mutex when adding geometry instances to the dirty list in the Forward Clustered renderer #71705

Merged
merged 1 commit into from
Jan 20, 2023

Conversation

clayjohn
Copy link
Member

Fixes: #68274

_mark_dirty() can be called from multiple threads simultaneously when we are using multithreaded culling and there is a light projector in the scene. We need a Mutex both to protect geometry_instance_surface_alloc and geometry_instance_dirty_list

I changed geometry_instance_lightmap_sh to be thread safe as there is a situation where it may be called to allocate memory from multiple threads.

This PR also changes a few related project settings to GLOBAL_DEF_RST

@RandomShaper is the MutexLock lock() syntax better than what I have here?

@clayjohn clayjohn added this to the 4.0 milestone Jan 19, 2023
@clayjohn clayjohn requested a review from RandomShaper January 19, 2023 23:19
@clayjohn clayjohn requested a review from a team as a code owner January 19, 2023 23:19
Copy link
Member

@RandomShaper RandomShaper left a comment

Choose a reason for hiding this comment

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

If I understand the design correctly, RenderForwardClustered and associates, such as GeometryInstanceForwardClustered, are not meant to be thread-safe. It's a responsibility of the caller deal with them in a safe manner.

Therefore, in this case it's RendererSceneCull::_scene_cull()'s responsibility to ensure thread safety. Other calls performed by that function are using a spin lock where it's necessary to ensure sync (cull_data.cull->lock.lock();).

So, my suggestion is to apply in RendererSceneCull::_scene_cull() the same locking idiom to the various calls to pairing functions, since they deal with shared data (and to any other that can possibly incurr in the same). That's precisely the case with pair_light_instances(); its call tree involves _mark_dirty(), where the race condition happen. Since it's up to each specific implementation how data is used in those functions, the idea is to be conservative and lock in case of doubt.

Furthermore, there are some push_back() calls to various PagedArrays in InstanceCullResult that may also need locking, because, as far as I can tell, PagedArray is not thread-safe. It'd be nice to check with @reduz.

I'd like to make it clear that the kind of thread safety needed here is only between threads in the pool running the same function concurrently. After all are done, data is guaranteed to be in sync (as it is just before the worker threads start), so that's another reason why involved functions (like _mark_dirty()) don't need general thread safety protection.

Finally (and sorry for so much text), it's a bit sad to have to pay the cost of the spin locks when multiple threads are not being used. It would be good that the spin lock implementation was optimistic (I have to check), or just have a template argument to have two compile-time versions of these functions, with and without locking. But that's unrelated to this PR. Just an idea for you in case you want to explore it.

@clayjohn
Copy link
Member Author

@RandomShaper Thank you very much for taking a look. I agree that the Mutex lock is better moved to _scene_cull() and have done so now.

Furthermore, there are some push_back() calls to various PagedArrays in InstanceCullResult that may also need locking, because, as far as I can tell, PagedArray is not thread-safe. It'd be nice to check with @reduz.

Each thread has its own InstanceCullResult that get merged together after all threads are finished. So those push_back()s should be thread safe.

@RandomShaper
Copy link
Member

Each thread has its own InstanceCullResult that get merged together after all threads are finished. So those push_back()s should be thread safe.

Oh, I overlooked that. I can sleep again. 😃

@akien-mga akien-mga merged commit de3514b into godotengine:master Jan 20, 2023
@akien-mga
Copy link
Member

Thanks!

@myaaaaaaaaa
Copy link
Contributor

myaaaaaaaaa commented Jun 8, 2023

Finally (and sorry for so much text), it's a bit sad to have to pay the cost of the spin locks when multiple threads are not being used. It would be good that the spin lock implementation was optimistic (I have to check), or just have a template argument to have two compile-time versions of these functions, with and without locking. But that's unrelated to this PR. Just an idea for you in case you want to explore it.

See #78016, which makes the spinlocks no longer necessary.

@clayjohn clayjohn deleted the RD-surface-free branch June 8, 2023 18:47
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.

Vulkan: Random meshes vanish in large scenes with lights that have a projector texture
4 participants