[OSX] Implemented IOSurface/MTLSharedEvent interop APIs (#18791)
* [OSX] Implemented IOSurface/MTLSharedEvent interop APIs * Bump Xcode? * Xcode? * APIDiff * Use different XCode versions because of how awesome appium is * A hack for crapium * Replace SkiaMetalApi usages * Update API suppressions --------- Co-authored-by: Timothy Miller <innerlogic4321@gmail.com> Co-authored-by: Julien Lebosquain <julien@lebosquain.net>
This commit is contained in:
@@ -25,6 +25,30 @@
|
|||||||
<Left>baseline/Avalonia/lib/net6.0/Avalonia.Base.dll</Left>
|
<Left>baseline/Avalonia/lib/net6.0/Avalonia.Base.dll</Left>
|
||||||
<Right>current/Avalonia/lib/net6.0/Avalonia.Base.dll</Right>
|
<Right>current/Avalonia/lib/net6.0/Avalonia.Base.dll</Right>
|
||||||
</Suppression>
|
</Suppression>
|
||||||
|
<Suppression>
|
||||||
|
<DiagnosticId>CP0006</DiagnosticId>
|
||||||
|
<Target>M:Avalonia.Platform.IPlatformRenderInterfaceImportedImage.SnapshotWithTimelineSemaphores(Avalonia.Platform.IPlatformRenderInterfaceImportedSemaphore,System.UInt64,Avalonia.Platform.IPlatformRenderInterfaceImportedSemaphore,System.UInt64)</Target>
|
||||||
|
<Left>baseline/Avalonia/lib/net6.0/Avalonia.Base.dll</Left>
|
||||||
|
<Right>current/Avalonia/lib/net6.0/Avalonia.Base.dll</Right>
|
||||||
|
</Suppression>
|
||||||
|
<Suppression>
|
||||||
|
<DiagnosticId>CP0006</DiagnosticId>
|
||||||
|
<Target>M:Avalonia.OpenGL.IGlExternalSemaphore.SignalTimelineSemaphore(Avalonia.OpenGL.IGlExternalImageTexture,System.UInt64)</Target>
|
||||||
|
<Left>baseline/Avalonia/lib/net6.0/Avalonia.OpenGL.dll</Left>
|
||||||
|
<Right>current/Avalonia/lib/net6.0/Avalonia.OpenGL.dll</Right>
|
||||||
|
</Suppression>
|
||||||
|
<Suppression>
|
||||||
|
<DiagnosticId>CP0006</DiagnosticId>
|
||||||
|
<Target>M:Avalonia.OpenGL.IGlExternalSemaphore.WaitTimelineSemaphore(Avalonia.OpenGL.IGlExternalImageTexture,System.UInt64)</Target>
|
||||||
|
<Left>baseline/Avalonia/lib/net6.0/Avalonia.OpenGL.dll</Left>
|
||||||
|
<Right>current/Avalonia/lib/net6.0/Avalonia.OpenGL.dll</Right>
|
||||||
|
</Suppression>
|
||||||
|
<Suppression>
|
||||||
|
<DiagnosticId>CP0006</DiagnosticId>
|
||||||
|
<Target>P:Avalonia.OpenGL.IGlExternalImageTexture.TextureType</Target>
|
||||||
|
<Left>baseline/Avalonia/lib/net6.0/Avalonia.OpenGL.dll</Left>
|
||||||
|
<Right>current/Avalonia/lib/net6.0/Avalonia.OpenGL.dll</Right>
|
||||||
|
</Suppression>
|
||||||
<Suppression>
|
<Suppression>
|
||||||
<DiagnosticId>CP0006</DiagnosticId>
|
<DiagnosticId>CP0006</DiagnosticId>
|
||||||
<Target>M:Avalonia.Input.Platform.IClipboard.SetDataAsync(Avalonia.Input.IAsyncDataTransfer)</Target>
|
<Target>M:Avalonia.Input.Platform.IClipboard.SetDataAsync(Avalonia.Input.IAsyncDataTransfer)</Target>
|
||||||
@@ -49,6 +73,30 @@
|
|||||||
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
|
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
|
||||||
<Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
|
<Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
|
||||||
</Suppression>
|
</Suppression>
|
||||||
|
<Suppression>
|
||||||
|
<DiagnosticId>CP0006</DiagnosticId>
|
||||||
|
<Target>M:Avalonia.Platform.IPlatformRenderInterfaceImportedImage.SnapshotWithTimelineSemaphores(Avalonia.Platform.IPlatformRenderInterfaceImportedSemaphore,System.UInt64,Avalonia.Platform.IPlatformRenderInterfaceImportedSemaphore,System.UInt64)</Target>
|
||||||
|
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
|
||||||
|
<Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
|
||||||
|
</Suppression>
|
||||||
|
<Suppression>
|
||||||
|
<DiagnosticId>CP0006</DiagnosticId>
|
||||||
|
<Target>M:Avalonia.OpenGL.IGlExternalSemaphore.SignalTimelineSemaphore(Avalonia.OpenGL.IGlExternalImageTexture,System.UInt64)</Target>
|
||||||
|
<Left>baseline/Avalonia/lib/net8.0/Avalonia.OpenGL.dll</Left>
|
||||||
|
<Right>current/Avalonia/lib/net8.0/Avalonia.OpenGL.dll</Right>
|
||||||
|
</Suppression>
|
||||||
|
<Suppression>
|
||||||
|
<DiagnosticId>CP0006</DiagnosticId>
|
||||||
|
<Target>M:Avalonia.OpenGL.IGlExternalSemaphore.WaitTimelineSemaphore(Avalonia.OpenGL.IGlExternalImageTexture,System.UInt64)</Target>
|
||||||
|
<Left>baseline/Avalonia/lib/net8.0/Avalonia.OpenGL.dll</Left>
|
||||||
|
<Right>current/Avalonia/lib/net8.0/Avalonia.OpenGL.dll</Right>
|
||||||
|
</Suppression>
|
||||||
|
<Suppression>
|
||||||
|
<DiagnosticId>CP0006</DiagnosticId>
|
||||||
|
<Target>P:Avalonia.OpenGL.IGlExternalImageTexture.TextureType</Target>
|
||||||
|
<Left>baseline/Avalonia/lib/net8.0/Avalonia.OpenGL.dll</Left>
|
||||||
|
<Right>current/Avalonia/lib/net8.0/Avalonia.OpenGL.dll</Right>
|
||||||
|
</Suppression>
|
||||||
<Suppression>
|
<Suppression>
|
||||||
<DiagnosticId>CP0006</DiagnosticId>
|
<DiagnosticId>CP0006</DiagnosticId>
|
||||||
<Target>M:Avalonia.Input.Platform.IClipboard.SetDataAsync(Avalonia.Input.IAsyncDataTransfer)</Target>
|
<Target>M:Avalonia.Input.Platform.IClipboard.SetDataAsync(Avalonia.Input.IAsyncDataTransfer)</Target>
|
||||||
@@ -73,4 +121,28 @@
|
|||||||
<Left>baseline/Avalonia/lib/netstandard2.0/Avalonia.Base.dll</Left>
|
<Left>baseline/Avalonia/lib/netstandard2.0/Avalonia.Base.dll</Left>
|
||||||
<Right>current/Avalonia/lib/netstandard2.0/Avalonia.Base.dll</Right>
|
<Right>current/Avalonia/lib/netstandard2.0/Avalonia.Base.dll</Right>
|
||||||
</Suppression>
|
</Suppression>
|
||||||
|
<Suppression>
|
||||||
|
<DiagnosticId>CP0006</DiagnosticId>
|
||||||
|
<Target>M:Avalonia.Platform.IPlatformRenderInterfaceImportedImage.SnapshotWithTimelineSemaphores(Avalonia.Platform.IPlatformRenderInterfaceImportedSemaphore,System.UInt64,Avalonia.Platform.IPlatformRenderInterfaceImportedSemaphore,System.UInt64)</Target>
|
||||||
|
<Left>baseline/Avalonia/lib/netstandard2.0/Avalonia.Base.dll</Left>
|
||||||
|
<Right>current/Avalonia/lib/netstandard2.0/Avalonia.Base.dll</Right>
|
||||||
|
</Suppression>
|
||||||
|
<Suppression>
|
||||||
|
<DiagnosticId>CP0006</DiagnosticId>
|
||||||
|
<Target>M:Avalonia.OpenGL.IGlExternalSemaphore.SignalTimelineSemaphore(Avalonia.OpenGL.IGlExternalImageTexture,System.UInt64)</Target>
|
||||||
|
<Left>baseline/Avalonia/lib/netstandard2.0/Avalonia.OpenGL.dll</Left>
|
||||||
|
<Right>current/Avalonia/lib/netstandard2.0/Avalonia.OpenGL.dll</Right>
|
||||||
|
</Suppression>
|
||||||
|
<Suppression>
|
||||||
|
<DiagnosticId>CP0006</DiagnosticId>
|
||||||
|
<Target>M:Avalonia.OpenGL.IGlExternalSemaphore.WaitTimelineSemaphore(Avalonia.OpenGL.IGlExternalImageTexture,System.UInt64)</Target>
|
||||||
|
<Left>baseline/Avalonia/lib/netstandard2.0/Avalonia.OpenGL.dll</Left>
|
||||||
|
<Right>current/Avalonia/lib/netstandard2.0/Avalonia.OpenGL.dll</Right>
|
||||||
|
</Suppression>
|
||||||
|
<Suppression>
|
||||||
|
<DiagnosticId>CP0006</DiagnosticId>
|
||||||
|
<Target>P:Avalonia.OpenGL.IGlExternalImageTexture.TextureType</Target>
|
||||||
|
<Left>baseline/Avalonia/lib/netstandard2.0/Avalonia.OpenGL.dll</Left>
|
||||||
|
<Right>current/Avalonia/lib/netstandard2.0/Avalonia.OpenGL.dll</Right>
|
||||||
|
</Suppression>
|
||||||
</Suppressions>
|
</Suppressions>
|
||||||
|
|||||||
@@ -25,12 +25,13 @@ jobs:
|
|||||||
arch="arm64"
|
arch="arm64"
|
||||||
fi
|
fi
|
||||||
git clean -ffdx
|
git clean -ffdx
|
||||||
sudo xcode-select -s /Applications/Xcode.app/Contents/Developer
|
|
||||||
pkill node
|
pkill node
|
||||||
pkill testmanagerd
|
pkill testmanagerd
|
||||||
appium > appium.out &
|
appium > appium.out &
|
||||||
pkill IntegrationTestApp
|
pkill IntegrationTestApp
|
||||||
|
sudo xcode-select -s /Applications/Xcode_15.2.app/Contents/Developer
|
||||||
./build.sh CompileNative
|
./build.sh CompileNative
|
||||||
|
sudo xcode-select -s /Applications/Xcode_14.3.app/Contents/Developer
|
||||||
rm -rf $(osascript -e "POSIX path of (path to application id \"net.avaloniaui.avalonia.integrationtestapp\")")
|
rm -rf $(osascript -e "POSIX path of (path to application id \"net.avaloniaui.avalonia.integrationtestapp\")")
|
||||||
pkill IntegrationTestApp
|
pkill IntegrationTestApp
|
||||||
./samples/IntegrationTestApp/bundle.sh
|
./samples/IntegrationTestApp/bundle.sh
|
||||||
|
|||||||
+2
-2
@@ -95,11 +95,11 @@ jobs:
|
|||||||
inputs:
|
inputs:
|
||||||
actions: 'build'
|
actions: 'build'
|
||||||
scheme: ''
|
scheme: ''
|
||||||
sdk: 'macosx13.0'
|
sdk: 'macosx14.2'
|
||||||
configuration: 'Release'
|
configuration: 'Release'
|
||||||
xcWorkspacePath: '**/*.xcodeproj/project.xcworkspace'
|
xcWorkspacePath: '**/*.xcodeproj/project.xcworkspace'
|
||||||
xcodeVersion: 'specifyPath' # Options: 8, 9, default, specifyPath
|
xcodeVersion: 'specifyPath' # Options: 8, 9, default, specifyPath
|
||||||
xcodeDeveloperDir: '/Applications/Xcode_14.1.app/Contents/Developer'
|
xcodeDeveloperDir: '/Applications/Xcode_15.2.app/Contents/Developer'
|
||||||
args: '-derivedDataPath ./'
|
args: '-derivedDataPath ./'
|
||||||
|
|
||||||
- task: CmdLine@2
|
- task: CmdLine@2
|
||||||
|
|||||||
@@ -8,4 +8,7 @@ public:
|
|||||||
~CppAutoreleasePool();
|
~CppAutoreleasePool();
|
||||||
};
|
};
|
||||||
|
|
||||||
#define START_ARP_CALL CppAutoreleasePool __autoreleasePool
|
#define START_ARP_CALL CppAutoreleasePool __autoreleasePool
|
||||||
|
extern void ReleaseNSObject(void* obj);
|
||||||
|
extern void RetainNSObject(void* obj);
|
||||||
|
extern uint64_t GetRetainCountForNSObject(void* obj);
|
||||||
|
|||||||
@@ -31,6 +31,8 @@
|
|||||||
1A3E5EAE23E9FB1300EDE661 /* cgl.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A3E5EAD23E9FB1300EDE661 /* cgl.mm */; };
|
1A3E5EAE23E9FB1300EDE661 /* cgl.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A3E5EAD23E9FB1300EDE661 /* cgl.mm */; };
|
||||||
1A3E5EB023E9FE8300EDE661 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1A3E5EAF23E9FE8300EDE661 /* QuartzCore.framework */; };
|
1A3E5EB023E9FE8300EDE661 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1A3E5EAF23E9FE8300EDE661 /* QuartzCore.framework */; };
|
||||||
1A465D10246AB61600C5858B /* dnd.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A465D0F246AB61600C5858B /* dnd.mm */; };
|
1A465D10246AB61600C5858B /* dnd.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A465D0F246AB61600C5858B /* dnd.mm */; };
|
||||||
|
1AC7F1432DCA0C2E003A161B /* crapium.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1AC7F1422DCA0C2E003A161B /* crapium.mm */; };
|
||||||
|
1AE55B8C2DC1060E00FD0BB3 /* memhelp.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1AE55B8B2DC1060E00FD0BB3 /* memhelp.mm */; };
|
||||||
1AFD334123E03C4F0042899B /* controlhost.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1AFD334023E03C4F0042899B /* controlhost.mm */; };
|
1AFD334123E03C4F0042899B /* controlhost.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1AFD334023E03C4F0042899B /* controlhost.mm */; };
|
||||||
37155CE4233C00EB0034DCE9 /* menu.h in Headers */ = {isa = PBXBuildFile; fileRef = 37155CE3233C00EB0034DCE9 /* menu.h */; };
|
37155CE4233C00EB0034DCE9 /* menu.h in Headers */ = {isa = PBXBuildFile; fileRef = 37155CE3233C00EB0034DCE9 /* menu.h */; };
|
||||||
37A517B32159597E00FBA241 /* Screens.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37A517B22159597E00FBA241 /* Screens.mm */; };
|
37A517B32159597E00FBA241 /* Screens.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37A517B22159597E00FBA241 /* Screens.mm */; };
|
||||||
@@ -91,6 +93,9 @@
|
|||||||
1A3E5EAD23E9FB1300EDE661 /* cgl.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = cgl.mm; sourceTree = "<group>"; };
|
1A3E5EAD23E9FB1300EDE661 /* cgl.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = cgl.mm; sourceTree = "<group>"; };
|
||||||
1A3E5EAF23E9FE8300EDE661 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
|
1A3E5EAF23E9FE8300EDE661 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
|
||||||
1A465D0F246AB61600C5858B /* dnd.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = dnd.mm; sourceTree = "<group>"; };
|
1A465D0F246AB61600C5858B /* dnd.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = dnd.mm; sourceTree = "<group>"; };
|
||||||
|
1AC7F1422DCA0C2E003A161B /* crapium.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objcpp; path = crapium.mm; sourceTree = "<group>"; };
|
||||||
|
1AC7F1442DCA0D6A003A161B /* crapium.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = crapium.h; sourceTree = "<group>"; };
|
||||||
|
1AE55B8B2DC1060E00FD0BB3 /* memhelp.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = memhelp.mm; sourceTree = "<group>"; };
|
||||||
1AFD334023E03C4F0042899B /* controlhost.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = controlhost.mm; sourceTree = "<group>"; };
|
1AFD334023E03C4F0042899B /* controlhost.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = controlhost.mm; sourceTree = "<group>"; };
|
||||||
37155CE3233C00EB0034DCE9 /* menu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = menu.h; sourceTree = "<group>"; };
|
37155CE3233C00EB0034DCE9 /* menu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = menu.h; sourceTree = "<group>"; };
|
||||||
379860FE214DA0C000CD0246 /* KeyTransform.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KeyTransform.h; sourceTree = "<group>"; };
|
379860FE214DA0C000CD0246 /* KeyTransform.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KeyTransform.h; sourceTree = "<group>"; };
|
||||||
@@ -171,6 +176,9 @@
|
|||||||
AB7A61E62147C814003C5833 = {
|
AB7A61E62147C814003C5833 = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
1AC7F1442DCA0D6A003A161B /* crapium.h */,
|
||||||
|
1AC7F1422DCA0C2E003A161B /* crapium.mm */,
|
||||||
|
1AE55B8B2DC1060E00FD0BB3 /* memhelp.mm */,
|
||||||
F10084852BFF1FB40024303E /* TopLevelImpl.mm */,
|
F10084852BFF1FB40024303E /* TopLevelImpl.mm */,
|
||||||
BC7C33832C066F1100945A48 /* AvnAccessibility.h */,
|
BC7C33832C066F1100945A48 /* AvnAccessibility.h */,
|
||||||
BC7C33812C066DBF00945A48 /* AvnAutomationNode.h */,
|
BC7C33812C066DBF00945A48 /* AvnAutomationNode.h */,
|
||||||
@@ -333,6 +341,7 @@
|
|||||||
523484CA26EA688F00EA0C2C /* trayicon.mm in Sources */,
|
523484CA26EA688F00EA0C2C /* trayicon.mm in Sources */,
|
||||||
AB8F7D6B21482D7F0057DBA5 /* platformthreading.mm in Sources */,
|
AB8F7D6B21482D7F0057DBA5 /* platformthreading.mm in Sources */,
|
||||||
1A3E5EA823E9E83B00EDE661 /* rendertarget.mm in Sources */,
|
1A3E5EA823E9E83B00EDE661 /* rendertarget.mm in Sources */,
|
||||||
|
1AE55B8C2DC1060E00FD0BB3 /* memhelp.mm in Sources */,
|
||||||
1A3E5EAE23E9FB1300EDE661 /* cgl.mm in Sources */,
|
1A3E5EAE23E9FB1300EDE661 /* cgl.mm in Sources */,
|
||||||
BC11A5BF2608D58F0017BAD0 /* automation.mm in Sources */,
|
BC11A5BF2608D58F0017BAD0 /* automation.mm in Sources */,
|
||||||
37E2330F21583241000CB7E2 /* KeyTransform.mm in Sources */,
|
37E2330F21583241000CB7E2 /* KeyTransform.mm in Sources */,
|
||||||
@@ -353,6 +362,7 @@
|
|||||||
ED754D262A97306B0078B4DF /* PlatformRenderTimer.mm in Sources */,
|
ED754D262A97306B0078B4DF /* PlatformRenderTimer.mm in Sources */,
|
||||||
1839151F32D1BB1AB51A7BB6 /* AvnPanelWindow.mm in Sources */,
|
1839151F32D1BB1AB51A7BB6 /* AvnPanelWindow.mm in Sources */,
|
||||||
18391AC16726CBC45856233B /* AvnWindow.mm in Sources */,
|
18391AC16726CBC45856233B /* AvnWindow.mm in Sources */,
|
||||||
|
1AC7F1432DCA0C2E003A161B /* crapium.mm in Sources */,
|
||||||
18391D8CD1756DC858DC1A09 /* PopupImpl.mm in Sources */,
|
18391D8CD1756DC858DC1A09 /* PopupImpl.mm in Sources */,
|
||||||
EDF8CDCD2964CB01001EE34F /* PlatformSettings.mm in Sources */,
|
EDF8CDCD2964CB01001EE34F /* PlatformSettings.mm in Sources */,
|
||||||
64B1EA48E308E574685AFB07 /* metal.mm in Sources */,
|
64B1EA48E308E574685AFB07 /* metal.mm in Sources */,
|
||||||
|
|||||||
@@ -107,6 +107,61 @@ public:
|
|||||||
return Context;
|
return Context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int texImageIOSurface2D(int target, int internal_format,
|
||||||
|
int width, int height, int format, int type, void* ioSurface, int plane) override
|
||||||
|
{
|
||||||
|
return CGLTexImageIOSurface2D(Context, target, internal_format, width, height, format, type, (IOSurfaceRef)ioSurface, plane);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GetIOKitRegistryId(uint64_t *value) override {
|
||||||
|
if (@available(macOS 10.13, *))
|
||||||
|
{
|
||||||
|
|
||||||
|
GLint rendererId;
|
||||||
|
if(CGLGetParameter(Context, kCGLCPCurrentRendererID, &rendererId) != 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
GLint rendererCount = 0;
|
||||||
|
CGLRendererInfoObj rendererInfo;
|
||||||
|
|
||||||
|
if(CGLQueryRendererInfo(0xFFFFFFFF, &rendererInfo, &rendererCount))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
@try
|
||||||
|
{
|
||||||
|
for(auto i = 0; i < rendererCount; i++)
|
||||||
|
{
|
||||||
|
GLint thisRendererID;
|
||||||
|
|
||||||
|
CGLDescribeRenderer(rendererInfo, i, kCGLRPRendererID, &thisRendererID);
|
||||||
|
if(thisRendererID == rendererId)
|
||||||
|
{
|
||||||
|
GLint gpuIDLow = 0;
|
||||||
|
GLint gpuIDHigh = 0;
|
||||||
|
|
||||||
|
if(CGLDescribeRenderer(rendererInfo, 0, kCGLRPRegistryIDLow, &gpuIDLow))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if(CGLDescribeRenderer(rendererInfo, 0, kCGLRPRegistryIDHigh, &gpuIDHigh))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
*value = ((uint64_t)gpuIDHigh << 32) | gpuIDLow;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
@finally
|
||||||
|
{
|
||||||
|
CGLDestroyRendererInfo(rendererInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
~AvnGlContext()
|
~AvnGlContext()
|
||||||
{
|
{
|
||||||
CGLReleaseContext(Context);
|
CGLReleaseContext(Context);
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ extern IAvnPlatformBehaviorInhibition* CreatePlatformBehaviorInhibition();
|
|||||||
extern IAvnNativeControlHost* CreateNativeControlHost(NSView* parent);
|
extern IAvnNativeControlHost* CreateNativeControlHost(NSView* parent);
|
||||||
extern IAvnPlatformSettings* CreatePlatformSettings();
|
extern IAvnPlatformSettings* CreatePlatformSettings();
|
||||||
extern IAvnPlatformRenderTimer* CreatePlatformRenderTimer();
|
extern IAvnPlatformRenderTimer* CreatePlatformRenderTimer();
|
||||||
|
extern IAvnNativeObjectsMemoryManagement* CreateMemoryManagementHelper();
|
||||||
extern void SetAppMenu(IAvnMenu *menu);
|
extern void SetAppMenu(IAvnMenu *menu);
|
||||||
extern void SetServicesMenu (IAvnMenu* menu);
|
extern void SetServicesMenu (IAvnMenu* menu);
|
||||||
extern IAvnMenu* GetAppMenu ();
|
extern IAvnMenu* GetAppMenu ();
|
||||||
@@ -47,6 +48,7 @@ extern AvnPoint ToAvnPoint (NSPoint p);
|
|||||||
extern AvnPoint ConvertPointY (AvnPoint p);
|
extern AvnPoint ConvertPointY (AvnPoint p);
|
||||||
extern NSSize ToNSSize (AvnSize s);
|
extern NSSize ToNSSize (AvnSize s);
|
||||||
extern AvnSize FromNSSize (NSSize s);
|
extern AvnSize FromNSSize (NSSize s);
|
||||||
|
extern IAvnMTLSharedEvent* ImportMTLSharedEvent(void* object);
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
#define NSDebugLog(...) NSLog(__VA_ARGS__)
|
#define NSDebugLog(...) NSLog(__VA_ARGS__)
|
||||||
#else
|
#else
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// The only reason this file exists is Appium which limits our highest Xcode version to 15.2. Please, purge Appium from our codebase
|
||||||
|
#ifndef crapium_h
|
||||||
|
#define crapium_h
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
@protocol MTLSharedEvent;
|
||||||
|
|
||||||
|
API_AVAILABLE(macos(12))
|
||||||
|
extern BOOL MtlSharedEventWaitUntilSignaledValueHack(id<MTLSharedEvent> ev, uint64_t value, uint64_t milliseconds);
|
||||||
|
#endif /* crapium_h */
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
// The only reason this file exists is Appium which limits our highest Xcode version to 15.2. Please, purge Appium from our codebase
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
#import "crapium.h"
|
||||||
|
@class MTLSharedEventHandle;
|
||||||
|
@protocol MTLSharedEvent;
|
||||||
|
@protocol MTLEvent;
|
||||||
|
|
||||||
|
typedef void (^MTLSharedEventNotificationBlock)(id <MTLSharedEvent>, uint64_t value);
|
||||||
|
|
||||||
|
API_AVAILABLE(macos(10.14), ios(12.0))
|
||||||
|
@protocol MTLSharedEvent <MTLEvent>
|
||||||
|
// Synchronously wait for the signaledValue to be greater than or equal to 'value', with a timeout
|
||||||
|
// specified in milliseconds. Returns YES if the value was signaled before the timeout, otherwise NO.
|
||||||
|
- (BOOL)waitUntilSignaledValue:(uint64_t)value timeoutMS:(uint64_t)milliseconds API_AVAILABLE(macos(12.0), ios(15.0));
|
||||||
|
@end
|
||||||
|
|
||||||
|
API_AVAILABLE(macos(12))
|
||||||
|
extern BOOL MtlSharedEventWaitUntilSignaledValueHack(id<MTLSharedEvent> ev, uint64_t value, uint64_t milliseconds)
|
||||||
|
{
|
||||||
|
return [ev waitUntilSignaledValue:value timeoutMS:milliseconds];
|
||||||
|
}
|
||||||
@@ -467,6 +467,22 @@ public:
|
|||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual HRESULT ImportMTLSharedEvent(void* event, IAvnMTLSharedEvent** ppv) override
|
||||||
|
{
|
||||||
|
START_COM_CALL;
|
||||||
|
*ppv = ::ImportMTLSharedEvent(event);
|
||||||
|
return *ppv != nullptr ? S_OK : E_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT CreateMemoryManagementHelper(IAvnNativeObjectsMemoryManagement **ppv) override {
|
||||||
|
START_COM_CALL;
|
||||||
|
*ppv = ::CreateMemoryManagementHelper();
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern "C" IAvaloniaNativeFactory* CreateAvaloniaNative()
|
extern "C" IAvaloniaNativeFactory* CreateAvaloniaNative()
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
#include "common.h"
|
||||||
|
class MemHelper : public ComSingleObject<IAvnNativeObjectsMemoryManagement, &IID_IAvnNativeObjectsMemoryManagement>
|
||||||
|
{
|
||||||
|
FORWARD_IUNKNOWN()
|
||||||
|
void RetainNSObject(void *object) override
|
||||||
|
{
|
||||||
|
::RetainNSObject(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReleaseNSObject(void *object) override
|
||||||
|
{
|
||||||
|
::ReleaseNSObject(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RetainCFObject(void *object) override
|
||||||
|
{
|
||||||
|
CFRetain(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReleaseCFObject(void *object) override
|
||||||
|
{
|
||||||
|
CFRelease(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t GetRetainCountForNSObject(void *obj) override {
|
||||||
|
return ::GetRetainCountForNSObject(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t GetRetainCountForCFObject(void *obj) override {
|
||||||
|
return CFGetRetainCount(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
extern IAvnNativeObjectsMemoryManagement* CreateMemoryManagementHelper()
|
||||||
|
{
|
||||||
|
return new MemHelper();
|
||||||
|
}
|
||||||
@@ -3,6 +3,74 @@
|
|||||||
#import <QuartzCore/QuartzCore.h>
|
#import <QuartzCore/QuartzCore.h>
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "rendertarget.h"
|
#include "rendertarget.h"
|
||||||
|
#import "crapium.h"
|
||||||
|
|
||||||
|
|
||||||
|
class API_AVAILABLE(macos(12.0)) AvnMTLSharedEvent : public ComSingleObject<IAvnMTLSharedEvent, &IID_IAvnMTLSharedEvent>
|
||||||
|
{
|
||||||
|
id<MTLSharedEvent> _event;
|
||||||
|
public:
|
||||||
|
|
||||||
|
AvnMTLSharedEvent(id<MTLSharedEvent> ev) : _event(ev)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
FORWARD_IUNKNOWN()
|
||||||
|
|
||||||
|
id<MTLSharedEvent> GetEvent()
|
||||||
|
{
|
||||||
|
return _event;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *GetNativeHandle() override {
|
||||||
|
return (__bridge void*)_event;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Wait(uint64_t value, uint64_t timeoutMS) override {
|
||||||
|
return MtlSharedEventWaitUntilSignaledValueHack(_event, value, timeoutMS);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetSignaledValue(uint64_t value) override {
|
||||||
|
_event.signaledValue = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t GetSignaledValue() override {
|
||||||
|
return _event.signaledValue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class AvnMetalTexture : public ComSingleObject<IAvnMetalTexture, &IID_IAvnMetalTexture>
|
||||||
|
{
|
||||||
|
id<MTLTexture> _texture;
|
||||||
|
public:
|
||||||
|
FORWARD_IUNKNOWN()
|
||||||
|
AvnMetalTexture(id<MTLTexture> texture) : _texture(texture)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
void *GetNativeHandle() override
|
||||||
|
{
|
||||||
|
return (__bridge void*)_texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetWidth() override
|
||||||
|
{
|
||||||
|
return (int)_texture.width;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetHeight() override
|
||||||
|
{
|
||||||
|
return (int)_texture.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetSampleCount() override
|
||||||
|
{
|
||||||
|
return (int)_texture.sampleCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
class AvnMetalDevice : public ComSingleObject<IAvnMetalDevice, &IID_IAvnMetalDevice>
|
class AvnMetalDevice : public ComSingleObject<IAvnMetalDevice, &IID_IAvnMetalDevice>
|
||||||
{
|
{
|
||||||
@@ -18,7 +86,86 @@ public:
|
|||||||
void *GetQueue() override {
|
void *GetQueue() override {
|
||||||
return (__bridge void*) queue;
|
return (__bridge void*) queue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HRESULT ImportIOSurface(void *handle, AvnPixelFormat pixelFormat, IAvnMetalTexture **ppv) override {
|
||||||
|
auto surf = (IOSurfaceRef)handle;
|
||||||
|
auto width = IOSurfaceGetWidth(surf);
|
||||||
|
auto height = IOSurfaceGetHeight(surf);
|
||||||
|
|
||||||
|
auto desc = [MTLTextureDescriptor new];
|
||||||
|
if(pixelFormat == kAvnRgba8888)
|
||||||
|
desc.pixelFormat = MTLPixelFormatRGBA8Unorm;
|
||||||
|
else if(pixelFormat == kAvnBgra8888)
|
||||||
|
desc.pixelFormat = MTLPixelFormatBGRA8Unorm;
|
||||||
|
else
|
||||||
|
return E_INVALIDARG;
|
||||||
|
desc.textureType = MTLTextureType2D;
|
||||||
|
desc.width = width;
|
||||||
|
desc.height = height;
|
||||||
|
desc.depth = 1;
|
||||||
|
desc.mipmapLevelCount = 1;
|
||||||
|
desc.sampleCount = 1;
|
||||||
|
desc.usage = MTLTextureUsageShaderRead | MTLTextureUsageRenderTarget;
|
||||||
|
|
||||||
|
auto texture = [device newTextureWithDescriptor:desc iosurface:surf plane:0];
|
||||||
|
if(texture == nullptr)
|
||||||
|
return E_FAIL;
|
||||||
|
*ppv = new AvnMetalTexture(texture);
|
||||||
|
return S_OK;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT ImportSharedEvent(void *mtlSharedEventInstance, IAvnMTLSharedEvent**ppv) override {
|
||||||
|
if (@available(macOS 12.0, *)) {
|
||||||
|
auto external = (__bridge id<MTLSharedEvent>)mtlSharedEventInstance;
|
||||||
|
auto handle = external.newSharedEventHandle;
|
||||||
|
auto imported = [device newSharedEventWithHandle: handle];
|
||||||
|
*ppv = new AvnMTLSharedEvent(imported);
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return E_NOTIMPL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
HRESULT SignalOrWait(IAvnMTLSharedEvent *ev, uint64_t value, bool wait)
|
||||||
|
{
|
||||||
|
if (@available(macOS 12.0, *))
|
||||||
|
{
|
||||||
|
auto e = dynamic_cast<AvnMTLSharedEvent*>(ev);
|
||||||
|
if(e == nullptr)
|
||||||
|
return E_FAIL;;
|
||||||
|
auto buf = [queue commandBuffer];
|
||||||
|
if(wait)
|
||||||
|
[buf encodeWaitForEvent:e->GetEvent() value:value];
|
||||||
|
else
|
||||||
|
[buf encodeSignalEvent:e->GetEvent() value:value];
|
||||||
|
[buf commit];
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return E_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT SubmitWait(IAvnMTLSharedEvent *ev, uint64_t value) override {
|
||||||
|
return SignalOrWait(ev, value, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT SubmitSignal(IAvnMTLSharedEvent *ev, uint64_t value) override {
|
||||||
|
return SignalOrWait(ev, value, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GetIOKitRegistryId(uint64_t *value) override {
|
||||||
|
if (@available(macOS 10.13, *)) {
|
||||||
|
*value = [device registryID];
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
AvnMetalDevice(id <MTLDevice> device, id <MTLCommandQueue> queue) : device(device), queue(queue) {
|
AvnMetalDevice(id <MTLDevice> device, id <MTLCommandQueue> queue) : device(device), queue(queue) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,3 +307,23 @@ extern IAvnMetalDisplay* GetMetalDisplay()
|
|||||||
{
|
{
|
||||||
return _display;
|
return _display;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
extern IAvnMTLSharedEvent* ImportMTLSharedEvent(void* object)
|
||||||
|
{
|
||||||
|
if (@available(macOS 12.0, *)) {
|
||||||
|
if(object == nullptr)
|
||||||
|
return nil;
|
||||||
|
auto evId = (__bridge id<MTLSharedEvent>)object;
|
||||||
|
|
||||||
|
if(evId == nil)
|
||||||
|
return nil;
|
||||||
|
|
||||||
|
|
||||||
|
return new AvnMTLSharedEvent(evId);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#include "noarc.h"
|
#include "noarc.h"
|
||||||
|
#include "avalonia-native.h"
|
||||||
CppAutoreleasePool::CppAutoreleasePool()
|
CppAutoreleasePool::CppAutoreleasePool()
|
||||||
{
|
{
|
||||||
_pool = [[NSAutoreleasePool alloc] init];
|
_pool = [[NSAutoreleasePool alloc] init];
|
||||||
@@ -9,3 +9,17 @@ CppAutoreleasePool::~CppAutoreleasePool() {
|
|||||||
auto ptr = (NSAutoreleasePool*)_pool;
|
auto ptr = (NSAutoreleasePool*)_pool;
|
||||||
[ptr release];
|
[ptr release];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern void ReleaseNSObject(void* obj)
|
||||||
|
{
|
||||||
|
[(NSObject*)obj release];
|
||||||
|
}
|
||||||
|
extern void RetainNSObject(void* obj)
|
||||||
|
{
|
||||||
|
[(NSObject*)obj retain];
|
||||||
|
}
|
||||||
|
|
||||||
|
extern uint64_t GetRetainCountForNSObject(void* obj)
|
||||||
|
{
|
||||||
|
return [(NSObject*)obj retainCount];
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace GpuInterop;
|
||||||
|
|
||||||
|
static class NativeMethods
|
||||||
|
{
|
||||||
|
[Flags]
|
||||||
|
public enum IOSurfaceLockOptions : uint
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
ReadOnly = 1 << 0,
|
||||||
|
AvoidSync = 1 << 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
[DllImport("/System/Library/Frameworks/IOSurface.framework/IOSurface")]
|
||||||
|
public static extern int IOSurfaceLock(IntPtr surface, IOSurfaceLockOptions options, IntPtr seed);
|
||||||
|
|
||||||
|
[DllImport("/System/Library/Frameworks/IOSurface.framework/IOSurface")]
|
||||||
|
public static extern nint IOSurfaceGetWidth(IntPtr surface);
|
||||||
|
|
||||||
|
[DllImport("/System/Library/Frameworks/IOSurface.framework/IOSurface")]
|
||||||
|
public static extern nint IOSurfaceGetHeight(IntPtr surface);
|
||||||
|
|
||||||
|
[DllImport("/System/Library/Frameworks/IOSurface.framework/IOSurface")]
|
||||||
|
public static extern nint IOSurfaceGetBytesPerRow(IntPtr surface);
|
||||||
|
|
||||||
|
[DllImport("/System/Library/Frameworks/IOSurface.framework/IOSurface")]
|
||||||
|
public static extern IntPtr IOSurfaceGetBaseAddress(IntPtr surface);
|
||||||
|
|
||||||
|
[DllImport("/System/Library/Frameworks/IOSurface.framework/IOSurface")]
|
||||||
|
public static extern void IOSurfaceUnlock(IntPtr surface, IOSurfaceLockOptions options, IntPtr seed);
|
||||||
|
|
||||||
|
[DllImport("/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation")]
|
||||||
|
public static extern IntPtr CFRetain(IntPtr cf);
|
||||||
|
|
||||||
|
[DllImport("/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation")]
|
||||||
|
public static extern void CFRelease(IntPtr cf);
|
||||||
|
|
||||||
|
[DllImport("/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation")]
|
||||||
|
public static extern nint CFGetRetainCount(IntPtr cf);
|
||||||
|
}
|
||||||
@@ -167,7 +167,8 @@ namespace Avalonia.Vulkan
|
|||||||
ReadOnlySpan<PipelineStageFlags> waitDstStageMask = default,
|
ReadOnlySpan<PipelineStageFlags> waitDstStageMask = default,
|
||||||
ReadOnlySpan<Semaphore> signalSemaphores = default,
|
ReadOnlySpan<Semaphore> signalSemaphores = default,
|
||||||
Fence? fence = null,
|
Fence? fence = null,
|
||||||
KeyedMutexSubmitInfo? keyedMutex = null)
|
KeyedMutexSubmitInfo? keyedMutex = null,
|
||||||
|
IntPtr pNext = default)
|
||||||
{
|
{
|
||||||
EndRecording();
|
EndRecording();
|
||||||
|
|
||||||
@@ -189,7 +190,8 @@ namespace Avalonia.Vulkan
|
|||||||
PReleaseKeys = &releaseKey,
|
PReleaseKeys = &releaseKey,
|
||||||
PAcquireSyncs = &devMem,
|
PAcquireSyncs = &devMem,
|
||||||
PReleaseSyncs = &devMem,
|
PReleaseSyncs = &devMem,
|
||||||
PAcquireTimeouts = &timeout
|
PAcquireTimeouts = &timeout,
|
||||||
|
PNext = (void*)pNext
|
||||||
};
|
};
|
||||||
|
|
||||||
fixed (Semaphore* pWaitSemaphores = waitSemaphores, pSignalSemaphores = signalSemaphores)
|
fixed (Semaphore* pWaitSemaphores = waitSemaphores, pSignalSemaphores = signalSemaphores)
|
||||||
@@ -199,7 +201,7 @@ namespace Avalonia.Vulkan
|
|||||||
var commandBuffer = InternalHandle;
|
var commandBuffer = InternalHandle;
|
||||||
var submitInfo = new SubmitInfo
|
var submitInfo = new SubmitInfo
|
||||||
{
|
{
|
||||||
PNext = keyedMutex != null ? &mutex : null,
|
PNext = keyedMutex != null ? &mutex : (void*)pNext,
|
||||||
SType = StructureType.SubmitInfo,
|
SType = StructureType.SubmitInfo,
|
||||||
WaitSemaphoreCount = waitSemaphores != null ? (uint)waitSemaphores.Length : 0,
|
WaitSemaphoreCount = waitSemaphores != null ? (uint)waitSemaphores.Length : 0,
|
||||||
PWaitSemaphores = pWaitSemaphores,
|
PWaitSemaphores = pWaitSemaphores,
|
||||||
|
|||||||
@@ -1,17 +1,21 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using Avalonia.Platform;
|
using Avalonia.Platform;
|
||||||
using Avalonia.Rendering.Composition;
|
using Avalonia.Rendering.Composition;
|
||||||
using Avalonia.Vulkan;
|
using Avalonia.Vulkan;
|
||||||
using Silk.NET.Core;
|
using Silk.NET.Core;
|
||||||
|
using Silk.NET.Core.Contexts;
|
||||||
|
using Silk.NET.Core.Loader;
|
||||||
using Silk.NET.Vulkan;
|
using Silk.NET.Vulkan;
|
||||||
using Silk.NET.Vulkan.Extensions.EXT;
|
using Silk.NET.Vulkan.Extensions.EXT;
|
||||||
using Silk.NET.Vulkan.Extensions.KHR;
|
using Silk.NET.Vulkan.Extensions.KHR;
|
||||||
using SilkNetDemo;
|
using SilkNetDemo;
|
||||||
using SkiaSharp;
|
using SkiaSharp;
|
||||||
using D3DDevice = SharpDX.Direct3D11.Device;
|
using D3DDevice = SharpDX.Direct3D11.Device;
|
||||||
|
#pragma warning disable CS0162 // Unreachable code detected
|
||||||
|
|
||||||
namespace GpuInterop.VulkanDemo;
|
namespace GpuInterop.VulkanDemo;
|
||||||
|
|
||||||
@@ -24,7 +28,7 @@ public unsafe class VulkanContext : IDisposable
|
|||||||
public required Queue Queue { get; init; }
|
public required Queue Queue { get; init; }
|
||||||
public required uint QueueFamilyIndex { get; init; }
|
public required uint QueueFamilyIndex { get; init; }
|
||||||
public required VulkanCommandBufferPool Pool { get; init; }
|
public required VulkanCommandBufferPool Pool { get; init; }
|
||||||
public required GRContext GrContext { get; init; }
|
public required GRContext? GrContext { get; init; }
|
||||||
public required DescriptorPool DescriptorPool { get; init; }
|
public required DescriptorPool DescriptorPool { get; init; }
|
||||||
public required D3DDevice? D3DDevice { get; init; }
|
public required D3DDevice? D3DDevice { get; init; }
|
||||||
|
|
||||||
@@ -44,14 +48,22 @@ public unsafe class VulkanContext : IDisposable
|
|||||||
|
|
||||||
var enabledExtensions = new List<string>()
|
var enabledExtensions = new List<string>()
|
||||||
{
|
{
|
||||||
"VK_KHR_get_physical_device_properties2",
|
"VK_KHR_get_physical_device_properties2"
|
||||||
"VK_KHR_external_memory_capabilities",
|
|
||||||
"VK_KHR_external_semaphore_capabilities"
|
|
||||||
};
|
};
|
||||||
|
if(RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||||
|
enabledExtensions.Add("VK_KHR_portability_enumeration");
|
||||||
|
|
||||||
|
if (!RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||||
|
{
|
||||||
|
enabledExtensions.AddRange([
|
||||||
|
"VK_KHR_external_memory_capabilities",
|
||||||
|
"VK_KHR_external_semaphore_capabilities"
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
var enabledLayers = new List<string>();
|
var enabledLayers = new List<string>();
|
||||||
|
|
||||||
Vk api = Vk.GetApi();
|
Vk api = GetApi();
|
||||||
enabledExtensions.Add("VK_EXT_debug_utils");
|
enabledExtensions.Add("VK_EXT_debug_utils");
|
||||||
if (IsLayerAvailable(api, "VK_LAYER_KHRONOS_validation"))
|
if (IsLayerAvailable(api, "VK_LAYER_KHRONOS_validation"))
|
||||||
enabledLayers.Add("VK_LAYER_KHRONOS_validation");
|
enabledLayers.Add("VK_LAYER_KHRONOS_validation");
|
||||||
@@ -61,8 +73,10 @@ public unsafe class VulkanContext : IDisposable
|
|||||||
DescriptorPool descriptorPool = default;
|
DescriptorPool descriptorPool = default;
|
||||||
VulkanCommandBufferPool? pool = null;
|
VulkanCommandBufferPool? pool = null;
|
||||||
GRContext? grContext = null;
|
GRContext? grContext = null;
|
||||||
|
bool success = false;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
enabledLayers.Clear();
|
||||||
using var pRequiredExtensions = new ByteStringList(enabledExtensions);
|
using var pRequiredExtensions = new ByteStringList(enabledExtensions);
|
||||||
using var pEnabledLayers = new ByteStringList(enabledLayers);
|
using var pEnabledLayers = new ByteStringList(enabledLayers);
|
||||||
api.CreateInstance(new InstanceCreateInfo
|
api.CreateInstance(new InstanceCreateInfo
|
||||||
@@ -72,7 +86,8 @@ public unsafe class VulkanContext : IDisposable
|
|||||||
PpEnabledExtensionNames = pRequiredExtensions,
|
PpEnabledExtensionNames = pRequiredExtensions,
|
||||||
EnabledExtensionCount = pRequiredExtensions.UCount,
|
EnabledExtensionCount = pRequiredExtensions.UCount,
|
||||||
PpEnabledLayerNames = pEnabledLayers,
|
PpEnabledLayerNames = pEnabledLayers,
|
||||||
EnabledLayerCount = pEnabledLayers.UCount
|
EnabledLayerCount = pEnabledLayers.UCount,
|
||||||
|
Flags = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? InstanceCreateFlags.EnumeratePortabilityBitKhr : default
|
||||||
}, null, out var vkInstance).ThrowOnError();
|
}, null, out var vkInstance).ThrowOnError();
|
||||||
|
|
||||||
|
|
||||||
@@ -93,10 +108,13 @@ public unsafe class VulkanContext : IDisposable
|
|||||||
debugUtils.CreateDebugUtilsMessenger(vkInstance, debugCreateInfo, null, out _);
|
debugUtils.CreateDebugUtilsMessenger(vkInstance, debugCreateInfo, null, out _);
|
||||||
}
|
}
|
||||||
|
|
||||||
var requireDeviceExtensions = new List<string>
|
var requireDeviceExtensions = new List<string>();
|
||||||
|
if(!RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||||
{
|
{
|
||||||
"VK_KHR_external_memory",
|
requireDeviceExtensions.AddRange([
|
||||||
"VK_KHR_external_semaphore"
|
"VK_KHR_external_memory",
|
||||||
|
"VK_KHR_external_semaphore"
|
||||||
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||||
@@ -112,6 +130,14 @@ public unsafe class VulkanContext : IDisposable
|
|||||||
requireDeviceExtensions.Add("VK_KHR_dedicated_allocation");
|
requireDeviceExtensions.Add("VK_KHR_dedicated_allocation");
|
||||||
requireDeviceExtensions.Add("VK_KHR_get_memory_requirements2");
|
requireDeviceExtensions.Add("VK_KHR_get_memory_requirements2");
|
||||||
}
|
}
|
||||||
|
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||||
|
{
|
||||||
|
if (!gpuInterop.SupportedImageHandleTypes.Contains(KnownPlatformGraphicsExternalImageHandleTypes
|
||||||
|
.IOSurfaceRef)
|
||||||
|
)
|
||||||
|
return (null, "Image sharing is not supported by the current backend");
|
||||||
|
requireDeviceExtensions.AddRange(["VK_EXT_metal_objects", "VK_KHR_timeline_semaphore"]);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (!gpuInterop.SupportedImageHandleTypes.Contains(KnownPlatformGraphicsExternalImageHandleTypes
|
if (!gpuInterop.SupportedImageHandleTypes.Contains(KnownPlatformGraphicsExternalImageHandleTypes
|
||||||
@@ -223,26 +249,29 @@ public unsafe class VulkanContext : IDisposable
|
|||||||
.ThrowOnError();
|
.ThrowOnError();
|
||||||
|
|
||||||
pool = new VulkanCommandBufferPool(api, device, queue, queueFamilyIndex);
|
pool = new VulkanCommandBufferPool(api, device, queue, queueFamilyIndex);
|
||||||
grContext = GRContext.CreateVulkan(new GRVkBackendContext
|
if (!RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||||
{
|
{
|
||||||
VkInstance = vkInstance.Handle,
|
grContext = GRContext.CreateVulkan(new GRVkBackendContext
|
||||||
VkDevice = device.Handle,
|
|
||||||
VkQueue = queue.Handle,
|
|
||||||
GraphicsQueueIndex = queueFamilyIndex,
|
|
||||||
VkPhysicalDevice = physicalDevice.Handle,
|
|
||||||
GetProcedureAddress = (proc, _, _) =>
|
|
||||||
{
|
{
|
||||||
var rv = api.GetDeviceProcAddr(device, proc);
|
VkInstance = vkInstance.Handle,
|
||||||
if (rv != IntPtr.Zero)
|
VkDevice = device.Handle,
|
||||||
return rv;
|
VkQueue = queue.Handle,
|
||||||
rv = api.GetInstanceProcAddr(vkInstance, proc);
|
GraphicsQueueIndex = queueFamilyIndex,
|
||||||
if (rv != IntPtr.Zero)
|
VkPhysicalDevice = physicalDevice.Handle,
|
||||||
return rv;
|
GetProcedureAddress = (proc, _, _) =>
|
||||||
return api.GetInstanceProcAddr(default, 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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (grContext == null)
|
||||||
|
return (null, "Can't create Skia GrContext, device is likely broken");
|
||||||
|
}
|
||||||
|
|
||||||
D3DDevice? d3dDevice = null;
|
D3DDevice? d3dDevice = null;
|
||||||
if (physicalDeviceIDProperties.DeviceLuidvalid &&
|
if (physicalDeviceIDProperties.DeviceLuidvalid &&
|
||||||
@@ -251,7 +280,7 @@ public unsafe class VulkanContext : IDisposable
|
|||||||
)
|
)
|
||||||
d3dDevice = D3DMemoryHelper.CreateDeviceByLuid(
|
d3dDevice = D3DMemoryHelper.CreateDeviceByLuid(
|
||||||
new Span<byte>(physicalDeviceIDProperties.DeviceLuid, 8));
|
new Span<byte>(physicalDeviceIDProperties.DeviceLuid, 8));
|
||||||
|
success = true;
|
||||||
return (new VulkanContext
|
return (new VulkanContext
|
||||||
{
|
{
|
||||||
Api = api,
|
Api = api,
|
||||||
@@ -278,7 +307,7 @@ public unsafe class VulkanContext : IDisposable
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
if (grContext == null && api != null)
|
if (!success)
|
||||||
{
|
{
|
||||||
pool?.Dispose();
|
pool?.Dispose();
|
||||||
if (descriptorPool.Handle != default)
|
if (descriptorPool.Handle != default)
|
||||||
@@ -322,11 +351,40 @@ public unsafe class VulkanContext : IDisposable
|
|||||||
|
|
||||||
return Vk.False;
|
return Vk.False;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private const string MacVulkanSdkGlobalPath = "/usr/local/lib/libvulkan.dylib";
|
||||||
|
class MacLibraryNameContainer : SearchPathContainer
|
||||||
|
{
|
||||||
|
public override string Windows64 { get; }
|
||||||
|
public override string Windows86 { get; }
|
||||||
|
public override string Linux { get; }
|
||||||
|
public override string MacOS { get; } = MacVulkanSdkGlobalPath;
|
||||||
|
}
|
||||||
|
private static Vk GetApi()
|
||||||
|
{
|
||||||
|
if (!RuntimeInformation.IsOSPlatform(OSPlatform.OSX) || !File.Exists(MacVulkanSdkGlobalPath))
|
||||||
|
return Vk.GetApi();
|
||||||
|
var ctx = new MultiNativeContext(new INativeContext[2]
|
||||||
|
{
|
||||||
|
Vk.CreateDefaultContext(new MacLibraryNameContainer().GetLibraryName()),
|
||||||
|
null!
|
||||||
|
});
|
||||||
|
var ret = new Vk(ctx);
|
||||||
|
ctx.Contexts[1] = new LamdaNativeContext((Func<string, IntPtr>) ((x) =>
|
||||||
|
{
|
||||||
|
if (x.EndsWith("ProcAddr"))
|
||||||
|
return IntPtr.Zero;
|
||||||
|
IntPtr deviceProcAddr = (IntPtr) ret.GetDeviceProcAddr(ret.CurrentDevice.GetValueOrDefault(), x);
|
||||||
|
return deviceProcAddr != IntPtr.Zero ? deviceProcAddr : (IntPtr) ret.GetInstanceProcAddr(ret.CurrentInstance.GetValueOrDefault(), x);
|
||||||
|
}));
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
D3DDevice?.Dispose();
|
D3DDevice?.Dispose();
|
||||||
GrContext.Dispose();
|
GrContext?.Dispose();
|
||||||
Pool.Dispose();
|
Pool.Dispose();
|
||||||
Api.DestroyDescriptorPool(Device, DescriptorPool, null);
|
Api.DestroyDescriptorPool(Device, DescriptorPool, null);
|
||||||
Api.DestroyDevice(Device, null);
|
Api.DestroyDevice(Device, null);
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ using Avalonia.Platform;
|
|||||||
using Avalonia.Vulkan;
|
using Avalonia.Vulkan;
|
||||||
using SharpDX.DXGI;
|
using SharpDX.DXGI;
|
||||||
using Silk.NET.Vulkan;
|
using Silk.NET.Vulkan;
|
||||||
|
using Silk.NET.Vulkan.Extensions.EXT;
|
||||||
using Silk.NET.Vulkan.Extensions.KHR;
|
using Silk.NET.Vulkan.Extensions.KHR;
|
||||||
using SilkNetDemo;
|
using SilkNetDemo;
|
||||||
using SkiaSharp;
|
using SkiaSharp;
|
||||||
@@ -45,6 +46,8 @@ public unsafe class VulkanImage : IDisposable
|
|||||||
public ulong MemorySize { get; }
|
public ulong MemorySize { get; }
|
||||||
public uint CurrentLayout => (uint) _currentLayout;
|
public uint CurrentLayout => (uint) _currentLayout;
|
||||||
|
|
||||||
|
private bool _hasIOSurface;
|
||||||
|
|
||||||
public VulkanImage(VulkanContext vk, uint format, PixelSize size,
|
public VulkanImage(VulkanContext vk, uint format, PixelSize size,
|
||||||
bool exportable, IReadOnlyList<string> supportedHandleTypes)
|
bool exportable, IReadOnlyList<string> supportedHandleTypes)
|
||||||
{
|
{
|
||||||
@@ -75,10 +78,22 @@ public unsafe class VulkanImage : IDisposable
|
|||||||
SType = StructureType.ExternalMemoryImageCreateInfo,
|
SType = StructureType.ExternalMemoryImageCreateInfo,
|
||||||
HandleTypes = handleType
|
HandleTypes = handleType
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
var ioSurfaceCreateInfo = new ExportMetalObjectCreateInfoEXT
|
||||||
|
{
|
||||||
|
SType = StructureType.ExportMetalObjectCreateInfoExt,
|
||||||
|
ExportObjectType = ExportMetalObjectTypeFlagsEXT.IosurfaceBitExt
|
||||||
|
};
|
||||||
|
|
||||||
|
_hasIOSurface = exportable && RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
|
||||||
|
|
||||||
var imageCreateInfo = new ImageCreateInfo
|
var imageCreateInfo = new ImageCreateInfo
|
||||||
{
|
{
|
||||||
PNext = exportable ? &externalMemoryCreateInfo : null,
|
PNext = exportable ?
|
||||||
|
RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ?
|
||||||
|
&ioSurfaceCreateInfo :
|
||||||
|
&externalMemoryCreateInfo : null,
|
||||||
SType = StructureType.ImageCreateInfo,
|
SType = StructureType.ImageCreateInfo,
|
||||||
ImageType = ImageType.Type2D,
|
ImageType = ImageType.Type2D,
|
||||||
Format = Format,
|
Format = Format,
|
||||||
@@ -98,59 +113,64 @@ public unsafe class VulkanImage : IDisposable
|
|||||||
Api
|
Api
|
||||||
.CreateImage(_device, imageCreateInfo, null, out var image).ThrowOnError();
|
.CreateImage(_device, imageCreateInfo, null, out var image).ThrowOnError();
|
||||||
InternalHandle = image;
|
InternalHandle = image;
|
||||||
|
|
||||||
Api.GetImageMemoryRequirements(_device, InternalHandle,
|
|
||||||
out var memoryRequirements);
|
|
||||||
|
|
||||||
var dedicatedAllocation = new MemoryDedicatedAllocateInfoKHR
|
if (!exportable || !RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||||
{
|
{
|
||||||
SType = StructureType.MemoryDedicatedAllocateInfoKhr,
|
|
||||||
Image = image
|
|
||||||
};
|
|
||||||
|
|
||||||
var fdExport = new ExportMemoryAllocateInfo
|
|
||||||
{
|
|
||||||
HandleTypes = handleType, SType = StructureType.ExportMemoryAllocateInfo,
|
|
||||||
PNext = &dedicatedAllocation
|
|
||||||
};
|
|
||||||
|
|
||||||
ImportMemoryWin32HandleInfoKHR handleImport = default;
|
Api.GetImageMemoryRequirements(_device, InternalHandle,
|
||||||
if (handleType == ExternalMemoryHandleTypeFlags.D3D11TextureBit && exportable)
|
out var memoryRequirements);
|
||||||
{
|
|
||||||
var d3dDevice = vk.D3DDevice ?? throw new NotSupportedException("Vulkan D3DDevice wasn't created");
|
|
||||||
_d3dTexture2D = D3DMemoryHelper.CreateMemoryHandle(d3dDevice, size, Format);
|
|
||||||
using var dxgi = _d3dTexture2D.QueryInterface<SharpDX.DXGI.Resource1>();
|
|
||||||
|
|
||||||
handleImport = new ImportMemoryWin32HandleInfoKHR
|
var dedicatedAllocation = new MemoryDedicatedAllocateInfoKHR
|
||||||
{
|
{
|
||||||
PNext = &dedicatedAllocation,
|
SType = StructureType.MemoryDedicatedAllocateInfoKhr, Image = image
|
||||||
SType = StructureType.ImportMemoryWin32HandleInfoKhr,
|
|
||||||
HandleType = ExternalMemoryHandleTypeFlags.D3D11TextureBit,
|
|
||||||
Handle = dxgi.CreateSharedHandle(null, SharedResourceFlags.Read | SharedResourceFlags.Write),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var fdExport = new ExportMemoryAllocateInfo
|
||||||
|
{
|
||||||
|
HandleTypes = handleType,
|
||||||
|
SType = StructureType.ExportMemoryAllocateInfo,
|
||||||
|
PNext = &dedicatedAllocation
|
||||||
|
};
|
||||||
|
|
||||||
|
ImportMemoryWin32HandleInfoKHR handleImport = default;
|
||||||
|
if (handleType == ExternalMemoryHandleTypeFlags.D3D11TextureBit && exportable)
|
||||||
|
{
|
||||||
|
var d3dDevice = vk.D3DDevice ?? throw new NotSupportedException("Vulkan D3DDevice wasn't created");
|
||||||
|
_d3dTexture2D = D3DMemoryHelper.CreateMemoryHandle(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 ? handleImport.Handle != IntPtr.Zero ? &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, _imageMemory, 0).ThrowOnError();
|
||||||
}
|
}
|
||||||
|
|
||||||
var memoryAllocateInfo = new MemoryAllocateInfo
|
|
||||||
{
|
|
||||||
PNext =
|
|
||||||
exportable ? handleImport.Handle != IntPtr.Zero ? &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, _imageMemory, 0).ThrowOnError();
|
|
||||||
var componentMapping = new ComponentMapping(
|
var componentMapping = new ComponentMapping(
|
||||||
ComponentSwizzle.Identity,
|
ComponentSwizzle.Identity,
|
||||||
ComponentSwizzle.Identity,
|
ComponentSwizzle.Identity,
|
||||||
@@ -209,6 +229,26 @@ public unsafe class VulkanImage : IDisposable
|
|||||||
ext.GetMemoryWin32Handle(_device, info, out var fd).ThrowOnError();
|
ext.GetMemoryWin32Handle(_device, info, out var fd).ThrowOnError();
|
||||||
return fd;
|
return fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IntPtr ExportIOSurface()
|
||||||
|
{
|
||||||
|
if (!Api.TryGetDeviceExtension<ExtMetalObjects>(_instance, _device, out var ext))
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
var surfaceExport = new ExportMetalIOSurfaceInfoEXT
|
||||||
|
{
|
||||||
|
SType = StructureType.ExportMetalIOSurfaceInfoExt,
|
||||||
|
Image = InternalHandle
|
||||||
|
};
|
||||||
|
var export = new ExportMetalObjectsInfoEXT()
|
||||||
|
{
|
||||||
|
SType = StructureType.ExportMetalObjectsInfoExt,
|
||||||
|
PNext = &surfaceExport
|
||||||
|
};
|
||||||
|
ext.ExportMetalObjects(_device, ref export);
|
||||||
|
if (surfaceExport.IoSurface == IntPtr.Zero)
|
||||||
|
throw new Exception("Unable to export IOSurfaceRef");
|
||||||
|
return surfaceExport.IoSurface;
|
||||||
|
}
|
||||||
|
|
||||||
public IPlatformHandle Export()
|
public IPlatformHandle Export()
|
||||||
{
|
{
|
||||||
@@ -225,6 +265,9 @@ public unsafe class VulkanImage : IDisposable
|
|||||||
return new PlatformHandle(ExportOpaqueNtHandle(),
|
return new PlatformHandle(ExportOpaqueNtHandle(),
|
||||||
KnownPlatformGraphicsExternalImageHandleTypes.VulkanOpaqueNtHandle);
|
KnownPlatformGraphicsExternalImageHandleTypes.VulkanOpaqueNtHandle);
|
||||||
}
|
}
|
||||||
|
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||||
|
return new PlatformHandle(ExportIOSurface(),
|
||||||
|
KnownPlatformGraphicsExternalImageHandleTypes.IOSurfaceRef);
|
||||||
else
|
else
|
||||||
return new PlatformHandle(new IntPtr(ExportFd()),
|
return new PlatformHandle(new IntPtr(ExportFd()),
|
||||||
KnownPlatformGraphicsExternalImageHandleTypes.VulkanOpaquePosixFileDescriptor);
|
KnownPlatformGraphicsExternalImageHandleTypes.VulkanOpaquePosixFileDescriptor);
|
||||||
@@ -281,6 +324,30 @@ public unsafe class VulkanImage : IDisposable
|
|||||||
|
|
||||||
public void SaveTexture(string path)
|
public void SaveTexture(string path)
|
||||||
{
|
{
|
||||||
|
if (_vk.GrContext == null)
|
||||||
|
{
|
||||||
|
if (_hasIOSurface)
|
||||||
|
{
|
||||||
|
var surf = ExportIOSurface();
|
||||||
|
if (NativeMethods.IOSurfaceLock(surf, 0, IntPtr.Zero) != 0)
|
||||||
|
throw new Exception("IOSurfaceLock failed");
|
||||||
|
var w = (int)NativeMethods.IOSurfaceGetWidth(surf);
|
||||||
|
var h = (int)NativeMethods.IOSurfaceGetHeight(surf);
|
||||||
|
var sstride = NativeMethods.IOSurfaceGetBytesPerRow(surf);
|
||||||
|
|
||||||
|
var pSurface = NativeMethods.IOSurfaceGetBaseAddress(surf);
|
||||||
|
using var b = new Avalonia.Media.Imaging.Bitmap(PixelFormat.Bgra8888,
|
||||||
|
AlphaFormat.Premul, pSurface, new PixelSize(w, h),
|
||||||
|
new Vector(96, 96), (int)sstride);
|
||||||
|
b.Save(path);
|
||||||
|
|
||||||
|
NativeMethods.IOSurfaceUnlock(surf, 0, IntPtr.Zero);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw new NotSupportedException("Need skia to dump textures, sorry");
|
||||||
|
}
|
||||||
|
|
||||||
_vk.GrContext.ResetContext();
|
_vk.GrContext.ResetContext();
|
||||||
var _image = this;
|
var _image = this;
|
||||||
var imageInfo = new GRVkImageInfo()
|
var imageInfo = new GRVkImageInfo()
|
||||||
|
|||||||
@@ -39,8 +39,10 @@ class VulkanSwapchainImage : ISwapchainImage
|
|||||||
private readonly ICompositionGpuInterop _interop;
|
private readonly ICompositionGpuInterop _interop;
|
||||||
private readonly CompositionDrawingSurface _target;
|
private readonly CompositionDrawingSurface _target;
|
||||||
private readonly VulkanImage _image;
|
private readonly VulkanImage _image;
|
||||||
private readonly VulkanSemaphorePair _semaphorePair;
|
private readonly VulkanSemaphorePair? _semaphorePair;
|
||||||
private ICompositionImportedGpuSemaphore? _availableSemaphore, _renderCompletedSemaphore;
|
private readonly VulkanTimelineSemaphore? _timelineSemaphore;
|
||||||
|
private ulong _timelineCounter;
|
||||||
|
private ICompositionImportedGpuSemaphore? _availableSemaphore, _renderCompletedSemaphore, _importedTimelineSemaphore;
|
||||||
private ICompositionImportedGpuImage? _importedImage;
|
private ICompositionImportedGpuImage? _importedImage;
|
||||||
private Task? _lastPresent;
|
private Task? _lastPresent;
|
||||||
public VulkanImage Image => _image;
|
public VulkanImage Image => _image;
|
||||||
@@ -52,8 +54,12 @@ class VulkanSwapchainImage : ISwapchainImage
|
|||||||
_interop = interop;
|
_interop = interop;
|
||||||
_target = target;
|
_target = target;
|
||||||
Size = size;
|
Size = size;
|
||||||
_image = new VulkanImage(vk, (uint)Format.R8G8B8A8Unorm, size, true, interop.SupportedImageHandleTypes);
|
var format = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? Format.B8G8R8A8Unorm : Format.R8G8B8A8Unorm;
|
||||||
_semaphorePair = new VulkanSemaphorePair(vk, true);
|
_image = new VulkanImage(vk, (uint)format, size, true, interop.SupportedImageHandleTypes);
|
||||||
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||||
|
_timelineSemaphore = new(vk);
|
||||||
|
else
|
||||||
|
_semaphorePair = new VulkanSemaphorePair(vk, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async ValueTask DisposeAsync()
|
public async ValueTask DisposeAsync()
|
||||||
@@ -62,11 +68,13 @@ class VulkanSwapchainImage : ISwapchainImage
|
|||||||
await LastPresent;
|
await LastPresent;
|
||||||
if (_importedImage != null)
|
if (_importedImage != null)
|
||||||
await _importedImage.DisposeAsync();
|
await _importedImage.DisposeAsync();
|
||||||
|
|
||||||
if (_availableSemaphore != null)
|
if (_availableSemaphore != null)
|
||||||
await _availableSemaphore.DisposeAsync();
|
await _availableSemaphore.DisposeAsync();
|
||||||
if (_renderCompletedSemaphore != null)
|
if (_renderCompletedSemaphore != null)
|
||||||
await _renderCompletedSemaphore.DisposeAsync();
|
await _renderCompletedSemaphore.DisposeAsync();
|
||||||
_semaphorePair.Dispose();
|
_semaphorePair?.Dispose();
|
||||||
|
_timelineSemaphore?.Dispose();
|
||||||
_image.Dispose();
|
_image.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,6 +97,22 @@ class VulkanSwapchainImage : ISwapchainImage
|
|||||||
AcquireKey = 0,
|
AcquireKey = 0,
|
||||||
DeviceMemory = _image.DeviceMemory
|
DeviceMemory = _image.DeviceMemory
|
||||||
});
|
});
|
||||||
|
else if (_timelineSemaphore != null)
|
||||||
|
{
|
||||||
|
unsafe
|
||||||
|
{
|
||||||
|
var wait = _timelineCounter;
|
||||||
|
var submitInfo = new TimelineSemaphoreSubmitInfo
|
||||||
|
{
|
||||||
|
PWaitSemaphoreValues = &wait,
|
||||||
|
WaitSemaphoreValueCount = 1,
|
||||||
|
SType = StructureType.TimelineSemaphoreSubmitInfo
|
||||||
|
};
|
||||||
|
var waitSemaphores = new[] { _timelineSemaphore.Handle };
|
||||||
|
|
||||||
|
buffer.Submit(waitSemaphores, pNext: (IntPtr)(&submitInfo));
|
||||||
|
}
|
||||||
|
}
|
||||||
else if (_initial)
|
else if (_initial)
|
||||||
{
|
{
|
||||||
_initial = false;
|
_initial = false;
|
||||||
@@ -110,7 +134,7 @@ class VulkanSwapchainImage : ISwapchainImage
|
|||||||
buffer.BeginRecording();
|
buffer.BeginRecording();
|
||||||
_image.TransitionLayout(buffer.InternalHandle, ImageLayout.TransferSrcOptimal, AccessFlags.TransferWriteBit);
|
_image.TransitionLayout(buffer.InternalHandle, ImageLayout.TransferSrcOptimal, AccessFlags.TransferWriteBit);
|
||||||
|
|
||||||
|
|
||||||
if (_image.IsDirectXBacked)
|
if (_image.IsDirectXBacked)
|
||||||
{
|
{
|
||||||
buffer.Submit(null, null, null, null,
|
buffer.Submit(null, null, null, null,
|
||||||
@@ -119,10 +143,30 @@ class VulkanSwapchainImage : ISwapchainImage
|
|||||||
DeviceMemory = _image.DeviceMemory, ReleaseKey = 1
|
DeviceMemory = _image.DeviceMemory, ReleaseKey = 1
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
else if (_timelineSemaphore != null)
|
||||||
|
{
|
||||||
|
unsafe
|
||||||
|
{
|
||||||
|
var signal = _timelineCounter + 1;
|
||||||
|
var submitInfo = new TimelineSemaphoreSubmitInfo
|
||||||
|
{
|
||||||
|
PSignalSemaphoreValues = &signal,
|
||||||
|
SignalSemaphoreValueCount = 1,
|
||||||
|
SType = StructureType.TimelineSemaphoreSubmitInfo
|
||||||
|
};
|
||||||
|
var signalSemaphores = new[] { _timelineSemaphore.Handle };
|
||||||
|
|
||||||
|
buffer.Submit(default, signalSemaphores: signalSemaphores, pNext: (IntPtr)(&submitInfo));
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
buffer.Submit(null, null, new[] { _semaphorePair.RenderFinishedSemaphore });
|
buffer.Submit(null, null, new[] { _semaphorePair.RenderFinishedSemaphore });
|
||||||
|
|
||||||
if (!_image.IsDirectXBacked)
|
if (_timelineSemaphore != null)
|
||||||
|
{
|
||||||
|
_importedTimelineSemaphore ??= _interop.ImportSemaphore(_timelineSemaphore.Export());
|
||||||
|
}
|
||||||
|
else if (!_image.IsDirectXBacked)
|
||||||
{
|
{
|
||||||
_availableSemaphore ??= _interop.ImportSemaphore(_semaphorePair.Export(false));
|
_availableSemaphore ??= _interop.ImportSemaphore(_semaphorePair.Export(false));
|
||||||
|
|
||||||
@@ -132,13 +176,18 @@ class VulkanSwapchainImage : ISwapchainImage
|
|||||||
_importedImage ??= _interop.ImportImage(_image.Export(),
|
_importedImage ??= _interop.ImportImage(_image.Export(),
|
||||||
new PlatformGraphicsExternalImageProperties
|
new PlatformGraphicsExternalImageProperties
|
||||||
{
|
{
|
||||||
Format = PlatformGraphicsExternalImageFormat.R8G8B8A8UNorm,
|
Format = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? PlatformGraphicsExternalImageFormat.B8G8R8A8UNorm : PlatformGraphicsExternalImageFormat.R8G8B8A8UNorm,
|
||||||
Width = Size.Width,
|
Width = Size.Width,
|
||||||
Height = Size.Height,
|
Height = Size.Height,
|
||||||
MemorySize = _image.MemorySize
|
MemorySize = _image.MemorySize
|
||||||
});
|
});
|
||||||
|
if (_importedTimelineSemaphore != null)
|
||||||
if (_image.IsDirectXBacked)
|
{
|
||||||
|
_lastPresent = _target.UpdateWithTimelineSemaphoresAsync(_importedImage,
|
||||||
|
_importedTimelineSemaphore, _timelineCounter + 1, _importedTimelineSemaphore, _timelineCounter + 2);
|
||||||
|
_timelineCounter += 2;
|
||||||
|
}
|
||||||
|
else if (_image.IsDirectXBacked)
|
||||||
_lastPresent = _target.UpdateWithKeyedMutexAsync(_importedImage, 1, 0);
|
_lastPresent = _target.UpdateWithKeyedMutexAsync(_importedImage, 1, 0);
|
||||||
else
|
else
|
||||||
_lastPresent = _target.UpdateWithSemaphoresAsync(_importedImage, _renderCompletedSemaphore!, _availableSemaphore!);
|
_lastPresent = _target.UpdateWithSemaphoresAsync(_importedImage, _renderCompletedSemaphore!, _availableSemaphore!);
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using Avalonia.Platform;
|
||||||
|
using Silk.NET.Vulkan;
|
||||||
|
using Silk.NET.Vulkan.Extensions.EXT;
|
||||||
|
using SilkNetDemo;
|
||||||
|
|
||||||
|
namespace GpuInterop.VulkanDemo;
|
||||||
|
|
||||||
|
class VulkanTimelineSemaphore : IDisposable
|
||||||
|
{
|
||||||
|
private VulkanContext _resources;
|
||||||
|
|
||||||
|
public unsafe VulkanTimelineSemaphore(VulkanContext resources)
|
||||||
|
{
|
||||||
|
_resources = resources;
|
||||||
|
var mtlEvent = new ExportMetalObjectCreateInfoEXT
|
||||||
|
{
|
||||||
|
SType = StructureType.ExportMetalObjectCreateInfoExt,
|
||||||
|
ExportObjectType = ExportMetalObjectTypeFlagsEXT.SharedEventBitExt
|
||||||
|
};
|
||||||
|
|
||||||
|
var semaphoreTypeInfo = new SemaphoreTypeCreateInfoKHR()
|
||||||
|
{
|
||||||
|
SType = StructureType.SemaphoreTypeCreateInfo,
|
||||||
|
SemaphoreType = SemaphoreType.Timeline,
|
||||||
|
PNext = &mtlEvent
|
||||||
|
};
|
||||||
|
|
||||||
|
var semaphoreCreateInfo = new SemaphoreCreateInfo
|
||||||
|
{
|
||||||
|
SType = StructureType.SemaphoreCreateInfo,
|
||||||
|
PNext = &semaphoreTypeInfo,
|
||||||
|
};
|
||||||
|
resources.Api.CreateSemaphore(resources.Device, semaphoreCreateInfo, null, out var semaphore).ThrowOnError();
|
||||||
|
Handle = semaphore;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Semaphore Handle { get; }
|
||||||
|
public unsafe void Dispose()
|
||||||
|
{
|
||||||
|
_resources.Api.DestroySemaphore(_resources.Device, Handle, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public unsafe IntPtr ExportSharedEvent()
|
||||||
|
{
|
||||||
|
if (!_resources.Api.TryGetDeviceExtension<ExtMetalObjects>(_resources.Instance, _resources.Device, out var ext))
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
var eventExport = new ExportMetalSharedEventInfoEXT()
|
||||||
|
{
|
||||||
|
SType = StructureType.ExportMetalSharedEventInfoExt,
|
||||||
|
Semaphore = Handle,
|
||||||
|
};
|
||||||
|
var export = new ExportMetalObjectsInfoEXT()
|
||||||
|
{
|
||||||
|
SType = StructureType.ExportMetalObjectsInfoExt,
|
||||||
|
PNext = &eventExport
|
||||||
|
};
|
||||||
|
ext.ExportMetalObjects(_resources.Device, ref export);
|
||||||
|
if (eventExport.MtlSharedEvent == IntPtr.Zero)
|
||||||
|
throw new Exception("Unable to export IOSurfaceRef");
|
||||||
|
return eventExport.MtlSharedEvent;
|
||||||
|
}
|
||||||
|
public IPlatformHandle Export()
|
||||||
|
{
|
||||||
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||||
|
return new PlatformHandle(ExportSharedEvent(),
|
||||||
|
KnownPlatformGraphicsExternalSemaphoreHandleTypes.MetalSharedEvent);
|
||||||
|
throw new PlatformNotSupportedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -24,11 +24,41 @@ public interface IExternalObjectsRenderInterfaceContextFeature
|
|||||||
IPlatformRenderInterfaceImportedImage ImportImage(ICompositionImportableSharedGpuContextImage image);
|
IPlatformRenderInterfaceImportedImage ImportImage(ICompositionImportableSharedGpuContextImage image);
|
||||||
|
|
||||||
IPlatformRenderInterfaceImportedSemaphore ImportSemaphore(IPlatformHandle handle);
|
IPlatformRenderInterfaceImportedSemaphore ImportSemaphore(IPlatformHandle handle);
|
||||||
|
|
||||||
CompositionGpuImportedImageSynchronizationCapabilities GetSynchronizationCapabilities(string imageHandleType);
|
CompositionGpuImportedImageSynchronizationCapabilities GetSynchronizationCapabilities(string imageHandleType);
|
||||||
public byte[]? DeviceUuid { get; }
|
public byte[]? DeviceUuid { get; }
|
||||||
public byte[]? DeviceLuid { get; }
|
public byte[]? DeviceLuid { get; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This interface allows proper management of ref-counted platform handles.
|
||||||
|
/// If we immediately wrap the handle, the caller can destroy its copy immediately after the call
|
||||||
|
/// This is needed for MoltenVK-based users that can e.g. get an MTLSharedEvent from a VkSemaphore.
|
||||||
|
/// This does NOT actually increase the ref-counter of MTLSharedEvent, since it's declared as
|
||||||
|
/// __unsafe_unretained in vulkan headers.
|
||||||
|
/// Same happens with exporting an IOSurfaceRef from a VkImage.
|
||||||
|
/// So in a case when the VkSemaphore or VkImage is destroyed, the "handle" which is actually a pointer
|
||||||
|
/// will be pointing to a dead object.
|
||||||
|
/// To prevent this we need to increase the reference counter in a handle-specific means
|
||||||
|
/// synchronously before returning control back to the user.
|
||||||
|
///
|
||||||
|
/// This is not needed for fds or DXGI handles, since those are _created_ on demand as proper NT handles
|
||||||
|
/// </summary>
|
||||||
|
[Unstable, NotClientImplementable]
|
||||||
|
public interface IExternalObjectsHandleWrapRenderInterfaceContextFeature
|
||||||
|
{
|
||||||
|
IExternalObjectsWrappedGpuHandle? WrapImageHandleOnAnyThread(IPlatformHandle handle,
|
||||||
|
PlatformGraphicsExternalImageProperties properties);
|
||||||
|
IExternalObjectsWrappedGpuHandle? WrapSemaphoreHandleOnAnyThread(IPlatformHandle handle);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[Unstable, NotClientImplementable]
|
||||||
|
public interface IExternalObjectsWrappedGpuHandle : IPlatformHandle, IDisposable
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
[Unstable]
|
[Unstable]
|
||||||
public interface IPlatformRenderInterfaceImportedObject : IDisposable
|
public interface IPlatformRenderInterfaceImportedObject : IDisposable
|
||||||
{
|
{
|
||||||
@@ -43,6 +73,10 @@ public interface IPlatformRenderInterfaceImportedImage : IPlatformRenderInterfac
|
|||||||
IBitmapImpl SnapshotWithSemaphores(IPlatformRenderInterfaceImportedSemaphore waitForSemaphore,
|
IBitmapImpl SnapshotWithSemaphores(IPlatformRenderInterfaceImportedSemaphore waitForSemaphore,
|
||||||
IPlatformRenderInterfaceImportedSemaphore signalSemaphore);
|
IPlatformRenderInterfaceImportedSemaphore signalSemaphore);
|
||||||
|
|
||||||
|
IBitmapImpl SnapshotWithTimelineSemaphores(
|
||||||
|
IPlatformRenderInterfaceImportedSemaphore waitForSemaphore, ulong waitForValue,
|
||||||
|
IPlatformRenderInterfaceImportedSemaphore signalSemaphore, ulong signalValue);
|
||||||
|
|
||||||
IBitmapImpl SnapshotWithAutomaticSync();
|
IBitmapImpl SnapshotWithAutomaticSync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,6 +43,11 @@ public static class KnownPlatformGraphicsExternalImageHandleTypes
|
|||||||
|
|
||||||
// A global shared handle that's been exported by Vulkan using VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT or in a compatible way
|
// A global shared handle that's been exported by Vulkan using VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT or in a compatible way
|
||||||
public const string VulkanOpaqueKmtHandle = nameof(VulkanOpaqueKmtHandle);
|
public const string VulkanOpaqueKmtHandle = nameof(VulkanOpaqueKmtHandle);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A reference to IOSurface
|
||||||
|
/// </summary>
|
||||||
|
public const string IOSurfaceRef = nameof(IOSurfaceRef);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -65,4 +70,9 @@ public static class KnownPlatformGraphicsExternalSemaphoreHandleTypes
|
|||||||
|
|
||||||
/// A DXGI NT handle returned by ID3D12Device::CreateSharedHandle or ID3D11Fence::CreateSharedHandle
|
/// A DXGI NT handle returned by ID3D12Device::CreateSharedHandle or ID3D11Fence::CreateSharedHandle
|
||||||
public const string Direct3D12FenceNtHandle = nameof(Direct3D12FenceNtHandle);
|
public const string Direct3D12FenceNtHandle = nameof(Direct3D12FenceNtHandle);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A pointer to MTLSharedEvent object
|
||||||
|
/// </summary>
|
||||||
|
public const string MetalSharedEvent = nameof(MetalSharedEvent);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,6 +41,23 @@ public sealed class CompositionDrawingSurface : CompositionSurface, IDisposable
|
|||||||
var signal = (CompositionImportedGpuSemaphore)signalSemaphore;
|
var signal = (CompositionImportedGpuSemaphore)signalSemaphore;
|
||||||
return Compositor.InvokeServerJobAsync(() => Server.UpdateWithSemaphores(img, wait, signal));
|
return Compositor.InvokeServerJobAsync(() => Server.UpdateWithSemaphores(img, wait, signal));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the surface contents using an imported memory image using a semaphore pair as the means of synchronization
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="image">GPU image with new surface contents</param>
|
||||||
|
/// <param name="waitForSemaphore">The semaphore to wait for before accessing the image</param>
|
||||||
|
/// <param name="signalSemaphore">The semaphore to signal after accessing the image</param>
|
||||||
|
/// <returns>A task that completes when update operation is completed and user code is free to destroy or dispose the image</returns>
|
||||||
|
public Task UpdateWithTimelineSemaphoresAsync(ICompositionImportedGpuImage image,
|
||||||
|
ICompositionImportedGpuSemaphore waitForSemaphore, ulong waitForValue,
|
||||||
|
ICompositionImportedGpuSemaphore signalSemaphore, ulong signalValue)
|
||||||
|
{
|
||||||
|
var img = (CompositionImportedGpuImage)image;
|
||||||
|
var wait = (CompositionImportedGpuSemaphore)waitForSemaphore;
|
||||||
|
var signal = (CompositionImportedGpuSemaphore)signalSemaphore;
|
||||||
|
return Compositor.InvokeServerJobAsync(() => Server.UpdateWithTimelineSemaphores(img, wait, waitForValue, signal, signalValue));
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Updates the surface contents using an unspecified automatic means of synchronization
|
/// Updates the surface contents using an unspecified automatic means of synchronization
|
||||||
|
|||||||
@@ -81,7 +81,11 @@ public enum CompositionGpuImportedImageSynchronizationCapabilities
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Synchronization and ordering is somehow handled by the underlying platform
|
/// Synchronization and ordering is somehow handled by the underlying platform
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Automatic = 4
|
Automatic = 4,
|
||||||
|
/// <summary>
|
||||||
|
/// Pre-render and after-render timeline semaphores must be provided alongside with the image
|
||||||
|
/// </summary>
|
||||||
|
TimelineSemaphores = 8
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ internal class CompositionInterop : ICompositionGpuInterop
|
|||||||
private readonly Compositor _compositor;
|
private readonly Compositor _compositor;
|
||||||
private readonly IPlatformRenderInterfaceContext _context;
|
private readonly IPlatformRenderInterfaceContext _context;
|
||||||
private readonly IExternalObjectsRenderInterfaceContextFeature _externalObjects;
|
private readonly IExternalObjectsRenderInterfaceContextFeature _externalObjects;
|
||||||
|
private readonly IExternalObjectsHandleWrapRenderInterfaceContextFeature? _externalObjectsWithHandleWrap;
|
||||||
|
|
||||||
|
|
||||||
public CompositionInterop(
|
public CompositionInterop(
|
||||||
Compositor compositor,
|
Compositor compositor,
|
||||||
@@ -21,6 +22,7 @@ internal class CompositionInterop : ICompositionGpuInterop
|
|||||||
DeviceLuid = externalObjects.DeviceLuid;
|
DeviceLuid = externalObjects.DeviceLuid;
|
||||||
DeviceUuid = externalObjects.DeviceUuid;
|
DeviceUuid = externalObjects.DeviceUuid;
|
||||||
_externalObjects = externalObjects;
|
_externalObjects = externalObjects;
|
||||||
|
_externalObjectsWithHandleWrap = _context.TryGetFeature<IExternalObjectsHandleWrapRenderInterfaceContextFeature>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public IReadOnlyList<string> SupportedImageHandleTypes => _externalObjects.SupportedImageHandleTypes;
|
public IReadOnlyList<string> SupportedImageHandleTypes => _externalObjects.SupportedImageHandleTypes;
|
||||||
@@ -31,17 +33,23 @@ internal class CompositionInterop : ICompositionGpuInterop
|
|||||||
|
|
||||||
public ICompositionImportedGpuImage ImportImage(IPlatformHandle handle,
|
public ICompositionImportedGpuImage ImportImage(IPlatformHandle handle,
|
||||||
PlatformGraphicsExternalImageProperties properties)
|
PlatformGraphicsExternalImageProperties properties)
|
||||||
=> new CompositionImportedGpuImage(_compositor, _context, _externalObjects,
|
{
|
||||||
() => _externalObjects.ImportImage(handle, properties));
|
handle = _externalObjectsWithHandleWrap?.WrapImageHandleOnAnyThread(handle, properties) ?? handle;
|
||||||
|
return new CompositionImportedGpuImage(_compositor, _context, _externalObjects,
|
||||||
|
() => _externalObjects.ImportImage(handle, properties), handle);
|
||||||
|
}
|
||||||
|
|
||||||
public ICompositionImportedGpuImage ImportImage(ICompositionImportableSharedGpuContextImage image)
|
public ICompositionImportedGpuImage ImportImage(ICompositionImportableSharedGpuContextImage image)
|
||||||
{
|
{
|
||||||
return new CompositionImportedGpuImage(_compositor, _context, _externalObjects,
|
return new CompositionImportedGpuImage(_compositor, _context, _externalObjects,
|
||||||
() => _externalObjects.ImportImage(image));
|
() => _externalObjects.ImportImage(image), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ICompositionImportedGpuSemaphore ImportSemaphore(IPlatformHandle handle)
|
public ICompositionImportedGpuSemaphore ImportSemaphore(IPlatformHandle handle)
|
||||||
=> new CompositionImportedGpuSemaphore(handle, _compositor, _context, _externalObjects);
|
{
|
||||||
|
handle = _externalObjectsWithHandleWrap?.WrapSemaphoreHandleOnAnyThread(handle) ?? handle;
|
||||||
|
return new CompositionImportedGpuSemaphore(handle, _compositor, _context, _externalObjects);
|
||||||
|
}
|
||||||
|
|
||||||
public ICompositionImportedGpuImage ImportSemaphore(ICompositionImportableSharedGpuContextSemaphore image)
|
public ICompositionImportedGpuImage ImportSemaphore(ICompositionImportableSharedGpuContextSemaphore image)
|
||||||
{
|
{
|
||||||
@@ -61,13 +69,17 @@ abstract class CompositionGpuImportedObjectBase : ICompositionGpuImportedObject
|
|||||||
|
|
||||||
public CompositionGpuImportedObjectBase(Compositor compositor,
|
public CompositionGpuImportedObjectBase(Compositor compositor,
|
||||||
IPlatformRenderInterfaceContext context,
|
IPlatformRenderInterfaceContext context,
|
||||||
IExternalObjectsRenderInterfaceContextFeature feature)
|
IExternalObjectsRenderInterfaceContextFeature feature, IPlatformHandle? handle)
|
||||||
{
|
{
|
||||||
Compositor = compositor;
|
Compositor = compositor;
|
||||||
Context = context;
|
Context = context;
|
||||||
Feature = feature;
|
Feature = feature;
|
||||||
|
|
||||||
ImportCompleted = Compositor.InvokeServerJobAsync(Import);
|
ImportCompleted = Compositor.InvokeServerJobAsync(() =>
|
||||||
|
{
|
||||||
|
using var _ = handle as IExternalObjectsWrappedGpuHandle;
|
||||||
|
Import();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract void Import();
|
protected abstract void Import();
|
||||||
@@ -93,7 +105,7 @@ class CompositionImportedGpuImage : CompositionGpuImportedObjectBase, ICompositi
|
|||||||
public CompositionImportedGpuImage(Compositor compositor,
|
public CompositionImportedGpuImage(Compositor compositor,
|
||||||
IPlatformRenderInterfaceContext context,
|
IPlatformRenderInterfaceContext context,
|
||||||
IExternalObjectsRenderInterfaceContextFeature feature,
|
IExternalObjectsRenderInterfaceContextFeature feature,
|
||||||
Func<IPlatformRenderInterfaceImportedImage> importer): base(compositor, context, feature)
|
Func<IPlatformRenderInterfaceImportedImage> importer, IPlatformHandle? handle): base(compositor, context, feature, handle)
|
||||||
{
|
{
|
||||||
_importer = importer;
|
_importer = importer;
|
||||||
}
|
}
|
||||||
@@ -128,7 +140,7 @@ class CompositionImportedGpuSemaphore : CompositionGpuImportedObjectBase, ICompo
|
|||||||
|
|
||||||
public CompositionImportedGpuSemaphore(IPlatformHandle handle,
|
public CompositionImportedGpuSemaphore(IPlatformHandle handle,
|
||||||
Compositor compositor, IPlatformRenderInterfaceContext context,
|
Compositor compositor, IPlatformRenderInterfaceContext context,
|
||||||
IExternalObjectsRenderInterfaceContextFeature feature) : base(compositor, context, feature)
|
IExternalObjectsRenderInterfaceContextFeature feature) : base(compositor, context, feature, handle)
|
||||||
{
|
{
|
||||||
_handle = handle;
|
_handle = handle;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,6 +75,19 @@ internal class ServerCompositionDrawingSurface : ServerCompositionSurface, IDisp
|
|||||||
Update(image.Image.SnapshotWithSemaphores(wait.Semaphore, signal.Semaphore), image.Context);
|
Update(image.Image.SnapshotWithSemaphores(wait.Semaphore, signal.Semaphore), image.Context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void UpdateWithTimelineSemaphores(CompositionImportedGpuImage image,
|
||||||
|
CompositionImportedGpuSemaphore wait, ulong waitForValue,
|
||||||
|
CompositionImportedGpuSemaphore signal, ulong signalValue)
|
||||||
|
{
|
||||||
|
using (Compositor.RenderInterface.EnsureCurrent())
|
||||||
|
{
|
||||||
|
PerformSanityChecks(image);
|
||||||
|
if (!wait.IsUsable || !signal.IsUsable)
|
||||||
|
throw new PlatformGraphicsContextLostException();
|
||||||
|
Update(image.Image.SnapshotWithTimelineSemaphores(wait.Semaphore, waitForValue, signal.Semaphore, signalValue), image.Context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Avalonia.Metadata;
|
||||||
|
using Avalonia.Platform;
|
||||||
|
using Avalonia.Rendering.Composition;
|
||||||
|
|
||||||
|
namespace Avalonia.Metal;
|
||||||
|
|
||||||
|
[PrivateApi]
|
||||||
|
public interface IMetalExternalObjectsFeature
|
||||||
|
{
|
||||||
|
IReadOnlyList<string> SupportedImageHandleTypes { get; }
|
||||||
|
IReadOnlyList<string> SupportedSemaphoreTypes { get; }
|
||||||
|
byte[]? DeviceLuid { get; }
|
||||||
|
CompositionGpuImportedImageSynchronizationCapabilities GetSynchronizationCapabilities(string imageHandleType);
|
||||||
|
IMetalExternalTexture ImportImage(IPlatformHandle handle, PlatformGraphicsExternalImageProperties properties);
|
||||||
|
IMetalSharedEvent ImportSharedEvent(IPlatformHandle handle);
|
||||||
|
|
||||||
|
void SubmitWait(IMetalSharedEvent @event, ulong waitForValue);
|
||||||
|
void SubmitSignal(IMetalSharedEvent @event, ulong signalValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
[PrivateApi]
|
||||||
|
public interface IMetalExternalTexture : IDisposable
|
||||||
|
{
|
||||||
|
int Width { get; }
|
||||||
|
int Height { get; }
|
||||||
|
int Samples { get; }
|
||||||
|
IntPtr Handle { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
[PrivateApi]
|
||||||
|
public interface IMetalSharedEvent : IDisposable
|
||||||
|
{
|
||||||
|
IntPtr Handle { get; }
|
||||||
|
}
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using Avalonia.OpenGL;
|
using Avalonia.OpenGL;
|
||||||
using Avalonia.Native.Interop;
|
using Avalonia.Native.Interop;
|
||||||
using System.Drawing;
|
|
||||||
using Avalonia.OpenGL.Surfaces;
|
using Avalonia.OpenGL.Surfaces;
|
||||||
using Avalonia.Platform;
|
using Avalonia.Platform;
|
||||||
|
using Avalonia.Rendering.Composition;
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
|
|
||||||
namespace Avalonia.Native
|
namespace Avalonia.Native
|
||||||
@@ -13,7 +14,7 @@ namespace Avalonia.Native
|
|||||||
{
|
{
|
||||||
private readonly IAvnGlDisplay _display;
|
private readonly IAvnGlDisplay _display;
|
||||||
|
|
||||||
public AvaloniaNativeGlPlatformGraphics(IAvnGlDisplay display)
|
public AvaloniaNativeGlPlatformGraphics(IAvnGlDisplay display, IAvaloniaNativeFactory factory)
|
||||||
{
|
{
|
||||||
_display = display;
|
_display = display;
|
||||||
var context = display.CreateContext(null);
|
var context = display.CreateContext(null);
|
||||||
@@ -36,7 +37,7 @@ namespace Avalonia.Native
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
GlDisplay = new GlDisplay(display, glInterface, context.SampleCount, context.StencilSize);
|
GlDisplay = new GlDisplay(display, glInterface, context.SampleCount, context.StencilSize, factory);
|
||||||
SharedContext =(GlContext)CreateContext();
|
SharedContext =(GlContext)CreateContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,12 +60,15 @@ namespace Avalonia.Native
|
|||||||
{
|
{
|
||||||
private readonly IAvnGlDisplay _display;
|
private readonly IAvnGlDisplay _display;
|
||||||
|
|
||||||
public GlDisplay(IAvnGlDisplay display, GlInterface glInterface, int sampleCount, int stencilSize)
|
public GlDisplay(IAvnGlDisplay display, GlInterface glInterface, int sampleCount, int stencilSize,
|
||||||
|
IAvaloniaNativeFactory factory)
|
||||||
{
|
{
|
||||||
_display = display;
|
_display = display;
|
||||||
SampleCount = sampleCount;
|
SampleCount = sampleCount;
|
||||||
StencilSize = stencilSize;
|
StencilSize = stencilSize;
|
||||||
|
Factory = factory;
|
||||||
GlInterface = glInterface;
|
GlInterface = glInterface;
|
||||||
|
MemoryHelper = factory.CreateMemoryManagementHelper();
|
||||||
}
|
}
|
||||||
|
|
||||||
public GlInterface GlInterface { get; }
|
public GlInterface GlInterface { get; }
|
||||||
@@ -72,6 +76,9 @@ namespace Avalonia.Native
|
|||||||
public int SampleCount { get; }
|
public int SampleCount { get; }
|
||||||
|
|
||||||
public int StencilSize { get; }
|
public int StencilSize { get; }
|
||||||
|
public IAvaloniaNativeFactory Factory { get; }
|
||||||
|
|
||||||
|
public IAvnNativeObjectsMemoryManagement MemoryHelper { get; }
|
||||||
|
|
||||||
public void ClearContext() => _display.LegacyClearCurrentContext();
|
public void ClearContext() => _display.LegacyClearCurrentContext();
|
||||||
|
|
||||||
@@ -84,6 +91,8 @@ namespace Avalonia.Native
|
|||||||
{
|
{
|
||||||
private readonly GlDisplay _display;
|
private readonly GlDisplay _display;
|
||||||
private readonly GlContext _sharedWith;
|
private readonly GlContext _sharedWith;
|
||||||
|
private readonly GpuHandleWrapFeature _handleWrapFeature;
|
||||||
|
private readonly GlExternalObjectsFeature _externalObjects;
|
||||||
public IAvnGlContext Context { get; private set; }
|
public IAvnGlContext Context { get; private set; }
|
||||||
|
|
||||||
public GlContext(GlDisplay display, GlContext sharedWith, IAvnGlContext context, GlVersion version)
|
public GlContext(GlDisplay display, GlContext sharedWith, IAvnGlContext context, GlVersion version)
|
||||||
@@ -92,6 +101,8 @@ namespace Avalonia.Native
|
|||||||
_sharedWith = sharedWith;
|
_sharedWith = sharedWith;
|
||||||
Context = context;
|
Context = context;
|
||||||
Version = version;
|
Version = version;
|
||||||
|
_handleWrapFeature = new GpuHandleWrapFeature(display.Factory);
|
||||||
|
_externalObjects = new GlExternalObjectsFeature(this, display);
|
||||||
}
|
}
|
||||||
|
|
||||||
public GlVersion Version { get; }
|
public GlVersion Version { get; }
|
||||||
@@ -128,7 +139,14 @@ namespace Avalonia.Native
|
|||||||
Context = null;
|
Context = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public object TryGetFeature(Type featureType) => null;
|
public object TryGetFeature(Type featureType)
|
||||||
|
{
|
||||||
|
if (featureType == typeof(IExternalObjectsHandleWrapRenderInterfaceContextFeature))
|
||||||
|
return _handleWrapFeature;
|
||||||
|
if (featureType == typeof(IGlContextExternalObjectsFeature))
|
||||||
|
return _externalObjects;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -203,6 +221,137 @@ namespace Avalonia.Native
|
|||||||
var avnContext = (GlContext)context;
|
var avnContext = (GlContext)context;
|
||||||
return new GlPlatformSurfaceRenderTarget(_topLevel.CreateGlRenderTarget(avnContext.Context), avnContext);
|
return new GlPlatformSurfaceRenderTarget(_topLevel.CreateGlRenderTarget(avnContext.Context), avnContext);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class GlExternalObjectsFeature : IGlContextExternalObjectsFeature
|
||||||
|
{
|
||||||
|
private readonly GlContext _context;
|
||||||
|
private readonly GlDisplay _display;
|
||||||
|
|
||||||
|
public unsafe GlExternalObjectsFeature(GlContext context, GlDisplay display)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
_display = display;
|
||||||
|
ulong registryId = 0;
|
||||||
|
if (context.Context.GetIOKitRegistryId(®istryId) != 0)
|
||||||
|
{
|
||||||
|
// We are reversing bytes to match MoltenVK (LUID is a Vulkan term after all)
|
||||||
|
DeviceLuid = BitConverter.GetBytes(registryId).Reverse().ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyList<string> SupportedImportableExternalImageTypes { get; } =
|
||||||
|
[KnownPlatformGraphicsExternalImageHandleTypes.IOSurfaceRef];
|
||||||
|
public IReadOnlyList<string> SupportedExportableExternalImageTypes { get; } = [];
|
||||||
|
|
||||||
|
public IReadOnlyList<string> SupportedImportableExternalSemaphoreTypes { get; } =
|
||||||
|
[KnownPlatformGraphicsExternalSemaphoreHandleTypes.MetalSharedEvent];
|
||||||
|
|
||||||
|
public IReadOnlyList<string> SupportedExportableExternalSemaphoreTypes { get; } = [];
|
||||||
|
public IReadOnlyList<PlatformGraphicsExternalImageFormat> GetSupportedFormatsForExternalMemoryType(string type)
|
||||||
|
{
|
||||||
|
return [PlatformGraphicsExternalImageFormat.B8G8R8A8UNorm];
|
||||||
|
}
|
||||||
|
|
||||||
|
public IGlExportableExternalImageTexture CreateImage(string type, PixelSize size, PlatformGraphicsExternalImageFormat format) =>
|
||||||
|
throw new NotSupportedException();
|
||||||
|
|
||||||
|
public IGlExportableExternalImageTexture CreateSemaphore(string type) => throw new NotSupportedException();
|
||||||
|
|
||||||
|
public IGlExternalImageTexture ImportImage(IPlatformHandle handle, PlatformGraphicsExternalImageProperties properties)
|
||||||
|
{
|
||||||
|
if (handle.HandleDescriptor == KnownPlatformGraphicsExternalImageHandleTypes.IOSurfaceRef)
|
||||||
|
{
|
||||||
|
if (properties.Format != PlatformGraphicsExternalImageFormat.B8G8R8A8UNorm)
|
||||||
|
throw new OpenGlException("Only B8G8R8A8UNorm format is supported for IOSurfaceRef");
|
||||||
|
using (_context.EnsureCurrent())
|
||||||
|
{
|
||||||
|
_context.GlInterface.GetIntegerv(GlConsts.GL_TEXTURE_BINDING_RECTANGLE, out var oldTexture);
|
||||||
|
var textureId = _context.GlInterface.GenTexture();
|
||||||
|
_context.GlInterface.BindTexture(GlConsts.GL_TEXTURE_RECTANGLE, textureId);
|
||||||
|
var error = _context.Context.texImageIOSurface2D(GlConsts.GL_TEXTURE_RECTANGLE, GlConsts.GL_RGBA8,
|
||||||
|
properties.Width, properties.Height, GlConsts.GL_BGRA, GlConsts.GL_UNSIGNED_INT_8_8_8_8_REV,
|
||||||
|
handle.Handle, 0);
|
||||||
|
//var error = 0;
|
||||||
|
_context.GlInterface.BindTexture(GlConsts.GL_TEXTURE_RECTANGLE, oldTexture);
|
||||||
|
|
||||||
|
if(error != 0)
|
||||||
|
{
|
||||||
|
_context.GlInterface.DeleteTexture(textureId);
|
||||||
|
throw new OpenGlException("CGLTexImageIOSurface2D returned " + error);
|
||||||
|
}
|
||||||
|
return new ImportedTexture(_context, GlConsts.GL_TEXTURE_RECTANGLE, textureId,
|
||||||
|
GlConsts.GL_RGBA8, properties);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new NotSupportedException("This handle type is not supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
public IGlExternalSemaphore ImportSemaphore(IPlatformHandle handle)
|
||||||
|
{
|
||||||
|
if (handle.HandleDescriptor == KnownPlatformGraphicsExternalSemaphoreHandleTypes.MetalSharedEvent)
|
||||||
|
{
|
||||||
|
var imported = _display.Factory.ImportMTLSharedEvent(handle.Handle);
|
||||||
|
return new MtlEventSemaphore(imported);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new NotSupportedException("This handle type is not supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
public CompositionGpuImportedImageSynchronizationCapabilities GetSynchronizationCapabilities(string imageHandleType)
|
||||||
|
{
|
||||||
|
return CompositionGpuImportedImageSynchronizationCapabilities.Automatic |
|
||||||
|
CompositionGpuImportedImageSynchronizationCapabilities.TimelineSemaphores;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] DeviceLuid { get; }
|
||||||
|
public byte[] DeviceUuid { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
class MtlEventSemaphore(IAvnMTLSharedEvent inner) : IGlExternalSemaphore
|
||||||
|
{
|
||||||
|
public void WaitSemaphore(IGlExternalImageTexture texture) =>
|
||||||
|
throw new NotSupportedException("This is a timeline semaphore");
|
||||||
|
|
||||||
|
public void SignalSemaphore(IGlExternalImageTexture texture) =>
|
||||||
|
throw new NotSupportedException("This is a timeline semaphore");
|
||||||
|
|
||||||
|
public void WaitTimelineSemaphore(IGlExternalImageTexture texture, ulong value) =>
|
||||||
|
inner.Wait(value, 1000);
|
||||||
|
|
||||||
|
public void SignalTimelineSemaphore(IGlExternalImageTexture texture, ulong value) =>
|
||||||
|
inner.SetSignaledValue(value);
|
||||||
|
|
||||||
|
public void Dispose() => inner.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
class ImportedTexture(GlContext context, int type, int id, int internalFormat,
|
||||||
|
PlatformGraphicsExternalImageProperties properties) : IGlExternalImageTexture
|
||||||
|
{
|
||||||
|
private bool _disposed;
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if(_disposed)
|
||||||
|
return;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_disposed = true;
|
||||||
|
using (context.EnsureCurrent())
|
||||||
|
context.GlInterface.DeleteTexture(id);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Ignore, context is likely broken
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AcquireKeyedMutex(uint key) => throw new NotSupportedException();
|
||||||
|
|
||||||
|
public void ReleaseKeyedMutex(uint key) => throw new NotSupportedException();
|
||||||
|
|
||||||
|
public int TextureId => id;
|
||||||
|
public int InternalFormat => internalFormat;
|
||||||
|
public int TextureType => type;
|
||||||
|
public PlatformGraphicsExternalImageProperties Properties => properties;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -149,7 +149,7 @@ namespace Avalonia.Native
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_platformGraphics = new AvaloniaNativeGlPlatformGraphics(_factory.ObtainGlDisplay());
|
_platformGraphics = new AvaloniaNativeGlPlatformGraphics(_factory.ObtainGlDisplay(), _factory);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
using System;
|
||||||
|
using Avalonia.Native.Interop;
|
||||||
|
using Avalonia.Platform;
|
||||||
|
|
||||||
|
namespace Avalonia.Native;
|
||||||
|
|
||||||
|
class GpuHandleWrapFeature : IExternalObjectsHandleWrapRenderInterfaceContextFeature
|
||||||
|
{
|
||||||
|
private readonly IAvnNativeObjectsMemoryManagement _helper;
|
||||||
|
|
||||||
|
public GpuHandleWrapFeature(IAvaloniaNativeFactory factory)
|
||||||
|
{
|
||||||
|
_helper = factory.CreateMemoryManagementHelper();
|
||||||
|
}
|
||||||
|
public IExternalObjectsWrappedGpuHandle? WrapImageHandleOnAnyThread(IPlatformHandle handle, PlatformGraphicsExternalImageProperties properties)
|
||||||
|
{
|
||||||
|
if (handle.HandleDescriptor == KnownPlatformGraphicsExternalImageHandleTypes.IOSurfaceRef)
|
||||||
|
{
|
||||||
|
_helper.RetainCFObject(handle.Handle);
|
||||||
|
return new CFObjectWrapper(_helper, handle.Handle, handle.HandleDescriptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IExternalObjectsWrappedGpuHandle? WrapSemaphoreHandleOnAnyThread(IPlatformHandle handle)
|
||||||
|
{
|
||||||
|
if (handle.HandleDescriptor == KnownPlatformGraphicsExternalSemaphoreHandleTypes.MetalSharedEvent)
|
||||||
|
{
|
||||||
|
_helper.RetainNSObject(handle.Handle);
|
||||||
|
return new NSObjectWrapper(_helper, handle.Handle, handle.HandleDescriptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
class NSObjectWrapper(IAvnNativeObjectsMemoryManagement helper, IntPtr handle, string descriptor) : IExternalObjectsWrappedGpuHandle
|
||||||
|
{
|
||||||
|
public void Dispose() => helper.ReleaseNSObject(handle);
|
||||||
|
|
||||||
|
public IntPtr Handle => handle;
|
||||||
|
public string HandleDescriptor => descriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
class CFObjectWrapper(IAvnNativeObjectsMemoryManagement helper, IntPtr handle, string descriptor) : IExternalObjectsWrappedGpuHandle
|
||||||
|
{
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
helper.ReleaseCFObject(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IntPtr Handle => handle;
|
||||||
|
public string HandleDescriptor => descriptor;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,10 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using Avalonia.Metal;
|
using Avalonia.Metal;
|
||||||
using Avalonia.Native.Interop;
|
using Avalonia.Native.Interop;
|
||||||
using Avalonia.Platform;
|
using Avalonia.Platform;
|
||||||
|
using Avalonia.Rendering.Composition;
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
using Avalonia.Utilities;
|
using Avalonia.Utilities;
|
||||||
|
|
||||||
@@ -9,14 +12,16 @@ namespace Avalonia.Native;
|
|||||||
|
|
||||||
class MetalPlatformGraphics : IPlatformGraphics
|
class MetalPlatformGraphics : IPlatformGraphics
|
||||||
{
|
{
|
||||||
|
private readonly IAvaloniaNativeFactory _factory;
|
||||||
private readonly IAvnMetalDisplay _display;
|
private readonly IAvnMetalDisplay _display;
|
||||||
|
|
||||||
public MetalPlatformGraphics(IAvaloniaNativeFactory factory)
|
public MetalPlatformGraphics(IAvaloniaNativeFactory factory)
|
||||||
{
|
{
|
||||||
|
_factory = factory;
|
||||||
_display = factory.ObtainMetalDisplay();
|
_display = factory.ObtainMetalDisplay();
|
||||||
}
|
}
|
||||||
public bool UsesSharedContext => false;
|
public bool UsesSharedContext => false;
|
||||||
public IPlatformGraphicsContext CreateContext() => new MetalDevice(_display.CreateDevice());
|
public IPlatformGraphicsContext CreateContext() => new MetalDevice(_factory, _display.CreateDevice());
|
||||||
|
|
||||||
public IPlatformGraphicsContext GetSharedContext() => throw new NotSupportedException();
|
public IPlatformGraphicsContext GetSharedContext() => throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
@@ -25,11 +30,15 @@ class MetalDevice : IMetalDevice
|
|||||||
{
|
{
|
||||||
public IAvnMetalDevice Native { get; private set; }
|
public IAvnMetalDevice Native { get; private set; }
|
||||||
private DisposableLock _syncRoot = new();
|
private DisposableLock _syncRoot = new();
|
||||||
|
private readonly GpuHandleWrapFeature _handleWrapFeature;
|
||||||
|
private readonly MetalExternalObjectsFeature _externalObjectsFeature;
|
||||||
|
|
||||||
public MetalDevice(IAvnMetalDevice native)
|
|
||||||
|
public MetalDevice(IAvaloniaNativeFactory factory, IAvnMetalDevice native)
|
||||||
{
|
{
|
||||||
Native = native;
|
Native = native;
|
||||||
|
_handleWrapFeature = new GpuHandleWrapFeature(factory);
|
||||||
|
_externalObjectsFeature = new MetalExternalObjectsFeature(native);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
@@ -38,7 +47,14 @@ class MetalDevice : IMetalDevice
|
|||||||
Native = null;
|
Native = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public object TryGetFeature(Type featureType) => null;
|
public object TryGetFeature(Type featureType)
|
||||||
|
{
|
||||||
|
if (featureType == typeof(IExternalObjectsHandleWrapRenderInterfaceContextFeature))
|
||||||
|
return _handleWrapFeature;
|
||||||
|
if (featureType == typeof(IMetalExternalObjectsFeature))
|
||||||
|
return _externalObjectsFeature;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public bool IsLost => false;
|
public bool IsLost => false;
|
||||||
|
|
||||||
@@ -67,6 +83,85 @@ class MetalPlatformSurface : IMetalPlatformSurface
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal class MetalExternalObjectsFeature : IMetalExternalObjectsFeature
|
||||||
|
{
|
||||||
|
private readonly IAvnMetalDevice _device;
|
||||||
|
|
||||||
|
public unsafe MetalExternalObjectsFeature(IAvnMetalDevice device)
|
||||||
|
{
|
||||||
|
_device = device;
|
||||||
|
ulong registryId;
|
||||||
|
if (_device.GetIOKitRegistryId(®istryId) != 0)
|
||||||
|
{
|
||||||
|
DeviceLuid = BitConverter.GetBytes(registryId).Reverse().ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyList<string> SupportedImageHandleTypes { get; } =
|
||||||
|
[KnownPlatformGraphicsExternalImageHandleTypes.IOSurfaceRef];
|
||||||
|
|
||||||
|
public IReadOnlyList<string> SupportedSemaphoreTypes { get; } =
|
||||||
|
[KnownPlatformGraphicsExternalSemaphoreHandleTypes.MetalSharedEvent];
|
||||||
|
|
||||||
|
public byte[] DeviceLuid { get; }
|
||||||
|
|
||||||
|
public CompositionGpuImportedImageSynchronizationCapabilities
|
||||||
|
GetSynchronizationCapabilities(string imageHandleType) =>
|
||||||
|
CompositionGpuImportedImageSynchronizationCapabilities.TimelineSemaphores;
|
||||||
|
|
||||||
|
public IMetalExternalTexture ImportImage(IPlatformHandle handle, PlatformGraphicsExternalImageProperties properties)
|
||||||
|
{
|
||||||
|
var format = properties.Format switch
|
||||||
|
{
|
||||||
|
PlatformGraphicsExternalImageFormat.R8G8B8A8UNorm => AvnPixelFormat.kAvnRgba8888,
|
||||||
|
PlatformGraphicsExternalImageFormat.B8G8R8A8UNorm => AvnPixelFormat.kAvnBgra8888,
|
||||||
|
_ => throw new NotSupportedException("Pixel format is not supported")
|
||||||
|
};
|
||||||
|
|
||||||
|
if (handle.HandleDescriptor != KnownPlatformGraphicsExternalImageHandleTypes.IOSurfaceRef)
|
||||||
|
throw new NotSupportedException();
|
||||||
|
|
||||||
|
return new ImportedTexture(_device.ImportIOSurface(handle.Handle, format));
|
||||||
|
}
|
||||||
|
|
||||||
|
public IMetalSharedEvent ImportSharedEvent(IPlatformHandle handle)
|
||||||
|
{
|
||||||
|
if (handle.HandleDescriptor != KnownPlatformGraphicsExternalSemaphoreHandleTypes.MetalSharedEvent)
|
||||||
|
throw new NotSupportedException();
|
||||||
|
return new SharedEvent(_device.ImportSharedEvent(handle.Handle));
|
||||||
|
}
|
||||||
|
|
||||||
|
class ImportedTexture(IAvnMetalTexture texture) : IMetalExternalTexture
|
||||||
|
{
|
||||||
|
public void Dispose() => texture.Dispose();
|
||||||
|
|
||||||
|
public int Width => texture.Width;
|
||||||
|
|
||||||
|
public int Height => texture.Height;
|
||||||
|
|
||||||
|
public int Samples => texture.SampleCount;
|
||||||
|
|
||||||
|
public IntPtr Handle => texture.NativeHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
class SharedEvent(IAvnMTLSharedEvent inner) : IMetalSharedEvent
|
||||||
|
{
|
||||||
|
public IAvnMTLSharedEvent Native => inner;
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
inner.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IntPtr Handle => inner.NativeHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SubmitWait(IMetalSharedEvent @event, ulong waitForValue) =>
|
||||||
|
_device.SubmitWait(((SharedEvent)@event).Native, waitForValue);
|
||||||
|
|
||||||
|
public void SubmitSignal(IMetalSharedEvent @event, ulong signalValue) =>
|
||||||
|
_device.SubmitSignal(((SharedEvent)@event).Native, signalValue);
|
||||||
|
}
|
||||||
|
|
||||||
internal class MetalRenderTarget : IMetalPlatformSurfaceRenderTarget
|
internal class MetalRenderTarget : IMetalPlatformSurfaceRenderTarget
|
||||||
{
|
{
|
||||||
private IAvnMetalRenderTarget _native;
|
private IAvnMetalRenderTarget _native;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
@clr-access internal
|
@clr-access internal
|
||||||
@clr-map bool int
|
@clr-map bool int
|
||||||
@clr-map u_int64_t ulong
|
@clr-map u_int64_t ulong
|
||||||
|
@clr-map uint64_t ulong
|
||||||
@clr-map int64_t long
|
@clr-map int64_t long
|
||||||
@clr-map long IntPtr
|
@clr-map long IntPtr
|
||||||
@cpp-preamble @@
|
@cpp-preamble @@
|
||||||
@@ -700,6 +701,8 @@ interface IAvaloniaNativeFactory : IUnknown
|
|||||||
HRESULT CreatePlatformSettings(IAvnPlatformSettings** ppv);
|
HRESULT CreatePlatformSettings(IAvnPlatformSettings** ppv);
|
||||||
HRESULT CreatePlatformBehaviorInhibition(IAvnPlatformBehaviorInhibition** ppv);
|
HRESULT CreatePlatformBehaviorInhibition(IAvnPlatformBehaviorInhibition** ppv);
|
||||||
HRESULT CreatePlatformRenderTimer(IAvnPlatformRenderTimer** ppv);
|
HRESULT CreatePlatformRenderTimer(IAvnPlatformRenderTimer** ppv);
|
||||||
|
HRESULT ImportMTLSharedEvent([intptr]void* idMtlSharedEvent, IAvnMTLSharedEvent** ppv);
|
||||||
|
HRESULT CreateMemoryManagementHelper(IAvnNativeObjectsMemoryManagement** ppv);
|
||||||
}
|
}
|
||||||
|
|
||||||
[uuid(233e094f-9b9f-44a3-9a6e-6948bbdd9fb1)]
|
[uuid(233e094f-9b9f-44a3-9a6e-6948bbdd9fb1)]
|
||||||
@@ -1043,6 +1046,9 @@ interface IAvnGlContext : IUnknown
|
|||||||
int GetSampleCount();
|
int GetSampleCount();
|
||||||
int GetStencilSize();
|
int GetStencilSize();
|
||||||
[intptr]void* GetNativeHandle();
|
[intptr]void* GetNativeHandle();
|
||||||
|
int texImageIOSurface2D(int target, int internal_format,
|
||||||
|
int width, int height, int format, int type, [intptr]void* ioSurface, int plane);
|
||||||
|
bool GetIOKitRegistryId(uint64_t* value);
|
||||||
}
|
}
|
||||||
|
|
||||||
[uuid(931062d2-5bc8-4062-8588-83dd8deb99c2)]
|
[uuid(931062d2-5bc8-4062-8588-83dd8deb99c2)]
|
||||||
@@ -1069,6 +1075,11 @@ interface IAvnMetalDevice : IUnknown
|
|||||||
{
|
{
|
||||||
[intptr]void* GetDevice();
|
[intptr]void* GetDevice();
|
||||||
[intptr]void* GetQueue();
|
[intptr]void* GetQueue();
|
||||||
|
bool GetIOKitRegistryId(uint64_t* value);
|
||||||
|
HRESULT ImportIOSurface([intptr]void* handle, AvnPixelFormat pixelFormat, IAvnMetalTexture** ppv);
|
||||||
|
HRESULT ImportSharedEvent([intptr]void*mtlSharedEventInstance, IAvnMTLSharedEvent**ppv);
|
||||||
|
HRESULT SubmitWait(IAvnMTLSharedEvent* ev, uint64_t value);
|
||||||
|
HRESULT SubmitSignal(IAvnMTLSharedEvent* ev, uint64_t value);
|
||||||
}
|
}
|
||||||
|
|
||||||
[uuid(f1306b71-eca0-426e-8700-105192693b1a)]
|
[uuid(f1306b71-eca0-426e-8700-105192693b1a)]
|
||||||
@@ -1076,6 +1087,35 @@ interface IAvnMetalRenderTarget : IUnknown
|
|||||||
{
|
{
|
||||||
HRESULT BeginDrawing(IAvnMetalRenderingSession** ret);
|
HRESULT BeginDrawing(IAvnMetalRenderingSession** ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[uuid(a1f4fcde-9152-48bd-bf8a-b1b651134a69)]
|
||||||
|
interface IAvnMTLSharedEvent : IUnknown
|
||||||
|
{
|
||||||
|
[intptr]void* GetNativeHandle();
|
||||||
|
bool Wait(uint64_t value, uint64_t timeoutMS);
|
||||||
|
void SetSignaledValue(uint64_t value);
|
||||||
|
uint64_t GetSignaledValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
[uuid(722aad20-a87b-4ce5-b50f-f05cfa4cda39)]
|
||||||
|
interface IAvnMetalTexture
|
||||||
|
{
|
||||||
|
[intptr]void* GetNativeHandle();
|
||||||
|
int GetWidth();
|
||||||
|
int GetHeight();
|
||||||
|
int GetSampleCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
[uuid(74027aa2-5262-45a5-a74a-5a53373dcc17)]
|
||||||
|
interface IAvnNativeObjectsMemoryManagement
|
||||||
|
{
|
||||||
|
void RetainNSObject([intptr] void* obj);
|
||||||
|
void ReleaseNSObject([intptr] void* obj);
|
||||||
|
uint64_t GetRetainCountForNSObject([intptr] void* obj);
|
||||||
|
void RetainCFObject([intptr] void* obj);
|
||||||
|
void ReleaseCFObject([intptr] void* obj);
|
||||||
|
int64_t GetRetainCountForCFObject([intptr] void* obj);
|
||||||
|
}
|
||||||
|
|
||||||
[uuid(e625b406-f04c-484e-946a-4abd2c6015ad)]
|
[uuid(e625b406-f04c-484e-946a-4abd2c6015ad)]
|
||||||
interface IAvnMetalRenderingSession : IUnknown
|
interface IAvnMetalRenderingSession : IUnknown
|
||||||
|
|||||||
@@ -249,6 +249,16 @@ public class ExternalObjectsOpenGlExtensionFeature : IGlContextExternalObjectsFe
|
|||||||
var dstLayout = 0;
|
var dstLayout = 0;
|
||||||
_ext.SignalSemaphoreEXT(_semaphore, 0, null, 1, &texId, &dstLayout);
|
_ext.SignalSemaphoreEXT(_semaphore, 0, null, 1, &texId, &dstLayout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void WaitTimelineSemaphore(IGlExternalImageTexture texture, ulong value)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException("This semaphore type doesn't support value-based wait");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SignalTimelineSemaphore(IGlExternalImageTexture texture, ulong value)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException("This semaphore type doesn't support value-based signaling");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ExternalImageTexture : IGlExternalImageTexture
|
private class ExternalImageTexture : IGlExternalImageTexture
|
||||||
@@ -286,6 +296,7 @@ public class ExternalObjectsOpenGlExtensionFeature : IGlContextExternalObjectsFe
|
|||||||
|
|
||||||
public int TextureId { get; }
|
public int TextureId { get; }
|
||||||
public int InternalFormat => GL_RGBA8;
|
public int InternalFormat => GL_RGBA8;
|
||||||
|
public int TextureType => GL_TEXTURE_2D;
|
||||||
public PlatformGraphicsExternalImageProperties Properties { get; }
|
public PlatformGraphicsExternalImageProperties Properties { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -544,7 +544,7 @@ namespace Avalonia.OpenGL
|
|||||||
// public const int GL_UNSIGNED_SHORT_5_5_5_1 = 0x8034;
|
// public const int GL_UNSIGNED_SHORT_5_5_5_1 = 0x8034;
|
||||||
// public const int GL_UNSIGNED_SHORT_1_5_5_5_REV = 0x8366;
|
// public const int GL_UNSIGNED_SHORT_1_5_5_5_REV = 0x8366;
|
||||||
// public const int GL_UNSIGNED_INT_8_8_8_8 = 0x8035;
|
// public const int GL_UNSIGNED_INT_8_8_8_8 = 0x8035;
|
||||||
// public const int GL_UNSIGNED_INT_8_8_8_8_REV = 0x8367;
|
public const int GL_UNSIGNED_INT_8_8_8_8_REV = 0x8367;
|
||||||
// public const int GL_UNSIGNED_INT_10_10_10_2 = 0x8036;
|
// public const int GL_UNSIGNED_INT_10_10_10_2 = 0x8036;
|
||||||
// public const int GL_UNSIGNED_INT_2_10_10_10_REV = 0x8368;
|
// public const int GL_UNSIGNED_INT_2_10_10_10_REV = 0x8368;
|
||||||
// public const int GL_LIGHT_MODEL_COLOR_CONTROL = 0x81F8;
|
// public const int GL_LIGHT_MODEL_COLOR_CONTROL = 0x81F8;
|
||||||
@@ -1231,8 +1231,8 @@ namespace Avalonia.OpenGL
|
|||||||
// public const int GL_MAX_TEXTURE_BUFFER_SIZE = 0x8C2B;
|
// public const int GL_MAX_TEXTURE_BUFFER_SIZE = 0x8C2B;
|
||||||
// public const int GL_TEXTURE_BINDING_BUFFER = 0x8C2C;
|
// public const int GL_TEXTURE_BINDING_BUFFER = 0x8C2C;
|
||||||
// public const int GL_TEXTURE_BUFFER_DATA_STORE_BINDING = 0x8C2D;
|
// public const int GL_TEXTURE_BUFFER_DATA_STORE_BINDING = 0x8C2D;
|
||||||
// public const int GL_TEXTURE_RECTANGLE = 0x84F5;
|
public const int GL_TEXTURE_RECTANGLE = 0x84F5;
|
||||||
// public const int GL_TEXTURE_BINDING_RECTANGLE = 0x84F6;
|
public const int GL_TEXTURE_BINDING_RECTANGLE = 0x84F6;
|
||||||
// public const int GL_PROXY_TEXTURE_RECTANGLE = 0x84F7;
|
// public const int GL_PROXY_TEXTURE_RECTANGLE = 0x84F7;
|
||||||
// public const int GL_MAX_RECTANGLE_TEXTURE_SIZE = 0x84F8;
|
// public const int GL_MAX_RECTANGLE_TEXTURE_SIZE = 0x84F8;
|
||||||
// public const int GL_R8_SNORM = 0x8F94;
|
// public const int GL_R8_SNORM = 0x8F94;
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Avalonia.Metadata;
|
||||||
using Avalonia.Platform;
|
using Avalonia.Platform;
|
||||||
using Avalonia.Rendering.Composition;
|
using Avalonia.Rendering.Composition;
|
||||||
|
|
||||||
namespace Avalonia.OpenGL;
|
namespace Avalonia.OpenGL;
|
||||||
|
|
||||||
|
//TODO12: Make private and expose IBitmapImpl-based import API for composition visuals
|
||||||
|
[NotClientImplementable]
|
||||||
public interface IGlContextExternalObjectsFeature
|
public interface IGlContextExternalObjectsFeature
|
||||||
{
|
{
|
||||||
IReadOnlyList<string> SupportedImportableExternalImageTypes { get; }
|
IReadOnlyList<string> SupportedImportableExternalImageTypes { get; }
|
||||||
@@ -23,10 +26,14 @@ public interface IGlContextExternalObjectsFeature
|
|||||||
public byte[]? DeviceUuid { get; }
|
public byte[]? DeviceUuid { get; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO12: Make private and expose IBitmapImpl-based import API for composition visuals
|
||||||
|
[NotClientImplementable]
|
||||||
public interface IGlExternalSemaphore : IDisposable
|
public interface IGlExternalSemaphore : IDisposable
|
||||||
{
|
{
|
||||||
void WaitSemaphore(IGlExternalImageTexture texture);
|
void WaitSemaphore(IGlExternalImageTexture texture);
|
||||||
void SignalSemaphore(IGlExternalImageTexture texture);
|
void SignalSemaphore(IGlExternalImageTexture texture);
|
||||||
|
void WaitTimelineSemaphore(IGlExternalImageTexture texture, ulong value);
|
||||||
|
void SignalTimelineSemaphore(IGlExternalImageTexture texture, ulong value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface IGlExportableExternalSemaphore : IGlExternalSemaphore
|
public interface IGlExportableExternalSemaphore : IGlExternalSemaphore
|
||||||
@@ -34,12 +41,17 @@ public interface IGlExportableExternalSemaphore : IGlExternalSemaphore
|
|||||||
IPlatformHandle GetHandle();
|
IPlatformHandle GetHandle();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[NotClientImplementable]
|
||||||
public interface IGlExternalImageTexture : IDisposable
|
public interface IGlExternalImageTexture : IDisposable
|
||||||
{
|
{
|
||||||
void AcquireKeyedMutex(uint key);
|
void AcquireKeyedMutex(uint key);
|
||||||
void ReleaseKeyedMutex(uint key);
|
void ReleaseKeyedMutex(uint key);
|
||||||
int TextureId { get; }
|
int TextureId { get; }
|
||||||
int InternalFormat { get; }
|
int InternalFormat { get; }
|
||||||
|
/// <summary>
|
||||||
|
/// GL_TEXTURE_2D or GL_TEXTURE_RECTANGLE
|
||||||
|
/// </summary>
|
||||||
|
public int TextureType { get; }
|
||||||
|
|
||||||
PlatformGraphicsExternalImageProperties Properties { get; }
|
PlatformGraphicsExternalImageProperties Properties { get; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,75 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Avalonia.Metal;
|
||||||
|
using Avalonia.Platform;
|
||||||
|
using Avalonia.Rendering.Composition;
|
||||||
|
using SkiaSharp;
|
||||||
|
|
||||||
|
namespace Avalonia.Skia.Metal;
|
||||||
|
|
||||||
|
class SkiaMetalExternalObjectsFeature(SkiaMetalGpu gpu, IMetalExternalObjectsFeature inner) : IExternalObjectsRenderInterfaceContextFeature
|
||||||
|
{
|
||||||
|
public IReadOnlyList<string> SupportedImageHandleTypes => inner.SupportedImageHandleTypes;
|
||||||
|
|
||||||
|
public IReadOnlyList<string> SupportedSemaphoreTypes => inner.SupportedSemaphoreTypes;
|
||||||
|
|
||||||
|
class ImportedSemaphore(IMetalSharedEvent ev) : IPlatformRenderInterfaceImportedSemaphore
|
||||||
|
{
|
||||||
|
public IMetalSharedEvent Event => ev;
|
||||||
|
public void Dispose() => ev.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
class ImportedImage(SkiaMetalGpu gpu, IMetalExternalObjectsFeature feature, IMetalExternalTexture texture,
|
||||||
|
SKColorType colorType, bool topLeftOrigin) : IPlatformRenderInterfaceImportedImage
|
||||||
|
{
|
||||||
|
public void Dispose() => texture.Dispose();
|
||||||
|
|
||||||
|
public IBitmapImpl SnapshotWithKeyedMutex(uint acquireIndex, uint releaseIndex) => throw new System.NotSupportedException();
|
||||||
|
|
||||||
|
public IBitmapImpl SnapshotWithSemaphores(IPlatformRenderInterfaceImportedSemaphore waitForSemaphore,
|
||||||
|
IPlatformRenderInterfaceImportedSemaphore signalSemaphore) =>
|
||||||
|
throw new System.NotSupportedException();
|
||||||
|
|
||||||
|
public IBitmapImpl SnapshotWithTimelineSemaphores(IPlatformRenderInterfaceImportedSemaphore waitForSemaphore,
|
||||||
|
ulong waitForValue, IPlatformRenderInterfaceImportedSemaphore signalSemaphore, ulong signalValue)
|
||||||
|
{
|
||||||
|
gpu.GrContext.Flush(true, false);
|
||||||
|
feature.SubmitWait(((ImportedSemaphore)waitForSemaphore).Event, waitForValue);
|
||||||
|
using var backendTarget = new GRBackendRenderTarget(texture.Width, texture.Height, new GRMtlTextureInfo(texture.Handle));
|
||||||
|
|
||||||
|
using var surface = SKSurface.Create(gpu.GrContext, backendTarget,
|
||||||
|
topLeftOrigin ? GRSurfaceOrigin.TopLeft : GRSurfaceOrigin.BottomLeft,
|
||||||
|
colorType);
|
||||||
|
var rv = new ImmutableBitmap(surface.Snapshot());
|
||||||
|
gpu.GrContext.Flush();
|
||||||
|
feature.SubmitSignal(((ImportedSemaphore)signalSemaphore).Event, signalValue);
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IBitmapImpl SnapshotWithAutomaticSync() => throw new System.NotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IPlatformRenderInterfaceImportedImage ImportImage(IPlatformHandle handle,
|
||||||
|
PlatformGraphicsExternalImageProperties properties)
|
||||||
|
{
|
||||||
|
var format = properties.Format switch
|
||||||
|
{
|
||||||
|
PlatformGraphicsExternalImageFormat.R8G8B8A8UNorm => SKColorType.Rgba8888,
|
||||||
|
PlatformGraphicsExternalImageFormat.B8G8R8A8UNorm => SKColorType.Bgra8888,
|
||||||
|
_ => throw new NotSupportedException("Pixel format is not supported")
|
||||||
|
};
|
||||||
|
|
||||||
|
return new ImportedImage(gpu, inner, inner.ImportImage(handle, properties), format,
|
||||||
|
properties.TopLeftOrigin);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IPlatformRenderInterfaceImportedImage ImportImage(ICompositionImportableSharedGpuContextImage image) => throw new System.NotSupportedException();
|
||||||
|
|
||||||
|
public IPlatformRenderInterfaceImportedSemaphore ImportSemaphore(IPlatformHandle handle) => new ImportedSemaphore(inner.ImportSharedEvent(handle));
|
||||||
|
|
||||||
|
public CompositionGpuImportedImageSynchronizationCapabilities GetSynchronizationCapabilities(string imageHandleType)
|
||||||
|
=> inner.GetSynchronizationCapabilities(imageHandleType);
|
||||||
|
|
||||||
|
public byte[]? DeviceUuid { get; } = null;
|
||||||
|
public byte[]? DeviceLuid => inner.DeviceLuid;
|
||||||
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using Avalonia.Metal;
|
using Avalonia.Metal;
|
||||||
using Avalonia.Platform;
|
using Avalonia.Platform;
|
||||||
using SkiaSharp;
|
using SkiaSharp;
|
||||||
@@ -11,6 +10,9 @@ internal class SkiaMetalGpu : ISkiaGpu, ISkiaGpuWithPlatformGraphicsContext
|
|||||||
{
|
{
|
||||||
private GRContext? _context;
|
private GRContext? _context;
|
||||||
private readonly IMetalDevice _device;
|
private readonly IMetalDevice _device;
|
||||||
|
private readonly SkiaMetalExternalObjectsFeature? _externalObjects;
|
||||||
|
|
||||||
|
internal GRContext GrContext => _context ?? throw new ObjectDisposedException(nameof(SkiaMetalGpu));
|
||||||
|
|
||||||
public SkiaMetalGpu(IMetalDevice device, long? maxResourceBytes)
|
public SkiaMetalGpu(IMetalDevice device, long? maxResourceBytes)
|
||||||
{
|
{
|
||||||
@@ -19,6 +21,8 @@ internal class SkiaMetalGpu : ISkiaGpu, ISkiaGpuWithPlatformGraphicsContext
|
|||||||
new GRContextOptions { AvoidStencilBuffers = true })
|
new GRContextOptions { AvoidStencilBuffers = true })
|
||||||
?? throw new InvalidOperationException("Unable to create GRContext from Metal device.");
|
?? throw new InvalidOperationException("Unable to create GRContext from Metal device.");
|
||||||
_device = device;
|
_device = device;
|
||||||
|
if (device.TryGetFeature<IMetalExternalObjectsFeature>() is { } externalObjects)
|
||||||
|
_externalObjects = new SkiaMetalExternalObjectsFeature(this, externalObjects);
|
||||||
if (maxResourceBytes.HasValue)
|
if (maxResourceBytes.HasValue)
|
||||||
_context.SetResourceCacheLimit(maxResourceBytes.Value);
|
_context.SetResourceCacheLimit(maxResourceBytes.Value);
|
||||||
}
|
}
|
||||||
@@ -29,15 +33,21 @@ internal class SkiaMetalGpu : ISkiaGpu, ISkiaGpuWithPlatformGraphicsContext
|
|||||||
_context = null;
|
_context = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public object? TryGetFeature(Type featureType) => null;
|
public object? TryGetFeature(Type featureType)
|
||||||
|
{
|
||||||
|
if (featureType == typeof(IExternalObjectsHandleWrapRenderInterfaceContextFeature))
|
||||||
|
return _device.TryGetFeature(featureType);
|
||||||
|
if (featureType == typeof(IExternalObjectsRenderInterfaceContextFeature))
|
||||||
|
return _externalObjects;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public bool IsLost => false;
|
public bool IsLost => false;
|
||||||
public IDisposable EnsureCurrent() => _device.EnsureCurrent();
|
public IDisposable EnsureCurrent() => _device.EnsureCurrent();
|
||||||
public IPlatformGraphicsContext? PlatformGraphicsContext => _device;
|
public IPlatformGraphicsContext? PlatformGraphicsContext => _device;
|
||||||
|
|
||||||
public IScopedResource<GRContext> TryGetGrContext() =>
|
public IScopedResource<GRContext> TryGetGrContext() =>
|
||||||
ScopedResource<GRContext>.Create(_context ?? throw new ObjectDisposedException(nameof(SkiaMetalGpu)),
|
ScopedResource<GRContext>.Create(GrContext, EnsureCurrent().Dispose);
|
||||||
EnsureCurrent().Dispose);
|
|
||||||
|
|
||||||
public ISkiaGpuRenderTarget? TryCreateRenderTarget(IEnumerable<object> surfaces)
|
public ISkiaGpuRenderTarget? TryCreateRenderTarget(IEnumerable<object> surfaces)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Avalonia.Media.Imaging;
|
||||||
using Avalonia.OpenGL;
|
using Avalonia.OpenGL;
|
||||||
using Avalonia.Platform;
|
using Avalonia.Platform;
|
||||||
using Avalonia.Rendering.Composition;
|
using Avalonia.Rendering.Composition;
|
||||||
@@ -108,17 +109,17 @@ internal class GlSkiaImportedImage : IPlatformRenderInterfaceImportedImage
|
|||||||
_ => SKColorType.Rgba8888
|
_ => SKColorType.Rgba8888
|
||||||
};
|
};
|
||||||
|
|
||||||
SKSurface? TryCreateSurface(int textureId, int format, int width, int height, bool topLeft)
|
SKSurface? TryCreateSurface(int target, int textureId, int format, int width, int height, bool topLeft)
|
||||||
{
|
{
|
||||||
var origin = topLeft ? GRSurfaceOrigin.TopLeft : GRSurfaceOrigin.BottomLeft;
|
var origin = topLeft ? GRSurfaceOrigin.TopLeft : GRSurfaceOrigin.BottomLeft;
|
||||||
using var texture = new GRBackendTexture(width, height, false,
|
using var texture = new GRBackendTexture(width, height, false,
|
||||||
new GRGlTextureInfo(GlConsts.GL_TEXTURE_2D, (uint)textureId, (uint)format));
|
new GRGlTextureInfo((uint)target, (uint)textureId, (uint)format));
|
||||||
var surf = SKSurface.Create(_gpu.GrContext, texture, origin, SKColorType.Rgba8888);
|
var surf = SKSurface.Create(_gpu.GrContext, texture, origin, SKColorType.Rgba8888);
|
||||||
if (surf != null)
|
if (surf != null)
|
||||||
return surf;
|
return surf;
|
||||||
|
|
||||||
using var unformatted = new GRBackendTexture(width, height, false,
|
using var unformatted = new GRBackendTexture(width, height, false,
|
||||||
new GRGlTextureInfo(GlConsts.GL_TEXTURE_2D, (uint)textureId));
|
new GRGlTextureInfo((uint)GlConsts.GL_TEXTURE_2D, (uint)textureId));
|
||||||
|
|
||||||
return SKSurface.Create(_gpu.GrContext, unformatted, origin, SKColorType.Rgba8888);
|
return SKSurface.Create(_gpu.GrContext, unformatted, origin, SKColorType.Rgba8888);
|
||||||
}
|
}
|
||||||
@@ -130,16 +131,34 @@ internal class GlSkiaImportedImage : IPlatformRenderInterfaceImportedImage
|
|||||||
var internalFormat = _image?.InternalFormat ?? _sharedTexture!.InternalFormat;
|
var internalFormat = _image?.InternalFormat ?? _sharedTexture!.InternalFormat;
|
||||||
var textureId = _image?.TextureId ?? _sharedTexture!.TextureId;
|
var textureId = _image?.TextureId ?? _sharedTexture!.TextureId;
|
||||||
var topLeft = _image?.Properties.TopLeftOrigin ?? false;
|
var topLeft = _image?.Properties.TopLeftOrigin ?? false;
|
||||||
|
var textureType = _image?.TextureType ?? GlConsts.GL_TEXTURE_2D;
|
||||||
|
|
||||||
using var texture = new GRBackendTexture(width, height, false,
|
|
||||||
new GRGlTextureInfo(GlConsts.GL_TEXTURE_2D, (uint)textureId, (uint)internalFormat));
|
|
||||||
|
|
||||||
IBitmapImpl rv;
|
IBitmapImpl rv;
|
||||||
using (var surf = TryCreateSurface(textureId, internalFormat, width, height, topLeft))
|
using (var surf = TryCreateSurface(textureType, textureId, internalFormat, width, height, topLeft))
|
||||||
{
|
{
|
||||||
if (surf == null)
|
if (surf == null)
|
||||||
throw new OpenGlException("Unable to consume provided texture");
|
throw new OpenGlException("Unable to consume provided texture");
|
||||||
rv = new ImmutableBitmap(surf.Snapshot());
|
var snapshot = surf.Snapshot();
|
||||||
|
var context = _gpu.GlContext;
|
||||||
|
|
||||||
|
rv = new ImmutableBitmap(snapshot, () =>
|
||||||
|
{
|
||||||
|
IDisposable? restoreContext = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
restoreContext = context.EnsureCurrent();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Ignore, context is likely dead
|
||||||
|
}
|
||||||
|
|
||||||
|
using (restoreContext)
|
||||||
|
{
|
||||||
|
snapshot.Dispose();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_gpu.GrContext.Flush();
|
_gpu.GrContext.Flush();
|
||||||
@@ -192,6 +211,30 @@ internal class GlSkiaImportedImage : IPlatformRenderInterfaceImportedImage
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IBitmapImpl SnapshotWithTimelineSemaphores(IPlatformRenderInterfaceImportedSemaphore waitForSemaphore,
|
||||||
|
ulong waitForValue, IPlatformRenderInterfaceImportedSemaphore signalSemaphore, ulong signalValue)
|
||||||
|
{
|
||||||
|
if (_image is null)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException("Only supported with an external image");
|
||||||
|
}
|
||||||
|
|
||||||
|
var wait = (GlSkiaImportedSemaphore)waitForSemaphore;
|
||||||
|
var signal = (GlSkiaImportedSemaphore)signalSemaphore;
|
||||||
|
using (_gpu.EnsureCurrent())
|
||||||
|
{
|
||||||
|
wait.Semaphore.WaitTimelineSemaphore(_image, waitForValue);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return TakeSnapshot();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
signal.Semaphore.SignalTimelineSemaphore(_image, signalValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public IBitmapImpl SnapshotWithAutomaticSync()
|
public IBitmapImpl SnapshotWithAutomaticSync()
|
||||||
{
|
{
|
||||||
using (_gpu.EnsureCurrent())
|
using (_gpu.EnsureCurrent())
|
||||||
|
|||||||
@@ -173,6 +173,8 @@ namespace Avalonia.Skia
|
|||||||
return this;
|
return this;
|
||||||
if (featureType == typeof(IExternalObjectsRenderInterfaceContextFeature))
|
if (featureType == typeof(IExternalObjectsRenderInterfaceContextFeature))
|
||||||
return _externalObjectsFeature;
|
return _externalObjectsFeature;
|
||||||
|
if (featureType == typeof(IExternalObjectsHandleWrapRenderInterfaceContextFeature))
|
||||||
|
return _glContext.TryGetFeature(featureType);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -108,6 +108,10 @@ internal class VulkanSkiaExternalObjectsFeature : IExternalObjectsRenderInterfac
|
|||||||
return new ImmutableBitmap(image);
|
return new ImmutableBitmap(image);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IBitmapImpl SnapshotWithTimelineSemaphores(IPlatformRenderInterfaceImportedSemaphore waitForSemaphore,
|
||||||
|
ulong waitForValue, IPlatformRenderInterfaceImportedSemaphore signalSemaphore, ulong signalValue) =>
|
||||||
|
throw new NotSupportedException();
|
||||||
|
|
||||||
public IBitmapImpl SnapshotWithAutomaticSync() => throw new NotSupportedException();
|
public IBitmapImpl SnapshotWithAutomaticSync() => throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ namespace Avalonia.Skia
|
|||||||
{
|
{
|
||||||
private readonly SKImage _image;
|
private readonly SKImage _image;
|
||||||
private readonly SKBitmap? _bitmap;
|
private readonly SKBitmap? _bitmap;
|
||||||
|
private readonly Action? _customImageDispose = null;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create immutable bitmap from given stream.
|
/// Create immutable bitmap from given stream.
|
||||||
@@ -39,9 +40,10 @@ namespace Avalonia.Skia
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ImmutableBitmap(SKImage image)
|
public ImmutableBitmap(SKImage image, Action? customImageDispose = null)
|
||||||
{
|
{
|
||||||
_image = image;
|
_image = image;
|
||||||
|
_customImageDispose = customImageDispose;
|
||||||
PixelSize = new PixelSize(image.Width, image.Height);
|
PixelSize = new PixelSize(image.Width, image.Height);
|
||||||
Dpi = new Vector(96, 96);
|
Dpi = new Vector(96, 96);
|
||||||
}
|
}
|
||||||
@@ -156,7 +158,10 @@ namespace Avalonia.Skia
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_image.Dispose();
|
if (_customImageDispose != null)
|
||||||
|
_customImageDispose();
|
||||||
|
else
|
||||||
|
_image.Dispose();
|
||||||
_bitmap?.Dispose();
|
_bitmap?.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -68,6 +68,8 @@ internal class AngleExternalMemoryD3D11Texture2D : IGlExternalImageTexture
|
|||||||
|
|
||||||
public int TextureId { get; private set; }
|
public int TextureId { get; private set; }
|
||||||
public int InternalFormat { get; }
|
public int InternalFormat { get; }
|
||||||
|
|
||||||
|
public int TextureType => GL_TEXTURE_2D;
|
||||||
public PlatformGraphicsExternalImageProperties Properties { get; }
|
public PlatformGraphicsExternalImageProperties Properties { get; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user