-
Notifications
You must be signed in to change notification settings - Fork 424
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 source generator for dependency injection #5541
Conversation
Only providing memory based tests for now (there will definitely be performance improvements on the CPU side as you'd expect, but these will roughly track the reduction in allocations and GC object tracking): Testing is done by loading the game from a cold start, and entering song select (with only 9 beatmap sets loaded). Note that this also includes my other recent optimisations to
|
Before merging this, please give any suggestions for names, if any. It's going to be extremely hard to change things after the fact since this is all compiled. |
Which naming in particular? |
|
Code reviewBecause this is slated for a quick merge, I explicitly did not read any of the generator internals at this point. I probably will at a later date, but reviewing a 3k line diffstat in detail would be counterproductive anyway. I see a lot of tests here and therefore I'm just assuming they're broad enough to cover most cases. In a way, as long as outputs of the generator are good the details don't matter too much, functionally speaking. I did read the actual changes in DI though as they are the key part of this diff. I think I get what this is trying to do, and it makes sense to me (that said I didn't read it that long). When it comes to naming, I don't think it matters that much as I had much more trouble with deciphering the control flow at first rather than the naming, for two main reasons:
That said, when it comes to the names questioned above, my view is thus:
See what you think about these. I don't insist on these, they're just subjective feelings. Curious if you see those as any better than what's there right now. Running the code fixI'm not sure why, but
was placed in
to scope down to just this one code fix rather than fix absolutely every inspection reported by everything ever (which is what the After doing the above I'm getting the same results as your test branches, though (after enough iterations). Build timeTested on game-side.
Not sure whether I measured wrong, but this is basically not an issue for me because I can't really tell that anything's changed. Test procedure was basically
Benchmark resultsRunning the benchmark attached in this PR on the test branch, I get:
That is pretty cut-and-dry. Wins across the board. Development & quick testing
So to sum all of the above up, aside from the naming suggestions above I don't have much to offer or object to. Good work on this 👍 |
Adjusted naming as proposed, favouring
I noticed this as well and it's something I want to refactor as a followup. |
I'm gonna need another nupkg after this is merged due to the naming changes. |
The new naming looks good to be, so I'll do that soon. |
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.
💯
This one's been brewing for a long time while I tried to figure out the best way to do things. It provides a structural foundation on top of which we can build other source generators.
Strategy
There are two primary goals that guided my decisions:
Before I explain further, let me mention that I'm not particularly proud of the naming in this PR and I understand it can be confusing. I'm open to alternatives.
For source generators to work at all for us, we unfortunately need to make every
Drawable
classpartial
.To meet the above goals, the structure I've come up with is to add a
partial
class that implements a specific interface -ISourceGeneratedDependencyActivator
. If the dependency activator detects that this interface has been implemented, it uses it instead of going via the reflection path.The
ISourceGeneratedDependencyActivator
interface exposes one method in particular -RegisterDependencyActivator(IDependencyActivator)
. The source generator implements this method in the form of:Of importance,
Register()
here generates twoFunc<>
s which inject/cache dependencies for the current object. It does not do the whole activation process in-line. The reason for this is that I want this to be transparent and want both the reflection and SG modes to work concurrently regardless of the type hierarchy (e.g. if code hasn't been recompiled to support the SG).That is to say, as far as
DependencyActivator
(the o!f static class orchestrating everything) is concerned, the returnedFunc<>
s are just the normal callbacks generated by the reflection pathway.This is all placed in a file named
g_{class_name}_{guid}_Dependencies.cs
which you can get to by going to the definition of classes in Rider. Why the GUID? Because class names can conflict. There doesn't seem to be a proper solution to this offered by the Roslyn team.Analyser + codefix
I've created a pretty "basic" analyser for warning against non-
partial
classes when required. It attempts to discover:DependencyContainer.Inject()
.CachedModelDependencyContainer<T>
.ITransformable
,IDrawable
orISourceGeneratedDependencyActivator
.This covers all current cases while being as least invasive as possible (i.e. not analysing every member of every class ever).
A code fix has been provided for the diagnostic, however if you're planning to codefix the whole solution I suggest the running the following command until it outputs 0 changes:
The code fix seems to not fix all issues on the first attempt. This is potentially caused by my implementation, however I've tried everything that I could find and nothing's worked. I'll look into fixing this as a future effort, however for the most part it's a one-time thing.
The analyser and code fixer don't have tests at the moment. These are also planned in a future effort and will be structured similarly to the source generator tests and the
osu-localisation-analyser
project.Notes
IIncrementalGenerator
, but there's a bit more reading to do for that.osu-localisation-analyser
) and the tests are lacking structure. This will be improved in a future effort.Testing
You can test this branch with the following trees:
osu-framework
: https://github.com/smoogipoo/osu-framework/tree/sg-dependency-activator-partial-classesosu
: https://github.com/smoogipoo/osu/tree/sg-test (includes local-framework)I've tested:
osu-framework
osu
+ local framework.osu
+osu-framework
package.I have not tested VSCode compatibility, but I have no reason to believe it won't work, unlike
osu-localisation-analyser
where there's differences in how IDEs add files to workspaces from analysers/codefixes.I'll leave it to @peppy to post profiling results.