diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml
index 8d4639070..488d14e12 100644
--- a/.github/workflows/dotnet.yml
+++ b/.github/workflows/dotnet.yml
@@ -334,6 +334,21 @@ jobs:
msbuild samples\ComputeSharp.SwapChain.WinUI\ComputeSharp.SwapChain.WinUI.csproj /restore -t:publish
/p:Configuration=Release /p:Platform=${{matrix.platform}} /p:RuntimeIdentifier=win-${{matrix.platform}}
+ # Publish the D2D1 UWP (CoreApplication) sample app with NativeAOT
+ - if: matrix.platform == 'x64'
+ name: Publish ComputeSharp.SwapChain.D2D1.Uwp
+ run: >
+ msbuild samples\ComputeSharp.SwapChain.D2D1.Uwp\ComputeSharp.SwapChain.D2D1.Uwp.csproj /restore -t:publish
+ /p:Configuration=Release /p:Platform=${{matrix.platform}} /p:RuntimeIdentifier=win-${{matrix.platform}}
+
+ # Publish the D2D1 UWP sample app again with NativeAOT, with Xbox support
+ - if: matrix.platform == 'x64'
+ name: Publish ComputeSharp.SwapChain.D2D1.Uwp (Xbox)
+ run: >
+ $env:EXPERIMENTAL_XBOX_SUPPORT='true';
+ msbuild samples\ComputeSharp.SwapChain.D2D1.Uwp\ComputeSharp.SwapChain.D2D1.Uwp.csproj /restore -t:publish
+ /p:Configuration=Release /p:Platform=${{matrix.platform}} /p:RuntimeIdentifier=win-${{matrix.platform}}
+
# Download the NuGet packages generated in the previous job and use them
# to build and run the sample project referencing them. This is used as
# a test to ensure the NuGet packages work in a consuming project.
diff --git a/ComputeSharp.sln b/ComputeSharp.sln
index 7bc0c0502..ecce3d873 100644
--- a/ComputeSharp.sln
+++ b/ComputeSharp.sln
@@ -176,6 +176,8 @@ Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "ComputeSharp.D2D1.UI", "src
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ComputeSharp.D2D1.Uwp", "src\ComputeSharp.D2D1.Uwp\ComputeSharp.D2D1.Uwp.csproj", "{716DF19E-69BA-4A9C-9CA4-BFD196152F46}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ComputeSharp.SwapChain.D2D1.Uwp", "samples\ComputeSharp.SwapChain.D2D1.Uwp\ComputeSharp.SwapChain.D2D1.Uwp.csproj", "{9FBE070E-A210-4CEF-9F04-61C2B269C600}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@@ -488,6 +490,18 @@ Global
{716DF19E-69BA-4A9C-9CA4-BFD196152F46}.Release|ARM64.Build.0 = Release|ARM64
{716DF19E-69BA-4A9C-9CA4-BFD196152F46}.Release|x64.ActiveCfg = Release|x64
{716DF19E-69BA-4A9C-9CA4-BFD196152F46}.Release|x64.Build.0 = Release|x64
+ {9FBE070E-A210-4CEF-9F04-61C2B269C600}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {9FBE070E-A210-4CEF-9F04-61C2B269C600}.Debug|ARM64.Build.0 = Debug|ARM64
+ {9FBE070E-A210-4CEF-9F04-61C2B269C600}.Debug|ARM64.Deploy.0 = Debug|ARM64
+ {9FBE070E-A210-4CEF-9F04-61C2B269C600}.Debug|x64.ActiveCfg = Debug|x64
+ {9FBE070E-A210-4CEF-9F04-61C2B269C600}.Debug|x64.Build.0 = Debug|x64
+ {9FBE070E-A210-4CEF-9F04-61C2B269C600}.Debug|x64.Deploy.0 = Debug|x64
+ {9FBE070E-A210-4CEF-9F04-61C2B269C600}.Release|ARM64.ActiveCfg = Release|ARM64
+ {9FBE070E-A210-4CEF-9F04-61C2B269C600}.Release|ARM64.Build.0 = Release|ARM64
+ {9FBE070E-A210-4CEF-9F04-61C2B269C600}.Release|ARM64.Deploy.0 = Release|ARM64
+ {9FBE070E-A210-4CEF-9F04-61C2B269C600}.Release|x64.ActiveCfg = Release|x64
+ {9FBE070E-A210-4CEF-9F04-61C2B269C600}.Release|x64.Build.0 = Release|x64
+ {9FBE070E-A210-4CEF-9F04-61C2B269C600}.Release|x64.Deploy.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -527,6 +541,7 @@ Global
{B4F6A41C-AA9A-4302-92B3-AC64CF2E30F5} = {A5013F9B-92EC-419F-B0B3-3D6B721E7104}
{1A3E20B3-F711-41C0-82EA-3B53A575955C} = {0ED8F632-5E17-46BE-8CC3-B14A82D4AEB1}
{551EF4FB-F34F-412A-B3E6-E345797560ED} = {0ED8F632-5E17-46BE-8CC3-B14A82D4AEB1}
+ {9FBE070E-A210-4CEF-9F04-61C2B269C600} = {0ED8F632-5E17-46BE-8CC3-B14A82D4AEB1}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {4664C5E3-0340-4E22-BCFD-98AAEDF5F2DC}
@@ -550,6 +565,7 @@ Global
src\ComputeSharp.CodeFixing\ComputeSharp.CodeFixing.projitems*{9b4448b1-200f-4966-8a13-a508691b3003}*SharedItemsImports = 5
src\ComputeSharp.Win32.D2D1\ComputeSharp.Win32.D2D1.projitems*{9da1da9f-f8b2-4b25-be80-c21f773029e3}*SharedItemsImports = 13
samples\ComputeSharp.SwapChain.Shaders.D2D1\ComputeSharp.SwapChain.Shaders.D2D1.projitems*{9ea5ae9d-c39a-4f43-b03e-0a848ea2558a}*SharedItemsImports = 5
+ samples\ComputeSharp.SwapChain.Shaders.D2D1\ComputeSharp.SwapChain.Shaders.D2D1.projitems*{9fbe070e-a210-4cef-9f04-61c2b269c600}*SharedItemsImports = 5
src\ComputeSharp.D2D1.UI\ComputeSharp.D2D1.UI.projitems*{a2a2171b-0baf-4a2a-bfb3-3357ef714bf0}*SharedItemsImports = 13
src\ComputeSharp.D2D1.UI\ComputeSharp.D2D1.UI.projitems*{bd9e6556-357e-4c20-bfcd-fb131f9372fa}*SharedItemsImports = 5
samples\ComputeSharp.SwapChain.Shaders\ComputeSharp.SwapChain.Shaders.projitems*{c12d7ace-98ed-4813-8118-6667c34f484f}*SharedItemsImports = 5
diff --git a/samples/ComputeSharp.SwapChain.D2D1.Cli/Program.cs b/samples/ComputeSharp.SwapChain.D2D1.Cli/Program.cs
index fddaba8f4..629eea497 100644
--- a/samples/ComputeSharp.SwapChain.D2D1.Cli/Program.cs
+++ b/samples/ComputeSharp.SwapChain.D2D1.Cli/Program.cs
@@ -33,9 +33,9 @@
// If a shader is found, run it
if (effect is not null)
{
- Win32Application win32Application = new();
+ Win32Application application = new();
- win32Application.Draw += (_, e) =>
+ application.Draw += (_, e) =>
{
// Set the effect properties
effect.ElapsedTime = e.TotalTime;
@@ -46,7 +46,7 @@
e.DrawingSession.DrawImage(effect);
};
- Win32ApplicationRunner.Run(win32Application, "ComputeSharp.D2D1");
+ Win32ApplicationRunner.Run(application, "ComputeSharp.D2D1");
return S.S_OK;
}
diff --git a/samples/ComputeSharp.SwapChain.D2D1.Uwp/App.cs b/samples/ComputeSharp.SwapChain.D2D1.Uwp/App.cs
new file mode 100644
index 000000000..1b3ffd118
--- /dev/null
+++ b/samples/ComputeSharp.SwapChain.D2D1.Uwp/App.cs
@@ -0,0 +1,120 @@
+using System.Diagnostics.CodeAnalysis;
+using ComputeSharp.SwapChain.D2D1.Backend;
+using ComputeSharp.SwapChain.D2D1.Extensions;
+using ComputeSharp.SwapChain.Shaders.D2D1;
+using Windows.ApplicationModel.Core;
+using Windows.UI.Core;
+using Windows.UI.ViewManagement;
+
+namespace ComputeSharp.SwapChain.D2D1;
+
+///
+/// A sample app rendering D2D shaders into a instance.
+///
+public sealed partial class App : IFrameworkViewSource, IFrameworkView
+{
+ ///
+ /// The collection of available pixel shader effects.
+ ///
+ private static readonly PixelShaderEffect[] Effects =
+ [
+ new PixelShaderEffect.For(static (time, width, height) => new((float)time.TotalSeconds, new int2(width, height))),
+ new PixelShaderEffect.For(static (time, width, height) => new((float)time.TotalSeconds, new int2(width, height))),
+ new PixelShaderEffect.For(static (time, width, height) => new((float)time.TotalSeconds, new int2(width, height))),
+ new PixelShaderEffect.For(static (time, width, height) => new((float)time.TotalSeconds, new int2(width, height))),
+ new PixelShaderEffect.For(static (time, width, height) => new((float)time.TotalSeconds, new int2(width, height))),
+ new PixelShaderEffect.For(static (time, width, height) => new((float)time.TotalSeconds, new int2(width, height))),
+ new PixelShaderEffect.For(static (time, width, height) => new((float)time.TotalSeconds, new int2(width, height))),
+ new PixelShaderEffect.For(static (time, width, height) => new((float)time.TotalSeconds, new int2(width, height))),
+ new PixelShaderEffect.For(static (time, width, height) => new((float)time.TotalSeconds, new int2(width, height)))
+ ];
+
+ ///
+ /// The for the current app instance.
+ ///
+ private CoreApplicationView? applicationView;
+
+ ///
+ /// The used to display the app content.
+ ///
+ private CoreWindow? window;
+
+ ///
+ /// The currently selected effect (one of the items in ).
+ ///
+ private PixelShaderEffect selectedEffect = Effects[0];
+
+ ///
+ /// The entry point for the application.
+ ///
+ public static void Main()
+ {
+ CoreApplication.Run(new App());
+ }
+
+ ///
+ public IFrameworkView CreateView()
+ {
+ return this;
+ }
+
+ ///
+ [MemberNotNull(nameof(applicationView))]
+ public void Initialize(CoreApplicationView applicationView)
+ {
+ this.applicationView = applicationView;
+ }
+
+ ///
+ [MemberNotNull(nameof(window))]
+ public void SetWindow(CoreWindow window)
+ {
+ this.window = window;
+ }
+
+ ///
+ public void Load(string entryPoint)
+ {
+ }
+
+ ///
+ public void Run()
+ {
+ this.applicationView!.TitleBar.ExtendViewIntoTitleBar = true;
+
+ ApplicationView.GetForCurrentView().TitleBar.StyleTitleBarForExtendedIntoViewMode();
+
+ // Switch the shader to use when pressing the number keys
+ this.window!.CharacterReceived += (s, e) =>
+ {
+ if (e.KeyCode is >= '1' and <= '9')
+ {
+ this.selectedEffect = Effects[(int)(e.KeyCode - '1')];
+
+ e.Handled = true;
+ }
+ };
+
+ CoreWindowApplication application = new();
+
+ application.Draw += (s, e) =>
+ {
+ PixelShaderEffect effect = this.selectedEffect;
+
+ // Set the effect properties
+ effect.ElapsedTime = e.TotalTime;
+ effect.ScreenWidth = (int)e.ScreenWidth;
+ effect.ScreenHeight = (int)e.ScreenHeight;
+
+ // Draw the effect
+ e.DrawingSession.DrawImage(effect);
+ };
+
+ CoreWindowApplicationRunner.Run(application, this.window);
+ }
+
+ ///
+ public void Uninitialize()
+ {
+ }
+}
diff --git a/samples/ComputeSharp.SwapChain.D2D1.Uwp/Assets/LockScreenLogo.scale-200.png b/samples/ComputeSharp.SwapChain.D2D1.Uwp/Assets/LockScreenLogo.scale-200.png
new file mode 100644
index 000000000..7440f0d4b
Binary files /dev/null and b/samples/ComputeSharp.SwapChain.D2D1.Uwp/Assets/LockScreenLogo.scale-200.png differ
diff --git a/samples/ComputeSharp.SwapChain.D2D1.Uwp/Assets/SplashScreen.scale-200.png b/samples/ComputeSharp.SwapChain.D2D1.Uwp/Assets/SplashScreen.scale-200.png
new file mode 100644
index 000000000..1df2cb624
Binary files /dev/null and b/samples/ComputeSharp.SwapChain.D2D1.Uwp/Assets/SplashScreen.scale-200.png differ
diff --git a/samples/ComputeSharp.SwapChain.D2D1.Uwp/Assets/Square150x150Logo.scale-200.png b/samples/ComputeSharp.SwapChain.D2D1.Uwp/Assets/Square150x150Logo.scale-200.png
new file mode 100644
index 000000000..84faf5ffb
Binary files /dev/null and b/samples/ComputeSharp.SwapChain.D2D1.Uwp/Assets/Square150x150Logo.scale-200.png differ
diff --git a/samples/ComputeSharp.SwapChain.D2D1.Uwp/Assets/Square44x44Logo.scale-200.png b/samples/ComputeSharp.SwapChain.D2D1.Uwp/Assets/Square44x44Logo.scale-200.png
new file mode 100644
index 000000000..f02661b2f
Binary files /dev/null and b/samples/ComputeSharp.SwapChain.D2D1.Uwp/Assets/Square44x44Logo.scale-200.png differ
diff --git a/samples/ComputeSharp.SwapChain.D2D1.Uwp/Assets/Square44x44Logo.targetsize-24_altform-unplated.png b/samples/ComputeSharp.SwapChain.D2D1.Uwp/Assets/Square44x44Logo.targetsize-24_altform-unplated.png
new file mode 100644
index 000000000..3e58e5b69
Binary files /dev/null and b/samples/ComputeSharp.SwapChain.D2D1.Uwp/Assets/Square44x44Logo.targetsize-24_altform-unplated.png differ
diff --git a/samples/ComputeSharp.SwapChain.D2D1.Uwp/Assets/StoreLogo.scale-200.png b/samples/ComputeSharp.SwapChain.D2D1.Uwp/Assets/StoreLogo.scale-200.png
new file mode 100644
index 000000000..7bc22428e
Binary files /dev/null and b/samples/ComputeSharp.SwapChain.D2D1.Uwp/Assets/StoreLogo.scale-200.png differ
diff --git a/samples/ComputeSharp.SwapChain.D2D1.Uwp/Assets/Wide310x150Logo.scale-200.png b/samples/ComputeSharp.SwapChain.D2D1.Uwp/Assets/Wide310x150Logo.scale-200.png
new file mode 100644
index 000000000..d800c4a0e
Binary files /dev/null and b/samples/ComputeSharp.SwapChain.D2D1.Uwp/Assets/Wide310x150Logo.scale-200.png differ
diff --git a/samples/ComputeSharp.SwapChain.D2D1.Uwp/Backend/CoreWindowApplication.cs b/samples/ComputeSharp.SwapChain.D2D1.Uwp/Backend/CoreWindowApplication.cs
new file mode 100644
index 000000000..4640c165c
--- /dev/null
+++ b/samples/ComputeSharp.SwapChain.D2D1.Uwp/Backend/CoreWindowApplication.cs
@@ -0,0 +1,142 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
+using Microsoft.Graphics.Canvas;
+using Windows.Graphics.Display;
+using Windows.Graphics.Imaging;
+using Windows.UI.Core;
+
+namespace ComputeSharp.SwapChain.D2D1.Backend;
+
+///
+/// A simple application handling a D2D swapchain.
+///
+internal sealed class CoreWindowApplication
+{
+ ///
+ /// The instance to use.
+ ///
+ private CanvasDevice? canvasDevice;
+
+ ///
+ /// The instance to use to render frames.
+ ///
+ private CanvasSwapChain? canvasSwapChain;
+
+ ///
+ /// Whether or not the window has been resized and requires the buffers to be updated.
+ ///
+ private volatile bool isResizePending;
+
+ ///
+ /// The current screen width in raw pixels.
+ ///
+ private uint screenWidth;
+
+ ///
+ /// The current screen height in raw pixels.
+ ///
+ private uint screenHeight;
+
+ ///
+ /// Raised whenever a draw operation can be performed.
+ ///
+ public event EventHandler? Draw;
+
+ ///
+ /// Initializes the current application.
+ ///
+ /// The instance.
+ [MemberNotNull(nameof(canvasDevice))]
+ [MemberNotNull(nameof(canvasSwapChain))]
+ public unsafe void OnInitialize(CoreWindow window)
+ {
+ // Create a new canvas device, which will handle DX11/D2D initialization
+ CanvasDevice canvasDevice = new();
+
+ // Create the swapchain for rendering (it is automatically tied to the window)
+ CanvasSwapChain canvasSwapChain = CanvasSwapChain.CreateForCoreWindow(
+ resourceCreator: canvasDevice,
+ coreWindow: window,
+ dpi: DisplayInformation.GetForCurrentView().LogicalDpi);
+
+ // Save the Win2D objects for later use
+ this.canvasDevice = canvasDevice;
+ this.canvasSwapChain = canvasSwapChain;
+
+ // Store the initial size of the screen
+ this.screenWidth = canvasSwapChain.SizeInPixels.Width;
+ this.screenHeight = canvasSwapChain.SizeInPixels.Height;
+ }
+
+ ///
+ /// Resizes the current application.
+ ///
+ public void OnResize()
+ {
+ this.isResizePending = true;
+ }
+
+ ///
+ /// Updates the current application.
+ ///
+ /// The current time since the start of the application.
+ public void OnUpdate(TimeSpan time)
+ {
+ if (this.isResizePending)
+ {
+ // Resize the swapchain if needed (the size is calculated automatically)
+ this.canvasSwapChain!.ResizeBuffers(0, 0);
+
+ BitmapSize bitmapSize = this.canvasSwapChain!.SizeInPixels;
+
+ this.screenWidth = bitmapSize.Width;
+ this.screenHeight = bitmapSize.Height;
+
+ this.isResizePending = false;
+ }
+
+ // Create the drawing session and invoke all registered draw handler
+ using (CanvasDrawingSession canvasDrawingSession = this.canvasSwapChain!.CreateDrawingSession(default))
+ {
+ Draw?.Invoke(this, new DrawEventArgs
+ {
+ ScreenWidth = this.screenWidth,
+ ScreenHeight = this.screenHeight,
+ TotalTime = time,
+ DrawingSession = canvasDrawingSession
+ });
+ }
+
+ // Wait for v-sync
+ this.canvasSwapChain.WaitForVerticalBlank();
+
+ // Present the new frame
+ this.canvasSwapChain.Present(syncInterval: 1);
+ }
+
+ ///
+ /// Arguments for .
+ ///
+ public sealed class DrawEventArgs : EventArgs
+ {
+ ///
+ /// Gets the screen width in raw pixels.
+ ///
+ public required uint ScreenWidth { get; init; }
+
+ ///
+ /// Gets the screen height in raw pixels.
+ ///
+ public required uint ScreenHeight { get; init; }
+
+ ///
+ /// Gets the total time for the rendering loop.
+ ///
+ public required TimeSpan TotalTime { get; init; }
+
+ ///
+ /// Gets the instance to use.
+ ///
+ public required CanvasDrawingSession DrawingSession { get; init; }
+ }
+}
\ No newline at end of file
diff --git a/samples/ComputeSharp.SwapChain.D2D1.Uwp/Backend/CoreWindowApplicationRunner.cs b/samples/ComputeSharp.SwapChain.D2D1.Uwp/Backend/CoreWindowApplicationRunner.cs
new file mode 100644
index 000000000..4d1a868d9
--- /dev/null
+++ b/samples/ComputeSharp.SwapChain.D2D1.Uwp/Backend/CoreWindowApplicationRunner.cs
@@ -0,0 +1,56 @@
+using System.Diagnostics;
+using System.Threading;
+using Windows.UI.Core;
+
+namespace ComputeSharp.SwapChain.D2D1.Backend;
+
+///
+/// A helper class to manage the creation and execution of applications.
+///
+internal static class CoreWindowApplicationRunner
+{
+ ///
+ /// Runs a specified application and starts the main loop to update its state. This is the entry point for a given application,
+ /// and it should be called as soon as the process is launched, excluding any other additional initialization needed.
+ ///
+ /// The input application instance to run.
+ /// The instance.
+ public static void Run(CoreWindowApplication application, CoreWindow window)
+ {
+ // Initialize the application
+ application.OnInitialize(window);
+
+ // Setup the render thread that enables smooth resizing of the window
+ Thread renderThread = new(static args =>
+ {
+ (CoreWindowApplication application, CancellationToken token) = ((CoreWindowApplication, CancellationToken))args!;
+
+ Stopwatch stopwatch = Stopwatch.StartNew();
+
+ while (!token.IsCancellationRequested)
+ {
+ application.OnUpdate(stopwatch.Elapsed);
+ }
+ });
+
+ CancellationTokenSource tokenSource = new();
+
+ // Start the render thread before activating the window.
+ // This allows the app to show up while already drawing.
+ renderThread.Start((application, tokenSource.Token));
+
+ // Register a callback to monitor resize operations
+ window.ResizeCompleted += (s, e) => application.OnResize();
+
+ // Activate the window
+ window.Activate();
+
+ // Process any messages in the queue
+ window.Dispatcher.ProcessEvents(CoreProcessEventsOption.ProcessUntilQuit);
+
+ tokenSource.Cancel();
+
+ // Wait for the render thread to stop before terminating
+ renderThread.Join();
+ }
+}
\ No newline at end of file
diff --git a/samples/ComputeSharp.SwapChain.D2D1.Uwp/Backend/PixelShaderEffect.cs b/samples/ComputeSharp.SwapChain.D2D1.Uwp/Backend/PixelShaderEffect.cs
new file mode 100644
index 000000000..dd97480fc
--- /dev/null
+++ b/samples/ComputeSharp.SwapChain.D2D1.Uwp/Backend/PixelShaderEffect.cs
@@ -0,0 +1,89 @@
+using System;
+using ComputeSharp.D2D1;
+using ComputeSharp.D2D1.Descriptors;
+using ComputeSharp.D2D1.Uwp;
+
+namespace ComputeSharp.SwapChain.D2D1.Backend;
+
+///
+/// An base effect for an animated pixel shader.
+///
+internal abstract partial class PixelShaderEffect : CanvasEffect
+{
+ ///
+ /// The current elapsed time.
+ ///
+ private TimeSpan elapsedTime;
+
+ ///
+ /// The current screen width in raw pixels.
+ ///
+ private int screenWidth;
+
+ ///
+ /// The current screen height in raw pixels.
+ ///
+ private int screenHeight;
+
+ ///
+ /// Gets or sets the total elapsed time.
+ ///
+ public TimeSpan ElapsedTime
+ {
+ get => this.elapsedTime;
+ set => SetAndInvalidateEffectGraph(ref this.elapsedTime, value);
+ }
+
+ ///
+ /// Gets or sets the screen width in raw pixels.
+ ///
+ public int ScreenWidth
+ {
+ get => this.screenWidth;
+ set => SetAndInvalidateEffectGraph(ref this.screenWidth, value);
+ }
+
+ ///
+ /// Gets or sets the screen height in raw pixels.
+ ///
+ public int ScreenHeight
+ {
+ get => this.screenHeight;
+ set => SetAndInvalidateEffectGraph(ref this.screenHeight, value);
+ }
+
+ ///
+ /// An effect for an animated pixel shader.
+ ///
+ /// The type of pixel shader to render.
+ /// The input factory.
+ public sealed partial class For(For.Factory factory) : PixelShaderEffect
+ where T : unmanaged, ID2D1PixelShader, ID2D1PixelShaderDescriptor
+ {
+ ///
+ /// The node in use.
+ ///
+ private static readonly CanvasEffectNode> Effect = new();
+
+ ///
+ protected override void BuildEffectGraph(CanvasEffectGraph effectGraph)
+ {
+ effectGraph.RegisterOutputNode(Effect, new PixelShaderEffect());
+ }
+
+ ///
+ protected override void ConfigureEffectGraph(CanvasEffectGraph effectGraph)
+ {
+ effectGraph.GetNode(Effect).ConstantBuffer = factory(ElapsedTime, ScreenWidth, ScreenHeight);
+ }
+
+ ///
+ /// A factory of a given shader instance.
+ ///
+ /// The total elapsed time.
+ /// The screen width in raw pixels.
+ /// The screen height in raw pixels.
+ /// A shader instance to render.
+ public delegate T Factory(TimeSpan elapsedTime, int screenWidth, int screenHeight);
+ }
+}
\ No newline at end of file
diff --git a/samples/ComputeSharp.SwapChain.D2D1.Uwp/ComputeSharp.SwapChain.D2D1.Uwp.csproj b/samples/ComputeSharp.SwapChain.D2D1.Uwp/ComputeSharp.SwapChain.D2D1.Uwp.csproj
new file mode 100644
index 000000000..71cbf95ef
--- /dev/null
+++ b/samples/ComputeSharp.SwapChain.D2D1.Uwp/ComputeSharp.SwapChain.D2D1.Uwp.csproj
@@ -0,0 +1,59 @@
+
+
+ WinExe
+ net8.0-windows10.0.22621.0
+ 10.0.17763.0
+ 10.0.22621.45
+ true
+ true
+ x64;arm64
+ win-x64;win-arm64
+ en-US
+ win-$(Platform).pubxml
+ true
+ false
+
+
+
+
+ true
+ true
+ true
+ false
+ Speed
+ false
+ true
+ false
+ false
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/samples/ComputeSharp.SwapChain.D2D1.Uwp/Extensions/ApplicationViewTitleBarExtensions.cs b/samples/ComputeSharp.SwapChain.D2D1.Uwp/Extensions/ApplicationViewTitleBarExtensions.cs
new file mode 100644
index 000000000..5e8325b14
--- /dev/null
+++ b/samples/ComputeSharp.SwapChain.D2D1.Uwp/Extensions/ApplicationViewTitleBarExtensions.cs
@@ -0,0 +1,33 @@
+using Windows.UI;
+using Windows.UI.ViewManagement;
+
+namespace ComputeSharp.SwapChain.D2D1.Extensions;
+
+///
+/// Extensions for .
+///
+internal static class ApplicationViewTitleBarExtensions
+{
+ ///
+ /// Styles the title bar to look correctly when the view is expanded into it.
+ ///
+ /// The input title bar.
+ public static void StyleTitleBarForExtendedIntoViewMode(this ApplicationViewTitleBar titleBar)
+ {
+ // Transparent colors
+ titleBar.ForegroundColor = Colors.Transparent;
+ titleBar.BackgroundColor = Colors.Transparent;
+ titleBar.ButtonBackgroundColor = Colors.Transparent;
+ titleBar.InactiveBackgroundColor = Colors.Transparent;
+ titleBar.ButtonInactiveBackgroundColor = Colors.Transparent;
+
+ // Theme aware colors
+ titleBar.ButtonForegroundColor = Colors.White;
+ titleBar.ButtonHoverForegroundColor = Colors.White;
+ titleBar.ButtonPressedForegroundColor = Colors.White;
+ titleBar.ButtonHoverBackgroundColor = Color.FromArgb(0x20, 0xFF, 0xFF, 0xFF);
+ titleBar.ButtonPressedBackgroundColor = Color.FromArgb(0x40, 0xFF, 0xFF, 0xFF);
+ titleBar.ButtonInactiveForegroundColor = Color.FromArgb(0xC0, 0xFF, 0xFF, 0xFF);
+ titleBar.InactiveForegroundColor = Color.FromArgb(0xA0, 0xA0, 0xA0, 0xA0);
+ }
+}
diff --git a/samples/ComputeSharp.SwapChain.D2D1.Uwp/Package.appxmanifest b/samples/ComputeSharp.SwapChain.D2D1.Uwp/Package.appxmanifest
new file mode 100644
index 000000000..71dd299ac
--- /dev/null
+++ b/samples/ComputeSharp.SwapChain.D2D1.Uwp/Package.appxmanifest
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+ ComputeSharp Sample App (Core)
+ Sergio Pedri
+ Assets\StoreLogo.png
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/ComputeSharp.SwapChain.D2D1.Uwp/Properties/PublishProfiles/win-arm64.pubxml b/samples/ComputeSharp.SwapChain.D2D1.Uwp/Properties/PublishProfiles/win-arm64.pubxml
new file mode 100644
index 000000000..5de0b991a
--- /dev/null
+++ b/samples/ComputeSharp.SwapChain.D2D1.Uwp/Properties/PublishProfiles/win-arm64.pubxml
@@ -0,0 +1,11 @@
+
+
+
+ FileSystem
+ ARM64
+ win-arm64
+ bin\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\publish\
+ true
+ true
+
+
\ No newline at end of file
diff --git a/samples/ComputeSharp.SwapChain.D2D1.Uwp/Properties/PublishProfiles/win-x64.pubxml b/samples/ComputeSharp.SwapChain.D2D1.Uwp/Properties/PublishProfiles/win-x64.pubxml
new file mode 100644
index 000000000..89844a876
--- /dev/null
+++ b/samples/ComputeSharp.SwapChain.D2D1.Uwp/Properties/PublishProfiles/win-x64.pubxml
@@ -0,0 +1,11 @@
+
+
+
+ FileSystem
+ x64
+ win-x64
+ bin\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\publish\
+ true
+ true
+
+
\ No newline at end of file
diff --git a/samples/ComputeSharp.SwapChain.D2D1.Uwp/Properties/PublishProfiles/win-x86.pubxml b/samples/ComputeSharp.SwapChain.D2D1.Uwp/Properties/PublishProfiles/win-x86.pubxml
new file mode 100644
index 000000000..2a99a7623
--- /dev/null
+++ b/samples/ComputeSharp.SwapChain.D2D1.Uwp/Properties/PublishProfiles/win-x86.pubxml
@@ -0,0 +1,11 @@
+
+
+
+ FileSystem
+ x86
+ win-x86
+ bin\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\publish\
+ true
+ true
+
+
\ No newline at end of file
diff --git a/samples/ComputeSharp.SwapChain.D2D1.Uwp/Properties/launchSettings.json b/samples/ComputeSharp.SwapChain.D2D1.Uwp/Properties/launchSettings.json
new file mode 100644
index 000000000..2dcd9bf63
--- /dev/null
+++ b/samples/ComputeSharp.SwapChain.D2D1.Uwp/Properties/launchSettings.json
@@ -0,0 +1,7 @@
+{
+ "profiles": {
+ "ComputeSharp.SwapChain.D2D1.Uwp": {
+ "commandName": "MsixPackage"
+ }
+ }
+}
\ No newline at end of file
diff --git a/samples/ComputeSharp.SwapChain.WinUI/Package.appxmanifest b/samples/ComputeSharp.SwapChain.WinUI/Package.appxmanifest
index c6ec9a28b..a5b965f71 100644
--- a/samples/ComputeSharp.SwapChain.WinUI/Package.appxmanifest
+++ b/samples/ComputeSharp.SwapChain.WinUI/Package.appxmanifest
@@ -17,8 +17,7 @@
-
-
+
@@ -35,8 +34,8 @@
BackgroundColor="#005491"
Square150x150Logo="Assets\Square150x150Logo.png"
Square44x44Logo="Assets\Square44x44Logo.png">
-
-
+
+
diff --git a/samples/ComputeSharp.SwapChain.WinUI/Properties/PublishProfiles/win-arm64.pubxml b/samples/ComputeSharp.SwapChain.WinUI/Properties/PublishProfiles/win-arm64.pubxml
index 0593a12d8..1c02b7f79 100644
--- a/samples/ComputeSharp.SwapChain.WinUI/Properties/PublishProfiles/win-arm64.pubxml
+++ b/samples/ComputeSharp.SwapChain.WinUI/Properties/PublishProfiles/win-arm64.pubxml
@@ -1,5 +1,4 @@
-
FileSystem
diff --git a/samples/ComputeSharp.SwapChain.WinUI/Properties/PublishProfiles/win-x64.pubxml b/samples/ComputeSharp.SwapChain.WinUI/Properties/PublishProfiles/win-x64.pubxml
index bb66783c0..89844a876 100644
--- a/samples/ComputeSharp.SwapChain.WinUI/Properties/PublishProfiles/win-x64.pubxml
+++ b/samples/ComputeSharp.SwapChain.WinUI/Properties/PublishProfiles/win-x64.pubxml
@@ -1,5 +1,4 @@
-
FileSystem