66using System ;
77using System . Runtime . InteropServices ;
88using osu . Framework . Allocation ;
9+ using osu . Framework . Graphics . Primitives ;
910using osu . Framework . Graphics . Rendering ;
11+ using osu . Framework . Graphics . Rendering . Vertices ;
1012using osu . Framework . Graphics . Shaders ;
1113using osu . Framework . Graphics . Shaders . Types ;
1214using osu . Framework . Graphics . Sprites ;
1315using osu . Framework . Graphics . Transforms ;
16+ using osuTK ;
1417
1518namespace osu . Framework . Graphics . UserInterface
1619{
@@ -101,26 +104,93 @@ public CircularProgressDrawNode(CircularProgress source)
101104 protected float TexelSize { get ; private set ; }
102105 protected bool RoundedCaps { get ; private set ; }
103106
107+ // Even though it's possible to adjust segment count based on thickness and/or progress value
108+ // we are not doing so to avoid creating new vertex batches on said changes.
109+ // Segment count has been chosen to increase fps and decrease gpu usage as much as possible
110+ // by using results from TestSceneCircularProgressRingsPerformance.
111+ private const int segment_count = 20 ;
112+ private const int vertex_count = ( segment_count + 1 ) * 2 ;
113+ private static readonly float angle_delta = float . DegreesToRadians ( 360f / ( segment_count * 2 ) ) ;
114+
115+ private Vector2 drawSize ;
116+ private RectangleF tRect ;
117+
104118 public override void ApplyState ( )
105119 {
106120 base . ApplyState ( ) ;
107121
108122 InnerRadius = Source . innerRadius ;
109123 Progress = Math . Abs ( ( float ) Source . progress ) ;
110124 RoundedCaps = Source . roundedCaps ;
125+ drawSize = Source . DrawSize ;
111126
112127 // smoothstep looks too sharp with 1px, let's give it a bit more
113128 TexelSize = 1.5f / ScreenSpaceDrawQuad . Size . X ;
129+ tRect = Texture . GetTextureRect ( ) ;
114130 }
115131
116132 private IUniformBuffer < CircularProgressParameters > parametersBuffer ;
133+ private IVertexBatch < TexturedVertex2D > vertexBatch ;
117134
118135 protected override void Blit ( IRenderer renderer )
119136 {
120137 if ( InnerRadius == 0 || ( ! RoundedCaps && Progress == 0 ) )
121138 return ;
122139
123- base . Blit ( renderer ) ;
140+ // Don't triangulate in case when circle is almost filled
141+ if ( InnerRadius > 0.95f )
142+ {
143+ base . Blit ( renderer ) ;
144+ return ;
145+ }
146+
147+ drawTriangulatedShape ( renderer ) ;
148+ }
149+
150+ private void drawTriangulatedShape ( IRenderer renderer )
151+ {
152+ if ( ! renderer . BindTexture ( Texture ) )
153+ return ;
154+
155+ vertexBatch ??= renderer . CreateLinearBatch < TexturedVertex2D > ( vertex_count , 1 , PrimitiveTopology . TriangleStrip ) ;
156+
157+ Vector2 outer = new Vector2 ( 0.5f , 0.5f - 0.5f / MathF . Cos ( angle_delta ) ) ;
158+ Vector2 inner = new Vector2 ( 0.5f , Math . Min ( Math . Max ( InnerRadius * 0.5f + TexelSize , TexelSize * 2 ) , 0.5f ) ) ;
159+ Vector2 origin = new Vector2 ( 0.5f ) ;
160+
161+ float angle = 0 ;
162+ bool isInnerVertex = true ;
163+
164+ renderer . PushLocalMatrix ( DrawInfo . Matrix ) ;
165+
166+ for ( int i = 0 ; i < vertex_count ; i ++ )
167+ {
168+ Vector2 relativePos = rotateAround ( isInnerVertex ? inner : outer , origin , angle ) ;
169+
170+ vertexBatch ? . Add ( new TexturedVertex2D ( renderer )
171+ {
172+ Position = relativePos * drawSize ,
173+ Colour = DrawColourInfo . Colour . Interpolate ( relativePos ) . SRGB ,
174+ TextureRect = new Vector4 ( tRect . Left , tRect . Top , tRect . Right , tRect . Bottom ) ,
175+ TexturePosition = new Vector2 ( tRect . Left + tRect . Width * relativePos . X , tRect . Top + tRect . Height * relativePos . Y )
176+ } ) ;
177+
178+ angle += angle_delta ;
179+ isInnerVertex = ! isInnerVertex ;
180+ }
181+
182+ renderer . PopLocalMatrix ( ) ;
183+ }
184+
185+ private static Vector2 rotateAround ( Vector2 input , Vector2 origin , float angle )
186+ {
187+ float sin = MathF . Sin ( angle ) ;
188+ float cos = MathF . Cos ( angle ) ;
189+
190+ float xTranslated = input . X - origin . X ;
191+ float yTranslated = input . Y - origin . Y ;
192+
193+ return new Vector2 ( xTranslated * cos - yTranslated * sin , xTranslated * sin + yTranslated * cos ) + origin ;
124194 }
125195
126196 protected override void BindUniformResources ( IShader shader , IRenderer renderer )
@@ -145,6 +215,7 @@ protected override void Dispose(bool isDisposing)
145215 {
146216 base . Dispose ( isDisposing ) ;
147217 parametersBuffer ? . Dispose ( ) ;
218+ vertexBatch ? . Dispose ( ) ;
148219 }
149220
150221 [ StructLayout ( LayoutKind . Sequential , Pack = 1 ) ]
0 commit comments