Do not let Render return an `IEnumerable`
Nobody has claimed this yet.
- Dominant language
- C#
- Stars
- 17.4k
- Forks
- 3k
- Avg merge
- 1d 12h
- Merged PRs (30d)
- 14
Description
Proposal to let the various Render functions like Actor.Render not return an IEnumerable.
Rather to pass an extra function argument i.e. IRenderContext to which IRenderables can be added. i.e. void Render(..., IRenderContext rc) { rc.Add(renderable); } or less clean void Render(..., ICollection<IRenderable> desList).
Code comments that that it was deliberately choses to return IEnumerable to allow for deferred calls to IRenderable and to possibly have less resource usage and allow the use of yield.
In practice WorldRenderere.GenerateRenderables will add all IRenderables to a lit and sort them by z-index.
About 1.7% of CPU time is spent in ordering by z-index in the Linq OrderedEnumerable.
Motivation:
- Renderables can be added to a list of pre allocated sized - size can grow based on last render.
- No allocation of
IEnumerables, andIEnumerators. - No calling of
Enumerables<IRenderable>and Add/InsertRange on the enumerables - No allocation of a KeyMap by
OrderedEnumerablesas returned by LINQOrderBy. Used for Quicksort. i.e. https://referencesource.microsoft.com/#System.Core/System/Linq/Enumerable.cs,2598 - Quicksort can take place on the list if IRenderables directly.
- In general, a lot less Linq and Enumerable overhead
- Comments in GenerateRendables mentions to avoid Linq, yet OrderyBy is still used.
About 1.7% CPU time is spent in sorting the IRenderables - only when showing the shell map. Another 1%+ seems to be spent in calling Add|InsertRange on the renderableBuffer list.
- 13.08% instance void [OpenRA.Game] OpenRA.Graphics.WorldRenderer::GenerateRenderables()[Optimized] ▒
- 9.35% instance void [System.Private.CoreLib] System.Collections.Generic.List`1[System.__Canon]::InsertRange(int32,class System.Collections.Generic.IEnumerable`1<!0>)[OptimizedTier1] ▒
- 7.65% instance bool [OpenRA.Game] OpenRA.Actor+<Renderables>d__75::MoveNext()[Optimized] ▒
- 5.89% instance bool [OpenRA.Mods.Common] OpenRA.Mods.Common.Traits.Render.RenderSprites+<Render>d__12::MoveNext()[Optimized] ▒
- 3.32% instance class OpenRA.Graphics.IRenderable[] [OpenRA.Game] OpenRA.Graphics.AnimationWithOffset::Render(class OpenRA.Actor,class OpenRA.Graphics.PaletteReference)[OptimizedTier1] ▒
1.76% instance class OpenRA.Graphics.IRenderable[] [OpenRA.Game] OpenRA.Graphics.Animation::Render(valuetype OpenRA.WPos,valuetype OpenRA.WVec&,int32,class OpenRA.Graphics.PaletteReference)[OptimizedTier1] ▒
- 1.69% instance bool [System.Linq] System.Linq.OrderedEnumerable`1+<GetEnumerator>d__17[System.__Canon]::MoveNext()[OptimizedTier1] ▒
- instance int32[] [System.Linq] System.Linq.EnumerableSorter`1[System.__Canon]::Sort(!0[],int32)[OptimizedTier1] ▒
- 1.26% instance void [System.Linq] System.Linq.EnumerableSorter`2[System.__Canon,System.Int32]::QuickSort(int32[],int32,int32)[OptimizedTier1] ▒
void [System.Private.CoreLib] System.Collections.Generic.ArraySortHelper`1[System.Int32]::Sort(valuetype System.Span`1<!0>,class System.Comparison`1<!0>)[OptimizedTier1] ▒
- void [System.Private.CoreLib] System.Collections.Generic.ArraySortHelper`1[System.Int32]::IntroSort(valuetype System.Span`1<!0>,int32,class System.Comparison`1<!0>)[OptimizedTier1] ▒
- 1.12% void [System.Private.CoreLib] System.Collections.Generic.ArraySortHelper`1[System.Int32]::IntroSort(valuetype System.Span`1<!0>,int32,class System.Comparison`1<!0>)[OptimizedTier1] ▒
0.65% void [System.Private.CoreLib] System.Collections.Generic.ArraySortHelper`1[System.Int32]::IntroSort(valuetype System.Span`1<!0>,int32,class System.Comparison`1<!0>)[OptimizedTier1] ▒
0.59% instance class [System.Runtime]System.Collections.Generic.IEnumerable`1<class OpenRA.Graphics.IRenderable> [OpenRA.Game] OpenRA.Actor::Render(class OpenRA.Graphics.WorldRenderer)[Optimized]
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with WorldRenderer.GenerateRenderables and Actor.Render, then trace the Render methods named in the profile, including RenderSprites.Render, AnimationWithOffset.Render, and Animation.Render. The work is done when renderables no longer rely on the proposed IEnumerable-based flow, while generation and z-index ordering retain their existing behavior and the reported allocation and sorting overhead is reduced.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp
- Domain
- game-dev, performance
- Issue type
- Refactor
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100