Skip to content

Unlike UWP, WindowsAppSdk does not support HDR composition #6291

@benstevens48

Description

@benstevens48

Describe the bug

In UWP, I can create a CompositionDrawingSurface with pixel format DirectXPixelFormat.R16G16B16A16Float and it then follows the rules of system composition for HDR: if Windows HDR display mode is enabled, then the content is passed to the system compositor, which uses a floating point format without scaling, and can display values outside [0,1]. Any SDR content rendered to the drawing surface should be scaled by the app by (the display's SDR white level) / 80 (in my case this is (240 / 80). However, it seems this is not supported in WindowsAppSDK. It seems the surface just undergoes a simple scRGB to sRGB color transform (basically just gamma correction) then is rendered as SDR. I am wondering if this is because WindowsAppSDK is composing to an 8-bit swap chain?

Getting this supported is critical for me to migrate my photo-viewing app. In HDR mode, WindowsAppSDK should use a 16-bit floating point swap chain. SDR content should be scaled by (the display's SDR white level) / 80 when rendered to it and 16-bit composition surfaces should be rendered unchanged.

Steps to reproduce the bug

The following outputs look identical in HDR mode, when in fact the HDR-rendered version should (a) produce a brighter than SDR brightest rectangle and (b) otherwise be darker, assuming the display's SDR white level is set to > 80 nits, because the code below doesn't allow for the display's SDR white level.

 public sealed partial class MainWindow : Window {
     public MainWindow() {
         InitializeComponent();
     }

     Size surfaceSize = new Size(200, 50);

     private void renderHdrButton_Click(object sender, RoutedEventArgs e) {
         var rawPixelsPerViewPixel = renderHdrButton.XamlRoot.RasterizationScale;
         try {
             var pixelSize = new Size(Math.Round(surfaceSize.Width * rawPixelsPerViewPixel), Math.Round(surfaceSize.Height * rawPixelsPerViewPixel));
             var compositor = this.Compositor;
             var device = CanvasDevice.GetSharedDevice();
             var compositionDevice = CanvasComposition.CreateCompositionGraphicsDevice(compositor, device);
             var drawingVisualSurface = compositionDevice.CreateDrawingSurface(pixelSize, Microsoft.Graphics.DirectX.DirectXPixelFormat.R16G16B16A16Float, Microsoft.Graphics.DirectX.DirectXAlphaMode.Premultiplied);
             var drawingVisualSurfaceBrush = compositor.CreateSurfaceBrush(drawingVisualSurface);
             drawingVisualSurfaceBrush.HorizontalAlignmentRatio = 0;
             drawingVisualSurfaceBrush.VerticalAlignmentRatio = 0;
             drawingVisualSurfaceBrush.Stretch = CompositionStretch.None;
             drawingVisualSurfaceBrush.Scale = new Vector2((float)(1.0 / rawPixelsPerViewPixel), (float)(1.0 / rawPixelsPerViewPixel));
             var drawingVisualSprite = compositor.CreateSpriteVisual();
             drawingVisualSprite.Size = surfaceSize.ToVector2();
             drawingVisualSprite.Brush = drawingVisualSurfaceBrush;
             ElementCompositionPreview.SetElementChildVisual(drawingVisualContainer, drawingVisualSprite);
             using (var ds = CanvasComposition.CreateDrawingSession(drawingVisualSurface)) {
                 ds.Clear(new Vector4(0, 0, 0, 1));
                 var correctedVal = (float)Math.Pow(0.5, 2.2); //Correct for gamma
                 ds.FillRectangle(new Rect(0, 0, 50, 50), CanvasSolidColorBrush.CreateHdr(device, new Vector4(correctedVal, correctedVal, correctedVal, 1)));
                 ds.FillRectangle(new Rect(50, 0, 50, 50), CanvasSolidColorBrush.CreateHdr(device, new Vector4(1, 1, 1, 1)));
                 ds.FillRectangle(new Rect(100,0, 50,50), CanvasSolidColorBrush.CreateHdr(device, new Vector4(5, 5, 5, 1)));
             }
         } catch (Exception) {
         }
     }

     private void renderSdrButton_Click(object sender, RoutedEventArgs e) {
         var rawPixelsPerViewPixel = renderHdrButton.XamlRoot.RasterizationScale;
         try {
             var pixelSize = new Size(Math.Round(surfaceSize.Width * rawPixelsPerViewPixel), Math.Round(surfaceSize.Height * rawPixelsPerViewPixel));
             var compositor = this.Compositor;
             var device = CanvasDevice.GetSharedDevice();
             var compositionDevice = CanvasComposition.CreateCompositionGraphicsDevice(compositor, device);
             var drawingVisualSurface = compositionDevice.CreateDrawingSurface(pixelSize, Microsoft.Graphics.DirectX.DirectXPixelFormat.B8G8R8A8UIntNormalized, Microsoft.Graphics.DirectX.DirectXAlphaMode.Premultiplied);
             var drawingVisualSurfaceBrush = compositor.CreateSurfaceBrush(drawingVisualSurface);
             drawingVisualSurfaceBrush.HorizontalAlignmentRatio = 0;
             drawingVisualSurfaceBrush.VerticalAlignmentRatio = 0;
             drawingVisualSurfaceBrush.Stretch = CompositionStretch.None;
             drawingVisualSurfaceBrush.Scale = new Vector2((float)(1.0 / rawPixelsPerViewPixel), (float)(1.0 / rawPixelsPerViewPixel));
             var drawingVisualSprite = compositor.CreateSpriteVisual();
             drawingVisualSprite.Size = surfaceSize.ToVector2();
             drawingVisualSprite.Brush = drawingVisualSurfaceBrush;
             ElementCompositionPreview.SetElementChildVisual(drawingVisualContainerSdr, drawingVisualSprite);
             using (var ds = CanvasComposition.CreateDrawingSession(drawingVisualSurface)) {
                 ds.Clear(new Vector4(0, 0, 0, 1));
                 ds.FillRectangle(new Rect(0, 0, 50, 50), CanvasSolidColorBrush.CreateHdr(device, new Vector4(0.5f, 0.5f, 0.5f, 1)));
                 ds.FillRectangle(new Rect(50, 0, 50, 50), CanvasSolidColorBrush.CreateHdr(device, new Vector4(1, 1, 1, 1)));
                 ds.FillRectangle(new Rect(100, 0, 50, 50), CanvasSolidColorBrush.CreateHdr(device, new Vector4(5, 5, 5, 1)));
             }
         } catch (Exception) {
         }
     }
 }

Expected behavior

No response

Screenshots

No response

NuGet package version

Windows App SDK 1.8.5: 1.8.260209005

Packaging type

Packaged (MSIX)

Windows version

Windows 11 version 24H2 (26100, June 2025 Update)

IDE

No response

Additional context

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions