Skip to content

Commit 24c7c6e

Browse files
Add retain regression tests
1 parent 78d680d commit 24c7c6e

5 files changed

Lines changed: 181 additions & 0 deletions

tests/ImageSharp.Drawing.Tests/Processing/Backends/WebGPUDrawingBackendTests.cs

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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)
Loading
Loading
Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 3 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)