forked from qian-o/VulkanSamples
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathSwapChainPanel.cs
More file actions
286 lines (235 loc) · 7.26 KB
/
SwapChainPanel.cs
File metadata and controls
286 lines (235 loc) · 7.26 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
#if WINDOWS
using AndroidApp.Controls;
using Core.Helpers;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media;
using Silk.NET.Core.Contexts;
using Silk.NET.Core.Native;
using Silk.NET.Vulkan;
using Silk.NET.Vulkan.Extensions.KHR;
using SkiaSharp.Views.Windows;
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace AndroidApp.Platforms.Windows.Controls;
internal sealed unsafe class VkSurface : IVkSurface, IDisposable
{
private readonly Vk _vk = Vk.GetApi();
private readonly Allocator _alloter = new();
private readonly nint _hwnd;
public VkSurface(nint hwnd)
{
_hwnd = hwnd;
}
~VkSurface()
{
Dispose();
}
public VkNonDispatchableHandle Create<T>(VkHandle instance, T* allocator) where T : unmanaged
{
if (!_vk.TryGetInstanceExtension(new Instance(instance.Handle), out KhrWin32Surface win32Surface))
{
throw new InvalidOperationException("KHR Win32 Surface extension is not supported.");
}
Win32SurfaceCreateInfoKHR createInfo = new()
{
SType = StructureType.Win32SurfaceCreateInfoKhr,
Hinstance = 0, // TODO: supply real HINSTANCE if needed
Hwnd = _hwnd
};
SurfaceKHR surface;
if (win32Surface.CreateWin32Surface(new Instance(instance.Handle), &createInfo, (AllocationCallbacks*)allocator, &surface) != Result.Success)
{
throw new InvalidOperationException("Failed to create Win32 surface.");
}
return new VkNonDispatchableHandle(surface.Handle);
}
public byte** GetRequiredExtensions(out uint count)
{
count = 2;
return _alloter.Alloc([KhrSurface.ExtensionName, KhrWin32Surface.ExtensionName]);
}
public void Dispose()
{
_alloter.Dispose();
_vk.Dispose();
GC.SuppressFinalize(this);
}
}
internal sealed class Timer : IDisposable
{
private readonly Stopwatch _stopwatch = new();
private readonly float _frequency = 1.0f / Stopwatch.Frequency;
public float DeltaTime { get; private set; }
public float TotalTime { get; private set; }
public void Start() => _stopwatch.Start();
public void Stop() => _stopwatch.Stop();
public void Update()
{
long elapsedTicks = _stopwatch.ElapsedTicks;
DeltaTime = elapsedTicks * _frequency;
TotalTime += DeltaTime;
_stopwatch.Restart();
}
public void Dispose()
{
_stopwatch.Stop();
_stopwatch.Reset();
}
}
internal sealed class FrameCallback
{
private readonly Timer _timer = new();
private readonly ISwapChainPanel _swapChainPanel;
public FrameCallback(ISwapChainPanel swapChainPanel)
{
_swapChainPanel = swapChainPanel;
CompositionTarget.Rendering += OnRendering;
_timer.Start();
}
private void OnRendering(object? sender, object e)
{
_timer.Update();
_swapChainPanel.Update(_timer.DeltaTime, _timer.TotalTime);
_timer.Update();
_swapChainPanel.Render(_timer.DeltaTime, _timer.TotalTime);
}
public void Stop()
{
CompositionTarget.Rendering -= OnRendering;
_timer.Stop();
_timer.Dispose();
}
}
internal static class Win32
{
public static int WS_VISIBLE = 0x10000000;
public static int WS_CAPTION = 0x00C00000;
public static int WS_SYSMENU = 0x00080000;
public static int WS_MINIMIZEBOX = 0x00020000;
public static int WS_MAXIMIZEBOX = 0x00010000;
public static int WS_THICKFRAME = 0x00040000;
public static int WS_OVERLAPPED = 0x00000000;
public static int WS_OVERLAPPEDWINDOW = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX;
public static int WS_TOPMOST = 0x00000008;
public static int WS_MAXIMIZE = 0x01000000;
public static int WS_MINIMIZE = 0x20000000;
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern nint CreateWindowExW
(
int exStyle,
string className,
string windowName,
int style,
int x,
int y,
int width,
int height,
nint parent,
nint menu,
nint instance,
nint param
);
[DllImport("user32.dll", SetLastError = true)]
public static extern bool SetWindowPos(
nint hWnd,
nint hWndInsertAfter,
int X,
int Y,
int cx,
int cy,
uint uFlags);
}
internal sealed class SwapChainPanel : Microsoft.UI.Xaml.Controls.SwapChainPanel
{
private ISwapChainPanel? _swapChainPanel;
private FrameCallback? _frameCallback;
private VkSurface? _vkSurface;
private nint _hwnd;
public SwapChainPanel(ISwapChainPanel swapChainPanel)
{
_swapChainPanel = swapChainPanel;
_hwnd = CreateRenderWindow();
if (_hwnd == 0)
throw new Exception("Failed to create child HWND.");
SetWindowPos(_hwnd);
_vkSurface = new VkSurface(_hwnd);
_swapChainPanel.CreateSwapChainPanel(_vkSurface);
_frameCallback = new FrameCallback(_swapChainPanel);
Loaded += OnLoaded;
Unloaded += OnUnloaded;
SizeChanged += OnSizeChanged;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
if (_swapChainPanel is null)
return;
var mauiWindow = Microsoft.Maui.Controls.Application.Current.Windows[0];
var winuiWindow = (Microsoft.UI.Xaml.Window)mauiWindow.Handler.PlatformView;
nint parentHwnd = WinRT.Interop.WindowNative.GetWindowHandle(winuiWindow);
_hwnd = CreateRenderWindow();
if (_hwnd == 0)
throw new Exception("Failed to create child HWND.");
SetWindowPos(_hwnd);
_vkSurface = new VkSurface(_hwnd);
_swapChainPanel.CreateSwapChainPanel(_vkSurface);
_frameCallback = new FrameCallback(_swapChainPanel);
}
private nint CreateRenderWindow(string wndClass = "STATIC", string title = "RenderView")
{
nint hwnd = Win32.CreateWindowExW
(
0, //Win32.WS_TOPMOST,
wndClass,
title,
Win32.WS_OVERLAPPEDWINDOW | Win32.WS_VISIBLE,
0,
0,
1920,
1080,
IntPtr.Zero,
0,
0,
0
);
return hwnd;
}
private void OnUnloaded(object sender, RoutedEventArgs e)
{
_frameCallback?.Stop();
_frameCallback = null;
if (_swapChainPanel is not null)
{
_swapChainPanel.DestroySwapChainPanel();
}
_vkSurface?.Dispose();
_vkSurface = null;
}
private void SetWindowPos(nint hwnd)
{
Win32.SetWindowPos
(
hwnd,
IntPtr.Zero,
0,
0,
1920,
1080,
0
);
}
private void OnSizeChanged(object sender, SizeChangedEventArgs e)
{
if (_hwnd != 0)
{
SetWindowPos(_hwnd);
}
_swapChainPanel?.Resize((uint)e.NewSize.Width, (uint)e.NewSize.Height);
}
public void AttachSwapChainPanel(ISwapChainPanel swapChainPanel)
{
_swapChainPanel = swapChainPanel;
}
}
#endif