@@ -1354,6 +1354,175 @@ void DrawAction(DrawingCanvas canvas)
13541354 AssertBackendPairReferenceOutputs ( provider , "MultipleFlushes" , defaultImage , nativeSurfaceImage ) ;
13551355 }
13561356
1357+ [ WebGPUTheory ]
1358+ [ WithSolidFilledImages ( 160 , 120 , "White" , PixelTypes . Rgba32 ) ]
1359+ public void RetainedScene_MixedWithApplyBarriersAndRegularCommands_MatchesDefaultOutput < TPixel > ( TestImageProvider < TPixel > provider )
1360+ where TPixel : unmanaged, IPixel < TPixel >
1361+ {
1362+ DrawingOptions drawingOptions = new ( )
1363+ {
1364+ GraphicsOptions = new GraphicsOptions { Antialias = false }
1365+ } ;
1366+
1367+ static void DrawRetainedScene ( DrawingCanvas canvas )
1368+ {
1369+ canvas . Fill ( Brushes . Solid ( Color . Red ) , new Rectangle ( 32 , 22 , 64 , 52 ) ) ;
1370+ canvas . Flush ( ) ;
1371+ canvas . Fill ( Brushes . Solid ( Color . Blue ) , new Rectangle ( 72 , 46 , 48 , 48 ) ) ;
1372+ }
1373+
1374+ static void DrawInlineFlow ( DrawingCanvas canvas )
1375+ {
1376+ canvas . Clear ( Brushes . Solid ( Color . White ) ) ;
1377+ canvas . Fill ( Brushes . Solid ( Color . Yellow ) , new Rectangle ( 0 , 0 , 48 , 120 ) ) ;
1378+ canvas . Fill ( Brushes . Solid ( Color . Purple ) , new Rectangle ( 16 , 16 , 24 , 24 ) ) ;
1379+
1380+ // The marker gives the pre-scene Apply barrier non-flat pixels for Invert to modify.
1381+ canvas . Apply ( new Rectangle ( 8 , 8 , 44 , 44 ) , ctx => ctx . Invert ( ) ) ;
1382+ DrawRetainedScene ( canvas ) ;
1383+ canvas . Fill ( Brushes . Solid ( Color . Black ) , new Rectangle ( 40 , 30 , 24 , 24 ) ) ;
1384+
1385+ // The inline reference keeps the same ordering without retaining the middle scene.
1386+ canvas . Apply ( new Rectangle ( 32 , 22 , 88 , 72 ) , ctx => ctx . GaussianBlur ( 6F ) ) ;
1387+ DrawRetainedScene ( canvas ) ;
1388+ canvas . Fill ( Brushes . Solid ( Color . Green ) , new Rectangle ( 88 , 58 , 44 , 28 ) ) ;
1389+ }
1390+
1391+ static void DrawRetainedFlow ( DrawingCanvas canvas , DrawingBackendScene retainedScene )
1392+ {
1393+ canvas . Clear ( Brushes . Solid ( Color . White ) ) ;
1394+ canvas . Fill ( Brushes . Solid ( Color . Yellow ) , new Rectangle ( 0 , 0 , 48 , 120 ) ) ;
1395+ canvas . Fill ( Brushes . Solid ( Color . Purple ) , new Rectangle ( 16 , 16 , 24 , 24 ) ) ;
1396+
1397+ // The retained scene must replay between barriers exactly where RenderScene records it.
1398+ canvas . Apply ( new Rectangle ( 8 , 8 , 44 , 44 ) , ctx => ctx . Invert ( ) ) ;
1399+ canvas . RenderScene ( retainedScene ) ;
1400+ canvas . Fill ( Brushes . Solid ( Color . Black ) , new Rectangle ( 40 , 30 , 24 , 24 ) ) ;
1401+ canvas . Apply ( new Rectangle ( 32 , 22 , 88 , 72 ) , ctx => ctx . GaussianBlur ( 6F ) ) ;
1402+ canvas . RenderScene ( retainedScene ) ;
1403+ canvas . Fill ( Brushes . Solid ( Color . Green ) , new Rectangle ( 88 , 58 , 44 , 28 ) ) ;
1404+ }
1405+
1406+ using Image < TPixel > defaultImage = provider . GetImage ( ) ;
1407+ defaultImage . Mutate ( c => c . Paint ( drawingOptions , DrawInlineFlow ) ) ;
1408+
1409+ using WebGPUDrawingBackend nativeSurfaceBackend = new ( ) ;
1410+ Configuration nativeConfig = Configuration . Default . Clone ( ) ;
1411+ nativeConfig . SetDrawingBackend ( nativeSurfaceBackend ) ;
1412+
1413+ using WebGPURenderTarget sceneRenderTarget = new ( defaultImage . Width , defaultImage . Height ) ;
1414+ using DrawingCanvas nativeSceneCanvas = WebGPUCanvasFactory . CreateCanvas (
1415+ nativeConfig ,
1416+ drawingOptions ,
1417+ nativeSurfaceBackend ,
1418+ sceneRenderTarget . Bounds ,
1419+ sceneRenderTarget . Surface ,
1420+ sceneRenderTarget . Format ) ;
1421+
1422+ // Create the scene through the WebGPU backend so the test covers retained encoding and replay.
1423+ DrawRetainedScene ( nativeSceneCanvas ) ;
1424+
1425+ using DrawingBackendScene nativeScene = nativeSceneCanvas . CreateScene ( ) ;
1426+ using WebGPURenderTarget renderTarget = new ( defaultImage . Width , defaultImage . Height ) ;
1427+ using ( DrawingCanvas nativeCanvas = WebGPUCanvasFactory . CreateCanvas (
1428+ nativeConfig ,
1429+ drawingOptions ,
1430+ nativeSurfaceBackend ,
1431+ renderTarget . Bounds ,
1432+ renderTarget . Surface ,
1433+ renderTarget . Format ) )
1434+ {
1435+ DrawRetainedFlow ( nativeCanvas , nativeScene ) ;
1436+ }
1437+
1438+ using Image < TPixel > nativeSurfaceImage = renderTarget . Readback < TPixel > ( ) ;
1439+ DebugSaveBackendPair ( provider , "RetainedScene_MixedWithApplyBarriers" , defaultImage , nativeSurfaceImage ) ;
1440+ AssertBackendPairSimilarity ( defaultImage , nativeSurfaceImage , 1F ) ;
1441+ AssertBackendPairReferenceOutputs ( provider , "RetainedScene_MixedWithApplyBarriers" , defaultImage , nativeSurfaceImage ) ;
1442+ }
1443+
1444+ [ WebGPUTheory ]
1445+ [ WithSolidFilledImages ( 160 , 120 , "White" , PixelTypes . Rgba32 ) ]
1446+ public void RetainedScene_WithLayerCommands_MatchesDefaultOutput < TPixel > ( TestImageProvider < TPixel > provider )
1447+ where TPixel : unmanaged, IPixel < TPixel >
1448+ {
1449+ DrawingOptions drawingOptions = new ( )
1450+ {
1451+ GraphicsOptions = new GraphicsOptions { Antialias = false }
1452+ } ;
1453+
1454+ static void DrawRetainedScene ( DrawingCanvas canvas )
1455+ {
1456+ canvas . Fill ( Brushes . Solid ( Color . Red ) , new Rectangle ( 24 , 20 , 92 , 64 ) ) ;
1457+
1458+ // Layer boundaries inside the retained scene must survive scene creation and replay.
1459+ canvas . SaveLayer (
1460+ new GraphicsOptions { BlendPercentage = 0.65F } ,
1461+ new Rectangle ( 36 , 26 , 76 , 52 ) ) ;
1462+
1463+ canvas . Fill ( Brushes . Solid ( Color . Blue ) , new Rectangle ( 54 , 34 , 76 , 46 ) ) ;
1464+ canvas . Restore ( ) ;
1465+ canvas . Fill ( Brushes . Solid ( Color . Green ) , new Rectangle ( 92 , 62 , 30 , 28 ) ) ;
1466+ }
1467+
1468+ static void DrawInlineFlow ( DrawingCanvas canvas )
1469+ {
1470+ canvas . Clear ( Brushes . Solid ( Color . White ) ) ;
1471+ canvas . Fill ( Brushes . Solid ( Color . Orange ) , new Rectangle ( 0 , 0 , 52 , 120 ) ) ;
1472+
1473+ // The default reference draws the retained-scene contents inline at the same position.
1474+ DrawRetainedScene ( canvas ) ;
1475+ canvas . Fill ( Brushes . Solid ( Color . Black ) , new Rectangle ( 122 , 8 , 16 , 96 ) ) ;
1476+ }
1477+
1478+ static void DrawRetainedFlow ( DrawingCanvas canvas , DrawingBackendScene retainedScene )
1479+ {
1480+ canvas . Clear ( Brushes . Solid ( Color . White ) ) ;
1481+ canvas . Fill ( Brushes . Solid ( Color . Orange ) , new Rectangle ( 0 , 0 , 52 , 120 ) ) ;
1482+
1483+ // RenderScene is the only difference from the inline reference above.
1484+ canvas . RenderScene ( retainedScene ) ;
1485+ canvas . Fill ( Brushes . Solid ( Color . Black ) , new Rectangle ( 122 , 8 , 16 , 96 ) ) ;
1486+ }
1487+
1488+ using Image < TPixel > defaultImage = provider . GetImage ( ) ;
1489+ defaultImage . Mutate ( c => c . Paint ( drawingOptions , DrawInlineFlow ) ) ;
1490+
1491+ using WebGPUDrawingBackend nativeSurfaceBackend = new ( ) ;
1492+ Configuration nativeConfig = Configuration . Default . Clone ( ) ;
1493+ nativeConfig . SetDrawingBackend ( nativeSurfaceBackend ) ;
1494+
1495+ using WebGPURenderTarget sceneRenderTarget = new ( defaultImage . Width , defaultImage . Height ) ;
1496+ using DrawingCanvas nativeSceneCanvas = WebGPUCanvasFactory . CreateCanvas (
1497+ nativeConfig ,
1498+ drawingOptions ,
1499+ nativeSurfaceBackend ,
1500+ sceneRenderTarget . Bounds ,
1501+ sceneRenderTarget . Surface ,
1502+ sceneRenderTarget . Format ) ;
1503+
1504+ // Create the scene through the WebGPU backend so retained layer commands are encoded.
1505+ DrawRetainedScene ( nativeSceneCanvas ) ;
1506+
1507+ using DrawingBackendScene nativeScene = nativeSceneCanvas . CreateScene ( ) ;
1508+ using WebGPURenderTarget renderTarget = new ( defaultImage . Width , defaultImage . Height ) ;
1509+ using ( DrawingCanvas nativeCanvas = WebGPUCanvasFactory . CreateCanvas (
1510+ nativeConfig ,
1511+ drawingOptions ,
1512+ nativeSurfaceBackend ,
1513+ renderTarget . Bounds ,
1514+ renderTarget . Surface ,
1515+ renderTarget . Format ) )
1516+ {
1517+ DrawRetainedFlow ( nativeCanvas , nativeScene ) ;
1518+ }
1519+
1520+ using Image < TPixel > nativeSurfaceImage = renderTarget . Readback < TPixel > ( ) ;
1521+ DebugSaveBackendPair ( provider , "RetainedScene_WithLayerCommands" , defaultImage , nativeSurfaceImage ) ;
1522+ AssertBackendPairSimilarity ( defaultImage , nativeSurfaceImage , 1F ) ;
1523+ AssertBackendPairReferenceOutputs ( provider , "RetainedScene_WithLayerCommands" , defaultImage , nativeSurfaceImage ) ;
1524+ }
1525+
13571526 [ WebGPUTheory ]
13581527 [ WithSolidFilledImages ( 256 , 256 , "White" , PixelTypes . Rgba32 ) ]
13591528 public void FillPath_WithLinearGradientBrush_MatchesDefaultOutput < TPixel > ( TestImageProvider < TPixel > provider )
0 commit comments