Skip to content

Conversation

@Naxel11
Copy link

@Naxel11 Naxel11 commented Jul 24, 2025

About the PR

This PR significantly optimizes the RadiationSystem to handle hundreds (maybe thousands?) of radiation sources without causing server lag. The system has been redesigned to use parallel processing in combination with a two-stage selection strategy, significantly reducing its impact on performance.

Why / Balance

The previous implementation had a computational complexity of O(Sources × Receivers). In scenarios with a large number of radiation sources (e.g., widespread tritium fires), this led to the system's update time exceeding 20ms, causing noticeable server-side lag and impacting the game's tick rate.

This change is purely a performance optimization. The underlying radiation mechanics and final calculated values remain identical, ensuring no change to game balance. It simply allows the system to perform as intended under heavy load.

There was a TODO for this

Technical details

1. Incremental, Event-Driven Architecture:

  • A new fundamental change is that the system no longer queries all sources and receivers every tick. It now maintains persistent collections that are updated reactively via entity lifecycle events (ComponentInit, ComponentShutdown, MoveEvent, etc.). This eliminates the performance cost of collection building from the main update loop, ensuring UpdateGridcast is extremely lightweight.
  1. Parallelization by Receiver: The main loop now iterates over radiation receivers in parallel using an IParallelRobustJob. Each thread is responsible for calculating the total radiation for a single receiver.

  2. Two-Stage Culling: To avoid the expensive O(S×R) complexity, each parallel task performs a two-stage culling process for its assigned receiver:

  • Broadphase (Spatial Culling): The broadphase is now handled by a persistent DynamicTree which stores all active radiation sources. For each receiver, a fast QueryAabb is performed to get a small list of potentially relevant nearby sources. After this initial spatial culling, a cheap distance check is performed using the pre-calculated maxRange (Intensity / Slope) to further refine the candidate list.
  • Narrowphase (Raycasting): Only the small subset of sources that pass the broadphase check are then processed with the more expensive Irradiate method, which performs the actual grid intersection tests and raycasting.
  1. Thread Safety:
  • Each parallel task uses its own local List<Entity<MapGridComponent>> for grid intersection checks, completely eliminating race conditions that were present in earlier parallelization attempts.
  • Additionally, a Gather-Apply pattern is now used to ensure safety with other game systems. The parallel job only calculates radiation values and stores them in a results array (the "Gather" phase). The application of these results (updating components and raising events via IrradiateEntity) is then done sequentially in the main thread (the "Apply" phase), preventing race conditions with non-thread-safe systems called via OnIrradiatedEvent.

Media

A result of 1.5 - 2.5 ms update time was achieved for 51 receivers, 628 sources.
Скриншот 24-07-2025 024952

Requirements

Breaking changes

None

Changelog

🆑 Naxel

  • fix: Heavily optimized the radiation system to prevent severe server lag when a large number of radiation sources are active.

@PJBot PJBot added S: Needs Review Status: Requires additional reviews before being fully accepted. Not to be replaced by S: Approved. S: Untriaged Status: Indicates an item has not been triaged and doesn't have appropriate labels. labels Jul 24, 2025
@github-actions github-actions bot added the size/M Denotes a PR that changes 100-999 lines. label Jul 24, 2025
@Naxel11
Copy link
Author

Naxel11 commented Jul 24, 2025

Need for tritium fire PR

@Naxel11 Naxel11 mentioned this pull request Jul 24, 2025
2 tasks
@metalgearsloth
Copy link
Contributor

2ms for that little to process still seems goofy to me?

@metalgearsloth
Copy link
Contributor

Realistically radiation was never built with performance in mind but if we actually want to use it more the first thing to do is to use b2dynamictree for radiation sources then iterate every receiver in parallel.

@Naxel11
Copy link
Author

Naxel11 commented Jul 25, 2025

Comparison on a laptop 8 GB RAM, 2.60 - 3.80 GHz
master:
~28 ms, 138 receivers, 962 sources, 5692 rays
Скриншот 26-07-2025 000556

RadiationSystem-Gridcast-rework:
~8 ms, 137 receivers, 957 sources, 4896 rays
image

Copy link
Contributor

@southbridge-fur southbridge-fur left a comment

Choose a reason for hiding this comment

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

Read through the code. Looks good just got a couple style changes. Still need to test in game.

@PJBot PJBot added S: Awaiting Changes Status: Changes are required before another review can happen and removed S: Needs Review Status: Requires additional reviews before being fully accepted. Not to be replaced by S: Approved. labels Jul 26, 2025
@southbridge-fur
Copy link
Contributor

Also possible testfail fake

@southbridge-fur southbridge-fur added P3: Standard Priority: Default priority for repository items. T: Refactor Type: Refactor of notable amount of codebase D1: High Difficulty: Extensive codebase knowledge required. A: Core Tech Area: Underlying core tech for the game and the Github repository. A: Engineering Area: Engineering department, including Atmospherics. and removed S: Untriaged Status: Indicates an item has not been triaged and doesn't have appropriate labels. labels Jul 26, 2025
@Naxel11
Copy link
Author

Naxel11 commented Jul 29, 2025

Realistically radiation was never built with performance in mind but if we actually want to use it more the first thing to do is to use b2dynamictree for radiation sources then iterate every receiver in parallel.

Good thing. I used it with the difference that I didn't use B2DynamicTree directly, but its wrapper DynamicTree (DynamicTree.cs line 76)

@FairlySadPanda FairlySadPanda removed the S: Awaiting Changes Status: Changes are required before another review can happen label Aug 13, 2025
@PJBot PJBot added the S: Awaiting Changes Status: Changes are required before another review can happen label Aug 28, 2025
Copy link
Contributor

@southbridge-fur southbridge-fur left a comment

Choose a reason for hiding this comment

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

A couple other minor changes

@PJBot PJBot added S: Needs Review Status: Requires additional reviews before being fully accepted. Not to be replaced by S: Approved. and removed S: Awaiting Changes Status: Changes are required before another review can happen labels Aug 29, 2025
@PJBot PJBot added the S: Approved Status: Reviewed and approved by at least one maintainer; a PR may require another approval. label Aug 29, 2025
@southbridge-fur
Copy link
Contributor

Looks good and thank you for contributing!

@JrInventor05Next
Copy link

second approve is true...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A: Core Tech Area: Underlying core tech for the game and the Github repository. A: Engineering Area: Engineering department, including Atmospherics. D1: High Difficulty: Extensive codebase knowledge required. P3: Standard Priority: Default priority for repository items. S: Approved Status: Reviewed and approved by at least one maintainer; a PR may require another approval. S: Needs Review Status: Requires additional reviews before being fully accepted. Not to be replaced by S: Approved. size/M Denotes a PR that changes 100-999 lines. T: Refactor Type: Refactor of notable amount of codebase

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants