1099 changed files with 28836 additions and 20081 deletions
@ -0,0 +1,5 @@ |
|||
<ProjectConfiguration> |
|||
<Settings> |
|||
<XUnit2Enabled>False</XUnit2Enabled> |
|||
</Settings> |
|||
</ProjectConfiguration> |
|||
@ -0,0 +1,5 @@ |
|||
<ProjectConfiguration> |
|||
<Settings> |
|||
<IgnoreThisComponentCompletely>True</IgnoreThisComponentCompletely> |
|||
</Settings> |
|||
</ProjectConfiguration> |
|||
@ -1,7 +1,7 @@ |
|||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
|||
<ItemGroup> |
|||
<PackageReference Include="HarfBuzzSharp" Version="2.8.2.1-preview.108" /> |
|||
<PackageReference Condition="'$(IncludeLinuxSkia)' == 'true'" Include="HarfBuzzSharp.NativeAssets.Linux" Version="2.8.2.1-preview.108" /> |
|||
<PackageReference Condition="'$(IncludeWasmSkia)' == 'true'" Include="HarfBuzzSharp.NativeAssets.WebAssembly" Version="2.8.2.1-preview.108" /> |
|||
<PackageReference Include="HarfBuzzSharp" Version="2.8.2.3" /> |
|||
<PackageReference Condition="'$(IncludeLinuxSkia)' == 'true'" Include="HarfBuzzSharp.NativeAssets.Linux" Version="2.8.2.3" /> |
|||
<PackageReference Condition="'$(IncludeWasmSkia)' == 'true'" Include="HarfBuzzSharp.NativeAssets.WebAssembly" Version="2.8.2.3" /> |
|||
</ItemGroup> |
|||
</Project> |
|||
|
|||
@ -1,5 +1,5 @@ |
|||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
|||
<ItemGroup> |
|||
<PackageReference Include="SixLabors.ImageSharp" Version="2.1.1" /> |
|||
<PackageReference Include="SixLabors.ImageSharp" Version="2.1.3" /> |
|||
</ItemGroup> |
|||
</Project> |
|||
|
|||
@ -1,5 +1,5 @@ |
|||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
|||
<ItemGroup> |
|||
<PackageReference Include="Moq" Version="4.14.1" /> |
|||
<PackageReference Include="Moq" Version="4.18.4" /> |
|||
</ItemGroup> |
|||
</Project> |
|||
|
|||
@ -1,9 +1,14 @@ |
|||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
|||
<PropertyGroup> |
|||
<SharpDXPackageVersion>4.0.1</SharpDXPackageVersion> |
|||
</PropertyGroup> |
|||
<ItemGroup> |
|||
<PackageReference Include="SharpDX" Version="4.0.1" /> |
|||
<PackageReference Include="SharpDX.Direct2D1" Version="4.0.1" /> |
|||
<PackageReference Include="SharpDX.Direct3D11" Version="4.0.1" /> |
|||
<PackageReference Include="SharpDX.DXGI" Version="4.0.1" /> |
|||
<PackageReference Include="SharpDX.Direct3D9" Version="4.0.1" Condition="'$(UseDirect3D9)' == 'true'" /> |
|||
<PackageReference Include="SharpDX" Version="$(SharpDXPackageVersion)" /> |
|||
<PackageReference Include="SharpDX.Direct2D1" Version="$(SharpDXPackageVersion)" /> |
|||
<PackageReference Include="SharpDX.Direct3D11" Version="$(SharpDXPackageVersion)" /> |
|||
<PackageReference Include="SharpDX.DXGI" Version="$(SharpDXPackageVersion)" /> |
|||
<PackageReference Include="SharpDX.Direct3D9" Version="$(SharpDXPackageVersion)" Condition="'$(UseDirect3D9)' == 'true'" /> |
|||
<PackageReference Include="SharpDX.D3DCompiler" Version="$(SharpDXPackageVersion)" Condition="'$(UseD3DCompiler)' == 'true'" /> |
|||
<PackageReference Include="SharpDX.Mathematics" Version="$(SharpDXPackageVersion)" Condition="'$(UseSharpDXMathematics)' == 'true'" /> |
|||
</ItemGroup> |
|||
</Project> |
|||
|
|||
@ -1,7 +1,7 @@ |
|||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
|||
<ItemGroup> |
|||
<PackageReference Include="SkiaSharp" Version="2.88.1" /> |
|||
<PackageReference Condition="'$(IncludeLinuxSkia)' == 'true'" Include="SkiaSharp.NativeAssets.Linux" Version="2.88.1" /> |
|||
<PackageReference Condition="'$(IncludeWasmSkia)' == 'true'" Include="SkiaSharp.NativeAssets.WebAssembly" Version="2.88.1" /> |
|||
<PackageReference Include="SkiaSharp" Version="2.88.3" /> |
|||
<PackageReference Condition="'$(IncludeLinuxSkia)' == 'true'" Include="SkiaSharp.NativeAssets.Linux" Version="2.88.3" /> |
|||
<PackageReference Condition="'$(IncludeWasmSkia)' == 'true'" Include="SkiaSharp.NativeAssets.WebAssembly" Version="2.88.3" /> |
|||
</ItemGroup> |
|||
</Project> |
|||
|
|||
@ -1,5 +1,6 @@ |
|||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
|||
<ItemGroup Condition="'$(TargetFramework)' != 'net6'"> |
|||
<!-- '!NET6_0_OR_GREATER' equivalent --> |
|||
<ItemGroup Condition="!('$(TargetFrameworkIdentifier)' == '.NETCoreApp' AND $([MSBuild]::VersionGreaterThanOrEquals($(TargetFrameworkVersion), '6.0')))"> |
|||
<PackageReference Include="System.Memory" Version="4.5.3" /> |
|||
</ItemGroup> |
|||
</Project> |
|||
|
|||
@ -0,0 +1,39 @@ |
|||
#include "common.h" |
|||
|
|||
namespace |
|||
{ |
|||
id<NSObject> s_inhibitAppSleepHandle{}; |
|||
} |
|||
|
|||
class PlatformBehaviorInhibition : public ComSingleObject<IAvnPlatformBehaviorInhibition, &IID_IAvnCursorFactory> |
|||
{ |
|||
public: |
|||
FORWARD_IUNKNOWN() |
|||
|
|||
virtual void SetInhibitAppSleep(bool inhibitAppSleep, char* reason) override |
|||
{ |
|||
START_COM_CALL; |
|||
|
|||
@autoreleasepool |
|||
{ |
|||
if (inhibitAppSleep && s_inhibitAppSleepHandle == nullptr) |
|||
{ |
|||
NSActivityOptions options = NSActivityUserInitiatedAllowingIdleSystemSleep; |
|||
s_inhibitAppSleepHandle = [[NSProcessInfo processInfo] beginActivityWithOptions:options reason:[NSString stringWithUTF8String: reason]]; |
|||
} |
|||
|
|||
if (!inhibitAppSleep) |
|||
{ |
|||
s_inhibitAppSleepHandle = nullptr; |
|||
} |
|||
} |
|||
} |
|||
}; |
|||
|
|||
extern IAvnPlatformBehaviorInhibition* CreatePlatformBehaviorInhibition() |
|||
{ |
|||
@autoreleasepool |
|||
{ |
|||
return new PlatformBehaviorInhibition(); |
|||
} |
|||
} |
|||
@ -0,0 +1,222 @@ |
|||
<UserControl xmlns="https://github.com/avaloniaui" |
|||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
|||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" |
|||
d:DesignHeight="800" |
|||
d:DesignWidth="400" |
|||
x:Class="ControlCatalog.Pages.ScrollSnapPage" |
|||
xmlns:pages="using:ControlCatalog.Pages" |
|||
x:DataType="pages:ScrollSnapPageViewModel"> |
|||
<StackPanel Orientation="Vertical" Spacing="4"> |
|||
<TextBlock TextWrapping="Wrap" |
|||
Classes="h2">Scrollviewer can snap supported content both vertically and horizontally. Snapping occurs from scrolling with touch or pen.</TextBlock> |
|||
|
|||
<Grid RowDefinitions="Auto, Auto, Auto, Auto, Auto"> |
|||
<StackPanel Orientation="Horizontal" |
|||
Spacing="4"> |
|||
<StackPanel Orientation="Vertical" |
|||
Spacing="4"> |
|||
<TextBlock Text="Snap Point Type" /> |
|||
<ComboBox Items="{Binding AvailableSnapPointsType}" |
|||
SelectedItem="{Binding SnapPointsType}" /> |
|||
</StackPanel> |
|||
|
|||
<StackPanel Orientation="Vertical" |
|||
Spacing="4"> |
|||
<TextBlock Text="Snap Point Alignment" /> |
|||
<ComboBox Items="{Binding AvailableSnapPointsAlignment}" |
|||
SelectedItem="{Binding SnapPointsAlignment}" /> |
|||
</StackPanel> |
|||
|
|||
<ToggleSwitch IsChecked="{Binding AreSnapPointsRegular}" |
|||
OffContent="No" |
|||
OnContent="Yes" |
|||
Content="Are Snap Points regular?" /> |
|||
</StackPanel> |
|||
<TextBlock TextWrapping="Wrap" |
|||
Grid.Row="1" |
|||
Margin="0,10" |
|||
Classes="h2">Vertical Snapping</TextBlock> |
|||
|
|||
<Border |
|||
BorderBrush="Green" |
|||
BorderThickness="1" |
|||
Padding="0" |
|||
Grid.Row="2" |
|||
Margin="10, 5"> |
|||
<ScrollViewer x:Name="VerticalSnapsScrollViewer" |
|||
VerticalSnapPointsType="{Binding SnapPointsType}" |
|||
VerticalSnapPointsAlignment="{Binding SnapPointsAlignment}" |
|||
HorizontalAlignment="Stretch" |
|||
Height="350" |
|||
HorizontalScrollBarVisibility="Disabled"> |
|||
<StackPanel AreVerticalSnapPointsRegular="{Binding AreSnapPointsRegular}" |
|||
Orientation="Vertical" |
|||
HorizontalAlignment="Stretch"> |
|||
<Border Padding="5, 30" |
|||
BorderBrush="Red" |
|||
HorizontalAlignment="Stretch" |
|||
BorderThickness="1"> |
|||
<TextBlock FontWeight="Bold" |
|||
Text="Child 1"/> |
|||
</Border> |
|||
<Border Padding="5, 30" |
|||
BorderBrush="Red" |
|||
HorizontalAlignment="Stretch" |
|||
BorderThickness="1"> |
|||
<TextBlock FontWeight="Bold" |
|||
Text="Child 2"/> |
|||
</Border> |
|||
<Border Padding="5, 20" |
|||
BorderBrush="Red" |
|||
HorizontalAlignment="Stretch" |
|||
BorderThickness="1"> |
|||
<TextBlock FontWeight="Bold" |
|||
Text="Child 3"/> |
|||
</Border> |
|||
<Border Padding="5, 30" |
|||
BorderBrush="Red" |
|||
HorizontalAlignment="Stretch" |
|||
BorderThickness="1"> |
|||
<TextBlock FontWeight="Bold" |
|||
Text="Child 4"/> |
|||
</Border> |
|||
<Border Padding="5, 30" |
|||
BorderBrush="Red" |
|||
HorizontalAlignment="Stretch" |
|||
BorderThickness="1"> |
|||
<TextBlock FontWeight="Bold" |
|||
Text="Child 5"/> |
|||
</Border> |
|||
<Border Padding="5, 30" |
|||
BorderBrush="Red" |
|||
HorizontalAlignment="Stretch" |
|||
BorderThickness="1"> |
|||
<TextBlock FontWeight="Bold" |
|||
Text="Child 6"/> |
|||
</Border> |
|||
<Border Padding="5,8" |
|||
BorderBrush="Red" |
|||
HorizontalAlignment="Stretch" |
|||
BorderThickness="1"> |
|||
<TextBlock FontWeight="Bold" |
|||
Text="Child 7"/> |
|||
</Border> |
|||
<Border Padding="5, 30" |
|||
BorderBrush="Red" |
|||
HorizontalAlignment="Stretch" |
|||
BorderThickness="1"> |
|||
<TextBlock FontWeight="Bold" |
|||
Text="Child 8"/> |
|||
</Border> |
|||
<Border Padding="5,4" |
|||
BorderBrush="Red" |
|||
HorizontalAlignment="Stretch" |
|||
BorderThickness="1"> |
|||
<TextBlock FontWeight="Bold" |
|||
Text="Child 9"/> |
|||
</Border> |
|||
<Border Padding="5, 30" |
|||
BorderBrush="Red" |
|||
HorizontalAlignment="Stretch" |
|||
BorderThickness="1"> |
|||
<TextBlock FontWeight="Bold" |
|||
Text="Child 20"/> |
|||
</Border> |
|||
<Border Padding="5, 30" |
|||
BorderBrush="Red" |
|||
HorizontalAlignment="Stretch" |
|||
BorderThickness="1"> |
|||
<TextBlock FontWeight="Bold" |
|||
Text="Child 11"/> |
|||
</Border> |
|||
<Border Padding="5, 30" |
|||
BorderBrush="Red" |
|||
HorizontalAlignment="Stretch" |
|||
BorderThickness="1"> |
|||
<TextBlock FontWeight="Bold" |
|||
Text="Child 12"/> |
|||
</Border> |
|||
</StackPanel> |
|||
</ScrollViewer> |
|||
</Border> |
|||
<TextBlock TextWrapping="Wrap" |
|||
Grid.Row="3" |
|||
Margin="0,10" |
|||
Classes="h2">Horizontal Snapping</TextBlock> |
|||
<Border |
|||
BorderBrush="Green" |
|||
BorderThickness="1" |
|||
Padding="0" |
|||
Grid.Row="4" |
|||
Margin="10, 10"> |
|||
<ScrollViewer x:Name="HorizontalSnapsScrollViewer" |
|||
HorizontalSnapPointsType="{Binding SnapPointsType}" |
|||
HorizontalSnapPointsAlignment="{Binding SnapPointsAlignment}" |
|||
HorizontalAlignment="Stretch" |
|||
Height="350" |
|||
HorizontalScrollBarVisibility="Auto" |
|||
VerticalScrollBarVisibility="Disabled"> |
|||
<StackPanel AreHorizontalSnapPointsRegular="{Binding AreSnapPointsRegular}" |
|||
Orientation="Horizontal" |
|||
HorizontalAlignment="Stretch"> |
|||
<Border Padding="5, 30" |
|||
Width="300" |
|||
BorderBrush="Red" |
|||
HorizontalAlignment="Stretch" |
|||
BorderThickness="1"> |
|||
<TextBlock FontWeight="Bold" |
|||
VerticalAlignment="Center" |
|||
Text="Child 1"/> |
|||
</Border> |
|||
<Border Padding="5, 30" |
|||
Width="300" |
|||
BorderBrush="Red" |
|||
VerticalAlignment="Stretch" |
|||
BorderThickness="1"> |
|||
<TextBlock FontWeight="Bold" |
|||
VerticalAlignment="Center" |
|||
Text="Child 2"/> |
|||
</Border> |
|||
<Border Padding="5, 20" |
|||
Width="300" |
|||
BorderBrush="Red" |
|||
VerticalAlignment="Stretch" |
|||
BorderThickness="1"> |
|||
<TextBlock FontWeight="Bold" |
|||
VerticalAlignment="Center" |
|||
Text="Child 3"/> |
|||
</Border> |
|||
<Border Padding="5, 30" |
|||
Width="300" |
|||
BorderBrush="Red" |
|||
VerticalAlignment="Stretch" |
|||
BorderThickness="1"> |
|||
<TextBlock FontWeight="Bold" |
|||
VerticalAlignment="Center" |
|||
Text="Child 4"/> |
|||
</Border> |
|||
<Border Padding="5, 30" |
|||
Width="300" |
|||
BorderBrush="Red" |
|||
VerticalAlignment="Stretch" |
|||
BorderThickness="1"> |
|||
<TextBlock FontWeight="Bold" |
|||
VerticalAlignment="Center" |
|||
Text="Child 5"/> |
|||
</Border> |
|||
<Border Padding="5, 30" |
|||
Width="300" |
|||
BorderBrush="Red" |
|||
VerticalAlignment="Stretch" |
|||
BorderThickness="1"> |
|||
<TextBlock FontWeight="Bold" |
|||
VerticalAlignment="Center" |
|||
Text="Child 6"/> |
|||
</Border> |
|||
|
|||
</StackPanel> |
|||
</ScrollViewer> |
|||
</Border> |
|||
</Grid> |
|||
</StackPanel> |
|||
</UserControl> |
|||
@ -0,0 +1,68 @@ |
|||
using System.Collections.Generic; |
|||
using Avalonia.Controls; |
|||
using Avalonia.Controls.Primitives; |
|||
using Avalonia.Markup.Xaml; |
|||
using MiniMvvm; |
|||
|
|||
namespace ControlCatalog.Pages |
|||
{ |
|||
public class ScrollSnapPageViewModel : ViewModelBase |
|||
{ |
|||
private SnapPointsType _snapPointsType; |
|||
private SnapPointsAlignment _snapPointsAlignment; |
|||
private bool _areSnapPointsRegular; |
|||
|
|||
public ScrollSnapPageViewModel() |
|||
{ |
|||
|
|||
AvailableSnapPointsType = new List<SnapPointsType>() |
|||
{ |
|||
SnapPointsType.None, |
|||
SnapPointsType.Mandatory, |
|||
SnapPointsType.MandatorySingle |
|||
}; |
|||
|
|||
AvailableSnapPointsAlignment = new List<SnapPointsAlignment>() |
|||
{ |
|||
SnapPointsAlignment.Near, |
|||
SnapPointsAlignment.Center, |
|||
SnapPointsAlignment.Far, |
|||
}; |
|||
} |
|||
|
|||
public bool AreSnapPointsRegular |
|||
{ |
|||
get => _areSnapPointsRegular; |
|||
set => this.RaiseAndSetIfChanged(ref _areSnapPointsRegular, value); |
|||
} |
|||
|
|||
public SnapPointsType SnapPointsType |
|||
{ |
|||
get => _snapPointsType; |
|||
set => this.RaiseAndSetIfChanged(ref _snapPointsType, value); |
|||
} |
|||
|
|||
public SnapPointsAlignment SnapPointsAlignment |
|||
{ |
|||
get => _snapPointsAlignment; |
|||
set => this.RaiseAndSetIfChanged(ref _snapPointsAlignment, value); |
|||
} |
|||
public List<SnapPointsType> AvailableSnapPointsType { get; } |
|||
public List<SnapPointsAlignment> AvailableSnapPointsAlignment { get; } |
|||
} |
|||
|
|||
public class ScrollSnapPage : UserControl |
|||
{ |
|||
public ScrollSnapPage() |
|||
{ |
|||
this.InitializeComponent(); |
|||
|
|||
DataContext = new ScrollSnapPageViewModel(); |
|||
} |
|||
|
|||
private void InitializeComponent() |
|||
{ |
|||
AvaloniaXamlLoader.Load(this); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,79 @@ |
|||
<UserControl x:Class="ControlCatalog.Pages.ThemePage" |
|||
xmlns="https://github.com/avaloniaui" |
|||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
|||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" |
|||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" |
|||
xmlns:pages="clr-namespace:ControlCatalog.Pages" |
|||
d:DesignWidth="300" |
|||
mc:Ignorable="d"> |
|||
|
|||
<UserControl.Resources> |
|||
<ResourceDictionary> |
|||
<ResourceDictionary.ThemeDictionaries> |
|||
<ResourceDictionary x:Key="Dark"> |
|||
<SolidColorBrush x:Key="DemoBackground">Black</SolidColorBrush> |
|||
</ResourceDictionary> |
|||
<ResourceDictionary x:Key="Light"> |
|||
<SolidColorBrush x:Key="DemoBackground">White</SolidColorBrush> |
|||
</ResourceDictionary> |
|||
<ResourceDictionary x:Key="{x:Static pages:ThemePage.Pink}"> |
|||
<SolidColorBrush x:Key="DemoBackground">#ffe5ea</SolidColorBrush> |
|||
<SolidColorBrush x:Key="NormalBackgroundBrush" Color="#ffc0cb" /> |
|||
<SolidColorBrush x:Key="PointerOverBackgroundBrush" Color="#ffb3c0" /> |
|||
<SolidColorBrush x:Key="PressedBackgroundBrush" Color="#ff4d6c" /> |
|||
<SolidColorBrush x:Key="NormalBorderBrush" Color="#ff8096" /> |
|||
<SolidColorBrush x:Key="PointerOverBorderBrush" Color="#ff8096" /> |
|||
<SolidColorBrush x:Key="PressedBorderBrush" Color="#ff4d6c" /> |
|||
|
|||
<!-- Override colors for fluent theme --> |
|||
<StaticResource x:Key="ButtonBackground" ResourceKey="NormalBackgroundBrush" /> |
|||
<StaticResource x:Key="ButtonBackgroundPointerOver" ResourceKey="PointerOverBackgroundBrush" /> |
|||
<StaticResource x:Key="ButtonBackgroundPressed" ResourceKey="PressedBackgroundBrush" /> |
|||
<StaticResource x:Key="ButtonBorderBrush" ResourceKey="NormalBorderBrush" /> |
|||
<StaticResource x:Key="ButtonBorderBrushPointerOver" ResourceKey="PointerOverBorderBrush" /> |
|||
<StaticResource x:Key="ButtonBorderBrushPressed" ResourceKey="PressedBorderBrush" /> |
|||
<StaticResource x:Key="TextControlBackground" ResourceKey="NormalBackgroundBrush" /> |
|||
<StaticResource x:Key="TextControlBackgroundPointerOver" ResourceKey="PointerOverBackgroundBrush" /> |
|||
<StaticResource x:Key="TextControlBackgroundFocused" ResourceKey="PointerOverBackgroundBrush" /> |
|||
<StaticResource x:Key="TextControlBorderBrush" ResourceKey="NormalBorderBrush" /> |
|||
<StaticResource x:Key="TextControlBorderBrushPointerOver" ResourceKey="PointerOverBorderBrush" /> |
|||
<StaticResource x:Key="TextControlBorderBrushFocused" ResourceKey="PressedBorderBrush" /> |
|||
<StaticResource x:Key="ComboBoxBackground" ResourceKey="NormalBackgroundBrush" /> |
|||
<StaticResource x:Key="ComboBoxBackgroundPointerOver" ResourceKey="PointerOverBackgroundBrush" /> |
|||
<StaticResource x:Key="ComboBoxBackgroundPressed" ResourceKey="PressedBackgroundBrush" /> |
|||
<StaticResource x:Key="ComboBoxBorderBrush" ResourceKey="NormalBorderBrush" /> |
|||
<StaticResource x:Key="ComboBoxBorderBrushPointerOver" ResourceKey="PointerOverBorderBrush" /> |
|||
<StaticResource x:Key="ComboBoxBorderBrushPressed" ResourceKey="PressedBorderBrush" /> |
|||
<!-- Override colors for default theme --> |
|||
<StaticResource x:Key="ThemeControlMidBrush" ResourceKey="NormalBackgroundBrush" /> |
|||
<StaticResource x:Key="ThemeControlHighBrush" ResourceKey="PressedBackgroundBrush" /> |
|||
<StaticResource x:Key="ThemeBorderLowBrush" ResourceKey="NormalBorderBrush" /> |
|||
<StaticResource x:Key="ThemeBorderMidBrush" ResourceKey="PointerOverBorderBrush" /> |
|||
</ResourceDictionary> |
|||
</ResourceDictionary.ThemeDictionaries> |
|||
</ResourceDictionary> |
|||
</UserControl.Resources> |
|||
|
|||
<ThemeVariantScope x:Name="ThemeVariantScope"> |
|||
<Border Background="{DynamicResource DemoBackground}" |
|||
VerticalAlignment="Top" |
|||
HorizontalAlignment="Left" |
|||
Padding="4" |
|||
CornerRadius="4"> |
|||
<Grid RowDefinitions="Auto, 4, Auto, 4, Auto, 4, Auto" ColumnDefinitions="150, 150"> |
|||
<ComboBox Grid.Column="0" Grid.Row="0" x:Name="Selector" HorizontalAlignment="Stretch"> |
|||
<ComboBox.ItemTemplate> |
|||
<DataTemplate x:DataType="x:String"> |
|||
<TextBlock Text="{Binding TargetNullValue=Unset}" /> |
|||
</DataTemplate> |
|||
</ComboBox.ItemTemplate> |
|||
</ComboBox> |
|||
<TextBlock Grid.Column="0" Grid.Row="2" Text="Username:" VerticalAlignment="Center" /> |
|||
<TextBlock Grid.Column="0" Grid.Row="4" Text="Password:" VerticalAlignment="Center" /> |
|||
<TextBox Grid.Column="1" Grid.Row="2" Watermark="Input here" HorizontalAlignment="Stretch" /> |
|||
<TextBox Grid.Column="1" Grid.Row="4" Watermark="Input here" HorizontalAlignment="Stretch" /> |
|||
<Button Grid.Column="1" Grid.Row="6" Content="Login" HorizontalAlignment="Stretch" /> |
|||
</Grid> |
|||
</Border> |
|||
</ThemeVariantScope> |
|||
</UserControl> |
|||
@ -0,0 +1,37 @@ |
|||
using Avalonia; |
|||
using Avalonia.Controls; |
|||
using Avalonia.Markup.Xaml; |
|||
using Avalonia.Styling; |
|||
|
|||
namespace ControlCatalog.Pages |
|||
{ |
|||
public class ThemePage : UserControl |
|||
{ |
|||
public static ThemeVariant Pink { get; } = new("Pink", ThemeVariant.Light); |
|||
|
|||
public ThemePage() |
|||
{ |
|||
AvaloniaXamlLoader.Load(this); |
|||
|
|||
var selector = this.FindControl<ComboBox>("Selector")!; |
|||
var themeVariantScope = this.FindControl<ThemeVariantScope>("ThemeVariantScope")!; |
|||
|
|||
selector.Items = new[] |
|||
{ |
|||
ThemeVariant.Default, |
|||
ThemeVariant.Dark, |
|||
ThemeVariant.Light, |
|||
Pink |
|||
}; |
|||
selector.SelectedIndex = 0; |
|||
|
|||
selector.SelectionChanged += (_, _) => |
|||
{ |
|||
if (selector.SelectedItem is ThemeVariant theme) |
|||
{ |
|||
themeVariantScope.RequestedThemeVariant = theme; |
|||
} |
|||
}; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
<Application |
|||
xmlns="https://github.com/avaloniaui" |
|||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
|||
x:Class="GpuInterop.App"> |
|||
<Application.Styles> |
|||
<FluentTheme /> |
|||
</Application.Styles> |
|||
</Application> |
|||
@ -0,0 +1,22 @@ |
|||
using Avalonia; |
|||
using Avalonia.Controls.ApplicationLifetimes; |
|||
using Avalonia.Markup.Xaml; |
|||
|
|||
namespace GpuInterop |
|||
{ |
|||
public class App : Application |
|||
{ |
|||
public override void Initialize() |
|||
{ |
|||
AvaloniaXamlLoader.Load(this); |
|||
} |
|||
|
|||
public override void OnFrameworkInitializationCompleted() |
|||
{ |
|||
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktopLifetime) |
|||
{ |
|||
desktopLifetime.MainWindow = new MainWindow(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,146 @@ |
|||
using System; |
|||
using System.Diagnostics; |
|||
using System.Linq; |
|||
using System.Numerics; |
|||
using Avalonia; |
|||
using Avalonia.Platform; |
|||
using Avalonia.Rendering.Composition; |
|||
using SharpDX; |
|||
using SharpDX.Direct3D11; |
|||
using SharpDX.DXGI; |
|||
using SharpDX.Mathematics.Interop; |
|||
using Buffer = SharpDX.Direct3D11.Buffer; |
|||
using DxgiFactory1 = SharpDX.DXGI.Factory1; |
|||
using Matrix = SharpDX.Matrix; |
|||
using D3DDevice = SharpDX.Direct3D11.Device; |
|||
using FeatureLevel = SharpDX.Direct3D.FeatureLevel; |
|||
using Vector3 = SharpDX.Vector3; |
|||
|
|||
namespace GpuInterop.D3DDemo; |
|||
|
|||
public class D3D11DemoControl : DrawingSurfaceDemoBase |
|||
{ |
|||
private D3DDevice _device; |
|||
private D3D11Swapchain _swapchain; |
|||
private SharpDX.Direct3D11.DeviceContext _context; |
|||
private Matrix _view; |
|||
private PixelSize _lastSize; |
|||
private Texture2D _depthBuffer; |
|||
private DepthStencilView _depthView; |
|||
private Matrix _proj; |
|||
private Buffer _constantBuffer; |
|||
private Stopwatch _st = Stopwatch.StartNew(); |
|||
|
|||
protected override (bool success, string info) InitializeGraphicsResources(Compositor compositor, |
|||
CompositionDrawingSurface surface, ICompositionGpuInterop interop) |
|||
{ |
|||
if (interop?.SupportedImageHandleTypes.Contains(KnownPlatformGraphicsExternalImageHandleTypes |
|||
.D3D11TextureGlobalSharedHandle) != true) |
|||
return (false, "DXGI shared handle import is not supported by the current graphics backend"); |
|||
|
|||
var factory = new DxgiFactory1(); |
|||
using var adapter = factory.GetAdapter1(0); |
|||
_device = new D3DDevice(adapter, DeviceCreationFlags.None, new[] |
|||
{ |
|||
FeatureLevel.Level_12_1, |
|||
FeatureLevel.Level_12_0, |
|||
FeatureLevel.Level_11_1, |
|||
FeatureLevel.Level_11_0, |
|||
FeatureLevel.Level_10_0, |
|||
FeatureLevel.Level_9_3, |
|||
FeatureLevel.Level_9_2, |
|||
FeatureLevel.Level_9_1, |
|||
}); |
|||
_swapchain = new D3D11Swapchain(_device, interop, surface); |
|||
_context = _device.ImmediateContext; |
|||
_constantBuffer = D3DContent.CreateMesh(_device); |
|||
_view = Matrix.LookAtLH(new Vector3(0, 0, -5), new Vector3(0, 0, 0), Vector3.UnitY); |
|||
return (true, $"D3D11 ({_device.FeatureLevel}) {adapter.Description1.Description}"); |
|||
} |
|||
|
|||
protected override void FreeGraphicsResources() |
|||
{ |
|||
_swapchain.DisposeAsync(); |
|||
_swapchain = null!; |
|||
Utilities.Dispose(ref _depthView); |
|||
Utilities.Dispose(ref _depthBuffer); |
|||
Utilities.Dispose(ref _constantBuffer); |
|||
Utilities.Dispose(ref _context); |
|||
Utilities.Dispose(ref _device); |
|||
} |
|||
|
|||
protected override bool SupportsDisco => true; |
|||
|
|||
protected override void RenderFrame(PixelSize pixelSize) |
|||
{ |
|||
if (pixelSize == default) |
|||
return; |
|||
if (pixelSize != _lastSize) |
|||
{ |
|||
_lastSize = pixelSize; |
|||
Resize(pixelSize); |
|||
} |
|||
using (_swapchain.BeginDraw(pixelSize, out var renderView)) |
|||
{ |
|||
|
|||
_device.ImmediateContext.OutputMerger.SetTargets(_depthView, renderView); |
|||
var viewProj = Matrix.Multiply(_view, _proj); |
|||
var context = _device.ImmediateContext; |
|||
|
|||
var now = _st.Elapsed.TotalSeconds * 5; |
|||
var scaleX = (float)(1f + Disco * (Math.Sin(now) + 1) / 6); |
|||
var scaleY = (float)(1f + Disco * (Math.Cos(now) + 1) / 8); |
|||
var colorOff =(float) (Math.Sin(now) + 1) / 2 * Disco; |
|||
|
|||
|
|||
// Clear views
|
|||
context.ClearDepthStencilView(_depthView, DepthStencilClearFlags.Depth, 1.0f, 0); |
|||
context.ClearRenderTargetView(renderView, |
|||
new RawColor4(1 - colorOff, colorOff, (float)0.5 + colorOff / 2, 1)); |
|||
|
|||
|
|||
var ypr = Matrix4x4.CreateFromYawPitchRoll(Yaw, Pitch, Roll); |
|||
// Update WorldViewProj Matrix
|
|||
var worldViewProj = Matrix.RotationX((float)Yaw) * Matrix.RotationY((float)Pitch) |
|||
* Matrix.RotationZ((float)Roll) |
|||
* Matrix.Scaling(new Vector3(scaleX, scaleY, 1)) |
|||
* viewProj; |
|||
worldViewProj.Transpose(); |
|||
context.UpdateSubresource(ref worldViewProj, _constantBuffer); |
|||
|
|||
// Draw the cube
|
|||
context.Draw(36, 0); |
|||
|
|||
|
|||
_context.Flush(); |
|||
} |
|||
} |
|||
|
|||
private void Resize(PixelSize size) |
|||
{ |
|||
Utilities.Dispose(ref _depthBuffer); |
|||
_depthBuffer = new Texture2D(_device, |
|||
new Texture2DDescription() |
|||
{ |
|||
Format = Format.D32_Float_S8X24_UInt, |
|||
ArraySize = 1, |
|||
MipLevels = 1, |
|||
Width = (int)size.Width, |
|||
Height = (int)size.Height, |
|||
SampleDescription = new SampleDescription(1, 0), |
|||
Usage = ResourceUsage.Default, |
|||
BindFlags = BindFlags.DepthStencil, |
|||
CpuAccessFlags = CpuAccessFlags.None, |
|||
OptionFlags = ResourceOptionFlags.None |
|||
}); |
|||
|
|||
Utilities.Dispose(ref _depthView); |
|||
_depthView = new DepthStencilView(_device, _depthBuffer); |
|||
|
|||
// Setup targets and viewport for rendering
|
|||
_device.ImmediateContext.Rasterizer.SetViewport(new Viewport(0, 0, (int)size.Width, (int)size.Height, 0.0f, 1.0f)); |
|||
|
|||
// Setup new projection matrix with correct aspect ratio
|
|||
_proj = Matrix.PerspectiveFovLH((float)Math.PI / 4.0f, (float)(size.Width / size.Height), 0.1f, 100.0f); |
|||
} |
|||
} |
|||
@ -0,0 +1,110 @@ |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Avalonia; |
|||
using Avalonia.Platform; |
|||
using Avalonia.Rendering; |
|||
using Avalonia.Rendering.Composition; |
|||
using SharpDX.Direct3D11; |
|||
using SharpDX.DXGI; |
|||
using DxgiFactory1 = SharpDX.DXGI.Factory1; |
|||
using D3DDevice = SharpDX.Direct3D11.Device; |
|||
using DxgiResource = SharpDX.DXGI.Resource; |
|||
|
|||
namespace GpuInterop.D3DDemo; |
|||
|
|||
class D3D11Swapchain : SwapchainBase<D3D11SwapchainImage> |
|||
{ |
|||
private readonly D3DDevice _device; |
|||
|
|||
public D3D11Swapchain(D3DDevice device, ICompositionGpuInterop interop, CompositionDrawingSurface target) |
|||
: base(interop, target) |
|||
{ |
|||
_device = device; |
|||
} |
|||
|
|||
protected override D3D11SwapchainImage CreateImage(PixelSize size) => new(_device, size, Interop, Target); |
|||
|
|||
public IDisposable BeginDraw(PixelSize size, out RenderTargetView view) |
|||
{ |
|||
var rv = BeginDrawCore(size, out var image); |
|||
view = image.RenderTargetView; |
|||
return rv; |
|||
} |
|||
} |
|||
|
|||
public class D3D11SwapchainImage : ISwapchainImage |
|||
{ |
|||
public PixelSize Size { get; } |
|||
private readonly ICompositionGpuInterop _interop; |
|||
private readonly CompositionDrawingSurface _target; |
|||
private readonly Texture2D _texture; |
|||
private readonly KeyedMutex _mutex; |
|||
private readonly IntPtr _handle; |
|||
private PlatformGraphicsExternalImageProperties _properties; |
|||
private ICompositionImportedGpuImage? _imported; |
|||
public Task? LastPresent { get; private set; } |
|||
public RenderTargetView RenderTargetView { get; } |
|||
|
|||
public D3D11SwapchainImage(D3DDevice device, PixelSize size, |
|||
ICompositionGpuInterop interop, |
|||
CompositionDrawingSurface target) |
|||
{ |
|||
Size = size; |
|||
_interop = interop; |
|||
_target = target; |
|||
_texture = new Texture2D(device, |
|||
new Texture2DDescription |
|||
{ |
|||
Format = Format.R8G8B8A8_UNorm, |
|||
Width = size.Width, |
|||
Height = size.Height, |
|||
ArraySize = 1, |
|||
MipLevels = 1, |
|||
SampleDescription = new SampleDescription { Count = 1, Quality = 0 }, |
|||
CpuAccessFlags = default, |
|||
OptionFlags = ResourceOptionFlags.SharedKeyedmutex, |
|||
BindFlags = BindFlags.RenderTarget | BindFlags.ShaderResource |
|||
}); |
|||
_mutex = _texture.QueryInterface<KeyedMutex>(); |
|||
using (var res = _texture.QueryInterface<DxgiResource>()) |
|||
_handle = res.SharedHandle; |
|||
_properties = new PlatformGraphicsExternalImageProperties |
|||
{ |
|||
Width = size.Width, Height = size.Height, Format = PlatformGraphicsExternalImageFormat.B8G8R8A8UNorm |
|||
}; |
|||
|
|||
RenderTargetView = new RenderTargetView(device, _texture); |
|||
} |
|||
|
|||
public void BeginDraw() |
|||
{ |
|||
_mutex.Acquire(0, int.MaxValue); |
|||
} |
|||
|
|||
public void Present() |
|||
{ |
|||
_mutex.Release(1); |
|||
_imported ??= _interop.ImportImage( |
|||
new PlatformHandle(_handle, KnownPlatformGraphicsExternalImageHandleTypes.D3D11TextureGlobalSharedHandle), |
|||
_properties); |
|||
LastPresent = _target.UpdateWithKeyedMutexAsync(_imported, 1, 0); |
|||
} |
|||
|
|||
|
|||
public async ValueTask DisposeAsync() |
|||
{ |
|||
if (LastPresent != null) |
|||
try |
|||
{ |
|||
await LastPresent; |
|||
} |
|||
catch |
|||
{ |
|||
// Ignore
|
|||
} |
|||
|
|||
RenderTargetView.Dispose(); |
|||
_mutex.Dispose(); |
|||
_texture.Dispose(); |
|||
} |
|||
} |
|||
@ -0,0 +1,110 @@ |
|||
using SharpDX; |
|||
using SharpDX.D3DCompiler; |
|||
using SharpDX.Direct3D; |
|||
|
|||
using System; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using SharpDX.Direct2D1; |
|||
using SharpDX.Direct3D11; |
|||
using SharpDX.DXGI; |
|||
using SharpDX.Mathematics.Interop; |
|||
using Buffer = SharpDX.Direct3D11.Buffer; |
|||
using DeviceContext = SharpDX.Direct2D1.DeviceContext; |
|||
using DxgiFactory1 = SharpDX.DXGI.Factory1; |
|||
using Matrix = SharpDX.Matrix; |
|||
using D3DDevice = SharpDX.Direct3D11.Device; |
|||
using DxgiResource = SharpDX.DXGI.Resource; |
|||
using FeatureLevel = SharpDX.Direct3D.FeatureLevel; |
|||
using InputElement = SharpDX.Direct3D11.InputElement; |
|||
|
|||
|
|||
namespace GpuInterop.D3DDemo; |
|||
|
|||
public class D3DContent |
|||
{ |
|||
|
|||
public static Buffer CreateMesh(D3DDevice device) |
|||
{ |
|||
// Compile Vertex and Pixel shaders
|
|||
var vertexShaderByteCode = ShaderBytecode.CompileFromFile("D3DDemo\\MiniCube.fx", "VS", "vs_4_0"); |
|||
var vertexShader = new VertexShader(device, vertexShaderByteCode); |
|||
|
|||
var pixelShaderByteCode = ShaderBytecode.CompileFromFile("D3DDemo\\MiniCube.fx", "PS", "ps_4_0"); |
|||
var pixelShader = new PixelShader(device, pixelShaderByteCode); |
|||
|
|||
var signature = ShaderSignature.GetInputSignature(vertexShaderByteCode); |
|||
|
|||
var inputElements = new[] |
|||
{ |
|||
new InputElement("POSITION", 0, Format.R32G32B32A32_Float, 0, 0), |
|||
new InputElement("COLOR", 0, Format.R32G32B32A32_Float, 16, 0) |
|||
}; |
|||
|
|||
// Layout from VertexShader input signature
|
|||
var layout = new InputLayout( |
|||
device, |
|||
signature, |
|||
inputElements); |
|||
|
|||
// Instantiate Vertex buffer from vertex data
|
|||
using var vertices = Buffer.Create( |
|||
device, |
|||
BindFlags.VertexBuffer, |
|||
new[] |
|||
{ |
|||
new Vector4(-1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f), // Front
|
|||
new Vector4(-1.0f, 1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f), |
|||
new Vector4(1.0f, 1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f), |
|||
new Vector4(-1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f), |
|||
new Vector4(1.0f, 1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f), |
|||
new Vector4(1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f), |
|||
new Vector4(-1.0f, -1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f), // BACK
|
|||
new Vector4(1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f), |
|||
new Vector4(-1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f), |
|||
new Vector4(-1.0f, -1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f), |
|||
new Vector4(1.0f, -1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f), |
|||
new Vector4(1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f), |
|||
new Vector4(-1.0f, 1.0f, -1.0f, 1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f), // Top
|
|||
new Vector4(-1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f), |
|||
new Vector4(1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f), |
|||
new Vector4(-1.0f, 1.0f, -1.0f, 1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f), |
|||
new Vector4(1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f), |
|||
new Vector4(1.0f, 1.0f, -1.0f, 1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f), |
|||
new Vector4(-1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f), // Bottom
|
|||
new Vector4(1.0f, -1.0f, 1.0f, 1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f), |
|||
new Vector4(-1.0f, -1.0f, 1.0f, 1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f), |
|||
new Vector4(-1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f), |
|||
new Vector4(1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f), |
|||
new Vector4(1.0f, -1.0f, 1.0f, 1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f), |
|||
new Vector4(-1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 1.0f, 1.0f), // Left
|
|||
new Vector4(-1.0f, -1.0f, 1.0f, 1.0f), new Vector4(1.0f, 0.0f, 1.0f, 1.0f), |
|||
new Vector4(-1.0f, 1.0f, 1.0f, 1.0f), new Vector4(1.0f, 0.0f, 1.0f, 1.0f), |
|||
new Vector4(-1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 1.0f, 1.0f), |
|||
new Vector4(-1.0f, 1.0f, 1.0f, 1.0f), new Vector4(1.0f, 0.0f, 1.0f, 1.0f), |
|||
new Vector4(-1.0f, 1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 1.0f, 1.0f), |
|||
new Vector4(1.0f, -1.0f, -1.0f, 1.0f), new Vector4(0.0f, 1.0f, 1.0f, 1.0f), // Right
|
|||
new Vector4(1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 1.0f, 1.0f), |
|||
new Vector4(1.0f, -1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 1.0f, 1.0f), |
|||
new Vector4(1.0f, -1.0f, -1.0f, 1.0f), new Vector4(0.0f, 1.0f, 1.0f, 1.0f), |
|||
new Vector4(1.0f, 1.0f, -1.0f, 1.0f), new Vector4(0.0f, 1.0f, 1.0f, 1.0f), |
|||
new Vector4(1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 1.0f, 1.0f), |
|||
}); |
|||
|
|||
// Create Constant Buffer
|
|||
var constantBuffer = new Buffer(device, Utilities.SizeOf<Matrix>(), ResourceUsage.Default, |
|||
BindFlags.ConstantBuffer, CpuAccessFlags.None, ResourceOptionFlags.None, 0); |
|||
|
|||
var context = device.ImmediateContext; |
|||
|
|||
// Prepare All the stages
|
|||
context.InputAssembler.InputLayout = layout; |
|||
context.InputAssembler.PrimitiveTopology = PrimitiveTopology.TriangleList; |
|||
context.InputAssembler.SetVertexBuffers(0, |
|||
new VertexBufferBinding(vertices, Utilities.SizeOf<Vector4>() * 2, 0)); |
|||
context.VertexShader.SetConstantBuffer(0, constantBuffer); |
|||
context.VertexShader.Set(vertexShader); |
|||
context.PixelShader.Set(pixelShader); |
|||
return constantBuffer; |
|||
} |
|||
} |
|||
@ -0,0 +1,47 @@ |
|||
// Copyright (c) 2010-2013 SharpDX - Alexandre Mutel |
|||
// |
|||
// Permission is hereby granted, free of charge, to any person obtaining a copy |
|||
// of this software and associated documentation files (the "Software"), to deal |
|||
// in the Software without restriction, including without limitation the rights |
|||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|||
// copies of the Software, and to permit persons to whom the Software is |
|||
// furnished to do so, subject to the following conditions: |
|||
// |
|||
// The above copyright notice and this permission notice shall be included in |
|||
// all copies or substantial portions of the Software. |
|||
// |
|||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
|||
// THE SOFTWARE. |
|||
struct VS_IN |
|||
{ |
|||
float4 pos : POSITION; |
|||
float4 col : COLOR; |
|||
}; |
|||
|
|||
struct PS_IN |
|||
{ |
|||
float4 pos : SV_POSITION; |
|||
float4 col : COLOR; |
|||
}; |
|||
|
|||
float4x4 worldViewProj; |
|||
|
|||
PS_IN VS( VS_IN input ) |
|||
{ |
|||
PS_IN output = (PS_IN)0; |
|||
|
|||
output.pos = mul(input.pos, worldViewProj); |
|||
output.col = input.col; |
|||
|
|||
return output; |
|||
} |
|||
|
|||
float4 PS( PS_IN input ) : SV_Target |
|||
{ |
|||
return input.col; |
|||
} |
|||
@ -0,0 +1,141 @@ |
|||
using System; |
|||
using System.Numerics; |
|||
using System.Threading.Tasks; |
|||
using Avalonia; |
|||
using Avalonia.Controls; |
|||
using Avalonia.LogicalTree; |
|||
using Avalonia.Rendering.Composition; |
|||
using Avalonia.VisualTree; |
|||
|
|||
namespace GpuInterop; |
|||
|
|||
public abstract class DrawingSurfaceDemoBase : Control, IGpuDemo |
|||
{ |
|||
private CompositionSurfaceVisual? _visual; |
|||
private Compositor? _compositor; |
|||
private Action _update; |
|||
private string _info; |
|||
private bool _updateQueued; |
|||
private bool _initialized; |
|||
|
|||
protected CompositionDrawingSurface Surface { get; private set; } |
|||
|
|||
public DrawingSurfaceDemoBase() |
|||
{ |
|||
_update = UpdateFrame; |
|||
} |
|||
|
|||
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) |
|||
{ |
|||
base.OnAttachedToVisualTree(e); |
|||
Initialize(); |
|||
} |
|||
|
|||
protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e) |
|||
{ |
|||
if (_initialized) |
|||
FreeGraphicsResources(); |
|||
_initialized = false; |
|||
base.OnDetachedFromLogicalTree(e); |
|||
} |
|||
|
|||
async void Initialize() |
|||
{ |
|||
try |
|||
{ |
|||
var selfVisual = ElementComposition.GetElementVisual(this)!; |
|||
_compositor = selfVisual.Compositor; |
|||
|
|||
Surface = _compositor.CreateDrawingSurface(); |
|||
_visual = _compositor.CreateSurfaceVisual(); |
|||
_visual.Size = new Vector2((float)Bounds.Width, (float)Bounds.Height); |
|||
_visual.Surface = Surface; |
|||
ElementComposition.SetElementChildVisual(this, _visual); |
|||
var (res, info) = await DoInitialize(_compositor, Surface); |
|||
_info = info; |
|||
if (ParentControl != null) |
|||
ParentControl.Info = info; |
|||
_initialized = res; |
|||
QueueNextFrame(); |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
if (ParentControl != null) |
|||
ParentControl.Info = e.ToString(); |
|||
} |
|||
} |
|||
|
|||
void UpdateFrame() |
|||
{ |
|||
_updateQueued = false; |
|||
var root = this.GetVisualRoot(); |
|||
if (root == null) |
|||
return; |
|||
|
|||
_visual!.Size = new Vector2((float)Bounds.Width, (float)Bounds.Height); |
|||
var size = PixelSize.FromSize(Bounds.Size, root.RenderScaling); |
|||
RenderFrame(size); |
|||
if (SupportsDisco && Disco > 0) |
|||
QueueNextFrame(); |
|||
} |
|||
|
|||
void QueueNextFrame() |
|||
{ |
|||
if (_initialized && !_updateQueued && _compositor != null) |
|||
{ |
|||
_updateQueued = true; |
|||
_compositor?.RequestCompositionUpdate(_update); |
|||
} |
|||
} |
|||
|
|||
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) |
|||
{ |
|||
if(change.Property == BoundsProperty) |
|||
QueueNextFrame(); |
|||
base.OnPropertyChanged(change); |
|||
} |
|||
|
|||
async Task<(bool success, string info)> DoInitialize(Compositor compositor, |
|||
CompositionDrawingSurface compositionDrawingSurface) |
|||
{ |
|||
var interop = await compositor.TryGetCompositionGpuInterop(); |
|||
if (interop == null) |
|||
return (false, "Compositor doesn't support interop for the current backend"); |
|||
return InitializeGraphicsResources(compositor, compositionDrawingSurface, interop); |
|||
} |
|||
|
|||
protected abstract (bool success, string info) InitializeGraphicsResources(Compositor compositor, |
|||
CompositionDrawingSurface compositionDrawingSurface, ICompositionGpuInterop gpuInterop); |
|||
|
|||
protected abstract void FreeGraphicsResources(); |
|||
|
|||
|
|||
protected abstract void RenderFrame(PixelSize pixelSize); |
|||
protected virtual bool SupportsDisco => false; |
|||
|
|||
public void Update(GpuDemo parent, float yaw, float pitch, float roll, float disco) |
|||
{ |
|||
ParentControl = parent; |
|||
if (ParentControl != null) |
|||
{ |
|||
ParentControl.Info = _info; |
|||
ParentControl.DiscoVisible = true; |
|||
} |
|||
|
|||
Yaw = yaw; |
|||
Pitch = pitch; |
|||
Roll = roll; |
|||
Disco = disco; |
|||
QueueNextFrame(); |
|||
} |
|||
|
|||
public GpuDemo? ParentControl { get; private set; } |
|||
|
|||
public float Disco { get; private set; } |
|||
|
|||
public float Roll { get; private set; } |
|||
|
|||
public float Pitch { get; private set; } |
|||
|
|||
public float Yaw { get; private set; } |
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
<UserControl xmlns="https://github.com/avaloniaui" |
|||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
|||
x:Class="GpuInterop.GpuDemo" |
|||
xmlns:local="clr-namespace:GpuInterop;assembly=GpuInterop"> |
|||
<Grid> |
|||
<ContentControl Content="{Binding $parent[local:GpuDemo].Demo}" /> |
|||
|
|||
<StackPanel> |
|||
<TextBlock Margin="0 40 0 0" Text="{Binding $parent[local:GpuDemo].Info}"/> |
|||
</StackPanel> |
|||
<Grid ColumnDefinitions="*,Auto" Margin="20"> |
|||
<StackPanel Grid.Column="1" MinWidth="300"> |
|||
<TextBlock>Yaw</TextBlock> |
|||
<Slider Value="{Binding $parent[local:GpuDemo].Yaw, Mode=TwoWay}" Maximum="10"/> |
|||
<TextBlock>Pitch</TextBlock> |
|||
<Slider Value="{Binding $parent[local:GpuDemo].Pitch, Mode=TwoWay}" Maximum="10"/> |
|||
<TextBlock>Roll</TextBlock> |
|||
<Slider Value="{Binding $parent[local:GpuDemo].Roll, Mode=TwoWay}" Maximum="10"/> |
|||
<StackPanel IsVisible="{Binding $parent[local:GpuDemo].DiscoVisible}"> |
|||
<StackPanel Orientation="Horizontal"> |
|||
<TextBlock FontWeight="Bold" Foreground="#C000C0">D</TextBlock> |
|||
<TextBlock FontWeight="Bold" Foreground="#00C090">I</TextBlock> |
|||
<TextBlock FontWeight="Bold" Foreground="#90C000">S</TextBlock> |
|||
<TextBlock FontWeight="Bold" Foreground="#C09000">C</TextBlock> |
|||
<TextBlock FontWeight="Bold" Foreground="#00C090">O</TextBlock> |
|||
</StackPanel> |
|||
<Slider Value="{Binding $parent[local:GpuDemo].Disco, Mode=TwoWay}" Maximum="1"/> |
|||
</StackPanel> |
|||
</StackPanel> |
|||
</Grid> |
|||
</Grid> |
|||
</UserControl> |
|||
@ -0,0 +1,116 @@ |
|||
using Avalonia; |
|||
using Avalonia.Controls; |
|||
using Avalonia.Markup.Xaml; |
|||
|
|||
namespace GpuInterop; |
|||
|
|||
public class GpuDemo : UserControl |
|||
{ |
|||
public GpuDemo() |
|||
{ |
|||
AvaloniaXamlLoader.Load(this); |
|||
} |
|||
|
|||
private float _yaw = 5; |
|||
|
|||
public static readonly DirectProperty<GpuDemo, float> YawProperty = |
|||
AvaloniaProperty.RegisterDirect<GpuDemo, float>("Yaw", o => o.Yaw, (o, v) => o.Yaw = v); |
|||
|
|||
public float Yaw |
|||
{ |
|||
get => _yaw; |
|||
set => SetAndRaise(YawProperty, ref _yaw, value); |
|||
} |
|||
|
|||
private float _pitch = 5; |
|||
|
|||
public static readonly DirectProperty<GpuDemo, float> PitchProperty = |
|||
AvaloniaProperty.RegisterDirect<GpuDemo, float>("Pitch", o => o.Pitch, (o, v) => o.Pitch = v); |
|||
|
|||
public float Pitch |
|||
{ |
|||
get => _pitch; |
|||
set => SetAndRaise(PitchProperty, ref _pitch, value); |
|||
} |
|||
|
|||
|
|||
private float _roll = 5; |
|||
|
|||
public static readonly DirectProperty<GpuDemo, float> RollProperty = |
|||
AvaloniaProperty.RegisterDirect<GpuDemo, float>("Roll", o => o.Roll, (o, v) => o.Roll = v); |
|||
|
|||
public float Roll |
|||
{ |
|||
get => _roll; |
|||
set => SetAndRaise(RollProperty, ref _roll, value); |
|||
} |
|||
|
|||
|
|||
private float _disco; |
|||
|
|||
public static readonly DirectProperty<GpuDemo, float> DiscoProperty = |
|||
AvaloniaProperty.RegisterDirect<GpuDemo, float>("Disco", o => o.Disco, (o, v) => o.Disco = v); |
|||
|
|||
public float Disco |
|||
{ |
|||
get => _disco; |
|||
set => SetAndRaise(DiscoProperty, ref _disco, value); |
|||
} |
|||
|
|||
private string _info = string.Empty; |
|||
|
|||
public static readonly DirectProperty<GpuDemo, string> InfoProperty = |
|||
AvaloniaProperty.RegisterDirect<GpuDemo, string>("Info", o => o.Info, (o, v) => o.Info = v); |
|||
|
|||
public string Info |
|||
{ |
|||
get => _info; |
|||
set => SetAndRaise(InfoProperty, ref _info, value); |
|||
} |
|||
|
|||
private bool _discoVisible; |
|||
|
|||
public static readonly DirectProperty<GpuDemo, bool> DiscoVisibleProperty = |
|||
AvaloniaProperty.RegisterDirect<GpuDemo, bool>("DiscoVisible", o => o.DiscoVisible, |
|||
(o, v) => o._discoVisible = v); |
|||
|
|||
public bool DiscoVisible |
|||
{ |
|||
get => _discoVisible; |
|||
set => SetAndRaise(DiscoVisibleProperty, ref _discoVisible, value); |
|||
} |
|||
|
|||
private IGpuDemo _demo; |
|||
|
|||
public static readonly DirectProperty<GpuDemo, IGpuDemo> DemoProperty = |
|||
AvaloniaProperty.RegisterDirect<GpuDemo, IGpuDemo>("Demo", o => o.Demo, |
|||
(o, v) => o._demo = v); |
|||
|
|||
public IGpuDemo Demo |
|||
{ |
|||
get => _demo; |
|||
set => SetAndRaise(DemoProperty, ref _demo, value); |
|||
} |
|||
|
|||
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) |
|||
{ |
|||
if (change.Property == YawProperty |
|||
|| change.Property == PitchProperty |
|||
|| change.Property == RollProperty |
|||
|| change.Property == DiscoProperty |
|||
|| change.Property == DemoProperty |
|||
) |
|||
{ |
|||
if (change.Property == DemoProperty) |
|||
((IGpuDemo)change.OldValue)?.Update(null, 0, 0, 0, 0); |
|||
_demo?.Update(this, Yaw, Pitch, Roll, Disco); |
|||
} |
|||
|
|||
base.OnPropertyChanged(change); |
|||
} |
|||
} |
|||
|
|||
public interface IGpuDemo |
|||
{ |
|||
void Update(GpuDemo parent, float yaw, float pitch, float roll, float disco); |
|||
} |
|||
@ -0,0 +1,51 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<OutputType>Exe</OutputType> |
|||
<TargetFramework>net7.0</TargetFramework> |
|||
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch> |
|||
<Nullable>enable</Nullable> |
|||
<GenerateDocumentationFile>false</GenerateDocumentationFile> |
|||
<UseD3DCompiler>true</UseD3DCompiler> |
|||
<UseSharpDXMathematics>true</UseSharpDXMathematics> |
|||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj" /> |
|||
<ProjectReference Include="..\..\src\Avalonia.Controls.ColorPicker\Avalonia.Controls.ColorPicker.csproj" /> |
|||
<ProjectReference Include="..\..\src\Avalonia.Controls.DataGrid\Avalonia.Controls.DataGrid.csproj" /> |
|||
<ProjectReference Include="..\..\src\Avalonia.Themes.Fluent\Avalonia.Themes.Fluent.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<Content Include="D3DDemo\MiniCube.fx"> |
|||
<CopyToOutputDirectory>Always</CopyToOutputDirectory> |
|||
</Content> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Silk.NET.Vulkan" Version="2.16.0" /> |
|||
<PackageReference Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.16.0" /> |
|||
<PackageReference Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.16.0" /> |
|||
<PackageReference Include="System.Reactive" Version="5.0.0" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<Folder Include="VulkanDemo\Assets\Shaders\Assets" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<Compile Include="..\..\src\Avalonia.Base\Rendering\SwapchainBase.cs" /> |
|||
<None Remove="VulkanDemo\Assets\Shaders\frag.spirv" /> |
|||
<EmbeddedResource Include="VulkanDemo\Assets\Shaders\frag.spirv" /> |
|||
<None Remove="VulkanDemo\Assets\Shaders\vert.spirv" /> |
|||
<EmbeddedResource Include="VulkanDemo\Assets\Shaders\vert.spirv" /> |
|||
<EmbeddedResource Include="../ControlCatalog/Pages/teapot.bin" /> |
|||
</ItemGroup> |
|||
|
|||
<Import Project="..\..\build\SampleApp.props" /> |
|||
<Import Project="..\..\build\ReferenceCoreLibraries.props" /> |
|||
<Import Project="..\..\build\BuildTargets.targets" /> |
|||
<Import Project="..\..\build\SharpDX.props" /> |
|||
</Project> |
|||
@ -0,0 +1,13 @@ |
|||
<Window xmlns="https://github.com/avaloniaui" |
|||
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml' |
|||
xmlns:gpuInterop="clr-namespace:GpuInterop" |
|||
xmlns:d3DDemo="clr-namespace:GpuInterop.D3DDemo" |
|||
xmlns:vulkanDemo="clr-namespace:GpuInterop.VulkanDemo" |
|||
x:Class="GpuInterop.MainWindow"> |
|||
<gpuInterop:GpuDemo> |
|||
<gpuInterop:GpuDemo.Demo> |
|||
<!--<d3DDemo:D3D11DemoControl/>--> |
|||
<vulkanDemo:VulkanDemoControl/> |
|||
</gpuInterop:GpuDemo.Demo> |
|||
</gpuInterop:GpuDemo> |
|||
</Window> |
|||
@ -0,0 +1,22 @@ |
|||
using Avalonia; |
|||
using Avalonia.Controls; |
|||
using Avalonia.Markup.Xaml; |
|||
using Avalonia.Rendering; |
|||
|
|||
namespace GpuInterop |
|||
{ |
|||
public class MainWindow : Window |
|||
{ |
|||
public MainWindow() |
|||
{ |
|||
InitializeComponent(); |
|||
this.AttachDevTools(); |
|||
Renderer.Diagnostics.DebugOverlays = RendererDebugOverlays.Fps; |
|||
} |
|||
|
|||
private void InitializeComponent() |
|||
{ |
|||
AvaloniaXamlLoader.Load(this); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
global using System.Reactive.Disposables; |
|||
using Avalonia; |
|||
namespace GpuInterop |
|||
{ |
|||
public class Program |
|||
{ |
|||
static void Main(string[] args) => BuildAvaloniaApp() |
|||
.StartWithClassicDesktopLifetime(args); |
|||
|
|||
public static AppBuilder BuildAvaloniaApp() => |
|||
AppBuilder.Configure<App>() |
|||
.UsePlatformDetect() |
|||
.LogToTrace(); |
|||
} |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
#!/usr/bin/make -f
|
|||
|
|||
all: vert.spirv frag.spirv |
|||
.PHONY: all |
|||
|
|||
vert.spirv: vert.glsl |
|||
glslc -fshader-stage=vert vert.glsl -o vert.spirv |
|||
|
|||
frag.spirv: frag.glsl |
|||
glslc -fshader-stage=frag frag.glsl -o frag.spirv |
|||
|
|||
|
|||
@ -0,0 +1,42 @@ |
|||
#version 450 |
|||
layout(location = 0) in vec3 FragPos; |
|||
layout(location = 1) in vec3 VecPos; |
|||
layout(location = 2) in vec3 Normal; |
|||
layout(push_constant) uniform constants{ |
|||
layout(offset = 0) float maxY; |
|||
layout(offset = 4) float minY; |
|||
layout(offset = 8) float time; |
|||
layout(offset = 12) float disco; |
|||
}; |
|||
layout(location = 0) out vec4 outFragColor; |
|||
|
|||
void main() |
|||
{ |
|||
float y = (VecPos.y - minY) / (maxY - minY); |
|||
float c = cos(atan(VecPos.x, VecPos.z) * 20.0 + time * 40.0 + y * 50.0); |
|||
float s = sin(-atan(VecPos.z, VecPos.x) * 20.0 - time * 20.0 - y * 30.0); |
|||
|
|||
vec3 discoColor = vec3( |
|||
0.5 + abs(0.5 - y) * cos(time * 10.0), |
|||
0.25 + (smoothstep(0.3, 0.8, y) * (0.5 - c / 4.0)), |
|||
0.25 + abs((smoothstep(0.1, 0.4, y) * (0.5 - s / 4.0)))); |
|||
|
|||
vec3 objectColor = vec3((1.0 - y), 0.40 + y / 4.0, y * 0.75 + 0.25); |
|||
objectColor = objectColor * (1.0 - disco) + discoColor * disco; |
|||
|
|||
float ambientStrength = 0.3; |
|||
vec3 lightColor = vec3(1.0, 1.0, 1.0); |
|||
vec3 lightPos = vec3(maxY * 2.0, maxY * 2.0, maxY * 2.0); |
|||
vec3 ambient = ambientStrength * lightColor; |
|||
|
|||
|
|||
vec3 norm = normalize(Normal); |
|||
vec3 lightDir = normalize(lightPos - FragPos); |
|||
|
|||
float diff = max(dot(norm, lightDir), 0.0); |
|||
vec3 diffuse = diff * lightColor; |
|||
|
|||
vec3 result = (ambient + diffuse) * objectColor; |
|||
outFragColor = vec4(result, 1.0); |
|||
|
|||
} |
|||
Binary file not shown.
@ -0,0 +1,36 @@ |
|||
#version 450 |
|||
layout(location = 0) in vec3 aPos; |
|||
layout(location = 1) in vec3 aNormal; |
|||
|
|||
layout(location = 0) out vec3 FragPos; |
|||
layout(location = 1) out vec3 VecPos; |
|||
layout(location = 2) out vec3 Normal; |
|||
|
|||
layout(push_constant) uniform constants{ |
|||
float maxY; |
|||
float minY; |
|||
float time; |
|||
float disco; |
|||
mat4 model; |
|||
}; |
|||
|
|||
layout(binding = 0) uniform UniformBufferObject { |
|||
mat4 projection; |
|||
} ubo; |
|||
|
|||
void main() |
|||
{ |
|||
float discoScale = sin(time * 10.0) / 10.0; |
|||
float distortionX = 1.0 + disco * cos(time * 20.0) / 10.0; |
|||
|
|||
float scale = 1.0 + disco * discoScale; |
|||
|
|||
vec3 scaledPos = aPos; |
|||
scaledPos.x = scaledPos.x * distortionX; |
|||
|
|||
scaledPos *= scale; |
|||
gl_Position = ubo.projection * model * vec4(scaledPos, 1.0); |
|||
FragPos = vec3(model * vec4(aPos, 1.0)); |
|||
VecPos = aPos; |
|||
Normal = normalize(vec3(model * vec4(aNormal, 1.0))); |
|||
} |
|||
Binary file not shown.
@ -0,0 +1,47 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Runtime.InteropServices; |
|||
|
|||
namespace GpuInterop.VulkanDemo; |
|||
|
|||
unsafe class ByteString : IDisposable |
|||
{ |
|||
public IntPtr Pointer { get; } |
|||
|
|||
public ByteString(string s) |
|||
{ |
|||
Pointer = Marshal.StringToHGlobalAnsi(s); |
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
Marshal.FreeHGlobal(Pointer); |
|||
} |
|||
|
|||
public static implicit operator byte*(ByteString h) => (byte*)h.Pointer; |
|||
} |
|||
|
|||
unsafe class ByteStringList : IDisposable |
|||
{ |
|||
private List<ByteString> _inner; |
|||
private byte** _ptr; |
|||
|
|||
public ByteStringList(IEnumerable<string> items) |
|||
{ |
|||
_inner = items.Select(x => new ByteString(x)).ToList(); |
|||
_ptr = (byte**)Marshal.AllocHGlobal(IntPtr.Size * _inner.Count + 1); |
|||
for (var c = 0; c < _inner.Count; c++) |
|||
_ptr[c] = (byte*)_inner[c].Pointer; |
|||
} |
|||
|
|||
public int Count => _inner.Count; |
|||
public uint UCount => (uint)_inner.Count; |
|||
|
|||
public void Dispose() |
|||
{ |
|||
Marshal.FreeHGlobal(new IntPtr(_ptr)); |
|||
} |
|||
|
|||
public static implicit operator byte**(ByteStringList h) => h._ptr; |
|||
} |
|||
@ -0,0 +1,54 @@ |
|||
using System; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
using Avalonia; |
|||
using SharpDX.Direct3D; |
|||
using SharpDX.Direct3D11; |
|||
using SharpDX.DXGI; |
|||
using D3DDevice = SharpDX.Direct3D11.Device; |
|||
using DxgiFactory1 = SharpDX.DXGI.Factory1; |
|||
namespace GpuInterop.VulkanDemo; |
|||
|
|||
public class D3DMemoryHelper |
|||
{ |
|||
public static D3DDevice CreateDeviceByLuid(Span<byte> luid) |
|||
{ |
|||
var factory = new DxgiFactory1(); |
|||
var longLuid = MemoryMarshal.Cast<byte, long>(luid)[0]; |
|||
for (var c = 0; c < factory.GetAdapterCount1(); c++) |
|||
{ |
|||
using var adapter = factory.GetAdapter1(0); |
|||
if (adapter.Description1.Luid != longLuid) |
|||
continue; |
|||
|
|||
return new D3DDevice(adapter, DeviceCreationFlags.None, |
|||
new[] |
|||
{ |
|||
FeatureLevel.Level_12_1, FeatureLevel.Level_12_0, FeatureLevel.Level_11_1, |
|||
FeatureLevel.Level_11_0, FeatureLevel.Level_10_0, FeatureLevel.Level_9_3, |
|||
FeatureLevel.Level_9_2, FeatureLevel.Level_9_1, |
|||
}); |
|||
} |
|||
|
|||
throw new ArgumentException("Device with the corresponding LUID not found"); |
|||
} |
|||
|
|||
public static Texture2D CreateMemoryHandle(D3DDevice device, PixelSize size, Silk.NET.Vulkan.Format format) |
|||
{ |
|||
if (format != Silk.NET.Vulkan.Format.R8G8B8A8Unorm) |
|||
throw new ArgumentException("Not supported format"); |
|||
return new Texture2D(device, |
|||
new Texture2DDescription |
|||
{ |
|||
Format = Format.R8G8B8A8_UNorm, |
|||
Width = size.Width, |
|||
Height = size.Height, |
|||
ArraySize = 1, |
|||
MipLevels = 1, |
|||
SampleDescription = new SampleDescription { Count = 1, Quality = 0 }, |
|||
CpuAccessFlags = default, |
|||
OptionFlags = ResourceOptionFlags.SharedKeyedmutex|ResourceOptionFlags.SharedNthandle, |
|||
BindFlags = BindFlags.RenderTarget | BindFlags.ShaderResource |
|||
}); |
|||
} |
|||
} |
|||
@ -0,0 +1,80 @@ |
|||
using System; |
|||
using System.Runtime.CompilerServices; |
|||
using Silk.NET.Vulkan; |
|||
using SilkNetDemo; |
|||
using Buffer = Silk.NET.Vulkan.Buffer; |
|||
using SystemBuffer = System.Buffer; |
|||
|
|||
namespace GpuInterop.VulkanDemo; |
|||
|
|||
static class VulkanBufferHelper |
|||
{ |
|||
public unsafe static void AllocateBuffer<T>(VulkanContext vk, |
|||
BufferUsageFlags bufferUsageFlags, |
|||
out Buffer buffer, out DeviceMemory memory, |
|||
Span<T> initialData) where T:unmanaged |
|||
{ |
|||
var api = vk.Api; |
|||
var device = vk.Device; |
|||
|
|||
var size = Unsafe.SizeOf<T>() * initialData.Length; |
|||
var bufferInfo = new BufferCreateInfo() |
|||
{ |
|||
SType = StructureType.BufferCreateInfo, |
|||
Size = (ulong)size, |
|||
Usage = bufferUsageFlags, |
|||
SharingMode = SharingMode.Exclusive |
|||
}; |
|||
api.CreateBuffer(device, bufferInfo, null, out buffer).ThrowOnError(); |
|||
|
|||
api.GetBufferMemoryRequirements(device, buffer, out var memoryRequirements); |
|||
|
|||
var physicalDevice = vk.PhysicalDevice; |
|||
|
|||
var memoryAllocateInfo = new MemoryAllocateInfo |
|||
{ |
|||
SType = StructureType.MemoryAllocateInfo, |
|||
AllocationSize = memoryRequirements.Size, |
|||
MemoryTypeIndex = (uint)FindSuitableMemoryTypeIndex(api, |
|||
physicalDevice, |
|||
memoryRequirements.MemoryTypeBits, |
|||
MemoryPropertyFlags.HostCoherentBit | |
|||
MemoryPropertyFlags.HostVisibleBit) |
|||
}; |
|||
|
|||
api.AllocateMemory(device, memoryAllocateInfo, null, out memory).ThrowOnError(); |
|||
api.BindBufferMemory(device, buffer, memory, 0); |
|||
UpdateBufferMemory(vk, memory, initialData); |
|||
} |
|||
|
|||
public static unsafe void UpdateBufferMemory<T>(VulkanContext vk, DeviceMemory memory, |
|||
Span<T> data) where T : unmanaged |
|||
{ |
|||
var api = vk.Api; |
|||
var device = vk.Device; |
|||
|
|||
var size = data.Length * Unsafe.SizeOf<T>(); |
|||
void* pointer = null; |
|||
api.MapMemory(device, memory, 0, (ulong)size, 0, ref pointer); |
|||
|
|||
data.CopyTo(new Span<T>(pointer, size)); |
|||
|
|||
api.UnmapMemory(device, memory); |
|||
|
|||
} |
|||
|
|||
private static int FindSuitableMemoryTypeIndex(Vk api, PhysicalDevice physicalDevice, uint memoryTypeBits, |
|||
MemoryPropertyFlags flags) |
|||
{ |
|||
api.GetPhysicalDeviceMemoryProperties(physicalDevice, out var properties); |
|||
|
|||
for (var i = 0; i < properties.MemoryTypeCount; i++) |
|||
{ |
|||
var type = properties.MemoryTypes[i]; |
|||
|
|||
if ((memoryTypeBits & (1 << i)) != 0 && type.PropertyFlags.HasFlag(flags)) return i; |
|||
} |
|||
|
|||
return -1; |
|||
} |
|||
} |
|||
@ -0,0 +1,223 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using Silk.NET.Vulkan; |
|||
using SilkNetDemo; |
|||
|
|||
namespace Avalonia.Vulkan |
|||
{ |
|||
public class VulkanCommandBufferPool : IDisposable |
|||
{ |
|||
private readonly Vk _api; |
|||
private readonly Device _device; |
|||
private readonly Queue _queue; |
|||
private readonly CommandPool _commandPool; |
|||
|
|||
private readonly List<VulkanCommandBuffer> _usedCommandBuffers = new(); |
|||
private object _lock = new object(); |
|||
|
|||
public unsafe VulkanCommandBufferPool(Vk api, Device device, Queue queue, uint queueFamilyIndex) |
|||
{ |
|||
_api = api; |
|||
_device = device; |
|||
_queue = queue; |
|||
|
|||
var commandPoolCreateInfo = new CommandPoolCreateInfo |
|||
{ |
|||
SType = StructureType.CommandPoolCreateInfo, |
|||
Flags = CommandPoolCreateFlags.ResetCommandBufferBit, |
|||
QueueFamilyIndex = queueFamilyIndex |
|||
}; |
|||
|
|||
_api.CreateCommandPool(_device, commandPoolCreateInfo, null, out _commandPool) |
|||
.ThrowOnError(); |
|||
} |
|||
|
|||
public unsafe void Dispose() |
|||
{ |
|||
lock (_lock) |
|||
{ |
|||
FreeUsedCommandBuffers(); |
|||
_api.DestroyCommandPool(_device, _commandPool, null); |
|||
} |
|||
} |
|||
|
|||
private CommandBuffer AllocateCommandBuffer() |
|||
{ |
|||
var commandBufferAllocateInfo = new CommandBufferAllocateInfo |
|||
{ |
|||
SType = StructureType.CommandBufferAllocateInfo, |
|||
CommandPool = _commandPool, |
|||
CommandBufferCount = 1, |
|||
Level = CommandBufferLevel.Primary |
|||
}; |
|||
|
|||
lock (_lock) |
|||
{ |
|||
_api.AllocateCommandBuffers(_device, commandBufferAllocateInfo, out var commandBuffer); |
|||
|
|||
return commandBuffer; |
|||
} |
|||
} |
|||
|
|||
public VulkanCommandBuffer CreateCommandBuffer() |
|||
{ |
|||
return new(_api, _device, _queue, this); |
|||
} |
|||
|
|||
public void FreeUsedCommandBuffers() |
|||
{ |
|||
lock (_lock) |
|||
{ |
|||
foreach (var usedCommandBuffer in _usedCommandBuffers) usedCommandBuffer.Dispose(); |
|||
|
|||
_usedCommandBuffers.Clear(); |
|||
} |
|||
} |
|||
|
|||
private void DisposeCommandBuffer(VulkanCommandBuffer commandBuffer) |
|||
{ |
|||
lock (_lock) |
|||
{ |
|||
_usedCommandBuffers.Add(commandBuffer); |
|||
} |
|||
} |
|||
|
|||
public class VulkanCommandBuffer : IDisposable |
|||
{ |
|||
private readonly VulkanCommandBufferPool _commandBufferPool; |
|||
private readonly Vk _api; |
|||
private readonly Device _device; |
|||
private readonly Queue _queue; |
|||
private readonly Fence _fence; |
|||
private bool _hasEnded; |
|||
private bool _hasStarted; |
|||
|
|||
public IntPtr Handle => InternalHandle.Handle; |
|||
|
|||
internal CommandBuffer InternalHandle { get; } |
|||
|
|||
internal unsafe VulkanCommandBuffer(Vk api, Device device, Queue queue, VulkanCommandBufferPool commandBufferPool) |
|||
{ |
|||
_api = api; |
|||
_device = device; |
|||
_queue = queue; |
|||
_commandBufferPool = commandBufferPool; |
|||
|
|||
InternalHandle = _commandBufferPool.AllocateCommandBuffer(); |
|||
|
|||
var fenceCreateInfo = new FenceCreateInfo() |
|||
{ |
|||
SType = StructureType.FenceCreateInfo, |
|||
Flags = FenceCreateFlags.SignaledBit |
|||
}; |
|||
|
|||
api.CreateFence(device, fenceCreateInfo, null, out _fence); |
|||
} |
|||
|
|||
public unsafe void Dispose() |
|||
{ |
|||
_api.WaitForFences(_device, 1, _fence, true, ulong.MaxValue); |
|||
lock (_commandBufferPool._lock) |
|||
{ |
|||
_api.FreeCommandBuffers(_device, _commandBufferPool._commandPool, 1, InternalHandle); |
|||
} |
|||
_api.DestroyFence(_device, _fence, null); |
|||
} |
|||
|
|||
public void BeginRecording() |
|||
{ |
|||
if (!_hasStarted) |
|||
{ |
|||
_hasStarted = true; |
|||
|
|||
var beginInfo = new CommandBufferBeginInfo |
|||
{ |
|||
SType = StructureType.CommandBufferBeginInfo, |
|||
Flags = CommandBufferUsageFlags.OneTimeSubmitBit |
|||
}; |
|||
|
|||
_api.BeginCommandBuffer(InternalHandle, beginInfo); |
|||
} |
|||
} |
|||
|
|||
public void EndRecording() |
|||
{ |
|||
if (_hasStarted && !_hasEnded) |
|||
{ |
|||
_hasEnded = true; |
|||
|
|||
_api.EndCommandBuffer(InternalHandle); |
|||
} |
|||
} |
|||
|
|||
public void Submit() |
|||
{ |
|||
Submit(null, null, null, _fence); |
|||
} |
|||
|
|||
public class KeyedMutexSubmitInfo |
|||
{ |
|||
public ulong? AcquireKey { get; set; } |
|||
public ulong? ReleaseKey { get; set; } |
|||
public DeviceMemory DeviceMemory { get; set; } |
|||
} |
|||
|
|||
public unsafe void Submit( |
|||
ReadOnlySpan<Semaphore> waitSemaphores, |
|||
ReadOnlySpan<PipelineStageFlags> waitDstStageMask = default, |
|||
ReadOnlySpan<Semaphore> signalSemaphores = default, |
|||
Fence? fence = null, |
|||
KeyedMutexSubmitInfo keyedMutex = null) |
|||
{ |
|||
EndRecording(); |
|||
|
|||
if (!fence.HasValue) |
|||
fence = _fence; |
|||
|
|||
|
|||
ulong acquireKey = keyedMutex?.AcquireKey ?? 0, releaseKey = keyedMutex?.ReleaseKey ?? 0; |
|||
DeviceMemory devMem = keyedMutex?.DeviceMemory ?? default; |
|||
uint timeout = uint.MaxValue; |
|||
Win32KeyedMutexAcquireReleaseInfoKHR mutex = default; |
|||
if (keyedMutex != null) |
|||
mutex = new Win32KeyedMutexAcquireReleaseInfoKHR |
|||
{ |
|||
SType = StructureType.Win32KeyedMutexAcquireReleaseInfoKhr, |
|||
AcquireCount = keyedMutex.AcquireKey.HasValue ? 1u : 0u, |
|||
ReleaseCount = keyedMutex.ReleaseKey.HasValue ? 1u : 0u, |
|||
PAcquireKeys = &acquireKey, |
|||
PReleaseKeys = &releaseKey, |
|||
PAcquireSyncs = &devMem, |
|||
PReleaseSyncs = &devMem, |
|||
PAcquireTimeouts = &timeout |
|||
}; |
|||
|
|||
fixed (Semaphore* pWaitSemaphores = waitSemaphores, pSignalSemaphores = signalSemaphores) |
|||
{ |
|||
fixed (PipelineStageFlags* pWaitDstStageMask = waitDstStageMask) |
|||
{ |
|||
var commandBuffer = InternalHandle; |
|||
var submitInfo = new SubmitInfo |
|||
{ |
|||
PNext = keyedMutex != null ? &mutex : null, |
|||
SType = StructureType.SubmitInfo, |
|||
WaitSemaphoreCount = waitSemaphores != null ? (uint)waitSemaphores.Length : 0, |
|||
PWaitSemaphores = pWaitSemaphores, |
|||
PWaitDstStageMask = pWaitDstStageMask, |
|||
CommandBufferCount = 1, |
|||
PCommandBuffers = &commandBuffer, |
|||
SignalSemaphoreCount = signalSemaphores != null ? (uint)signalSemaphores.Length : 0, |
|||
PSignalSemaphores = pSignalSemaphores, |
|||
}; |
|||
|
|||
_api.ResetFences(_device, 1, fence.Value); |
|||
|
|||
_api.QueueSubmit(_queue, 1, submitInfo, fence.Value); |
|||
} |
|||
} |
|||
|
|||
_commandBufferPool.DisposeCommandBuffer(this); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,829 @@ |
|||
using System; |
|||
using System.Diagnostics; |
|||
using System.IO; |
|||
using System.Linq; |
|||
using System.Numerics; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
using Avalonia; |
|||
using Avalonia.Threading; |
|||
using Silk.NET.Vulkan; |
|||
using SilkNetDemo; |
|||
using Buffer = System.Buffer; |
|||
using Image = Silk.NET.Vulkan.Image; |
|||
|
|||
namespace GpuInterop.VulkanDemo; |
|||
|
|||
unsafe class VulkanContent : IDisposable |
|||
{ |
|||
private readonly VulkanContext _context; |
|||
private ShaderModule _vertShader; |
|||
private ShaderModule _fragShader; |
|||
private PipelineLayout _pipelineLayout; |
|||
private RenderPass _renderPass; |
|||
private Pipeline _pipeline; |
|||
private DescriptorSetLayout _descriptorSetLayout; |
|||
private Silk.NET.Vulkan.Buffer _vertexBuffer; |
|||
private DeviceMemory _vertexBufferMemory; |
|||
private Silk.NET.Vulkan.Buffer _indexBuffer; |
|||
private DeviceMemory _indexBufferMemory; |
|||
private Silk.NET.Vulkan.Buffer _uniformBuffer; |
|||
private DeviceMemory _uniformBufferMemory; |
|||
private Framebuffer _framebuffer; |
|||
|
|||
private Image _depthImage; |
|||
private DeviceMemory _depthImageMemory; |
|||
private ImageView _depthImageView; |
|||
|
|||
public VulkanContent(VulkanContext context) |
|||
{ |
|||
_context = context; |
|||
var name = typeof(VulkanContent).Assembly.GetManifestResourceNames().First(x => x.Contains("teapot.bin")); |
|||
using (var sr = new BinaryReader(typeof(VulkanContent).Assembly.GetManifestResourceStream(name))) |
|||
{ |
|||
var buf = new byte[sr.ReadInt32()]; |
|||
sr.Read(buf, 0, buf.Length); |
|||
var points = new float[buf.Length / 4]; |
|||
Buffer.BlockCopy(buf, 0, points, 0, buf.Length); |
|||
buf = new byte[sr.ReadInt32()]; |
|||
sr.Read(buf, 0, buf.Length); |
|||
_indices = new ushort[buf.Length / 2]; |
|||
Buffer.BlockCopy(buf, 0, _indices, 0, buf.Length); |
|||
_points = new Vertex[points.Length / 3]; |
|||
for (var primitive = 0; primitive < points.Length / 3; primitive++) |
|||
{ |
|||
var srci = primitive * 3; |
|||
_points[primitive] = new Vertex |
|||
{ |
|||
Position = new Vector3(points[srci], points[srci + 1], points[srci + 2]) |
|||
}; |
|||
} |
|||
|
|||
for (int i = 0; i < _indices.Length; i += 3) |
|||
{ |
|||
Vector3 a = _points[_indices[i]].Position; |
|||
Vector3 b = _points[_indices[i + 1]].Position; |
|||
Vector3 c = _points[_indices[i + 2]].Position; |
|||
var normal = Vector3.Normalize(Vector3.Cross(c - b, a - b)); |
|||
|
|||
_points[_indices[i]].Normal += normal; |
|||
_points[_indices[i + 1]].Normal += normal; |
|||
_points[_indices[i + 2]].Normal += normal; |
|||
} |
|||
|
|||
for (int i = 0; i < _points.Length; i++) |
|||
{ |
|||
_points[i].Normal = Vector3.Normalize(_points[i].Normal); |
|||
_maxY = Math.Max(_maxY, _points[i].Position.Y); |
|||
_minY = Math.Min(_minY, _points[i].Position.Y); |
|||
} |
|||
} |
|||
|
|||
var api = _context.Api; |
|||
var device = _context.Device; |
|||
var vertShaderData = GetShader(false); |
|||
var fragShaderData = GetShader(true); |
|||
|
|||
fixed (byte* ptr = vertShaderData) |
|||
{ |
|||
var shaderCreateInfo = new ShaderModuleCreateInfo() |
|||
{ |
|||
SType = StructureType.ShaderModuleCreateInfo, |
|||
CodeSize = (nuint)vertShaderData.Length, |
|||
PCode = (uint*)ptr, |
|||
}; |
|||
|
|||
api.CreateShaderModule(device, shaderCreateInfo, null, out _vertShader); |
|||
} |
|||
|
|||
fixed (byte* ptr = fragShaderData) |
|||
{ |
|||
var shaderCreateInfo = new ShaderModuleCreateInfo() |
|||
{ |
|||
SType = StructureType.ShaderModuleCreateInfo, |
|||
CodeSize = (nuint)fragShaderData.Length, |
|||
PCode = (uint*)ptr, |
|||
}; |
|||
|
|||
api.CreateShaderModule(device, shaderCreateInfo, null, out _fragShader); |
|||
} |
|||
|
|||
CreateBuffers(); |
|||
} |
|||
|
|||
private byte[] GetShader(bool fragment) |
|||
{ |
|||
var name = typeof(VulkanContent).Assembly.GetManifestResourceNames() |
|||
.First(x => x.Contains((fragment ? "frag" : "vert") + ".spirv")); |
|||
using (var sr = typeof(VulkanContent).Assembly.GetManifestResourceStream(name)) |
|||
{ |
|||
using (var mem = new MemoryStream()) |
|||
{ |
|||
sr.CopyTo(mem); |
|||
return mem.ToArray(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
private PixelSize? _previousImageSize = PixelSize.Empty; |
|||
|
|||
|
|||
public void Render(VulkanImage image, |
|||
double yaw, double pitch, double roll, double disco) |
|||
{ |
|||
|
|||
var api = _context.Api; |
|||
|
|||
if (image.Size != _previousImageSize) |
|||
CreateTemporalObjects(image.Size); |
|||
|
|||
_previousImageSize = image.Size; |
|||
|
|||
|
|||
var model = Matrix4x4.CreateFromYawPitchRoll((float)yaw, (float)pitch, (float)roll); |
|||
var view = Matrix4x4.CreateLookAt(new Vector3(25, 25, 25), new Vector3(), new Vector3(0, -1, 0)); |
|||
var projection = |
|||
Matrix4x4.CreatePerspectiveFieldOfView((float)(Math.PI / 4), (float)((float)image.Size.Width / image.Size.Height), |
|||
0.01f, 1000); |
|||
|
|||
var vertexConstant = new VertextPushConstant() |
|||
{ |
|||
Disco = (float)disco, |
|||
MinY = _minY, |
|||
MaxY = _maxY, |
|||
Model = model, |
|||
Time = (float)St.Elapsed.TotalSeconds |
|||
}; |
|||
|
|||
var commandBuffer = _context.Pool.CreateCommandBuffer(); |
|||
commandBuffer.BeginRecording(); |
|||
|
|||
_colorAttachment.TransitionLayout(commandBuffer.InternalHandle, |
|||
ImageLayout.Undefined, AccessFlags.None, |
|||
ImageLayout.ColorAttachmentOptimal, AccessFlags.ColorAttachmentWriteBit); |
|||
|
|||
var commandBufferHandle = new CommandBuffer(commandBuffer.Handle); |
|||
|
|||
api.CmdSetViewport(commandBufferHandle, 0, 1, |
|||
new Viewport() |
|||
{ |
|||
Width = (float)image.Size.Width, |
|||
Height = (float)image.Size.Height, |
|||
MaxDepth = 1, |
|||
MinDepth = 0, |
|||
X = 0, |
|||
Y = 0 |
|||
}); |
|||
|
|||
var scissor = new Rect2D |
|||
{ |
|||
Extent = new Extent2D((uint?)image.Size.Width, (uint?)image.Size.Height) |
|||
}; |
|||
|
|||
api.CmdSetScissor(commandBufferHandle, 0, 1, &scissor); |
|||
|
|||
var clearColor = new ClearValue(new ClearColorValue(1, 0, 0, 0.1f), new ClearDepthStencilValue(1, 0)); |
|||
|
|||
var clearValues = new[] { clearColor, clearColor }; |
|||
|
|||
|
|||
fixed (ClearValue* clearValue = clearValues) |
|||
{ |
|||
var beginInfo = new RenderPassBeginInfo() |
|||
{ |
|||
SType = StructureType.RenderPassBeginInfo, |
|||
RenderPass = _renderPass, |
|||
Framebuffer = _framebuffer, |
|||
RenderArea = new Rect2D(new Offset2D(0, 0), new Extent2D((uint?)image.Size.Width, (uint?)image.Size.Height)), |
|||
ClearValueCount = 2, |
|||
PClearValues = clearValue |
|||
}; |
|||
|
|||
api.CmdBeginRenderPass(commandBufferHandle, beginInfo, SubpassContents.Inline); |
|||
} |
|||
|
|||
api.CmdBindPipeline(commandBufferHandle, PipelineBindPoint.Graphics, _pipeline); |
|||
|
|||
var dset = _descriptorSet; |
|||
api.CmdBindDescriptorSets(commandBufferHandle, PipelineBindPoint.Graphics, |
|||
_pipelineLayout,0,1, &dset, null); |
|||
|
|||
api.CmdPushConstants(commandBufferHandle, _pipelineLayout, ShaderStageFlags.VertexBit | ShaderStageFlags.FragmentBit, 0, |
|||
(uint)Marshal.SizeOf<VertextPushConstant>(), &vertexConstant); |
|||
api.CmdBindVertexBuffers(commandBufferHandle, 0, 1, _vertexBuffer, 0); |
|||
api.CmdBindIndexBuffer(commandBufferHandle, _indexBuffer, 0, IndexType.Uint16); |
|||
|
|||
api.CmdDrawIndexed(commandBufferHandle, (uint)_indices.Length, 1, 0, 0, 0); |
|||
|
|||
|
|||
api.CmdEndRenderPass(commandBufferHandle); |
|||
|
|||
_colorAttachment.TransitionLayout(commandBuffer.InternalHandle, ImageLayout.TransferSrcOptimal, AccessFlags.TransferReadBit); |
|||
image.TransitionLayout(commandBuffer.InternalHandle, ImageLayout.TransferDstOptimal, AccessFlags.TransferWriteBit); |
|||
|
|||
|
|||
var srcBlitRegion = new ImageBlit |
|||
{ |
|||
SrcOffsets = new ImageBlit.SrcOffsetsBuffer |
|||
{ |
|||
Element0 = new Offset3D(0, 0, 0), |
|||
Element1 = new Offset3D(image.Size.Width, image.Size.Height, 1), |
|||
}, |
|||
DstOffsets = new ImageBlit.DstOffsetsBuffer |
|||
{ |
|||
Element0 = new Offset3D(0, 0, 0), |
|||
Element1 = new Offset3D(image.Size.Width, image.Size.Height, 1), |
|||
}, |
|||
SrcSubresource = |
|||
new ImageSubresourceLayers |
|||
{ |
|||
AspectMask = ImageAspectFlags.ColorBit, |
|||
BaseArrayLayer = 0, |
|||
LayerCount = 1, |
|||
MipLevel = 0 |
|||
}, |
|||
DstSubresource = new ImageSubresourceLayers |
|||
{ |
|||
AspectMask = ImageAspectFlags.ColorBit, |
|||
BaseArrayLayer = 0, |
|||
LayerCount = 1, |
|||
MipLevel = 0 |
|||
} |
|||
}; |
|||
|
|||
api.CmdBlitImage(commandBuffer.InternalHandle, _colorAttachment.InternalHandle.Value, |
|||
ImageLayout.TransferSrcOptimal, |
|||
image.InternalHandle.Value, ImageLayout.TransferDstOptimal, 1, srcBlitRegion, Filter.Linear); |
|||
|
|||
commandBuffer.Submit(); |
|||
} |
|||
|
|||
public unsafe void Dispose() |
|||
{ |
|||
if (_isInit) |
|||
{ |
|||
var api = _context.Api; |
|||
var device = _context.Device; |
|||
|
|||
DestroyTemporalObjects(); |
|||
|
|||
api.DestroyShaderModule(device, _vertShader, null); |
|||
api.DestroyShaderModule(device, _fragShader, null); |
|||
|
|||
api.DestroyBuffer(device, _vertexBuffer, null); |
|||
api.FreeMemory(device, _vertexBufferMemory, null); |
|||
|
|||
api.DestroyBuffer(device, _indexBuffer, null); |
|||
api.FreeMemory(device, _indexBufferMemory, null); |
|||
|
|||
|
|||
} |
|||
|
|||
_isInit = false; |
|||
} |
|||
|
|||
public unsafe void DestroyTemporalObjects() |
|||
{ |
|||
if (_isInit) |
|||
{ |
|||
if (_renderPass.Handle != 0) |
|||
{ |
|||
var api = _context.Api; |
|||
var device = _context.Device; |
|||
api.FreeDescriptorSets(_context.Device, _context.DescriptorPool, new[] { _descriptorSet }); |
|||
|
|||
api.DestroyImageView(device, _depthImageView, null); |
|||
api.DestroyImage(device, _depthImage, null); |
|||
api.FreeMemory(device, _depthImageMemory, null); |
|||
|
|||
api.DestroyFramebuffer(device, _framebuffer, null); |
|||
api.DestroyPipeline(device, _pipeline, null); |
|||
api.DestroyPipelineLayout(device, _pipelineLayout, null); |
|||
api.DestroyRenderPass(device, _renderPass, null); |
|||
api.DestroyDescriptorSetLayout(device, _descriptorSetLayout, null); |
|||
|
|||
api.DestroyBuffer(device, _uniformBuffer, null); |
|||
api.FreeMemory(device, _uniformBufferMemory, null); |
|||
_colorAttachment?.Dispose(); |
|||
|
|||
_colorAttachment = null; |
|||
_depthImage = default; |
|||
_depthImageView = default; |
|||
_depthImageView = default; |
|||
_framebuffer = default; |
|||
_pipeline = default; |
|||
_renderPass = default; |
|||
_pipelineLayout = default; |
|||
_descriptorSetLayout = default; |
|||
_uniformBuffer = default; |
|||
_uniformBufferMemory = default; |
|||
} |
|||
} |
|||
} |
|||
|
|||
private unsafe void CreateDepthAttachment(PixelSize size) |
|||
{ |
|||
var imageCreateInfo = new ImageCreateInfo |
|||
{ |
|||
SType = StructureType.ImageCreateInfo, |
|||
ImageType = ImageType.Type2D, |
|||
Format = Format.D32Sfloat, |
|||
Extent = |
|||
new Extent3D((uint?)size.Width, |
|||
(uint?)size.Height, 1), |
|||
MipLevels = 1, |
|||
ArrayLayers = 1, |
|||
Samples = SampleCountFlags.Count1Bit, |
|||
Tiling = ImageTiling.Optimal, |
|||
Usage = ImageUsageFlags.DepthStencilAttachmentBit, |
|||
SharingMode = SharingMode.Exclusive, |
|||
InitialLayout = ImageLayout.Undefined, |
|||
Flags = ImageCreateFlags.CreateMutableFormatBit |
|||
}; |
|||
|
|||
var api = _context.Api; |
|||
var device = _context.Device; |
|||
api |
|||
.CreateImage(device, imageCreateInfo, null, out _depthImage).ThrowOnError(); |
|||
|
|||
api.GetImageMemoryRequirements(device, _depthImage, |
|||
out var memoryRequirements); |
|||
|
|||
var memoryAllocateInfo = new MemoryAllocateInfo |
|||
{ |
|||
SType = StructureType.MemoryAllocateInfo, |
|||
AllocationSize = memoryRequirements.Size, |
|||
MemoryTypeIndex = (uint)FindSuitableMemoryTypeIndex(api, |
|||
_context.PhysicalDevice, |
|||
memoryRequirements.MemoryTypeBits, MemoryPropertyFlags.DeviceLocalBit) |
|||
}; |
|||
|
|||
api.AllocateMemory(device, memoryAllocateInfo, null, |
|||
out _depthImageMemory).ThrowOnError(); |
|||
|
|||
api.BindImageMemory(device, _depthImage, _depthImageMemory, 0); |
|||
|
|||
var componentMapping = new ComponentMapping( |
|||
ComponentSwizzle.R, |
|||
ComponentSwizzle.G, |
|||
ComponentSwizzle.B, |
|||
ComponentSwizzle.A); |
|||
|
|||
var subresourceRange = new ImageSubresourceRange(ImageAspectFlags.DepthBit, |
|||
0, 1, 0, 1); |
|||
|
|||
var imageViewCreateInfo = new ImageViewCreateInfo |
|||
{ |
|||
SType = StructureType.ImageViewCreateInfo, |
|||
Image = _depthImage, |
|||
ViewType = ImageViewType.Type2D, |
|||
Format = Format.D32Sfloat, |
|||
Components = componentMapping, |
|||
SubresourceRange = subresourceRange |
|||
}; |
|||
|
|||
api |
|||
.CreateImageView(device, imageViewCreateInfo, null, out _depthImageView) |
|||
.ThrowOnError(); |
|||
} |
|||
|
|||
private unsafe void CreateTemporalObjects(PixelSize size) |
|||
{ |
|||
DestroyTemporalObjects(); |
|||
|
|||
var view = Matrix4x4.CreateLookAt(new Vector3(25, 25, 25), new Vector3(), new Vector3(0, -1, 0)); |
|||
var projection = |
|||
Matrix4x4.CreatePerspectiveFieldOfView((float)(Math.PI / 4), (float)((float)size.Width / size.Height), |
|||
0.01f, 1000); |
|||
|
|||
_colorAttachment = new VulkanImage(_context, (uint)Format.R8G8B8A8Unorm, size, false); |
|||
CreateDepthAttachment(size); |
|||
|
|||
var api = _context.Api; |
|||
var device = _context.Device; |
|||
|
|||
// create renderpasses
|
|||
var colorAttachment = new AttachmentDescription() |
|||
{ |
|||
Format = Format.R8G8B8A8Unorm, |
|||
Samples = SampleCountFlags.Count1Bit, |
|||
LoadOp = AttachmentLoadOp.Clear, |
|||
StoreOp = AttachmentStoreOp.Store, |
|||
InitialLayout = ImageLayout.Undefined, |
|||
FinalLayout = ImageLayout.ColorAttachmentOptimal, |
|||
StencilLoadOp = AttachmentLoadOp.DontCare, |
|||
StencilStoreOp = AttachmentStoreOp.DontCare |
|||
}; |
|||
|
|||
var depthAttachment = new AttachmentDescription() |
|||
{ |
|||
Format = Format.D32Sfloat, |
|||
Samples = SampleCountFlags.Count1Bit, |
|||
LoadOp = AttachmentLoadOp.Clear, |
|||
StoreOp = AttachmentStoreOp.DontCare, |
|||
InitialLayout = ImageLayout.Undefined, |
|||
FinalLayout = ImageLayout.DepthStencilAttachmentOptimal, |
|||
StencilLoadOp = AttachmentLoadOp.DontCare, |
|||
StencilStoreOp = AttachmentStoreOp.DontCare |
|||
}; |
|||
|
|||
var subpassDependency = new SubpassDependency() |
|||
{ |
|||
SrcSubpass = Vk.SubpassExternal, |
|||
DstSubpass = 0, |
|||
SrcStageMask = PipelineStageFlags.ColorAttachmentOutputBit, |
|||
SrcAccessMask = 0, |
|||
DstStageMask = PipelineStageFlags.ColorAttachmentOutputBit, |
|||
DstAccessMask = AccessFlags.ColorAttachmentWriteBit |
|||
}; |
|||
|
|||
var colorAttachmentReference = new AttachmentReference() |
|||
{ |
|||
Attachment = 0, Layout = ImageLayout.ColorAttachmentOptimal |
|||
}; |
|||
|
|||
var depthAttachmentReference = new AttachmentReference() |
|||
{ |
|||
Attachment = 1, Layout = ImageLayout.DepthStencilAttachmentOptimal |
|||
}; |
|||
|
|||
var subpassDescription = new SubpassDescription() |
|||
{ |
|||
PipelineBindPoint = PipelineBindPoint.Graphics, |
|||
ColorAttachmentCount = 1, |
|||
PColorAttachments = &colorAttachmentReference, |
|||
PDepthStencilAttachment = &depthAttachmentReference |
|||
}; |
|||
|
|||
var attachments = new[] { colorAttachment, depthAttachment }; |
|||
|
|||
fixed (AttachmentDescription* atPtr = attachments) |
|||
{ |
|||
var renderPassCreateInfo = new RenderPassCreateInfo() |
|||
{ |
|||
SType = StructureType.RenderPassCreateInfo, |
|||
AttachmentCount = (uint)attachments.Length, |
|||
PAttachments = atPtr, |
|||
SubpassCount = 1, |
|||
PSubpasses = &subpassDescription, |
|||
DependencyCount = 1, |
|||
PDependencies = &subpassDependency |
|||
}; |
|||
|
|||
api.CreateRenderPass(device, renderPassCreateInfo, null, out _renderPass).ThrowOnError(); |
|||
|
|||
|
|||
// create framebuffer
|
|||
var frameBufferAttachments = new[] { new ImageView(_colorAttachment.ViewHandle), _depthImageView }; |
|||
|
|||
fixed (ImageView* frAtPtr = frameBufferAttachments) |
|||
{ |
|||
var framebufferCreateInfo = new FramebufferCreateInfo() |
|||
{ |
|||
SType = StructureType.FramebufferCreateInfo, |
|||
RenderPass = _renderPass, |
|||
AttachmentCount = (uint)frameBufferAttachments.Length, |
|||
PAttachments = frAtPtr, |
|||
Width = (uint)size.Width, |
|||
Height = (uint)size.Height, |
|||
Layers = 1 |
|||
}; |
|||
|
|||
api.CreateFramebuffer(device, framebufferCreateInfo, null, out _framebuffer).ThrowOnError(); |
|||
} |
|||
} |
|||
|
|||
// Create pipeline
|
|||
var pname = Marshal.StringToHGlobalAnsi("main"); |
|||
var vertShaderStageInfo = new PipelineShaderStageCreateInfo() |
|||
{ |
|||
SType = StructureType.PipelineShaderStageCreateInfo, |
|||
Stage = ShaderStageFlags.VertexBit, |
|||
Module = _vertShader, |
|||
PName = (byte*)pname, |
|||
}; |
|||
var fragShaderStageInfo = new PipelineShaderStageCreateInfo() |
|||
{ |
|||
SType = StructureType.PipelineShaderStageCreateInfo, |
|||
Stage = ShaderStageFlags.FragmentBit, |
|||
Module = _fragShader, |
|||
PName = (byte*)pname, |
|||
}; |
|||
|
|||
var stages = new[] { vertShaderStageInfo, fragShaderStageInfo }; |
|||
|
|||
var bindingDescription = Vertex.VertexInputBindingDescription; |
|||
var attributeDescription = Vertex.VertexInputAttributeDescription; |
|||
|
|||
fixed (VertexInputAttributeDescription* attrPtr = attributeDescription) |
|||
{ |
|||
var vertextInputInfo = new PipelineVertexInputStateCreateInfo() |
|||
{ |
|||
SType = StructureType.PipelineVertexInputStateCreateInfo, |
|||
VertexAttributeDescriptionCount = (uint)attributeDescription.Length, |
|||
VertexBindingDescriptionCount = 1, |
|||
PVertexAttributeDescriptions = attrPtr, |
|||
PVertexBindingDescriptions = &bindingDescription |
|||
}; |
|||
|
|||
var inputAssembly = new PipelineInputAssemblyStateCreateInfo() |
|||
{ |
|||
SType = StructureType.PipelineInputAssemblyStateCreateInfo, |
|||
Topology = PrimitiveTopology.TriangleList, |
|||
PrimitiveRestartEnable = false |
|||
}; |
|||
|
|||
var viewport = new Viewport() |
|||
{ |
|||
X = 0, |
|||
Y = 0, |
|||
Width = (float)size.Width, |
|||
Height = (float)size.Height, |
|||
MinDepth = 0, |
|||
MaxDepth = 1 |
|||
}; |
|||
|
|||
var scissor = new Rect2D() |
|||
{ |
|||
Offset = new Offset2D(0, 0), Extent = new Extent2D((uint)viewport.Width, (uint)viewport.Height) |
|||
}; |
|||
|
|||
var pipelineViewPortCreateInfo = new PipelineViewportStateCreateInfo() |
|||
{ |
|||
SType = StructureType.PipelineViewportStateCreateInfo, |
|||
ViewportCount = 1, |
|||
PViewports = &viewport, |
|||
ScissorCount = 1, |
|||
PScissors = &scissor |
|||
}; |
|||
|
|||
var rasterizerStateCreateInfo = new PipelineRasterizationStateCreateInfo() |
|||
{ |
|||
SType = StructureType.PipelineRasterizationStateCreateInfo, |
|||
DepthClampEnable = false, |
|||
RasterizerDiscardEnable = false, |
|||
PolygonMode = PolygonMode.Fill, |
|||
LineWidth = 1, |
|||
CullMode = CullModeFlags.None, |
|||
DepthBiasEnable = false |
|||
}; |
|||
|
|||
var multisampleStateCreateInfo = new PipelineMultisampleStateCreateInfo() |
|||
{ |
|||
SType = StructureType.PipelineMultisampleStateCreateInfo, |
|||
SampleShadingEnable = false, |
|||
RasterizationSamples = SampleCountFlags.Count1Bit |
|||
}; |
|||
|
|||
var depthStencilCreateInfo = new PipelineDepthStencilStateCreateInfo() |
|||
{ |
|||
SType = StructureType.PipelineDepthStencilStateCreateInfo, |
|||
StencilTestEnable = false, |
|||
DepthCompareOp = CompareOp.Less, |
|||
DepthTestEnable = true, |
|||
DepthWriteEnable = true, |
|||
DepthBoundsTestEnable = false, |
|||
}; |
|||
|
|||
var colorBlendAttachmentState = new PipelineColorBlendAttachmentState() |
|||
{ |
|||
ColorWriteMask = ColorComponentFlags.ABit | |
|||
ColorComponentFlags.RBit | |
|||
ColorComponentFlags.GBit | |
|||
ColorComponentFlags.BBit, |
|||
BlendEnable = false |
|||
}; |
|||
|
|||
var colorBlendState = new PipelineColorBlendStateCreateInfo() |
|||
{ |
|||
SType = StructureType.PipelineColorBlendStateCreateInfo, |
|||
LogicOpEnable = false, |
|||
AttachmentCount = 1, |
|||
PAttachments = &colorBlendAttachmentState |
|||
}; |
|||
|
|||
var dynamicStates = new DynamicState[] { DynamicState.Viewport, DynamicState.Scissor }; |
|||
|
|||
fixed (DynamicState* states = dynamicStates) |
|||
{ |
|||
var dynamicStateCreateInfo = new PipelineDynamicStateCreateInfo() |
|||
{ |
|||
SType = StructureType.PipelineDynamicStateCreateInfo, |
|||
DynamicStateCount = (uint)dynamicStates.Length, |
|||
PDynamicStates = states |
|||
}; |
|||
|
|||
var vertexPushConstantRange = new PushConstantRange() |
|||
{ |
|||
Offset = 0, |
|||
Size = (uint)Marshal.SizeOf<VertextPushConstant>(), |
|||
StageFlags = ShaderStageFlags.VertexBit |
|||
}; |
|||
|
|||
var fragPushConstantRange = new PushConstantRange() |
|||
{ |
|||
//Offset = vertexPushConstantRange.Size,
|
|||
Size = (uint)Marshal.SizeOf<VertextPushConstant>(), |
|||
StageFlags = ShaderStageFlags.FragmentBit |
|||
}; |
|||
|
|||
var layoutBindingInfo = new DescriptorSetLayoutBinding |
|||
{ |
|||
Binding = 0, |
|||
StageFlags = ShaderStageFlags.VertexBit, |
|||
DescriptorCount = 1, |
|||
DescriptorType = DescriptorType.UniformBuffer, |
|||
}; |
|||
|
|||
var layoutInfo = new DescriptorSetLayoutCreateInfo |
|||
{ |
|||
SType = StructureType.DescriptorSetLayoutCreateInfo, |
|||
BindingCount = 1, |
|||
PBindings = &layoutBindingInfo |
|||
}; |
|||
|
|||
api.CreateDescriptorSetLayout(device, &layoutInfo, null, out _descriptorSetLayout).ThrowOnError(); |
|||
|
|||
var projView = view * projection; |
|||
VulkanBufferHelper.AllocateBuffer<UniformBuffer>(_context, BufferUsageFlags.UniformBufferBit, |
|||
out _uniformBuffer, |
|||
out _uniformBufferMemory, new[] |
|||
{ |
|||
new UniformBuffer |
|||
{ |
|||
Projection = projView |
|||
} |
|||
}); |
|||
|
|||
var descriptorSetLayout = _descriptorSetLayout; |
|||
var descriptorCreateInfo = new DescriptorSetAllocateInfo |
|||
{ |
|||
SType = StructureType.DescriptorSetAllocateInfo, |
|||
DescriptorPool = _context.DescriptorPool, |
|||
DescriptorSetCount = 1, |
|||
PSetLayouts = &descriptorSetLayout |
|||
}; |
|||
api.AllocateDescriptorSets(device, &descriptorCreateInfo, out _descriptorSet).ThrowOnError(); |
|||
|
|||
var descriptorBufferInfo = new DescriptorBufferInfo |
|||
{ |
|||
Buffer = _uniformBuffer, |
|||
Range = (ulong)Unsafe.SizeOf<UniformBuffer>(), |
|||
}; |
|||
var descriptorWrite = new WriteDescriptorSet |
|||
{ |
|||
SType = StructureType.WriteDescriptorSet, |
|||
DstSet = _descriptorSet, |
|||
DescriptorType = DescriptorType.UniformBuffer, |
|||
DescriptorCount = 1, |
|||
PBufferInfo = &descriptorBufferInfo, |
|||
}; |
|||
api.UpdateDescriptorSets(device, 1, &descriptorWrite, 0, null); |
|||
|
|||
var constants = new[] { vertexPushConstantRange, fragPushConstantRange }; |
|||
|
|||
fixed (PushConstantRange* constant = constants) |
|||
{ |
|||
var setLayout = _descriptorSetLayout; |
|||
var pipelineLayoutCreateInfo = new PipelineLayoutCreateInfo() |
|||
{ |
|||
SType = StructureType.PipelineLayoutCreateInfo, |
|||
PushConstantRangeCount = (uint)constants.Length, |
|||
PPushConstantRanges = constant, |
|||
SetLayoutCount = 1, |
|||
PSetLayouts = &setLayout |
|||
}; |
|||
|
|||
api.CreatePipelineLayout(device, pipelineLayoutCreateInfo, null, out _pipelineLayout) |
|||
.ThrowOnError(); |
|||
} |
|||
|
|||
|
|||
fixed (PipelineShaderStageCreateInfo* stPtr = stages) |
|||
{ |
|||
var pipelineCreateInfo = new GraphicsPipelineCreateInfo() |
|||
{ |
|||
SType = StructureType.GraphicsPipelineCreateInfo, |
|||
StageCount = 2, |
|||
PStages = stPtr, |
|||
PVertexInputState = &vertextInputInfo, |
|||
PInputAssemblyState = &inputAssembly, |
|||
PViewportState = &pipelineViewPortCreateInfo, |
|||
PRasterizationState = &rasterizerStateCreateInfo, |
|||
PMultisampleState = &multisampleStateCreateInfo, |
|||
PDepthStencilState = &depthStencilCreateInfo, |
|||
PColorBlendState = &colorBlendState, |
|||
PDynamicState = &dynamicStateCreateInfo, |
|||
Layout = _pipelineLayout, |
|||
RenderPass = _renderPass, |
|||
Subpass = 0, |
|||
BasePipelineHandle = _pipeline.Handle != 0 ? _pipeline : new Pipeline(), |
|||
BasePipelineIndex = _pipeline.Handle != 0 ? 0 : -1 |
|||
}; |
|||
|
|||
api.CreateGraphicsPipelines(device, new PipelineCache(), 1, &pipelineCreateInfo, null, |
|||
out _pipeline).ThrowOnError(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
Marshal.FreeHGlobal(pname); |
|||
_isInit = true; |
|||
} |
|||
|
|||
private unsafe void CreateBuffers() |
|||
{ |
|||
VulkanBufferHelper.AllocateBuffer<Vertex>(_context, BufferUsageFlags.VertexBufferBit, out _vertexBuffer, |
|||
out _vertexBufferMemory, _points); |
|||
VulkanBufferHelper.AllocateBuffer<ushort>(_context, BufferUsageFlags.IndexBufferBit, out _indexBuffer, |
|||
out _indexBufferMemory, _indices); |
|||
} |
|||
|
|||
private static int FindSuitableMemoryTypeIndex(Vk api, PhysicalDevice physicalDevice, uint memoryTypeBits, |
|||
MemoryPropertyFlags flags) |
|||
{ |
|||
api.GetPhysicalDeviceMemoryProperties(physicalDevice, out var properties); |
|||
|
|||
for (var i = 0; i < properties.MemoryTypeCount; i++) |
|||
{ |
|||
var type = properties.MemoryTypes[i]; |
|||
|
|||
if ((memoryTypeBits & (1 << i)) != 0 && type.PropertyFlags.HasFlag(flags)) return i; |
|||
} |
|||
|
|||
return -1; |
|||
} |
|||
|
|||
|
|||
|
|||
[StructLayout(LayoutKind.Sequential, Pack = 4)] |
|||
private struct Vertex |
|||
{ |
|||
public Vector3 Position; |
|||
public Vector3 Normal; |
|||
|
|||
public static unsafe VertexInputBindingDescription VertexInputBindingDescription |
|||
{ |
|||
get |
|||
{ |
|||
return new VertexInputBindingDescription() |
|||
{ |
|||
Binding = 0, |
|||
Stride = (uint)Marshal.SizeOf<Vertex>(), |
|||
InputRate = VertexInputRate.Vertex |
|||
}; |
|||
} |
|||
} |
|||
|
|||
public static unsafe VertexInputAttributeDescription[] VertexInputAttributeDescription |
|||
{ |
|||
get |
|||
{ |
|||
return new VertexInputAttributeDescription[] |
|||
{ |
|||
new VertexInputAttributeDescription |
|||
{ |
|||
Binding = 0, |
|||
Location = 0, |
|||
Format = Format.R32G32B32Sfloat, |
|||
Offset = (uint)Marshal.OffsetOf<Vertex>("Position") |
|||
}, |
|||
new VertexInputAttributeDescription |
|||
{ |
|||
Binding = 0, |
|||
Location = 1, |
|||
Format = Format.R32G32B32Sfloat, |
|||
Offset = (uint)Marshal.OffsetOf<Vertex>("Normal") |
|||
} |
|||
}; |
|||
} |
|||
} |
|||
} |
|||
|
|||
private readonly Vertex[] _points; |
|||
private readonly ushort[] _indices; |
|||
private readonly float _minY; |
|||
private readonly float _maxY; |
|||
|
|||
|
|||
static Stopwatch St = Stopwatch.StartNew(); |
|||
private bool _isInit; |
|||
private VulkanImage _colorAttachment; |
|||
private DescriptorSet _descriptorSet; |
|||
|
|||
[StructLayout(LayoutKind.Sequential, Pack = 4)] |
|||
private struct VertextPushConstant |
|||
{ |
|||
public float MaxY; |
|||
public float MinY; |
|||
public float Time; |
|||
public float Disco; |
|||
public Matrix4x4 Model; |
|||
} |
|||
|
|||
[StructLayout(LayoutKind.Sequential, Pack = 4)] |
|||
private struct UniformBuffer |
|||
{ |
|||
public Matrix4x4 Projection; |
|||
} |
|||
} |
|||
@ -0,0 +1,335 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Runtime.InteropServices; |
|||
using Avalonia; |
|||
using Avalonia.Platform; |
|||
using Avalonia.Rendering.Composition; |
|||
using Avalonia.Vulkan; |
|||
using Silk.NET.Core; |
|||
using Silk.NET.Core.Native; |
|||
using Silk.NET.Vulkan; |
|||
using Silk.NET.Vulkan.Extensions.EXT; |
|||
using Silk.NET.Vulkan.Extensions.KHR; |
|||
using SilkNetDemo; |
|||
using SkiaSharp; |
|||
using D3DDevice = SharpDX.Direct3D11.Device; |
|||
using DxgiDevice = SharpDX.DXGI.Device; |
|||
|
|||
namespace GpuInterop.VulkanDemo; |
|||
|
|||
public unsafe class VulkanContext : IDisposable |
|||
{ |
|||
public Vk Api { get; init; } |
|||
public Instance Instance { get; init; } |
|||
public PhysicalDevice PhysicalDevice { get; init; } |
|||
public Device Device { get; init; } |
|||
public Queue Queue { get; init; } |
|||
public uint QueueFamilyIndex { get; init; } |
|||
public VulkanCommandBufferPool Pool { get; init; } |
|||
public GRContext GrContext { get; init; } |
|||
public DescriptorPool DescriptorPool { get; init; } |
|||
public D3DDevice? D3DDevice { get; init; } |
|||
|
|||
public static (VulkanContext? result, string info) TryCreate(ICompositionGpuInterop gpuInterop) |
|||
{ |
|||
using var appName = new ByteString("GpuInterop"); |
|||
using var engineName = new ByteString("Test"); |
|||
var applicationInfo = new ApplicationInfo |
|||
{ |
|||
SType = StructureType.ApplicationInfo, |
|||
PApplicationName = appName, |
|||
ApiVersion = new Version32(1, 1, 0), |
|||
PEngineName = appName, |
|||
EngineVersion = new Version32(1, 0, 0), |
|||
ApplicationVersion = new Version32(1, 0, 0) |
|||
}; |
|||
|
|||
var enabledExtensions = new List<string>() |
|||
{ |
|||
"VK_KHR_get_physical_device_properties2", |
|||
"VK_KHR_external_memory_capabilities", |
|||
"VK_KHR_external_semaphore_capabilities" |
|||
}; |
|||
|
|||
var enabledLayers = new List<string>(); |
|||
|
|||
Vk api = Vk.GetApi(); |
|||
enabledExtensions.Add("VK_EXT_debug_utils"); |
|||
if (IsLayerAvailable(api, "VK_LAYER_KHRONOS_validation")) |
|||
enabledLayers.Add("VK_LAYER_KHRONOS_validation"); |
|||
|
|||
|
|||
Instance vkInstance = default; |
|||
Silk.NET.Vulkan.PhysicalDevice physicalDevice = default; |
|||
Device device = default; |
|||
DescriptorPool descriptorPool = default; |
|||
VulkanCommandBufferPool? pool = null; |
|||
GRContext? grContext = null; |
|||
try |
|||
{ |
|||
using var pRequiredExtensions = new ByteStringList(enabledExtensions); |
|||
using var pEnabledLayers = new ByteStringList(enabledLayers); |
|||
api.CreateInstance(new InstanceCreateInfo |
|||
{ |
|||
SType = StructureType.InstanceCreateInfo, |
|||
PApplicationInfo = &applicationInfo, |
|||
PpEnabledExtensionNames = pRequiredExtensions, |
|||
EnabledExtensionCount = pRequiredExtensions.UCount, |
|||
PpEnabledLayerNames = pEnabledLayers, |
|||
EnabledLayerCount = pEnabledLayers.UCount |
|||
}, null, out vkInstance).ThrowOnError(); |
|||
|
|||
|
|||
if (api.TryGetInstanceExtension(vkInstance, out ExtDebugUtils debugUtils)) |
|||
{ |
|||
var debugCreateInfo = new DebugUtilsMessengerCreateInfoEXT |
|||
{ |
|||
SType = StructureType.DebugUtilsMessengerCreateInfoExt, |
|||
MessageSeverity = DebugUtilsMessageSeverityFlagsEXT.VerboseBitExt | |
|||
DebugUtilsMessageSeverityFlagsEXT.WarningBitExt | |
|||
DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt, |
|||
MessageType = DebugUtilsMessageTypeFlagsEXT.GeneralBitExt | |
|||
DebugUtilsMessageTypeFlagsEXT.ValidationBitExt | |
|||
DebugUtilsMessageTypeFlagsEXT.PerformanceBitExt, |
|||
PfnUserCallback = new PfnDebugUtilsMessengerCallbackEXT(LogCallback), |
|||
}; |
|||
|
|||
debugUtils.CreateDebugUtilsMessenger(vkInstance, debugCreateInfo, null, out var messenger); |
|||
} |
|||
|
|||
var requireDeviceExtensions = new List<string> |
|||
{ |
|||
"VK_KHR_external_memory", |
|||
"VK_KHR_external_semaphore" |
|||
}; |
|||
|
|||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) |
|||
{ |
|||
if (!gpuInterop.SupportedImageHandleTypes.Contains(KnownPlatformGraphicsExternalImageHandleTypes |
|||
.D3D11TextureGlobalSharedHandle) |
|||
) |
|||
return (null, "Image sharing is not supported by the current backend"); |
|||
requireDeviceExtensions.Add(KhrExternalMemoryWin32.ExtensionName); |
|||
requireDeviceExtensions.Add(KhrExternalSemaphoreWin32.ExtensionName); |
|||
requireDeviceExtensions.Add("VK_KHR_dedicated_allocation"); |
|||
requireDeviceExtensions.Add("VK_KHR_get_memory_requirements2"); |
|||
} |
|||
else |
|||
{ |
|||
if (!gpuInterop.SupportedImageHandleTypes.Contains(KnownPlatformGraphicsExternalImageHandleTypes |
|||
.VulkanOpaquePosixFileDescriptor) |
|||
|| !gpuInterop.SupportedSemaphoreTypes.Contains(KnownPlatformGraphicsExternalSemaphoreHandleTypes |
|||
.VulkanOpaquePosixFileDescriptor) |
|||
) |
|||
return (null, "Image sharing is not supported by the current backend"); |
|||
requireDeviceExtensions.Add(KhrExternalMemoryFd.ExtensionName); |
|||
requireDeviceExtensions.Add(KhrExternalSemaphoreFd.ExtensionName); |
|||
} |
|||
|
|||
uint count = 0; |
|||
api.EnumeratePhysicalDevices(vkInstance, ref count, null).ThrowOnError(); |
|||
var physicalDevices = stackalloc PhysicalDevice[(int)count]; |
|||
api.EnumeratePhysicalDevices(vkInstance, ref count, physicalDevices) |
|||
.ThrowOnError(); |
|||
|
|||
for (uint c = 0; c < count; c++) |
|||
{ |
|||
if (requireDeviceExtensions.Any(ext => !api.IsDeviceExtensionPresent(physicalDevices[c], ext))) |
|||
continue; |
|||
|
|||
var physicalDeviceIDProperties = new PhysicalDeviceIDProperties() |
|||
{ |
|||
SType = StructureType.PhysicalDeviceIDProperties |
|||
}; |
|||
var physicalDeviceProperties2 = new PhysicalDeviceProperties2() |
|||
{ |
|||
SType = StructureType.PhysicalDeviceProperties2, |
|||
PNext = &physicalDeviceIDProperties |
|||
}; |
|||
api.GetPhysicalDeviceProperties2(physicalDevices[c], &physicalDeviceProperties2); |
|||
|
|||
if (gpuInterop.DeviceLuid != null && physicalDeviceIDProperties.DeviceLuidvalid) |
|||
{ |
|||
if (!new Span<byte>(physicalDeviceIDProperties.DeviceLuid, 8) |
|||
.SequenceEqual(gpuInterop.DeviceLuid)) |
|||
continue; |
|||
} |
|||
else if (gpuInterop.DeviceUuid != null) |
|||
{ |
|||
if (!new Span<byte>(physicalDeviceIDProperties.DeviceUuid, 16) |
|||
.SequenceEqual(gpuInterop?.DeviceUuid)) |
|||
continue; |
|||
} |
|||
|
|||
physicalDevice = physicalDevices[c]; |
|||
|
|||
var name = Marshal.PtrToStringAnsi(new IntPtr(physicalDeviceProperties2.Properties.DeviceName))!; |
|||
|
|||
|
|||
uint queueFamilyCount = 0; |
|||
api.GetPhysicalDeviceQueueFamilyProperties(physicalDevice, ref queueFamilyCount, null); |
|||
var familyProperties = stackalloc QueueFamilyProperties[(int)queueFamilyCount]; |
|||
api.GetPhysicalDeviceQueueFamilyProperties(physicalDevice, ref queueFamilyCount, familyProperties); |
|||
for (uint queueFamilyIndex = 0; queueFamilyIndex < queueFamilyCount; queueFamilyIndex++) |
|||
{ |
|||
var family = familyProperties[queueFamilyIndex]; |
|||
if (!family.QueueFlags.HasAllFlags(QueueFlags.GraphicsBit)) |
|||
continue; |
|||
|
|||
|
|||
var queuePriorities = stackalloc float[(int)family.QueueCount]; |
|||
|
|||
for (var i = 0; i < family.QueueCount; i++) |
|||
queuePriorities[i] = 1f; |
|||
|
|||
var features = new PhysicalDeviceFeatures(); |
|||
|
|||
var queueCreateInfo = new DeviceQueueCreateInfo |
|||
{ |
|||
SType = StructureType.DeviceQueueCreateInfo, |
|||
QueueFamilyIndex = queueFamilyIndex, |
|||
QueueCount = family.QueueCount, |
|||
PQueuePriorities = queuePriorities |
|||
}; |
|||
|
|||
using var pEnabledDeviceExtensions = new ByteStringList(requireDeviceExtensions); |
|||
var deviceCreateInfo = new DeviceCreateInfo |
|||
{ |
|||
SType = StructureType.DeviceCreateInfo, |
|||
QueueCreateInfoCount = 1, |
|||
PQueueCreateInfos = &queueCreateInfo, |
|||
PpEnabledExtensionNames = pEnabledDeviceExtensions, |
|||
EnabledExtensionCount = pEnabledDeviceExtensions.UCount, |
|||
PEnabledFeatures = &features |
|||
}; |
|||
|
|||
api.CreateDevice(physicalDevice, in deviceCreateInfo, null, out device) |
|||
.ThrowOnError(); |
|||
|
|||
api.GetDeviceQueue(device, queueFamilyIndex, 0, out var queue); |
|||
|
|||
var descriptorPoolSize = new DescriptorPoolSize |
|||
{ |
|||
Type = DescriptorType.UniformBuffer, DescriptorCount = 16 |
|||
}; |
|||
var descriptorPoolInfo = new DescriptorPoolCreateInfo |
|||
{ |
|||
SType = StructureType.DescriptorPoolCreateInfo, |
|||
PoolSizeCount = 1, |
|||
PPoolSizes = &descriptorPoolSize, |
|||
MaxSets = 16, |
|||
Flags = DescriptorPoolCreateFlags.FreeDescriptorSetBit |
|||
}; |
|||
|
|||
api.CreateDescriptorPool(device, &descriptorPoolInfo, null, out descriptorPool) |
|||
.ThrowOnError(); |
|||
|
|||
pool = new VulkanCommandBufferPool(api, device, queue, queueFamilyIndex); |
|||
grContext = GRContext.CreateVulkan(new GRVkBackendContext |
|||
{ |
|||
VkInstance = vkInstance.Handle, |
|||
VkDevice = device.Handle, |
|||
VkQueue = queue.Handle, |
|||
GraphicsQueueIndex = queueFamilyIndex, |
|||
VkPhysicalDevice = physicalDevice.Handle, |
|||
GetProcedureAddress = (proc, _, _) => |
|||
{ |
|||
var rv = api.GetDeviceProcAddr(device, proc); |
|||
if (rv != IntPtr.Zero) |
|||
return rv; |
|||
rv = api.GetInstanceProcAddr(vkInstance, proc); |
|||
if (rv != IntPtr.Zero) |
|||
return rv; |
|||
return api.GetInstanceProcAddr(default, proc); |
|||
} |
|||
}); |
|||
|
|||
|
|||
D3DDevice? d3dDevice = null; |
|||
if (physicalDeviceIDProperties.DeviceLuidvalid && |
|||
RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) |
|||
d3dDevice = D3DMemoryHelper.CreateDeviceByLuid( |
|||
new Span<byte>(physicalDeviceIDProperties.DeviceLuid, 8)); |
|||
|
|||
var dxgiDevice = d3dDevice?.QueryInterface<DxgiDevice>(); |
|||
return (new VulkanContext |
|||
{ |
|||
Api = api, |
|||
Device = device, |
|||
Instance = vkInstance, |
|||
PhysicalDevice = physicalDevice, |
|||
Queue = queue, |
|||
QueueFamilyIndex = queueFamilyIndex, |
|||
Pool = pool, |
|||
DescriptorPool = descriptorPool, |
|||
GrContext = grContext, |
|||
D3DDevice = d3dDevice |
|||
}, name); |
|||
} |
|||
return (null, "No suitable device queue found"); |
|||
} |
|||
|
|||
return (null, "Suitable device not found"); |
|||
|
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
return (null, e.ToString()); |
|||
} |
|||
finally |
|||
{ |
|||
if (grContext == null && api != null) |
|||
{ |
|||
pool?.Dispose(); |
|||
if (descriptorPool.Handle != default) |
|||
api.DestroyDescriptorPool(device, descriptorPool, null); |
|||
if (device.Handle != default) |
|||
api.DestroyDevice(device, null); |
|||
} |
|||
} |
|||
} |
|||
|
|||
private static unsafe bool IsLayerAvailable(Vk api, string layerName) |
|||
{ |
|||
uint layerPropertiesCount; |
|||
|
|||
api.EnumerateInstanceLayerProperties(&layerPropertiesCount, null).ThrowOnError(); |
|||
|
|||
var layerProperties = new LayerProperties[layerPropertiesCount]; |
|||
|
|||
fixed (LayerProperties* pLayerProperties = layerProperties) |
|||
{ |
|||
api.EnumerateInstanceLayerProperties(&layerPropertiesCount, layerProperties).ThrowOnError(); |
|||
|
|||
for (var i = 0; i < layerPropertiesCount; i++) |
|||
{ |
|||
var currentLayerName = Marshal.PtrToStringAnsi((IntPtr)pLayerProperties[i].LayerName); |
|||
|
|||
if (currentLayerName == layerName) return true; |
|||
} |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
private static unsafe uint LogCallback(DebugUtilsMessageSeverityFlagsEXT messageSeverity, DebugUtilsMessageTypeFlagsEXT messageTypes, DebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData) |
|||
{ |
|||
if (messageSeverity != DebugUtilsMessageSeverityFlagsEXT.VerboseBitExt) |
|||
{ |
|||
var message = Marshal.PtrToStringAnsi((nint)pCallbackData->PMessage); |
|||
Console.WriteLine(message); |
|||
} |
|||
|
|||
return Vk.False; |
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
D3DDevice?.Dispose(); |
|||
GrContext.Dispose(); |
|||
Pool.Dispose(); |
|||
Api.DestroyDescriptorPool(Device, DescriptorPool, null); |
|||
Api.DestroyDevice(Device, null); |
|||
} |
|||
} |
|||
@ -0,0 +1,89 @@ |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Avalonia; |
|||
using Avalonia.Rendering.Composition; |
|||
|
|||
namespace GpuInterop.VulkanDemo; |
|||
|
|||
public class VulkanDemoControl : DrawingSurfaceDemoBase |
|||
{ |
|||
class VulkanResources : IAsyncDisposable |
|||
{ |
|||
public VulkanContext Context { get; } |
|||
public VulkanSwapchain Swapchain { get; } |
|||
public VulkanContent Content { get; } |
|||
|
|||
public VulkanResources(VulkanContext context, VulkanSwapchain swapchain, VulkanContent content) |
|||
{ |
|||
Context = context; |
|||
Swapchain = swapchain; |
|||
Content = content; |
|||
} |
|||
public async ValueTask DisposeAsync() |
|||
{ |
|||
Context.Pool.FreeUsedCommandBuffers(); |
|||
Content.Dispose(); |
|||
await Swapchain.DisposeAsync(); |
|||
Context.Dispose(); |
|||
} |
|||
} |
|||
|
|||
protected override bool SupportsDisco => true; |
|||
|
|||
private VulkanResources? _resources; |
|||
|
|||
protected override (bool success, string info) InitializeGraphicsResources(Compositor compositor, |
|||
CompositionDrawingSurface compositionDrawingSurface, ICompositionGpuInterop gpuInterop) |
|||
{ |
|||
var (context, info) = VulkanContext.TryCreate(gpuInterop); |
|||
if (context == null) |
|||
return (false, info); |
|||
try |
|||
{ |
|||
var content = new VulkanContent(context); |
|||
_resources = new VulkanResources(context, |
|||
new VulkanSwapchain(context, gpuInterop, compositionDrawingSurface), content); |
|||
return (true, info); |
|||
} |
|||
catch(Exception e) |
|||
{ |
|||
return (false, e.ToString()); |
|||
} |
|||
} |
|||
|
|||
protected override void FreeGraphicsResources() |
|||
{ |
|||
_resources?.DisposeAsync(); |
|||
_resources = null; |
|||
} |
|||
|
|||
protected override unsafe void RenderFrame(PixelSize pixelSize) |
|||
{ |
|||
if (_resources == null) |
|||
return; |
|||
using (_resources.Swapchain.BeginDraw(pixelSize, out var image)) |
|||
{ |
|||
/* |
|||
var commandBuffer = _resources.Context.Pool.CreateCommandBuffer(); |
|||
commandBuffer.BeginRecording(); |
|||
image.TransitionLayout(commandBuffer.InternalHandle, ImageLayout.TransferDstOptimal, AccessFlags.None); |
|||
|
|||
var range = new ImageSubresourceRange |
|||
{ |
|||
AspectMask = ImageAspectFlags.ColorBit, |
|||
LayerCount = 1, |
|||
LevelCount = 1, |
|||
BaseArrayLayer = 0, |
|||
BaseMipLevel = 0 |
|||
}; |
|||
var color = new ClearColorValue |
|||
{ |
|||
Float32_0 = 1, Float32_1 = 0, Float32_2 = 0, Float32_3 = 1 |
|||
}; |
|||
_resources.Context.Api.CmdClearColorImage(commandBuffer.InternalHandle, image.InternalHandle.Value, ImageLayout.TransferDstOptimal, |
|||
&color, 1, &range); |
|||
commandBuffer.Submit();*/ |
|||
_resources.Content.Render(image, Yaw, Pitch, Roll, Disco); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
using System; |
|||
using Silk.NET.Vulkan; |
|||
|
|||
namespace SilkNetDemo; |
|||
|
|||
public static class VulkanExtensions |
|||
{ |
|||
public static void ThrowOnError(this Result result) |
|||
{ |
|||
if (result != Result.Success) throw new Exception($"Unexpected API error \"{result}\"."); |
|||
} |
|||
} |
|||
@ -0,0 +1,286 @@ |
|||
using System; |
|||
using System.IO; |
|||
using System.Runtime.InteropServices; |
|||
using Avalonia; |
|||
using Avalonia.Platform; |
|||
using Avalonia.Vulkan; |
|||
using SharpDX.DXGI; |
|||
using Silk.NET.Vulkan; |
|||
using Silk.NET.Vulkan.Extensions.KHR; |
|||
using SilkNetDemo; |
|||
using SkiaSharp; |
|||
using Device = Silk.NET.Vulkan.Device; |
|||
using Format = Silk.NET.Vulkan.Format; |
|||
|
|||
namespace GpuInterop.VulkanDemo; |
|||
|
|||
public unsafe class VulkanImage : IDisposable |
|||
{ |
|||
private readonly VulkanContext _vk; |
|||
private readonly Instance _instance; |
|||
private readonly Device _device; |
|||
private readonly PhysicalDevice _physicalDevice; |
|||
private readonly VulkanCommandBufferPool _commandBufferPool; |
|||
private ImageLayout _currentLayout; |
|||
private AccessFlags _currentAccessFlags; |
|||
private ImageUsageFlags _imageUsageFlags { get; } |
|||
private ImageView? _imageView { get; set; } |
|||
private DeviceMemory _imageMemory { get; set; } |
|||
private SharpDX.Direct3D11.Texture2D? _d3dTexture2D; |
|||
|
|||
internal Image? InternalHandle { get; private set; } |
|||
internal Format Format { get; } |
|||
internal ImageAspectFlags AspectFlags { get; private set; } |
|||
|
|||
public ulong Handle => InternalHandle?.Handle ?? 0; |
|||
public ulong ViewHandle => _imageView?.Handle ?? 0; |
|||
public uint UsageFlags => (uint) _imageUsageFlags; |
|||
public ulong MemoryHandle => _imageMemory.Handle; |
|||
public DeviceMemory DeviceMemory => _imageMemory; |
|||
public uint MipLevels { get; private set; } |
|||
public Vk Api { get; } |
|||
public PixelSize Size { get; } |
|||
public ulong MemorySize { get; private set; } |
|||
public uint CurrentLayout => (uint) _currentLayout; |
|||
|
|||
public VulkanImage(VulkanContext vk, uint format, PixelSize size, |
|||
bool exportable, uint mipLevels = 0) |
|||
{ |
|||
_vk = vk; |
|||
_instance = vk.Instance; |
|||
_device = vk.Device; |
|||
_physicalDevice = vk.PhysicalDevice; |
|||
_commandBufferPool = vk.Pool; |
|||
Format = (Format)format; |
|||
Api = vk.Api; |
|||
Size = size; |
|||
MipLevels = 1;//mipLevels;
|
|||
_imageUsageFlags = |
|||
ImageUsageFlags.ColorAttachmentBit | ImageUsageFlags.TransferDstBit | |
|||
ImageUsageFlags.TransferSrcBit | ImageUsageFlags.SampledBit; |
|||
|
|||
//MipLevels = MipLevels != 0 ? MipLevels : (uint)Math.Floor(Math.Log(Math.Max(Size.Width, Size.Height), 2));
|
|||
|
|||
var handleType = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? |
|||
ExternalMemoryHandleTypeFlags.D3D11TextureBit : |
|||
ExternalMemoryHandleTypeFlags.OpaqueFDBit; |
|||
var externalMemoryCreateInfo = new ExternalMemoryImageCreateInfo |
|||
{ |
|||
SType = StructureType.ExternalMemoryImageCreateInfo, |
|||
HandleTypes = handleType |
|||
}; |
|||
|
|||
var imageCreateInfo = new ImageCreateInfo |
|||
{ |
|||
PNext = exportable ? &externalMemoryCreateInfo : null, |
|||
SType = StructureType.ImageCreateInfo, |
|||
ImageType = ImageType.Type2D, |
|||
Format = Format, |
|||
Extent = |
|||
new Extent3D((uint?)Size.Width, |
|||
(uint?)Size.Height, 1), |
|||
MipLevels = MipLevels, |
|||
ArrayLayers = 1, |
|||
Samples = SampleCountFlags.Count1Bit, |
|||
Tiling = Tiling, |
|||
Usage = _imageUsageFlags, |
|||
SharingMode = SharingMode.Exclusive, |
|||
InitialLayout = ImageLayout.Undefined, |
|||
Flags = ImageCreateFlags.CreateMutableFormatBit |
|||
}; |
|||
|
|||
Api |
|||
.CreateImage(_device, imageCreateInfo, null, out var image).ThrowOnError(); |
|||
InternalHandle = image; |
|||
|
|||
Api.GetImageMemoryRequirements(_device, InternalHandle.Value, |
|||
out var memoryRequirements); |
|||
|
|||
|
|||
var fdExport = new ExportMemoryAllocateInfo |
|||
{ |
|||
HandleTypes = handleType, SType = StructureType.ExportMemoryAllocateInfo |
|||
}; |
|||
var dedicatedAllocation = new MemoryDedicatedAllocateInfoKHR |
|||
{ |
|||
SType = StructureType.MemoryDedicatedAllocateInfoKhr, |
|||
Image = image |
|||
}; |
|||
ImportMemoryWin32HandleInfoKHR handleImport = default; |
|||
if (exportable && RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) |
|||
{ |
|||
_d3dTexture2D = D3DMemoryHelper.CreateMemoryHandle(vk.D3DDevice, size, Format); |
|||
using var dxgi = _d3dTexture2D.QueryInterface<SharpDX.DXGI.Resource1>(); |
|||
|
|||
handleImport = new ImportMemoryWin32HandleInfoKHR |
|||
{ |
|||
PNext = &dedicatedAllocation, |
|||
SType = StructureType.ImportMemoryWin32HandleInfoKhr, |
|||
HandleType = ExternalMemoryHandleTypeFlags.D3D11TextureBit, |
|||
Handle = dxgi.CreateSharedHandle(null, SharedResourceFlags.Read | SharedResourceFlags.Write), |
|||
}; |
|||
} |
|||
|
|||
var memoryAllocateInfo = new MemoryAllocateInfo |
|||
{ |
|||
PNext = |
|||
exportable ? RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? &handleImport : &fdExport : null, |
|||
SType = StructureType.MemoryAllocateInfo, |
|||
AllocationSize = memoryRequirements.Size, |
|||
MemoryTypeIndex = (uint)VulkanMemoryHelper.FindSuitableMemoryTypeIndex( |
|||
Api, |
|||
_physicalDevice, |
|||
memoryRequirements.MemoryTypeBits, MemoryPropertyFlags.DeviceLocalBit) |
|||
}; |
|||
|
|||
Api.AllocateMemory(_device, memoryAllocateInfo, null, |
|||
out var imageMemory).ThrowOnError(); |
|||
|
|||
_imageMemory = imageMemory; |
|||
|
|||
|
|||
MemorySize = memoryRequirements.Size; |
|||
|
|||
Api.BindImageMemory(_device, InternalHandle.Value, _imageMemory, 0).ThrowOnError(); |
|||
var componentMapping = new ComponentMapping( |
|||
ComponentSwizzle.Identity, |
|||
ComponentSwizzle.Identity, |
|||
ComponentSwizzle.Identity, |
|||
ComponentSwizzle.Identity); |
|||
|
|||
AspectFlags = ImageAspectFlags.ColorBit; |
|||
|
|||
var subresourceRange = new ImageSubresourceRange(AspectFlags, 0, MipLevels, 0, 1); |
|||
|
|||
var imageViewCreateInfo = new ImageViewCreateInfo |
|||
{ |
|||
SType = StructureType.ImageViewCreateInfo, |
|||
Image = InternalHandle.Value, |
|||
ViewType = ImageViewType.Type2D, |
|||
Format = Format, |
|||
Components = componentMapping, |
|||
SubresourceRange = subresourceRange |
|||
}; |
|||
|
|||
Api |
|||
.CreateImageView(_device, imageViewCreateInfo, null, out var imageView) |
|||
.ThrowOnError(); |
|||
|
|||
_imageView = imageView; |
|||
|
|||
_currentLayout = ImageLayout.Undefined; |
|||
|
|||
TransitionLayout(ImageLayout.ColorAttachmentOptimal, AccessFlags.NoneKhr); |
|||
} |
|||
|
|||
public int ExportFd() |
|||
{ |
|||
if (!Api.TryGetDeviceExtension<KhrExternalMemoryFd>(_instance, _device, out var ext)) |
|||
throw new InvalidOperationException(); |
|||
var info = new MemoryGetFdInfoKHR |
|||
{ |
|||
Memory = _imageMemory, |
|||
SType = StructureType.MemoryGetFDInfoKhr, |
|||
HandleType = ExternalMemoryHandleTypeFlags.OpaqueFDBit |
|||
}; |
|||
ext.GetMemoryF(_device, info, out var fd).ThrowOnError(); |
|||
return fd; |
|||
} |
|||
|
|||
public IPlatformHandle Export() |
|||
{ |
|||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) |
|||
{ |
|||
using var dxgi = _d3dTexture2D!.QueryInterface<Resource1>(); |
|||
return new PlatformHandle( |
|||
dxgi.CreateSharedHandle(null, SharedResourceFlags.Read | SharedResourceFlags.Write), |
|||
KnownPlatformGraphicsExternalImageHandleTypes.D3D11TextureNtHandle); |
|||
} |
|||
else |
|||
return new PlatformHandle(new IntPtr(ExportFd()), |
|||
KnownPlatformGraphicsExternalImageHandleTypes.VulkanOpaquePosixFileDescriptor); |
|||
} |
|||
|
|||
public ImageTiling Tiling => ImageTiling.Optimal; |
|||
|
|||
|
|||
|
|||
internal void TransitionLayout(CommandBuffer commandBuffer, |
|||
ImageLayout fromLayout, AccessFlags fromAccessFlags, |
|||
ImageLayout destinationLayout, AccessFlags destinationAccessFlags) |
|||
{ |
|||
VulkanMemoryHelper.TransitionLayout(Api, commandBuffer, InternalHandle.Value, |
|||
fromLayout, |
|||
fromAccessFlags, |
|||
destinationLayout, destinationAccessFlags, |
|||
MipLevels); |
|||
|
|||
_currentLayout = destinationLayout; |
|||
_currentAccessFlags = destinationAccessFlags; |
|||
} |
|||
|
|||
internal void TransitionLayout(CommandBuffer commandBuffer, |
|||
ImageLayout destinationLayout, AccessFlags destinationAccessFlags) |
|||
=> TransitionLayout(commandBuffer, _currentLayout, _currentAccessFlags, destinationLayout, |
|||
destinationAccessFlags); |
|||
|
|||
|
|||
internal void TransitionLayout(ImageLayout destinationLayout, AccessFlags destinationAccessFlags) |
|||
{ |
|||
var commandBuffer = _commandBufferPool.CreateCommandBuffer(); |
|||
commandBuffer.BeginRecording(); |
|||
TransitionLayout(commandBuffer.InternalHandle, destinationLayout, destinationAccessFlags); |
|||
commandBuffer.EndRecording(); |
|||
commandBuffer.Submit(); |
|||
} |
|||
|
|||
public void TransitionLayout(uint destinationLayout, uint destinationAccessFlags) |
|||
{ |
|||
TransitionLayout((ImageLayout)destinationLayout, (AccessFlags)destinationAccessFlags); |
|||
} |
|||
|
|||
public unsafe void Dispose() |
|||
{ |
|||
Api.DestroyImageView(_device, _imageView.Value, null); |
|||
Api.DestroyImage(_device, InternalHandle.Value, null); |
|||
Api.FreeMemory(_device, _imageMemory, null); |
|||
|
|||
_imageView = default; |
|||
InternalHandle = default; |
|||
_imageMemory = default; |
|||
} |
|||
|
|||
public void SaveTexture(string path) |
|||
{ |
|||
_vk.GrContext.ResetContext(); |
|||
var _image = this; |
|||
var imageInfo = new GRVkImageInfo() |
|||
{ |
|||
CurrentQueueFamily = _vk.QueueFamilyIndex, |
|||
Format = (uint)_image.Format, |
|||
Image = _image.Handle, |
|||
ImageLayout = (uint)_image.CurrentLayout, |
|||
ImageTiling = (uint)_image.Tiling, |
|||
ImageUsageFlags = (uint)_image.UsageFlags, |
|||
LevelCount = _image.MipLevels, |
|||
SampleCount = 1, |
|||
Protected = false, |
|||
Alloc = new GRVkAlloc() |
|||
{ |
|||
Memory = _image.MemoryHandle, Flags = 0, Offset = 0, Size = _image.MemorySize |
|||
} |
|||
}; |
|||
|
|||
using (var backendTexture = new GRBackendRenderTarget(_image.Size.Width, _image.Size.Height, 1, |
|||
imageInfo)) |
|||
using (var surface = SKSurface.Create(_vk.GrContext, backendTexture, |
|||
GRSurfaceOrigin.TopLeft, |
|||
SKColorType.Rgba8888, SKColorSpace.CreateSrgb())) |
|||
{ |
|||
using var snap = surface.Snapshot(); |
|||
using var encoded = snap.Encode(); |
|||
using (var s = File.Create(path)) |
|||
encoded.SaveTo(s); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,59 @@ |
|||
using Silk.NET.Vulkan; |
|||
|
|||
namespace GpuInterop.VulkanDemo; |
|||
|
|||
internal static class VulkanMemoryHelper |
|||
{ |
|||
internal static int FindSuitableMemoryTypeIndex(Vk api, PhysicalDevice physicalDevice, uint memoryTypeBits, |
|||
MemoryPropertyFlags flags) |
|||
{ |
|||
api.GetPhysicalDeviceMemoryProperties(physicalDevice, out var properties); |
|||
|
|||
for (var i = 0; i < properties.MemoryTypeCount; i++) |
|||
{ |
|||
var type = properties.MemoryTypes[i]; |
|||
|
|||
if ((memoryTypeBits & (1 << i)) != 0 && type.PropertyFlags.HasFlag(flags)) return i; |
|||
} |
|||
|
|||
return -1; |
|||
} |
|||
|
|||
internal static unsafe void TransitionLayout( |
|||
Vk api, |
|||
CommandBuffer commandBuffer, |
|||
Image image, |
|||
ImageLayout sourceLayout, |
|||
AccessFlags sourceAccessMask, |
|||
ImageLayout destinationLayout, |
|||
AccessFlags destinationAccessMask, |
|||
uint mipLevels) |
|||
{ |
|||
var subresourceRange = new ImageSubresourceRange(ImageAspectFlags.ColorBit, 0, mipLevels, 0, 1); |
|||
|
|||
var barrier = new ImageMemoryBarrier |
|||
{ |
|||
SType = StructureType.ImageMemoryBarrier, |
|||
SrcAccessMask = sourceAccessMask, |
|||
DstAccessMask = destinationAccessMask, |
|||
OldLayout = sourceLayout, |
|||
NewLayout = destinationLayout, |
|||
SrcQueueFamilyIndex = Vk.QueueFamilyIgnored, |
|||
DstQueueFamilyIndex = Vk.QueueFamilyIgnored, |
|||
Image = image, |
|||
SubresourceRange = subresourceRange |
|||
}; |
|||
|
|||
api.CmdPipelineBarrier( |
|||
commandBuffer, |
|||
PipelineStageFlags.AllCommandsBit, |
|||
PipelineStageFlags.AllCommandsBit, |
|||
0, |
|||
0, |
|||
null, |
|||
0, |
|||
null, |
|||
1, |
|||
barrier); |
|||
} |
|||
} |
|||
@ -0,0 +1,58 @@ |
|||
using System; |
|||
using Silk.NET.Vulkan; |
|||
using Silk.NET.Vulkan.Extensions.KHR; |
|||
using SilkNetDemo; |
|||
|
|||
namespace GpuInterop.VulkanDemo; |
|||
|
|||
class VulkanSemaphorePair : IDisposable |
|||
{ |
|||
private readonly VulkanContext _resources; |
|||
|
|||
public unsafe VulkanSemaphorePair(VulkanContext resources, bool exportable) |
|||
{ |
|||
_resources = resources; |
|||
|
|||
var semaphoreExportInfo = new ExportSemaphoreCreateInfo |
|||
{ |
|||
SType = StructureType.ExportSemaphoreCreateInfo, |
|||
HandleTypes = ExternalSemaphoreHandleTypeFlags.OpaqueFDBit |
|||
}; |
|||
|
|||
var semaphoreCreateInfo = new SemaphoreCreateInfo |
|||
{ |
|||
SType = StructureType.SemaphoreCreateInfo, |
|||
PNext = exportable ? &semaphoreExportInfo : null |
|||
}; |
|||
|
|||
resources.Api.CreateSemaphore(resources.Device, semaphoreCreateInfo, null, out var semaphore).ThrowOnError(); |
|||
ImageAvailableSemaphore = semaphore; |
|||
|
|||
resources.Api.CreateSemaphore(resources.Device, semaphoreCreateInfo, null, out semaphore).ThrowOnError(); |
|||
RenderFinishedSemaphore = semaphore; |
|||
} |
|||
|
|||
public int ExportFd(bool renderFinished) |
|||
{ |
|||
if (!_resources.Api.TryGetDeviceExtension<KhrExternalSemaphoreFd>(_resources.Instance, _resources.Device, |
|||
out var ext)) |
|||
throw new InvalidOperationException(); |
|||
var info = new SemaphoreGetFdInfoKHR() |
|||
{ |
|||
SType = StructureType.SemaphoreGetFDInfoKhr, |
|||
Semaphore = renderFinished ? RenderFinishedSemaphore : ImageAvailableSemaphore, |
|||
HandleType = ExternalSemaphoreHandleTypeFlags.OpaqueFDBit |
|||
}; |
|||
ext.GetSemaphoreF(_resources.Device, info, out var fd).ThrowOnError(); |
|||
return fd; |
|||
} |
|||
|
|||
internal Semaphore ImageAvailableSemaphore { get; } |
|||
internal Semaphore RenderFinishedSemaphore { get; } |
|||
|
|||
public unsafe void Dispose() |
|||
{ |
|||
_resources.Api.DestroySemaphore(_resources.Device, ImageAvailableSemaphore, null); |
|||
_resources.Api.DestroySemaphore(_resources.Device, RenderFinishedSemaphore, null); |
|||
} |
|||
} |
|||
@ -0,0 +1,151 @@ |
|||
using System; |
|||
using System.Runtime.InteropServices; |
|||
using System.Threading.Tasks; |
|||
using Avalonia; |
|||
using Avalonia.Platform; |
|||
using Avalonia.Rendering; |
|||
using Avalonia.Rendering.Composition; |
|||
using Avalonia.Vulkan; |
|||
using Silk.NET.Vulkan; |
|||
|
|||
namespace GpuInterop.VulkanDemo; |
|||
|
|||
class VulkanSwapchain : SwapchainBase<VulkanSwapchainImage> |
|||
{ |
|||
private readonly VulkanContext _vk; |
|||
|
|||
public VulkanSwapchain(VulkanContext vk, ICompositionGpuInterop interop, CompositionDrawingSurface target) : base(interop, target) |
|||
{ |
|||
_vk = vk; |
|||
} |
|||
|
|||
protected override VulkanSwapchainImage CreateImage(PixelSize size) |
|||
{ |
|||
return new VulkanSwapchainImage(_vk, size, Interop, Target); |
|||
} |
|||
|
|||
public IDisposable BeginDraw(PixelSize size, out VulkanImage image) |
|||
{ |
|||
_vk.Pool.FreeUsedCommandBuffers(); |
|||
var rv = BeginDrawCore(size, out var swapchainImage); |
|||
image = swapchainImage.Image; |
|||
return rv; |
|||
} |
|||
} |
|||
|
|||
class VulkanSwapchainImage : ISwapchainImage |
|||
{ |
|||
private readonly VulkanContext _vk; |
|||
private readonly ICompositionGpuInterop _interop; |
|||
private readonly CompositionDrawingSurface _target; |
|||
private readonly VulkanImage _image; |
|||
private readonly VulkanSemaphorePair _semaphorePair; |
|||
private ICompositionImportedGpuSemaphore? _availableSemaphore, _renderCompletedSemaphore; |
|||
private ICompositionImportedGpuImage? _importedImage; |
|||
private Task? _lastPresent; |
|||
public VulkanImage Image => _image; |
|||
private bool _initial = true; |
|||
|
|||
public VulkanSwapchainImage(VulkanContext vk, PixelSize size, ICompositionGpuInterop interop, CompositionDrawingSurface target) |
|||
{ |
|||
_vk = vk; |
|||
_interop = interop; |
|||
_target = target; |
|||
Size = size; |
|||
_image = new VulkanImage(vk, (uint)Format.R8G8B8A8Unorm, size, true); |
|||
_semaphorePair = new VulkanSemaphorePair(vk, true); |
|||
} |
|||
|
|||
public async ValueTask DisposeAsync() |
|||
{ |
|||
if (LastPresent != null) |
|||
await LastPresent; |
|||
if (_importedImage != null) |
|||
await _importedImage.DisposeAsync(); |
|||
if (_availableSemaphore != null) |
|||
await _availableSemaphore.DisposeAsync(); |
|||
if (_renderCompletedSemaphore != null) |
|||
await _renderCompletedSemaphore.DisposeAsync(); |
|||
_semaphorePair.Dispose(); |
|||
_image.Dispose(); |
|||
} |
|||
|
|||
public PixelSize Size { get; } |
|||
|
|||
public Task? LastPresent => _lastPresent; |
|||
|
|||
public void BeginDraw() |
|||
{ |
|||
var buffer = _vk.Pool.CreateCommandBuffer(); |
|||
buffer.BeginRecording(); |
|||
|
|||
_image.TransitionLayout(buffer.InternalHandle, |
|||
ImageLayout.Undefined, AccessFlags.None, |
|||
ImageLayout.ColorAttachmentOptimal, AccessFlags.ColorAttachmentReadBit); |
|||
|
|||
if(RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) |
|||
buffer.Submit(null,null,null, null, new VulkanCommandBufferPool.VulkanCommandBuffer.KeyedMutexSubmitInfo |
|||
{ |
|||
AcquireKey = 0, |
|||
DeviceMemory = _image.DeviceMemory |
|||
}); |
|||
else if (_initial) |
|||
{ |
|||
_initial = false; |
|||
buffer.Submit(); |
|||
} |
|||
else |
|||
buffer.Submit(new[] { _semaphorePair.ImageAvailableSemaphore }, |
|||
new[] |
|||
{ |
|||
PipelineStageFlags.AllGraphicsBit |
|||
}); |
|||
} |
|||
|
|||
|
|||
|
|||
public void Present() |
|||
{ |
|||
var buffer = _vk.Pool.CreateCommandBuffer(); |
|||
buffer.BeginRecording(); |
|||
_image.TransitionLayout(buffer.InternalHandle, ImageLayout.TransferSrcOptimal, AccessFlags.TransferWriteBit); |
|||
|
|||
|
|||
|
|||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) |
|||
{ |
|||
buffer.Submit(null, null, null, null, |
|||
new VulkanCommandBufferPool.VulkanCommandBuffer.KeyedMutexSubmitInfo |
|||
{ |
|||
DeviceMemory = _image.DeviceMemory, ReleaseKey = 1 |
|||
}); |
|||
} |
|||
else |
|||
buffer.Submit(null, null, new[] { _semaphorePair.RenderFinishedSemaphore }); |
|||
|
|||
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) |
|||
{ |
|||
_availableSemaphore ??= _interop.ImportSemaphore(new PlatformHandle( |
|||
new IntPtr(_semaphorePair.ExportFd(false)), |
|||
KnownPlatformGraphicsExternalSemaphoreHandleTypes.VulkanOpaquePosixFileDescriptor)); |
|||
|
|||
_renderCompletedSemaphore ??= _interop.ImportSemaphore(new PlatformHandle( |
|||
new IntPtr(_semaphorePair.ExportFd(true)), |
|||
KnownPlatformGraphicsExternalSemaphoreHandleTypes.VulkanOpaquePosixFileDescriptor)); |
|||
} |
|||
|
|||
_importedImage ??= _interop.ImportImage(_image.Export(), |
|||
new PlatformGraphicsExternalImageProperties |
|||
{ |
|||
Format = PlatformGraphicsExternalImageFormat.R8G8B8A8UNorm, |
|||
Width = Size.Width, |
|||
Height = Size.Height, |
|||
MemorySize = _image.MemorySize |
|||
}); |
|||
|
|||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) |
|||
_lastPresent = _target.UpdateWithKeyedMutexAsync(_importedImage, 1, 0); |
|||
else |
|||
_lastPresent = _target.UpdateWithSemaphoresAsync(_importedImage, _renderCompletedSemaphore, _availableSemaphore); |
|||
} |
|||
} |
|||
@ -1,8 +1,9 @@ |
|||
<Application xmlns="https://github.com/avaloniaui" |
|||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
|||
Name="Mobile Sandbox" |
|||
x:Class="MobileSandbox.App"> |
|||
x:Class="MobileSandbox.App" |
|||
RequestedThemeVariant="Dark"> |
|||
<Application.Styles> |
|||
<FluentTheme Mode="Dark" /> |
|||
<FluentTheme /> |
|||
</Application.Styles> |
|||
</Application> |
|||
|
|||
@ -1,5 +1,5 @@ |
|||
<Application xmlns="https://github.com/avaloniaui"> |
|||
<Application.Styles> |
|||
<SimpleTheme Mode="Light" /> |
|||
<SimpleTheme /> |
|||
</Application.Styles> |
|||
</Application> |
|||
|
|||
@ -1,5 +1,5 @@ |
|||
<Application xmlns="https://github.com/avaloniaui"> |
|||
<Application.Styles> |
|||
<SimpleTheme Mode="Light" /> |
|||
<SimpleTheme /> |
|||
</Application.Styles> |
|||
</Application> |
|||
|
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue