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

Provide a way to avoid GC allocations (e.g. allow low-level API access in C#) #2757

Closed
minim271 opened this issue May 22, 2021 · 25 comments
Closed

Comments

@minim271
Copy link

Describe the project you are working on

A rhythm game

Describe the problem or limitation you are having in your project

GodotSharp.dll provides godot API access in c# style(encapsulated as classes), and low level API is marked as internal and it's not directly accessible(can access via reflection).

In my case, I want to avoid all possible GC alloc in core part, GC's stop the world time is a serious impact for rhythm game, i think is same for other "real time" game, using the low level API(very similar to GDNative's C API)can achieve this goal.

C# can write very "low level", such as using unsafe code to handle pointers, and it provides many convenient tool to deal with the structs, this allows developers to choose better performance or higher development efficiency according to the requirements of different parts, that's why I love this language, I hope Godot Sharp can also play these advantages.

Describe the feature / enhancement and how it helps to overcome the problem or limitation

Using the low level API can definitely avoid GC alloc, and may get better performance.

Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams

Modify the mono glue generator, change lowlevel API from internal to public, then we can use it.

Mark the risk in the XML document and/or using a compile param like #GODOT_SHARP_LOW_LEVEL_ENABLE can avoid misuse by people who don't need to use it.

If this enhancement will not be used often, can it be worked around with a few lines of script?

Maybe it is, but I don't think it needs a lot of work (if the relevant PR can be accepted, I can try to complete it). It should not have a negative impact on people who don't need to use it. It will be very convenient to complete it directly in the main branch.

Is there a reason why this should be core and not an add-on in the asset library?

Using plug-ins to implement is troublesome and may has negative performance impact.

@aaronfranke
Copy link
Member

Can you give an example of which low-level APIs you need access to, and how they are currently generated / used?

@minim271
Copy link
Author

minim271 commented May 23, 2021

Can you give an example of which low-level APIs you need access to, and how they are currently generated / used?

I used ILSpy look at the low level implementation of Godot.Object, It basically encapsulates the pointer of the C++ part(Godot.Object'sinternal IntPtr ptr;),

Lable.SetText as an example,it get a function pointer via Godot.Object.godot_icall_Object_ClassDB_get_method("Label", "set_text"); at mono's initialization.When we call Lable.SetText,it useGodot.Object.GetPtrto get the lable's pointer,and usingNativeCalls.godot_icall_1_53with function pointer, object pointer, string value.

For developers who can understand pointers, understand and use those API is not difficult, I have to admit that the current API is not friendly and the GC alloc can't be avoided in current version(3.3.1)(For example internal static extern IntPtr godot_icall_Label_Ctor(Label obj);, we can't construct a node directly without c# class)

Update: The above content is not intended to express that this API should be used, just to express that low-level API can be understood and used. What I suggest is to allow API similar to GDNative's C API.

According to godotengine/godot#48409

Code that deals with native structs is less safe than before as there's no RAII and copy constructors in C#. It's like using the GDNative C API directly.

Since it is so, I think it's very helpful to public low level APIs in the future, just lick GDNative's C API, C# can directly handle it just like C, and it could be able to basically avoid the negative impact on developers who do not need it through XML document annotation, etc.

@aaronfranke
Copy link
Member

aaronfranke commented May 23, 2021

@zarmot Thanks for the detailed reply, indeed exposing these would be useful for some people with niche use cases that need the extra performance. It should probably be disabled by default and given a compile-time option to enable it as you mentioned, since we don't want to confuse most users who shouldn't touch this, and people who need this kind of low-level access will also have the knowledge to compile Godot themselves anyway.

However, these kinds of APIs you want to expose may change in the future, and this is a low priority, so it would make sense to implement it later (likely after Godot switches to .NET Core). I'll give this the 4.1 milestone so that we don't forget about it by then, it's also possible that it will get pushed back further or there is also a small chance that this will end up getting done for 4.0.

@aaronfranke aaronfranke added this to the 4.1 milestone May 23, 2021
@minim271
Copy link
Author

I'll give this the 4.1 milestone so that we don't forget about it by then, it's also possible that it will get pushed back further or there is also a small chance that this will end up getting done for 4.0.

I also think it's good to do it when switching to.Net, happy to hear that, thanks.

People who need this kind of low-level access will also have the knowledge to compile Godot themselves anyway.

It makes sense to avoid "division" as much as possible, using self compiled programs has uncertainty and is relatively difficult to get help, let more users use the official binary can reduce a lot of trouble.

As for myself, I force myself to use official binaries, even if I can compile them myself, I'm more willing to make compromises than to modify the engine(Since I also use unity, it's very acceptable).

