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

Early C++ implementation for the CompoundClouds' system #5606

Open
wants to merge 16 commits into
base: master
Choose a base branch
from

Conversation

AverageNerdz
Copy link
Contributor

This PR introduces a basic implementation of the CompoundClouds system in C++. While it lays the groundwork for future development, there are several known issues and limitations that need to be addressed. Once these issues are resolved, this PR will close Issue #4917.

Related Issues:

  • Color Overload Inside the Clouds: The cloud rendering exhibits excessive color variation leading to unrealistic appearances across the compounds even when they shouldn't be there.
  • Basic Absorption System: The current absorption mechanics are rudimentary and require further refinement.
  • Overgeneration of Textures: The texture generation process is inefficient leading to performance issues and potential visual clutter.
  • Unoptimized Code: The implementation is not optimized, resulting in increased resource usage.
  • Memory Leaks: There are potential memory leaks that need to be addressed to ensure stable performance.

This implementation serves as a starting point, and I look forward to refining it further with your feedback.

Benchmark Results

I'd also like to share benchmark results comparing the CS benchmark with the C++ implementation:


Benchmark results for CloudBenchmark CS

  • Cloud spawn score: 139
  • Absorber score: 150.706
  • Many spawners score: 138.353
  • Cloud sim multiplier before under 60 FPS: 3.2615
  • Stress test spawners: 38
  • Stress test average FPS: 127.221
  • Stress test min FPS: 26
  • Total test duration: 134.5s
  • CPU: Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz (used tasks: 4, native: 2, sim threads: True)
  • GPU: NVIDIA TITAN Xp
  • OS: Windows

Benchmark results for CloudBenchmark C++

  • Cloud spawn score: 225
  • Absorber score: 277.392
  • Many spawners score: 280.235
  • Cloud sim multiplier before under 60 FPS: 8.6923
  • Stress test spawners: 127
  • Stress test average FPS: 269.913
  • Stress test min FPS: 168
  • Total test duration: 274.8s
  • CPU: Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz (used tasks: 4, native: 2, sim threads: True)
  • GPU: NVIDIA TITAN Xp
  • OS: Windows

Here's the visual results of my implentation:
2024-10-15_23 43 06 3790

@revolutionary-bot
Copy link

We are currently in feature freeze until the next release.
If your PR is not just a simple fix, then it may take until the release to get reviewed and merged.

