Skip to content

Commit 56a3351

Browse files
authored
Port Win2D docs and add new custom effects docs (#3605)
* Update index to include UWP * Add more UWP mentions and update tags * Add custom effects docs * Move Win2D docs images to subfolder * Port interop docs https://microsoft.github.io/Win2D/WinUI3/html/Interop.htm * Port offscreen rendering docs https://microsoft.github.io/Win2D/WinUI3/html/Offscreen.htm * Port DPI and DIPs docs https://microsoft.github.io/Win2D/WinUI3/html/DPI.htm * Port choosing control resolution docs https://microsoft.github.io/Win2D/WinUI3/html/ChoosingResolution.htm * Port pixel formats docs https://microsoft.github.io/Win2D/WinUI2/html/PixelFormats.htm * Port premultiplied alpha docs https://microsoft.github.io/Win2D/WinUI2/html/PremultipliedAlpha.htm * Port bitmap block compression docs https://microsoft.github.io/Win2D/WinUI2/html/BlockCompression.htm * Port effect precision and clamping docs https://microsoft.github.io/Win2D/WinUI2/html/EffectPrecision.htm * Port handling device lost docs https://microsoft.github.io/Win2D/WinUI2/html/HandlingDeviceLost.htm * Port using Win2D without built-in controls docs https://microsoft.github.io/Win2D/WinUI2/html/WithoutControls.htm * Port the loading resources manually docs https://microsoft.github.io/Win2D/WinUI2/html/LoadingResourcesOutsideCreateResources.htm * Port the avoiding memory leaks docs https://microsoft.github.io/Win2D/WinUI2/html/RefCycles.htm * Update hello world docs * Port quick start docs https://microsoft.github.io/Win2D/WinUI2/html/QuickStart.htm * Port features list docs https://microsoft.github.io/Win2D/WinUI2/html/Features.htm * Remove en-us from docs links * Change links from HTTP to HTTPS * Fix duplicate file in resource loading docs * Fix alt text of images * Change MS Learn URLs to be relative
1 parent 2d6372e commit 56a3351

23 files changed

+3766
-11
lines changed
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
---
2+
title: Avoiding memory leaks
3+
description: A guide on how to make sure not to introduce memory leaks in XAML applications using Win2D.
4+
ms.date: 05/28/2023
5+
ms.topic: article
6+
keywords: windows 10, windows 11, uwp, xaml, windows app sdk, winui, windows ui, graphics, games, effect win2d d2d d2d1 direct2d interop cpp csharp
7+
ms.localizationpriority: medium
8+
---
9+
10+
# Avoiding memory leaks
11+
12+
When using Win2D controls in managed XAML applications, care must be taken to avoid reference count cycles that could prevent these controls ever being reclaimed by the garbage collector.
13+
14+
## You have a problem if...
15+
16+
- You are using Win2D from a .NET language such as C# (not native C++)
17+
- You use one of the Win2D XAML controls:
18+
- [`CanvasControl`](https://microsoft.github.io/Win2D/WinUI2/html/T_Microsoft_Graphics_Canvas_UI_Xaml_CanvasControl.htm)
19+
- [`CanvasVirtualControl`](https://microsoft.github.io/Win2D/WinUI2/html/T_Microsoft_Graphics_Canvas_UI_Xaml_CanvasVirtualControl.htm)
20+
- [`CanvasAnimatedControl`](https://microsoft.github.io/Win2D/WinUI2/html/T_Microsoft_Graphics_Canvas_UI_Xaml_CanvasAnimatedControl.htm)
21+
- [`CanvasSwapChainPanel`](https://microsoft.github.io/Win2D/WinUI2/html/T_Microsoft_Graphics_Canvas_UI_Xaml_CanvasSwapChainPanel.htm)
22+
- You subscribe to events of the Win2D control (eg. `Draw`, `CreateResources`, `SizeChanged`...)
23+
- Your app moves back and forth between more than one XAML page
24+
25+
If all these conditions are met, a reference count cycle will keep the Win2D control from ever being garbage collected. New Win2D resources are allocated each time the app moves to a different page, but the old ones are never freed so memory is leaked. To avoid this, you must add code to explicitly break the cycle.
26+
27+
## How to fix it
28+
29+
To break the reference count cycle and let your page be garbage collected:
30+
31+
- Hook the `Unloaded` event of the XAML page which contains the Win2D control
32+
- In the `Unloaded` handler, call `RemoveFromVisualTree` on the Win2D control
33+
- In the `Unloaded` handler, release (by setting to `null`) any explicit references to the Win2D control
34+
35+
Example code:
36+
37+
```csharp
38+
void page_Unloaded(object sender, RoutedEventArgs e)
39+
{
40+
this.canvas.RemoveFromVisualTree();
41+
this.canvas = null;
42+
}
43+
```
44+
45+
For working examples, see any of the [Example Gallery](https://github.com/Microsoft/Win2D-Samples/tree/master/ExampleGallery/Shared) demo pages.
46+
47+
## How to test for cycle leaks
48+
49+
To test whether your application is correctly breaking refcount cycles, add a finalizer method to any XAML pages which contain Win2D controls:
50+
51+
```csharp
52+
~MyPage()
53+
{
54+
System.Diagnostics.Debug.WriteLine("~" + GetType().Name);
55+
}
56+
```
57+
58+
In your `App` constructor, set up a timer that will make sure garbage collection occurs at regular intervals:
59+
60+
```csharp
61+
var gcTimer = new DispatcherTimer();
62+
gcTimer.Tick += (sender, e) => { GC.Collect(); };
63+
gcTimer.Interval = TimeSpan.FromSeconds(1);
64+
gcTimer.Start();
65+
```
66+
67+
Navigate to the page, then away from it to some other page. If all cycles have been broken, you will see `Debug.WriteLine` output in the Visual Studio output pane within a second or two.
68+
69+
Note that calling `GC.Collect` is disruptive and hurts performace, so you should remove this test code as soon as you finish testing for leaks!
70+
71+
## The gory details
72+
73+
A cycle occurs when an object A has a reference to B, at the same time as B also has a reference to A. Or when A references B, and B references C, while C references A, etc.
74+
75+
When subscribing to events of a XAML control, this sort of cycle is pretty much inevitable:
76+
- XAML page holds references to all the controls contained within it
77+
- Controls hold references to the handler delegates that have been subscribed to their events
78+
- Each delegate holds a reference to its target instance
79+
- Event handlers are typically instance methods of the XAML page class, so their target instance references point back to the XAML page, creating a cycle
80+
81+
If all the objects involved are implemented in .NET, such cycles are not a problem because .NET is garbage collected, and the garbage collection algorithm is able to identify and reclaim groups of objects even if they are linked in a cycle.
82+
83+
Unlike .NET, C++ manages memory by reference counting, which is unable to detect and reclaim cycles of objects. In spite of this limitation, C++ apps using Win2D have no problem because C++ event handlers default to holding weak rather than strong references to their target instance. Therefore the page references the control, and the control references the event handler delegate, but this delegate does not reference back to the page so there is no cycle.
84+
85+
The problem comes when a C++ WinRT component such as Win2D is used by a .NET application:
86+
- The XAML page is part of the application, so uses garbage collection
87+
- The Win2D control is implemented in C++, so uses reference counting
88+
- The event handler delegate is part of the application, so uses garbage collection and holds a strong reference to its target instance
89+
90+
A cycle is present, but the Win2D objects participating in this cycle are not using .NET garbage collection. This means the garbage collector is unable to see the entire chain, so it cannot detect or reclaim the objects. When this occurs, the application must help out by explicitly breaking the cycle. This can be done either by releasing all references from the page to the control (as recommended above) or by releasing all references from the control to event handler delegates that might point back to the page (using the page Unloaded event to unsubscribe all event handlers).
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
---
2+
title: Bitmap block compression
3+
description: An explanation of how block compressed bitmaps can be used with Win2D
4+
ms.date: 05/28/2023
5+
ms.topic: article
6+
keywords: windows 10, windows 11, uwp, xaml, windows app sdk, winui, windows ui, graphics, games, effect win2d d2d d2d1 direct2d interop cpp csharp
7+
ms.localizationpriority: medium
8+
---
9+
10+
# Bitmap block compression
11+
12+
`CanvasBitmap` supports block compressed bitmaps. These can be loaded from a DDS file, or created with [`CreateFromBytes(ICanvasResourceCreator,Byte[], Int32, Int32, DirectXPixelFormat)`](https://microsoft.github.io/Win2D/WinUI2/html/M_Microsoft_Graphics_Canvas_CanvasBitmap_CreateFromBytes.htm).
13+
14+
Block compressed bitmaps are great for bitmap heavy applications (such as games) because they take up less memory and can be drawn more efficiently. A block compressed bitmap uses up to 1/8th of the memory of an uncompressed bitmap. As a result, the GPU needs to access much less memory when drawing the bitmap, resulting in faster drawing.
15+
16+
## About block compression
17+
18+
Block compression is different to the compression employed by PNG or JPG files. Compressed file formats are stored compressed but when they are loaded by Win2D they are decompressed into a bitmap with a format such as `DirectXPixelFormat::B8G8R8A8UIntNormalized`, which uses 32 bits (4 bytes) per pixel. So a 256x256 bitmap would take up 256 \* 256 \* 4 = 262,144 bytes.
19+
20+
A block compressed bitmap uses 8 or 16 bytes (depending on the format -- more on this later) to store a block of 4x4 pixels. So in this case a 256x256 bitmap would take up 256 \* 256 / (4 \* 4) \* 8 = 32,768 bytes, or 256 \* 256 / (4 \* 4) \* 16 = 65,536 bytes. That's up to 8 times smaller! Since block compression is supported directly by the GPU hardware, the bitmap can be kept compressed in memory and drawn directly from the compressed format without ever having to completely uncompress it.
21+
22+
Win2D supports three block compressed formats. The table below describes these formats, along with an uncompressed format for comparison.
23+
24+
| `DirectXPixelFormat` | Size of 4x4 bitmap | Size of 256x256 bitmap | Alpha |
25+
| -- | -- | -- | -- |
26+
| `BC1Unorm` | 8 bytes | 32,768 bytes | 1 bit |
27+
| `BC2Unorm` | 16 bytes | 65,536 bytes | 4 bit |
28+
| `BC3Unorm` | 16 bytes | 65,536 bytes | ~8 bit (compressed) |
29+
| `B8G8R8A8UintNormalized` | 64 bytes | 262,144 bytes | 8 bit |
30+
31+
`BC1Unorm`, `BC2Unorm` and `BC3Unorm` differ mostly in how they support alpha. BC1 supports only 1-bit alpha. BC2 supports each pixel in the block having a unique 4-bit alpha value. BC3 compresses the alpha values.
32+
33+
See [Direct2D Block Compression documentation](https://msdn.microsoft.com/library/windows/desktop/dn424084(v=vs.85).aspx) and [Direct3D Block Compression documentation](https://msdn.microsoft.com/library/windows/desktop/bb694531(v=vs.85).aspx) for more information about how block compression works.
34+
35+
## Restrictions
36+
37+
- All block compressed textures must have a width and height that is a multiple of 4. This is because block compression works on blocks of 4x4 pixels.
38+
- Any operation on a sub-rectangle of a block compressed texture (using `GetPixelBytes()`, `SetPixelBytes(Byte[])`, `CopyPixelsFromBitmap(CanvasBitmap, Int32, Int32)`) require that the sub-rectangle is 4-pixel aligned.
39+
- Win2D requires premultiplied alpha when using block compressed formats.
40+
41+
## Authoring DDS files
42+
43+
Block compressed images can be saved in DDS files. Although these can be generated by plugins to applications such as Photoshop or Paint.NET, care must be taken to ensure that the resulting file is saved with premultiplied alpha. Win2D will load any DDS file containing a `BC1Unorm`, `BC2Unorm` or `BC3Unorm` image and assume that it is authored with premultiplied alpha.
44+
45+
If you are authoring a C++ project then you can use the Image Content Pipeline to convert the image, as described on [MSDN](https://msdn.microsoft.com/library/dn392693(v=vs.140).aspx).
46+
47+
Alternatively, you can use `texconv.exe` from https://github.com/Microsoft/DirectXTex. `texconv.exe` can be built using the DirectXTex_Desktop_2015.sln solution. The following command converts "smoke.png" to a `BC3Unorm` with premultiplied alpha:
48+
49+
```cmd
50+
texconv -pmalpha -m 1 -f BC3_UNORM smoke.png
51+
```
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
---
2+
title: Choosing control resolution
3+
description: An explanation of how to configure the resolution used by Win2D's XAML controls.
4+
ms.date: 05/26/2023
5+
ms.topic: article
6+
keywords: windows 10, windows 11, uwp, xaml, windows app sdk, winui, windows ui, graphics, games, effect win2d d2d d2d1 direct2d interop cpp csharp
7+
ms.localizationpriority: medium
8+
---
9+
10+
# Choosing control resolution
11+
12+
This article explains how to configure the resolution used by Win2D's XAML controls. It explains how to:
13+
14+
- Make Win2D controls run at a fixed resolution.
15+
- Adjust control DPI to improve performance by rendering fewer pixels.
16+
17+
## Resolution and control sizing
18+
19+
"Resolution", as used in this document, is another word for the size of a bitmap. It consists of a width, and height.
20+
21+
The objects that Win2D's XAML controls draw to have a resolution. They also have a DPI. An object's DPI is a measurement of how dense the pixels of that object are, when drawn. DPI behaves like a scale factor- a high DPI increases the number of pixels that comprise the drawn object. On the other hand, lowering the DPI of an object means that it will span fewer pixels. For more information about Win2D's handling of DPI in general, see [this page](dpi-and-dips.md).
22+
23+
DPI-independent size is sometimes called "logical size". And a DPI-dependent size, in pixels, is called "physical size".
24+
25+
In terms of resolution and sizing, a control's default behavior when it's loaded is:
26+
- The control's logical size is determined by its layout, as determined by where it falls in the XAML tree.
27+
- A DPI is queried from the environment. The control's DPI is set to that.
28+
- The amount of physical pixels that comprise the control's drawable area is determined by the control's size, scaled by its DPI.
29+
- On high DPI, the physical size will be greater (more pixels) compared to the logical size.
30+
- On low DPI, the physical size will be smaller (fewer pixels) compared to the logical size.
31+
- On default DPI, the physical size and logical size of the drawable area are the same.
32+
- The control's drawing resource (`CanvasImageSource` for `CanvasControl`, `CanvasVirtualImageSource` for `CanvasVirtualControl` and `CanvasSwapChain` for `CanvasAnimatedControl`) is set to match the size and DPI of the control.
33+
34+
Most Win2D operations are in dips (DPI-independent units), and Win2D's XAML controls' drawing resources are automatically sized to take DPI into account. This means applications can often ignore DPI. Sizes and co-ordinates are always DPI-independent unless specified otherwise. An application can hard-code various sizes and co-ordinates at which things are drawn into the controls, and that content gets scaled when the app is run in environments with different DPIs.
35+
36+
But for some applications, the default behavior isn't sufficient. This article outlines a couple scenarios where the default is not sufficient, and what apps can do to fix it.
37+
38+
## Scenario: the control's contents must be a fixed, lower-than-normal resolution
39+
40+
This scenario may arise, for instance, on a 2D sprite game that should always render at a fixed 640x480 resolution, regardless of what actual display hardware it is run on.
41+
42+
Solving this doesn't strictly require writing any new Win2D code at all.
43+
44+
The [`Viewbox`](/uwp/api/Windows.UI.Xaml.Controls.Viewbox) XAML object lets you constrain the sizes of its child visual elements, automatically adding scaling, with letterboxing or pillarboxing to preseve aspect ratios as necessary.
45+
46+
Simply ensure your `CanvasControl`, `CanvasVirtualControl` or `CanvasAnimatedControl` is a child element of a `ViewBox`, and restrict the size of that control.
47+
48+
For example, to constrain the size of the control to 320 pixels wide, and 224 pixels high, regardless of DPI, then instead of:
49+
50+
```XAML
51+
<canvas:CanvasAnimatedControl/>
52+
```
53+
54+
Use:
55+
56+
```XAML
57+
<Viewbox>
58+
<canvas:CanvasAnimatedControl Width="320" Height="224"/>
59+
</Viewbox>
60+
```
61+
62+
If your app should not preserve the aspect ratio by adding letterboxing/pillarboxing, then add the `Stretch` attribute:
63+
64+
```XAML
65+
<Viewbox Stretch="Fill">
66+
<canvas:CanvasAnimatedControl Width="320" Height="224"/>
67+
</Viewbox>
68+
```
69+
70+
Note that the scaling performed by the `Viewbox` element does not guarantee any control over the interpolation mode. The filtering method may look like `CanvasInterpolationMode.Linear`, or something similar. If your app needs a particular interpolation mode, then don't use `ViewBox` with a fixed-size control. Instead, draw to an intermediate, fixed-size `CanvasRenderTarget`, and use the desired interpolation mode to draw the scaled intermediate to the target.
71+
72+
## Scenario: the app cannot perform well at high resolutions
73+
74+
Some devices have very high-resolution displays, but their graphics processing unit is not powerful enough to animate that many pixels smoothly. Developers may not be readily aware of how their apps perform on these devices without testing them.
75+
76+
One option is to use the `DpiScale` property of the control to reduce the control's DPI.
77+
78+
For example, to fix the control at half-resolution, use:
79+
80+
```XAML
81+
<canvas:CanvasAnimatedControl DpiScale="0.5" />
82+
```
83+
84+
The actual DPI scale factor depends upon the needs of your app. One option is to compute a scale factor that will fix the app's DPI at 96, and no higher.
85+
86+
For example:
87+
88+
```csharp
89+
float dpiLimit = 96.0f;
90+
91+
if(control.Dpi > dpiLimit)
92+
{
93+
control.DpiScale = dpiLimit / control.Dpi;
94+
}
95+
```
96+
97+
To ensure this setting works across DPI changes, the application should subscribe to [`DisplayInformation.DpiChanged`](https://msdn.microsoft.com/library/windows/apps/windows.graphics.display.displayinformation.dpichanged) and use this logic in the handler to set the DPI scale against the new DPI.
98+
99+
This saves the app some perf overhead, exploiting the fact that users may not be able to easily percieve the reduced resolution on a high-DPI display.
100+
101+
The scaling performed in having a lower-than-native resolution control resource cannot guarantee control over the interpolation mode, similar to `ViewBox` mentioned above. If your app needs a particular interpolation mode, use an intermediate instead.

0 commit comments

Comments
 (0)