@minim271
Copy link
Author

And i'll try to done this when C#'s internal interface to be more stable.(godotengine/godot#48409)

@Xrayez
Copy link
Contributor

Xrayez commented May 25, 2021

If performance is the major concern, why not use GDNative C/C++ bindings alongside C#? I understand that learning a new language can be time-consuming but I think it would be definitely worth it if you do make performance-critical games. Not sure if the extra performance boost that you'd get from implementing this proposal would be on par with native C++ programming. Also, by using internal API you'll likely make your code less readable in C#.

@Xrayez
Copy link
Contributor

Xrayez commented May 25, 2021

As for myself, I force myself to use official binaries, even if I can compile them myself, I'm more willing to make compromises than to modify the engine(Since I also use unity, it's very acceptable).

It depends. Due to the nature of open-source development, people work on things they personally need themselves (and community should find it useful as well). For any non-trivial project, I think you may need to modify the source at some point. The authors of games such as City Game Studio, or apps like HEAVYPAINT do build Godot from source that need to apply crucial core patches, or take advantage of third-party modules written in C++: #913 (comment).

@minim271
Copy link
Author

minim271 commented May 25, 2021

@Xrayez In general, GDNative is suitable for such needs.And I'm actually familiar with GDNative/C/C++/Rust.The main reason why I choose C# is efficiency , using native in a project it's a lot more complex than development using only C#, this will involve external dependence, which will lead to environment specific problems, i can deal with it, but considering what I'm doing is an open source project, using C# is more convenient for others to start and greater probability of get contribution.

Sorry, i'm a little off topic, the advantages and disadvantages of stack are not the focus of this issues.I file this issues mainly because of the new situation mentioned before(godotengine/godot#48409):

Code that deals with native structs is less safe than before as there's no RAII and copy constructors in C#. It's like using the GDNative C API directly.

Because of this changes, I think it's helpful to add the exposure of the low-level API(similar to GDNative's C API), it probably doesn't take much work(i'll try to done this when C#'s internal API to be more stable(godotengine/godot#48409)), and can not affect other people who do not need to use it.

if performance is the major concern

As for me, this issue is not mainly about performance(frame per seconds), but garbage collection‘s "stop the world time"(lags).

by using internal API you'll likely make your code less readable in C#

Yes, but just readable like C, and i'll encapsulation godot API myself, but in struct, it can avoid GC alloc, so other part is readable as usual. The significance of providing the low-level API is to provide more operation space and freedom, which is almost pure gain without or with little side effect.

@Xrayez
Copy link
Contributor

Xrayez commented May 25, 2021

Thanks, your response makes this proposal more clear to me in terms of performance aspect and motivations, sorry if that sounded a bit off-topic. Godot's development is extremely pragmatic so usually exposing internal API for the sake of it is frowned upon within the community of core developers. I do not have extensive experience in C# to be the judge, though. 🙂

@minim271
Copy link
Author

minim271 commented May 25, 2021

sorry if that sounded a bit off-topic

Sorry, english is not my native language, so it's a little bit of a misexpression, I mean I want to say that my first paragraph is a little off topic.😂

@minim271
Copy link
Author

minim271 commented May 25, 2021

Godot's development is extremely pragmatic so usually exposing internal API for the sake of it is frowned upon within the community of core developers.

Fully understand, I just make this proposal to express the needs, I personally think this proposal is acceptable for the above reasons.

It depends. Due to the nature of open-source development, people work on things they personally need themselves (and community should find it useful as well). For any non-trivial project, I think you may need to modify the source at some point. The authors of games such as City Game Studio, or apps like HEAVYPAINT do build Godot from source that need to apply crucial core patches, or take advantage of third-party modules written in C++: #913 (comment).

Yes, I'm not saying it's bad, it's also important to explore other directions. Just for myself, I develop games in my spare time, In terms of available time, It's a better strategy for me.

@GeorgeS2019
Copy link

GeorgeS2019 commented May 29, 2021

@zarmot I have yet to understand with an overview of the benefits you proposed here.

Question1: is it possible to use e.g. ILSpy to automate the complete listing of e.g. the low level API access in c#.
Question2: is it possible to apply mocks onto these low level API access in c# so we could write unit tests against these mocked low level API. The aim is to speed up test driven design (TDD) in .NET languages (e.g. C# or F#) just with GodotEngine.dll without involving running Godot Engine

I also seek feedback if this step will help towards achieving the goal of embedding Mono to Godot as a library

@minim271
Copy link
Author