return true;
public override bool CanConvert(Type objectType)
{
// Since CompoundCloudPlane is not a C# type, we return false
Copy link
Member

Choose a reason for hiding this comment

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

How is the data saved then if this converter no longer does it? Because this was the way if I recall correctly how saving and loading didn't clear out all the compound clouds.

Copy link
Member

Choose a reason for hiding this comment

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

This can be taken care of later, but I'll note that this file doesn't follow our naming conventions: our C++ code should use the same naming conventions as our C# code (so no snake_case naming of variables or methods).

void CompoundCloudPlane::_bind_methods() {
using namespace godot;

ClassDB::bind_method(D_METHOD("get_native_instance"), &CompoundCloudPlane::get_native_instance);
Copy link
Member

Choose a reason for hiding this comment

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

But when exposed through Godot the godot naming convention should be used. So this line should be:

Suggested change
ClassDB::bind_method(D_METHOD("get_native_instance"), &CompoundCloudPlane::get_native_instance);
ClassDB::bind_method(D_METHOD("get_native_instance"), &CompoundCloudPlane::GetNativeInstance);

size = 104;

if (size <= 0) {
godot::UtilityFunctions::printerr("Invalid size for CompoundCloudPlane");
Copy link
Member

Choose a reason for hiding this comment

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

The helper error macro should be used instead as that will automatically include the method name and line number the error happens so that's much more helpful way to print errors.

density[x][y][index] += new_density;

texture_needs_update = true;
set_process(true);
Copy link
Member

Choose a reason for hiding this comment

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

Have you tested the performance impact of constantly calling the enable processing? Most clouds are basically never empty meaning that I think the overhead from enabling / disabling processing is probably more than what is gained by disabling the processing. I think this probably artificially inflates the benchmark numbers. If required I can modify the benchmark to use all compound cloud types in sequence to ensure fairness against the C# implementation that way.


void CompoundCloudPlane::update_compound_colors() {
// Define a map of compound IDs to their corresponding colors from compounds.json
std::unordered_map<int, godot::Color> compound_color_map = {
Copy link
Member

Choose a reason for hiding this comment

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

Is this temporary code or? If this is not temporary this definitely needs to be sent by the C# side to configure the cloud correctly.

}
catch (Exception e)
{
GD.PrintErr($"Error in CompoundCloudSystem._Process: {e.Message}\n{e.StackTrace}");
Copy link
Member

Choose a reason for hiding this comment

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

I'm pretty sure there's no way anything here would cause an exception... because Godot Call will just print an error and not throw anything.


private int GetCompoundID(CompoundDefinition compoundDefinition)
{
return (int)compoundDefinition.ID;
Copy link
Member

Choose a reason for hiding this comment

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

A lot of memory I think could be saved by using the underlying type of the ID, which I specifically chose to be uint16 as that gives still plenty of compound types but requires only half as much memory as an int.

/// </summary>
/// <param name="compound">The compound type to take</param>
/// <param name="worldPosition">World position to take from</param>
/// <param name="fraction">The fraction of compound to take. Should be &lt;= 1</param>
public float TakeCompound(Compound compound, Vector3 worldPosition, float fraction)
Copy link
Member

Choose a reason for hiding this comment

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

I think this method needs to be re-thought out as this is called on many points for a circle for a single absorb operation, meaning that there's a ton of calls going between C# and C++ when absorbing. Also why did you remove the first part of the summary? And also you seemed to add a bunch of locks in the C++ code so it is less parallel than this C# code. You should instead consider trying to use atomic reads and writes in C++.

Comment on lines +307 to +309
var compounds = (int[])cloudInstance.Call("get_compounds");

if (!Array.Exists(compounds, id => id == compoundID))
Copy link
Member

Choose a reason for hiding this comment

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

This allocates a bit of memory, would be really nice if a core operation like this didn't need to constantly allocate memory.

continue;

// Then just need to check that it is within the cloud simulation array
if (x < cloud.Size && y < cloud.Size)
float amount = (float)cloudInstance.Call("amount_available", compoundID, x, y, 1.0f);
Copy link
Member

Choose a reason for hiding this comment

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

This is in a very big loop calling a ton of methods through the Godot calling interface.

Copy link
Member

@hhyyrylainen hhyyrylainen left a comment

Choose a reason for hiding this comment

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

In general I think the goal with the conversion of the clouds to C++ would be that the visuals or functionality don't change in any significant way, which from your screenshots doesn't seem to be the case, but only performance would increase... Also out of the code architecture changes you've done many seem not necessary (and I didn't find out why you needed to add a global list of existing cloud planes) and when thinking about this conversion many architecture changes I anticipated needing to be done seem to not have been done (like converting the absorb / emit compound methods to an approach that reduces the number of calls between C++ and C#). So I have quite a lot of architecture feedback, some of which I specifically pointed out but there's so many instances that I couldn't comment on all of them.

Copy link
Member

Choose a reason for hiding this comment

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

It looks like to me that you have done a ton of unnecessary changes to this file that make things a lot harder (so unrelated to just having the cloud types now be implemented in C++) and including removing documentation on methods. At the same time it looks like the methods I really think should be entirely rewritten for the new approach to reduce the C#<->C++ call count, are not entirely rewritten. So I sadly need to say that I think you've kind of done the opposite here than what I think should have been done.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: In progress
Development

Successfully merging this pull request may close these issues.

Test if compound cloud simulation can be written in C++ for more performance
3 participants