44#pragma warning disable SA1201 // Phase-1 scene-model types are grouped together in one file for now.
55
66using System . Buffers ;
7- using System . Collections . Generic ;
87using System . Diagnostics ;
98using System . Numerics ;
109using System . Runtime . CompilerServices ;
@@ -34,6 +33,7 @@ internal static class WebGPUSceneEncoder
3433 private const int StyleBlendAlphaShift = 14 ;
3534 private const uint StyleBlendAlphaMask = 0xFFFFU << StyleBlendAlphaShift ;
3635 private const uint StyleFlagsFill = 0x40000000U ;
36+ private static readonly GraphicsOptions DefaultClipGraphicsOptions = new ( ) ;
3737
3838 /// <summary>
3939 /// Encodes prepared composition commands into flush-scoped scene buffers.
@@ -69,7 +69,7 @@ public static bool TryValidateBrushSupport(IReadOnlyList<CompositionCommand> com
6969 for ( int i = 0 ; i < commands . Count ; i ++ )
7070 {
7171 CompositionCommand command = commands [ i ] ;
72- if ( ! command . IsVisible )
72+ if ( command . Kind is not CompositionCommandKind . FillLayer || ! command . IsVisible )
7373 {
7474 continue ;
7575 }
@@ -91,8 +91,10 @@ private ref struct SupportedSubsetSceneEncoding
9191 private bool gradientPixelsDetached ;
9292 private uint lastStyle0 ;
9393 private uint lastStyle1 ;
94+ private readonly Rectangle rootTargetBounds ;
95+ private List < Rectangle > ? openLayerBounds ;
9496
95- private SupportedSubsetSceneEncoding ( MemoryAllocator allocator , int commandCount )
97+ private SupportedSubsetSceneEncoding ( MemoryAllocator allocator , int commandCount , in Rectangle rootTargetBounds )
9698 {
9799 this . PathTags = new OwnedStream < byte > ( allocator , Math . Max ( commandCount * 8 , 256 ) ) ;
98100 this . PathData = new OwnedStream < uint > ( allocator , Math . Max ( commandCount * 16 , 256 ) ) ;
@@ -113,6 +115,8 @@ private SupportedSubsetSceneEncoding(MemoryAllocator allocator, int commandCount
113115 this . gradientPixelsDetached = false ;
114116 this . lastStyle0 = 0 ;
115117 this . lastStyle1 = 0 ;
118+ this . rootTargetBounds = rootTargetBounds ;
119+ this . openLayerBounds = null ;
116120
117121 this . PathTags . Add ( PathTagTransform ) ;
118122 AppendIdentityTransform ( ref this . Transforms ) ;
@@ -155,8 +159,8 @@ public static SupportedSubsetSceneEncoding Create(
155159 in Rectangle targetBounds ,
156160 MemoryAllocator allocator )
157161 {
158- SupportedSubsetSceneEncoding encoding = new ( allocator , commands . Count ) ;
159- encoding . Build ( commands , targetBounds ) ;
162+ SupportedSubsetSceneEncoding encoding = new ( allocator , commands . Count , targetBounds ) ;
163+ encoding . Build ( commands ) ;
160164 return encoding ;
161165 }
162166
@@ -177,7 +181,7 @@ public void Dispose()
177181
178182 public void MarkGradientPixelsDetached ( ) => this . gradientPixelsDetached = true ;
179183
180- private void Build ( IReadOnlyList < CompositionCommand > commands , in Rectangle targetBounds )
184+ private void Build ( IReadOnlyList < CompositionCommand > commands )
181185 {
182186 for ( int i = 0 ; i < commands . Count ; i ++ )
183187 {
@@ -187,15 +191,29 @@ private void Build(IReadOnlyList<CompositionCommand> commands, in Rectangle targ
187191
188192 private void Append ( in CompositionCommand command )
189193 {
190- if ( ! command . IsVisible )
194+ switch ( command . Kind )
191195 {
192- return ;
196+ case CompositionCommandKind . FillLayer :
197+ if ( ! command . IsVisible )
198+ {
199+ return ;
200+ }
201+
202+ IPath preparedPath = command . PreparedPath ! ;
203+ this . AppendPlainFill ( command , preparedPath ) ;
204+ return ;
205+
206+ case CompositionCommandKind . BeginLayer :
207+ this . AppendBeginLayer ( command ) ;
208+ return ;
209+
210+ case CompositionCommandKind . EndLayer :
211+ this . AppendEndLayer ( ) ;
212+ return ;
213+
214+ default :
215+ return ;
193216 }
194-
195- IPath preparedPath = command . PreparedPath
196- ?? throw new InvalidOperationException ( "Commands must be prepared before GPU scene encoding." ) ;
197-
198- this . AppendPlainFill ( command , preparedPath ) ;
199217 }
200218
201219 private void AppendPlainFill ( in CompositionCommand command , IPath preparedPath )
@@ -216,6 +234,7 @@ private void AppendPlainFill(in CompositionCommand command, IPath preparedPath)
216234 int encodedPathCount = EncodePath (
217235 command ,
218236 preparedPath ,
237+ this . rootTargetBounds ,
219238 ref this . PathTags ,
220239 ref this . PathData ,
221240 out int geometryLineCount ,
@@ -246,13 +265,13 @@ private void AppendPlainFill(in CompositionCommand command, IPath preparedPath)
246265 ref gradientRowCount ) ;
247266 this . GradientRowCount = gradientRowCount ;
248267
249- this . TotalTileMembershipCount += CountTileMembership ( command . DestinationRegion ) ;
268+ this . TotalTileMembershipCount += CountTileMembership ( GetTargetLocalDestination ( command , this . rootTargetBounds ) ) ;
250269 }
251270
252- private void AppendLayeredFill ( in CompositionCommand command , IPath preparedPath )
271+ private void AppendBeginLayer ( in CompositionCommand command )
253272 {
254- Rectangle layerBounds = Rectangle . Inflate ( command . DestinationRegion , 1 , 1 ) ;
255- ( uint style0 , uint style1 ) = GetFillStyle ( command . GraphicsOptions , command . RasterizerOptions . IntersectionRule ) ;
273+ Rectangle layerBounds = ToTargetLocal ( command . LayerBounds , this . rootTargetBounds ) ;
274+ ( uint style0 , uint style1 ) = GetFillStyle ( DefaultClipGraphicsOptions , IntersectionRule . NonZero ) ;
256275 int pathTagCheckpoint = this . PathTags . Count ;
257276 int styleCheckpoint = this . Styles . Count ;
258277
@@ -280,37 +299,20 @@ private void AppendLayeredFill(in CompositionCommand command, IPath preparedPath
280299 AppendBeginClipData ( command . GraphicsOptions , ref this . DrawData ) ;
281300 this . ClipCount ++ ;
282301 this . TotalTileMembershipCount += CountTileMembership ( layerBounds ) ;
302+ this . openLayerBounds ??= new List < Rectangle > ( 4 ) ;
303+ this . openLayerBounds . Add ( layerBounds ) ;
304+ }
283305
284- uint fillDrawTag = GetDrawTag ( command ) ;
285- GpuSceneDrawMonoid fillDrawTagMonoid = GpuSceneDrawTag . Map ( fillDrawTag ) ;
286- int encodedPathCount = EncodePath (
287- command ,
288- preparedPath ,
289- ref this . PathTags ,
290- ref this . PathData ,
291- out int geometryLineCount ,
292- out _ ) ;
293-
294- if ( encodedPathCount == 0 )
306+ private void AppendEndLayer ( )
307+ {
308+ if ( this . openLayerBounds is not { Count : > 0 } )
295309 {
296- throw new InvalidOperationException ( "A visible layered command encoded an empty fill path." ) ;
310+ return ;
297311 }
298312
299- this . FillCount ++ ;
300- this . PathCount += encodedPathCount ;
301- this . LineCount += geometryLineCount ;
302- this . InfoWordCount += ( int ) fillDrawTagMonoid . InfoOffset ;
303- this . DrawTags . Add ( fillDrawTag ) ;
304- int gradientRowCount = this . GradientRowCount ;
305- AppendDrawData (
306- command ,
307- fillDrawTag ,
308- ref this . DrawData ,
309- ref this . GradientPixels ,
310- this . Images ,
311- ref gradientRowCount ) ;
312- this . GradientRowCount = gradientRowCount ;
313- this . TotalTileMembershipCount += CountTileMembership ( command . DestinationRegion ) ;
313+ int lastIndex = this . openLayerBounds . Count - 1 ;
314+ Rectangle layerBounds = this . openLayerBounds [ lastIndex ] ;
315+ this . openLayerBounds . RemoveAt ( lastIndex ) ;
314316
315317 this . DrawTags . Add ( GpuSceneDrawTag . EndClip ) ;
316318 this . PathTags . Add ( PathTagPath ) ;
@@ -440,15 +442,18 @@ private static uint GetDrawTag(in CompositionCommand command)
440442 private static int EncodePath (
441443 in CompositionCommand command ,
442444 IPath preparedPath ,
445+ in Rectangle rootTargetBounds ,
443446 ref OwnedStream < byte > pathTags ,
444447 ref OwnedStream < uint > pathData ,
445448 out int lineCount ,
446449 out int pathSegmentCount )
447450 {
448451 Rectangle interest = command . RasterizerOptions . Interest ;
449452 float samplingOffset = command . RasterizerOptions . SamplingOrigin == RasterizerSamplingOrigin . PixelCenter ? 0.5F : 0F ;
450- float translateX = command . DestinationRegion . X - command . SourceOffset . X ;
451- float translateY = command . DestinationRegion . Y - command . SourceOffset . Y ;
453+ float targetOffsetX = command . TargetBounds . X - rootTargetBounds . X ;
454+ float targetOffsetY = command . TargetBounds . Y - rootTargetBounds . Y ;
455+ float translateX = targetOffsetX + command . DestinationRegion . X - command . SourceOffset . X ;
456+ float translateY = targetOffsetY + command . DestinationRegion . Y - command . SourceOffset . Y ;
452457
453458 // Move path points from interest-local coverage space into target-local space.
454459 float pointTranslateX = translateX + samplingOffset - interest . Left ;
@@ -636,28 +641,6 @@ private static bool PointsEqual(float ax, float ay, float bx, float by)
636641 private static uint BitcastSingle ( float value )
637642 => unchecked ( ( uint ) BitConverter . SingleToInt32Bits ( value ) ) ;
638643
639- private static int CountLineTileSlices ( float x0 , float y0 , float x1 , float y1 )
640- {
641- if ( x0 == x1 && y0 == y1 )
642- {
643- return 0 ;
644- }
645-
646- bool isDown = y1 >= y0 ;
647- float startX = isDown ? x0 : x1 ;
648- float startY = isDown ? y0 : y1 ;
649- float endX = isDown ? x1 : x0 ;
650- float endY = isDown ? y1 : y0 ;
651- float s0x = startX / TileWidth ;
652- float s0y = startY / TileHeight ;
653- float s1x = endX / TileWidth ;
654- float s1y = endY / TileHeight ;
655-
656- int spanX = Math . Max ( ( int ) MathF . Ceiling ( MathF . Max ( s0x , s1x ) ) - ( int ) MathF . Floor ( MathF . Min ( s0x , s1x ) ) , 1 ) ;
657- int spanY = Math . Max ( ( int ) MathF . Ceiling ( MathF . Max ( s0y , s1y ) ) - ( int ) MathF . Floor ( MathF . Min ( s0y , s1y ) ) , 1 ) ;
658- return checked ( ( spanX - 1 ) + spanY ) ;
659- }
660-
661644 [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
662645 private static int DivideRoundUp ( int value , int divisor )
663646 => ( value + divisor - 1 ) / divisor ;
@@ -676,6 +659,22 @@ private static int CountTileMembership(in Rectangle destinationRegion)
676659 return ( tileMaxX - tileMinX ) * ( tileMaxY - tileMinY ) ;
677660 }
678661
662+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
663+ private static Rectangle ToTargetLocal ( in Rectangle absoluteBounds , in Rectangle rootTargetBounds )
664+ => new (
665+ absoluteBounds . X - rootTargetBounds . X ,
666+ absoluteBounds . Y - rootTargetBounds . Y ,
667+ absoluteBounds . Width ,
668+ absoluteBounds . Height ) ;
669+
670+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
671+ private static Rectangle GetTargetLocalDestination ( in CompositionCommand command , in Rectangle rootTargetBounds )
672+ => new (
673+ ( command . TargetBounds . X - rootTargetBounds . X ) + command . DestinationRegion . X ,
674+ ( command . TargetBounds . Y - rootTargetBounds . Y ) + command . DestinationRegion . Y ,
675+ command . DestinationRegion . Width ,
676+ command . DestinationRegion . Height ) ;
677+
679678 [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
680679 private static uint PackSolidColor ( SolidBrush solidBrush )
681680 {
0 commit comments