@GeorgeS2019 I think my previous content is sufficient to explain the main improvement goal: avoiding GC alloc, and it can also improve performance, now there is just a right time to achieve this goal and it doesn't take much work.This proposal did not help with your problem.

@GeorgeS2019
Copy link

GeorgeS2019 commented May 30, 2021

@zarmot Just out of curiosity, what IDE U use for c# development. What is you view to improve e.g. VS2019 as an IDE. What need to be done for using VS2019 IDE for Godot. What other proposals have you written. I am curious to see other feedback from you.

@minim271
Copy link
Author

@GeorgeS2019 Kubuntu20.04LTS+VSCode+C# Extention, I don't care about VS2019 at all. As for dev with godot, a debug adapter is enough for me. I tend to minimize external dependencies.

@neikeq
Copy link

neikeq commented Jun 4, 2021

I don't like the idea of giving access to some low-level functions. Maybe the best would be to generate an alternative scripting API that uses structs instead of classes?

@GeorgeS2019
Copy link

Just curious, is this issue : Mono: Improve engine methods object parameters (and use/allow structs? relevant to our discussion here?

@GeorgeS2019
Copy link

GeorgeS2019 commented Jun 4, 2021

@neikeq

The fact we have "crazy" ideas (e.g. low level API c# access and ECS unique to Godot) speak VOLUME of the attractiveness of Godot for .NET developers who feel restrictive with other high end game engines like e.g. Unity3D, but now found new freedom offered by Godot :-)

@neikeq
Copy link

neikeq commented Jun 5, 2021

Just curious, is this issue : Mono: Improve engine methods object parameters (and use/allow structs? relevant to our discussion here?

No, that's a different thing. It would still be the same problem even with low level API access or GDNative.

@minim271
Copy link
Author

minim271 commented Jun 5, 2021

I don't like the idea of giving access to some low-level functions. Maybe the best would be to generate an alternative scripting API that uses structs instead of classes?

@neikeq Because my main purpose is to avoid GC alloc, my first thought was also to change or provide an alternative API based on struct. Using class is convenient for inheritance, but it cause GC alloc, and we still needs to call Object.Free manually.Providing alternative API based on struct can completely solve my problem.Maybe providing two similar but not universal APIs can lead to some misunderstanding, I believe it can be solved.

In fact, I'm surprised that providing an alternative API based on struct maybe an acceptable proposal for the core team, I didn't know that before I file this proposal, I will change the title of the proposal to make it motivations more clear.

@minim271 minim271 changed the title Allow low level API access in C# Provide a way to avoid GC alloc(Like allow low level API access in C#) Jun 5, 2021
@minim271
Copy link
Author

minim271 commented Jun 5, 2021

May be relevant: Unity is working for DOTS(ECS) API, that allows avoid GC alloc, it's based on struct. (ECS is not the point, that is not the scope of this proposal.)It means unity may have ways to avoid GC alloc in the future.

Garbage collection‘s stop the world time can cause long lags, It's not good for game development, I'm glad to hear that GDScript has no GC system(maybe it's also a reason python was not directly used as the scripting language in the initial design), and C# can also avoid GC alloc with additional APIs.

@Calinou Calinou changed the title Provide a way to avoid GC alloc(Like allow low level API access in C#) Provide a way to avoid GC allocations (e.g. allow low-level API access in C#) Jun 5, 2021
@minim271
Copy link
Author

According to #2333 (comment)

Maybe it's also an opportunity to achieve the goal of this proposal.

@minim271
Copy link
Author

minim271 commented Jun 10, 2021

@neikeq I just noticed that in the future(godotengine/godot#48409), the C# layer call the Godot API via P/invoke, is it possible that allow C# user call Godot's low level API via P/Invoke directly in a C# project just like GodotSharp.dll? This means that the official C# encapsulation(GodotSharp.dll) is a "default implementation", developers can re implement it on their own(like based on struct) with no pain.

If that's possible, I think this is probably the best way to implement this proposal, no additional documentation is required, does not affect anyone who does not need this feature, if someone need it, just explicit define [DllImport(GodotDllName)].

@minim271
Copy link
Author

A circuitous solution: Send a struct pointer as an int data from GDNative, it can contain function pointers as needed and call those functions in C#(TFM:net5.0, C#9, delegate* unmanaged<T>).

This is an acceptable solution, but if it is possible to use C API via P/Invoke directly, it will be much better than the current way, so I haven't closed this issue yet.

@Calinou
Copy link
Member

Calinou commented Sep 26, 2023

Closing in favor of #7842.

@Calinou Calinou closed this as completed Sep 26, 2023
@Calinou Calinou removed this from the 4.1 milestone Sep 26, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants