[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>
|
||||
<Right>current/Avalonia/lib/net6.0/Avalonia.Base.dll</Right>
|
||||
</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>
|
||||
<DiagnosticId>CP0006</DiagnosticId>
|
||||
<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>
|
||||
<Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
|
||||
</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>
|
||||
<DiagnosticId>CP0006</DiagnosticId>
|
||||
<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>
|
||||
<Right>current/Avalonia/lib/netstandard2.0/Avalonia.Base.dll</Right>
|
||||
</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>
|
||||
|
||||
@@ -25,12 +25,13 @@ jobs:
|
||||
arch="arm64"
|
||||
fi
|
||||
git clean -ffdx
|
||||
sudo xcode-select -s /Applications/Xcode.app/Contents/Developer
|
||||
pkill node
|
||||
pkill testmanagerd
|
||||
appium > appium.out &
|
||||
pkill IntegrationTestApp
|
||||
sudo xcode-select -s /Applications/Xcode_15.2.app/Contents/Developer
|
||||
./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\")")
|
||||
pkill IntegrationTestApp
|
||||
./samples/IntegrationTestApp/bundle.sh
|
||||
|
||||
+2
-2
@@ -95,11 +95,11 @@ jobs:
|
||||
inputs:
|
||||
actions: 'build'
|
||||
scheme: ''
|
||||
sdk: 'macosx13.0'
|
||||
sdk: 'macosx14.2'
|
||||
configuration: 'Release'
|
||||
xcWorkspacePath: '**/*.xcodeproj/project.xcworkspace'
|
||||
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 ./'
|
||||
|
||||
- task: CmdLine@2
|
||||
|
||||
@@ -9,3 +9,6 @@ public:
|
||||
};
|
||||
|
||||
#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 */; };
|
||||
1A3E5EB023E9FE8300EDE661 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1A3E5EAF23E9FE8300EDE661 /* QuartzCore.framework */; };
|
||||
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 */; };
|
||||
37155CE4233C00EB0034DCE9 /* menu.h in Headers */ = {isa = PBXBuildFile; fileRef = 37155CE3233C00EB0034DCE9 /* menu.h */; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
@@ -171,6 +176,9 @@
|
||||
AB7A61E62147C814003C5833 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
1AC7F1442DCA0D6A003A161B /* crapium.h */,
|
||||
1AC7F1422DCA0C2E003A161B /* crapium.mm */,
|
||||
1AE55B8B2DC1060E00FD0BB3 /* memhelp.mm */,
|
||||
F10084852BFF1FB40024303E /* TopLevelImpl.mm */,
|
||||
BC7C33832C066F1100945A48 /* AvnAccessibility.h */,
|
||||
BC7C33812C066DBF00945A48 /* AvnAutomationNode.h */,
|
||||
@@ -333,6 +341,7 @@
|
||||
523484CA26EA688F00EA0C2C /* trayicon.mm in Sources */,
|
||||
AB8F7D6B21482D7F0057DBA5 /* platformthreading.mm in Sources */,
|
||||
1A3E5EA823E9E83B00EDE661 /* rendertarget.mm in Sources */,
|
||||
1AE55B8C2DC1060E00FD0BB3 /* memhelp.mm in Sources */,
|
||||
1A3E5EAE23E9FB1300EDE661 /* cgl.mm in Sources */,
|
||||
BC11A5BF2608D58F0017BAD0 /* automation.mm in Sources */,
|
||||
37E2330F21583241000CB7E2 /* KeyTransform.mm in Sources */,
|
||||
@@ -353,6 +362,7 @@
|
||||
ED754D262A97306B0078B4DF /* PlatformRenderTimer.mm in Sources */,
|
||||
1839151F32D1BB1AB51A7BB6 /* AvnPanelWindow.mm in Sources */,
|
||||
18391AC16726CBC45856233B /* AvnWindow.mm in Sources */,
|
||||
1AC7F1432DCA0C2E003A161B /* crapium.mm in Sources */,
|
||||
18391D8CD1756DC858DC1A09 /* PopupImpl.mm in Sources */,
|
||||
EDF8CDCD2964CB01001EE34F /* PlatformSettings.mm in Sources */,
|
||||
64B1EA48E308E574685AFB07 /* metal.mm in Sources */,
|
||||
|
||||
@@ -107,6 +107,61 @@ public:
|
||||
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()
|
||||
{
|
||||
CGLReleaseContext(Context);
|
||||
|
||||
@@ -33,6 +33,7 @@ extern IAvnPlatformBehaviorInhibition* CreatePlatformBehaviorInhibition();
|
||||
extern IAvnNativeControlHost* CreateNativeControlHost(NSView* parent);
|
||||
extern IAvnPlatformSettings* CreatePlatformSettings();
|
||||
extern IAvnPlatformRenderTimer* CreatePlatformRenderTimer();
|
||||
extern IAvnNativeObjectsMemoryManagement* CreateMemoryManagementHelper();
|
||||
extern void SetAppMenu(IAvnMenu *menu);
|
||||
extern void SetServicesMenu (IAvnMenu* menu);
|
||||
extern IAvnMenu* GetAppMenu ();
|
||||
@@ -47,6 +48,7 @@ extern AvnPoint ToAvnPoint (NSPoint p);
|
||||
extern AvnPoint ConvertPointY (AvnPoint p);
|
||||
extern NSSize ToNSSize (AvnSize s);
|
||||
extern AvnSize FromNSSize (NSSize s);
|
||||
extern IAvnMTLSharedEvent* ImportMTLSharedEvent(void* object);
|
||||
#ifdef DEBUG
|
||||
#define NSDebugLog(...) NSLog(__VA_ARGS__)
|
||||
#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;
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
|
||||
@@ -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>
|
||||
#include "common.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>
|
||||
{
|
||||
@@ -19,6 +87,85 @@ public:
|
||||
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) {
|
||||
}
|
||||
|
||||
@@ -160,3 +307,23 @@ extern IAvnMetalDisplay* GetMetalDisplay()
|
||||
{
|
||||
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 "avalonia-native.h"
|
||||
CppAutoreleasePool::CppAutoreleasePool()
|
||||
{
|
||||
_pool = [[NSAutoreleasePool alloc] init];
|
||||
@@ -9,3 +9,17 @@ CppAutoreleasePool::~CppAutoreleasePool() {
|
||||
auto ptr = (NSAutoreleasePool*)_pool;
|
||||
[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<Semaphore> signalSemaphores = default,
|
||||
Fence? fence = null,
|
||||
KeyedMutexSubmitInfo? keyedMutex = null)
|
||||
KeyedMutexSubmitInfo? keyedMutex = null,
|
||||
IntPtr pNext = default)
|
||||
{
|
||||
EndRecording();
|
||||
|
||||
@@ -189,7 +190,8 @@ namespace Avalonia.Vulkan
|
||||
PReleaseKeys = &releaseKey,
|
||||
PAcquireSyncs = &devMem,
|
||||
PReleaseSyncs = &devMem,
|
||||
PAcquireTimeouts = &timeout
|
||||
PAcquireTimeouts = &timeout,
|
||||
PNext = (void*)pNext
|
||||
};
|
||||
|
||||
fixed (Semaphore* pWaitSemaphores = waitSemaphores, pSignalSemaphores = signalSemaphores)
|
||||
@@ -199,7 +201,7 @@ namespace Avalonia.Vulkan
|
||||
var commandBuffer = InternalHandle;
|
||||
var submitInfo = new SubmitInfo
|
||||
{
|
||||
PNext = keyedMutex != null ? &mutex : null,
|
||||
PNext = keyedMutex != null ? &mutex : (void*)pNext,
|
||||
SType = StructureType.SubmitInfo,
|
||||
WaitSemaphoreCount = waitSemaphores != null ? (uint)waitSemaphores.Length : 0,
|
||||
PWaitSemaphores = pWaitSemaphores,
|
||||
|
||||
@@ -1,17 +1,21 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using Avalonia.Platform;
|
||||
using Avalonia.Rendering.Composition;
|
||||
using Avalonia.Vulkan;
|
||||
using Silk.NET.Core;
|
||||
using Silk.NET.Core.Contexts;
|
||||
using Silk.NET.Core.Loader;
|
||||
using Silk.NET.Vulkan;
|
||||
using Silk.NET.Vulkan.Extensions.EXT;
|
||||
using Silk.NET.Vulkan.Extensions.KHR;
|
||||
using SilkNetDemo;
|
||||
using SkiaSharp;
|
||||
using D3DDevice = SharpDX.Direct3D11.Device;
|
||||
#pragma warning disable CS0162 // Unreachable code detected
|
||||
|
||||
namespace GpuInterop.VulkanDemo;
|
||||
|
||||
@@ -24,7 +28,7 @@ public unsafe class VulkanContext : IDisposable
|
||||
public required Queue Queue { get; init; }
|
||||
public required uint QueueFamilyIndex { 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 D3DDevice? D3DDevice { get; init; }
|
||||
|
||||
@@ -44,14 +48,22 @@ public unsafe class VulkanContext : IDisposable
|
||||
|
||||
var enabledExtensions = new List<string>()
|
||||
{
|
||||
"VK_KHR_get_physical_device_properties2",
|
||||
"VK_KHR_external_memory_capabilities",
|
||||
"VK_KHR_external_semaphore_capabilities"
|
||||
"VK_KHR_get_physical_device_properties2"
|
||||
};
|
||||
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>();
|
||||
|
||||
Vk api = Vk.GetApi();
|
||||
Vk api = GetApi();
|
||||
enabledExtensions.Add("VK_EXT_debug_utils");
|
||||
if (IsLayerAvailable(api, "VK_LAYER_KHRONOS_validation"))
|
||||
enabledLayers.Add("VK_LAYER_KHRONOS_validation");
|
||||
@@ -61,8 +73,10 @@ public unsafe class VulkanContext : IDisposable
|
||||
DescriptorPool descriptorPool = default;
|
||||
VulkanCommandBufferPool? pool = null;
|
||||
GRContext? grContext = null;
|
||||
bool success = false;
|
||||
try
|
||||
{
|
||||
enabledLayers.Clear();
|
||||
using var pRequiredExtensions = new ByteStringList(enabledExtensions);
|
||||
using var pEnabledLayers = new ByteStringList(enabledLayers);
|
||||
api.CreateInstance(new InstanceCreateInfo
|
||||
@@ -72,7 +86,8 @@ public unsafe class VulkanContext : IDisposable
|
||||
PpEnabledExtensionNames = pRequiredExtensions,
|
||||
EnabledExtensionCount = pRequiredExtensions.UCount,
|
||||
PpEnabledLayerNames = pEnabledLayers,
|
||||
EnabledLayerCount = pEnabledLayers.UCount
|
||||
EnabledLayerCount = pEnabledLayers.UCount,
|
||||
Flags = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? InstanceCreateFlags.EnumeratePortabilityBitKhr : default
|
||||
}, null, out var vkInstance).ThrowOnError();
|
||||
|
||||
|
||||
@@ -93,10 +108,13 @@ public unsafe class VulkanContext : IDisposable
|
||||
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",
|
||||
"VK_KHR_external_semaphore"
|
||||
requireDeviceExtensions.AddRange([
|
||||
"VK_KHR_external_memory",
|
||||
"VK_KHR_external_semaphore"
|
||||
]);
|
||||
};
|
||||
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
@@ -112,6 +130,14 @@ public unsafe class VulkanContext : IDisposable
|
||||
requireDeviceExtensions.Add("VK_KHR_dedicated_allocation");
|
||||
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
|
||||
{
|
||||
if (!gpuInterop.SupportedImageHandleTypes.Contains(KnownPlatformGraphicsExternalImageHandleTypes
|
||||
@@ -223,26 +249,29 @@ public unsafe class VulkanContext : IDisposable
|
||||
.ThrowOnError();
|
||||
|
||||
pool = new VulkanCommandBufferPool(api, device, queue, queueFamilyIndex);
|
||||
grContext = GRContext.CreateVulkan(new GRVkBackendContext
|
||||
if (!RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||
{
|
||||
VkInstance = vkInstance.Handle,
|
||||
VkDevice = device.Handle,
|
||||
VkQueue = queue.Handle,
|
||||
GraphicsQueueIndex = queueFamilyIndex,
|
||||
VkPhysicalDevice = physicalDevice.Handle,
|
||||
GetProcedureAddress = (proc, _, _) =>
|
||||
grContext = GRContext.CreateVulkan(new GRVkBackendContext
|
||||
{
|
||||
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);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
VkInstance = vkInstance.Handle,
|
||||
VkDevice = device.Handle,
|
||||
VkQueue = queue.Handle,
|
||||
GraphicsQueueIndex = queueFamilyIndex,
|
||||
VkPhysicalDevice = physicalDevice.Handle,
|
||||
GetProcedureAddress = (proc, _, _) =>
|
||||
{
|
||||
var rv = api.GetDeviceProcAddr(device, proc);
|
||||
if (rv != IntPtr.Zero)
|
||||
return rv;
|
||||
rv = api.GetInstanceProcAddr(vkInstance, proc);
|
||||
if (rv != IntPtr.Zero)
|
||||
return rv;
|
||||
return api.GetInstanceProcAddr(default, proc);
|
||||
}
|
||||
});
|
||||
if (grContext == null)
|
||||
return (null, "Can't create Skia GrContext, device is likely broken");
|
||||
}
|
||||
|
||||
D3DDevice? d3dDevice = null;
|
||||
if (physicalDeviceIDProperties.DeviceLuidvalid &&
|
||||
@@ -251,7 +280,7 @@ public unsafe class VulkanContext : IDisposable
|
||||
)
|
||||
d3dDevice = D3DMemoryHelper.CreateDeviceByLuid(
|
||||
new Span<byte>(physicalDeviceIDProperties.DeviceLuid, 8));
|
||||
|
||||
success = true;
|
||||
return (new VulkanContext
|
||||
{
|
||||
Api = api,
|
||||
@@ -278,7 +307,7 @@ public unsafe class VulkanContext : IDisposable
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (grContext == null && api != null)
|
||||
if (!success)
|
||||
{
|
||||
pool?.Dispose();
|
||||
if (descriptorPool.Handle != default)
|
||||
@@ -323,10 +352,39 @@ public unsafe class VulkanContext : IDisposable
|
||||
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()
|
||||
{
|
||||
D3DDevice?.Dispose();
|
||||
GrContext.Dispose();
|
||||
GrContext?.Dispose();
|
||||
Pool.Dispose();
|
||||
Api.DestroyDescriptorPool(Device, DescriptorPool, null);
|
||||
Api.DestroyDevice(Device, null);
|
||||
|
||||
@@ -8,6 +8,7 @@ using Avalonia.Platform;
|
||||
using Avalonia.Vulkan;
|
||||
using SharpDX.DXGI;
|
||||
using Silk.NET.Vulkan;
|
||||
using Silk.NET.Vulkan.Extensions.EXT;
|
||||
using Silk.NET.Vulkan.Extensions.KHR;
|
||||
using SilkNetDemo;
|
||||
using SkiaSharp;
|
||||
@@ -45,6 +46,8 @@ public unsafe class VulkanImage : IDisposable
|
||||
public ulong MemorySize { get; }
|
||||
public uint CurrentLayout => (uint) _currentLayout;
|
||||
|
||||
private bool _hasIOSurface;
|
||||
|
||||
public VulkanImage(VulkanContext vk, uint format, PixelSize size,
|
||||
bool exportable, IReadOnlyList<string> supportedHandleTypes)
|
||||
{
|
||||
@@ -76,9 +79,21 @@ public unsafe class VulkanImage : IDisposable
|
||||
HandleTypes = handleType
|
||||
};
|
||||
|
||||
|
||||
var ioSurfaceCreateInfo = new ExportMetalObjectCreateInfoEXT
|
||||
{
|
||||
SType = StructureType.ExportMetalObjectCreateInfoExt,
|
||||
ExportObjectType = ExportMetalObjectTypeFlagsEXT.IosurfaceBitExt
|
||||
};
|
||||
|
||||
_hasIOSurface = exportable && RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
|
||||
|
||||
var imageCreateInfo = new ImageCreateInfo
|
||||
{
|
||||
PNext = exportable ? &externalMemoryCreateInfo : null,
|
||||
PNext = exportable ?
|
||||
RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ?
|
||||
&ioSurfaceCreateInfo :
|
||||
&externalMemoryCreateInfo : null,
|
||||
SType = StructureType.ImageCreateInfo,
|
||||
ImageType = ImageType.Type2D,
|
||||
Format = Format,
|
||||
@@ -99,58 +114,63 @@ public unsafe class VulkanImage : IDisposable
|
||||
.CreateImage(_device, imageCreateInfo, null, out var image).ThrowOnError();
|
||||
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
|
||||
};
|
||||
Api.GetImageMemoryRequirements(_device, InternalHandle,
|
||||
out var memoryRequirements);
|
||||
|
||||
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
|
||||
var dedicatedAllocation = new MemoryDedicatedAllocateInfoKHR
|
||||
{
|
||||
PNext = &dedicatedAllocation,
|
||||
SType = StructureType.ImportMemoryWin32HandleInfoKhr,
|
||||
HandleType = ExternalMemoryHandleTypeFlags.D3D11TextureBit,
|
||||
Handle = dxgi.CreateSharedHandle(null, SharedResourceFlags.Read | SharedResourceFlags.Write),
|
||||
SType = StructureType.MemoryDedicatedAllocateInfoKhr, Image = image
|
||||
};
|
||||
|
||||
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(
|
||||
ComponentSwizzle.Identity,
|
||||
ComponentSwizzle.Identity,
|
||||
@@ -210,6 +230,26 @@ public unsafe class VulkanImage : IDisposable
|
||||
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()
|
||||
{
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
@@ -225,6 +265,9 @@ public unsafe class VulkanImage : IDisposable
|
||||
return new PlatformHandle(ExportOpaqueNtHandle(),
|
||||
KnownPlatformGraphicsExternalImageHandleTypes.VulkanOpaqueNtHandle);
|
||||
}
|
||||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||
return new PlatformHandle(ExportIOSurface(),
|
||||
KnownPlatformGraphicsExternalImageHandleTypes.IOSurfaceRef);
|
||||
else
|
||||
return new PlatformHandle(new IntPtr(ExportFd()),
|
||||
KnownPlatformGraphicsExternalImageHandleTypes.VulkanOpaquePosixFileDescriptor);
|
||||
@@ -281,6 +324,30 @@ public unsafe class VulkanImage : IDisposable
|
||||
|
||||
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();
|
||||
var _image = this;
|
||||
var imageInfo = new GRVkImageInfo()
|
||||
|
||||
@@ -39,8 +39,10 @@ class VulkanSwapchainImage : ISwapchainImage
|
||||
private readonly ICompositionGpuInterop _interop;
|
||||
private readonly CompositionDrawingSurface _target;
|
||||
private readonly VulkanImage _image;
|
||||
private readonly VulkanSemaphorePair _semaphorePair;
|
||||
private ICompositionImportedGpuSemaphore? _availableSemaphore, _renderCompletedSemaphore;
|
||||
private readonly VulkanSemaphorePair? _semaphorePair;
|
||||
private readonly VulkanTimelineSemaphore? _timelineSemaphore;
|
||||
private ulong _timelineCounter;
|
||||
private ICompositionImportedGpuSemaphore? _availableSemaphore, _renderCompletedSemaphore, _importedTimelineSemaphore;
|
||||
private ICompositionImportedGpuImage? _importedImage;
|
||||
private Task? _lastPresent;
|
||||
public VulkanImage Image => _image;
|
||||
@@ -52,8 +54,12 @@ class VulkanSwapchainImage : ISwapchainImage
|
||||
_interop = interop;
|
||||
_target = target;
|
||||
Size = size;
|
||||
_image = new VulkanImage(vk, (uint)Format.R8G8B8A8Unorm, size, true, interop.SupportedImageHandleTypes);
|
||||
_semaphorePair = new VulkanSemaphorePair(vk, true);
|
||||
var format = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? Format.B8G8R8A8Unorm : Format.R8G8B8A8Unorm;
|
||||
_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()
|
||||
@@ -62,11 +68,13 @@ class VulkanSwapchainImage : ISwapchainImage
|
||||
await LastPresent;
|
||||
if (_importedImage != null)
|
||||
await _importedImage.DisposeAsync();
|
||||
|
||||
if (_availableSemaphore != null)
|
||||
await _availableSemaphore.DisposeAsync();
|
||||
if (_renderCompletedSemaphore != null)
|
||||
await _renderCompletedSemaphore.DisposeAsync();
|
||||
_semaphorePair.Dispose();
|
||||
_semaphorePair?.Dispose();
|
||||
_timelineSemaphore?.Dispose();
|
||||
_image.Dispose();
|
||||
}
|
||||
|
||||
@@ -89,6 +97,22 @@ class VulkanSwapchainImage : ISwapchainImage
|
||||
AcquireKey = 0,
|
||||
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)
|
||||
{
|
||||
_initial = false;
|
||||
@@ -119,10 +143,30 @@ class VulkanSwapchainImage : ISwapchainImage
|
||||
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
|
||||
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));
|
||||
|
||||
@@ -132,13 +176,18 @@ class VulkanSwapchainImage : ISwapchainImage
|
||||
_importedImage ??= _interop.ImportImage(_image.Export(),
|
||||
new PlatformGraphicsExternalImageProperties
|
||||
{
|
||||
Format = PlatformGraphicsExternalImageFormat.R8G8B8A8UNorm,
|
||||
Format = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? PlatformGraphicsExternalImageFormat.B8G8R8A8UNorm : PlatformGraphicsExternalImageFormat.R8G8B8A8UNorm,
|
||||
Width = Size.Width,
|
||||
Height = Size.Height,
|
||||
MemorySize = _image.MemorySize
|
||||
});
|
||||
|
||||
if (_image.IsDirectXBacked)
|
||||
if (_importedTimelineSemaphore != null)
|
||||
{
|
||||
_lastPresent = _target.UpdateWithTimelineSemaphoresAsync(_importedImage,
|
||||
_importedTimelineSemaphore, _timelineCounter + 1, _importedTimelineSemaphore, _timelineCounter + 2);
|
||||
_timelineCounter += 2;
|
||||
}
|
||||
else if (_image.IsDirectXBacked)
|
||||
_lastPresent = _target.UpdateWithKeyedMutexAsync(_importedImage, 1, 0);
|
||||
else
|
||||
_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);
|
||||
|
||||
IPlatformRenderInterfaceImportedSemaphore ImportSemaphore(IPlatformHandle handle);
|
||||
|
||||
CompositionGpuImportedImageSynchronizationCapabilities GetSynchronizationCapabilities(string imageHandleType);
|
||||
public byte[]? DeviceUuid { 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]
|
||||
public interface IPlatformRenderInterfaceImportedObject : IDisposable
|
||||
{
|
||||
@@ -43,6 +73,10 @@ public interface IPlatformRenderInterfaceImportedImage : IPlatformRenderInterfac
|
||||
IBitmapImpl SnapshotWithSemaphores(IPlatformRenderInterfaceImportedSemaphore waitForSemaphore,
|
||||
IPlatformRenderInterfaceImportedSemaphore signalSemaphore);
|
||||
|
||||
IBitmapImpl SnapshotWithTimelineSemaphores(
|
||||
IPlatformRenderInterfaceImportedSemaphore waitForSemaphore, ulong waitForValue,
|
||||
IPlatformRenderInterfaceImportedSemaphore signalSemaphore, ulong signalValue);
|
||||
|
||||
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
|
||||
public const string VulkanOpaqueKmtHandle = nameof(VulkanOpaqueKmtHandle);
|
||||
|
||||
/// <summary>
|
||||
/// A reference to IOSurface
|
||||
/// </summary>
|
||||
public const string IOSurfaceRef = nameof(IOSurfaceRef);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -65,4 +70,9 @@ public static class KnownPlatformGraphicsExternalSemaphoreHandleTypes
|
||||
|
||||
/// A DXGI NT handle returned by ID3D12Device::CreateSharedHandle or ID3D11Fence::CreateSharedHandle
|
||||
public const string Direct3D12FenceNtHandle = nameof(Direct3D12FenceNtHandle);
|
||||
|
||||
/// <summary>
|
||||
/// A pointer to MTLSharedEvent object
|
||||
/// </summary>
|
||||
public const string MetalSharedEvent = nameof(MetalSharedEvent);
|
||||
}
|
||||
|
||||
@@ -42,6 +42,23 @@ public sealed class CompositionDrawingSurface : CompositionSurface, IDisposable
|
||||
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>
|
||||
/// Updates the surface contents using an unspecified automatic means of synchronization
|
||||
/// provided by the underlying platform
|
||||
|
||||
@@ -81,7 +81,11 @@ public enum CompositionGpuImportedImageSynchronizationCapabilities
|
||||
/// <summary>
|
||||
/// Synchronization and ordering is somehow handled by the underlying platform
|
||||
/// </summary>
|
||||
Automatic = 4
|
||||
Automatic = 4,
|
||||
/// <summary>
|
||||
/// Pre-render and after-render timeline semaphores must be provided alongside with the image
|
||||
/// </summary>
|
||||
TimelineSemaphores = 8
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -10,6 +10,7 @@ internal class CompositionInterop : ICompositionGpuInterop
|
||||
private readonly Compositor _compositor;
|
||||
private readonly IPlatformRenderInterfaceContext _context;
|
||||
private readonly IExternalObjectsRenderInterfaceContextFeature _externalObjects;
|
||||
private readonly IExternalObjectsHandleWrapRenderInterfaceContextFeature? _externalObjectsWithHandleWrap;
|
||||
|
||||
|
||||
public CompositionInterop(
|
||||
@@ -21,6 +22,7 @@ internal class CompositionInterop : ICompositionGpuInterop
|
||||
DeviceLuid = externalObjects.DeviceLuid;
|
||||
DeviceUuid = externalObjects.DeviceUuid;
|
||||
_externalObjects = externalObjects;
|
||||
_externalObjectsWithHandleWrap = _context.TryGetFeature<IExternalObjectsHandleWrapRenderInterfaceContextFeature>();
|
||||
}
|
||||
|
||||
public IReadOnlyList<string> SupportedImageHandleTypes => _externalObjects.SupportedImageHandleTypes;
|
||||
@@ -31,17 +33,23 @@ internal class CompositionInterop : ICompositionGpuInterop
|
||||
|
||||
public ICompositionImportedGpuImage ImportImage(IPlatformHandle handle,
|
||||
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)
|
||||
{
|
||||
return new CompositionImportedGpuImage(_compositor, _context, _externalObjects,
|
||||
() => _externalObjects.ImportImage(image));
|
||||
() => _externalObjects.ImportImage(image), null);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
@@ -61,13 +69,17 @@ abstract class CompositionGpuImportedObjectBase : ICompositionGpuImportedObject
|
||||
|
||||
public CompositionGpuImportedObjectBase(Compositor compositor,
|
||||
IPlatformRenderInterfaceContext context,
|
||||
IExternalObjectsRenderInterfaceContextFeature feature)
|
||||
IExternalObjectsRenderInterfaceContextFeature feature, IPlatformHandle? handle)
|
||||
{
|
||||
Compositor = compositor;
|
||||
Context = context;
|
||||
Feature = feature;
|
||||
|
||||
ImportCompleted = Compositor.InvokeServerJobAsync(Import);
|
||||
ImportCompleted = Compositor.InvokeServerJobAsync(() =>
|
||||
{
|
||||
using var _ = handle as IExternalObjectsWrappedGpuHandle;
|
||||
Import();
|
||||
});
|
||||
}
|
||||
|
||||
protected abstract void Import();
|
||||
@@ -93,7 +105,7 @@ class CompositionImportedGpuImage : CompositionGpuImportedObjectBase, ICompositi
|
||||
public CompositionImportedGpuImage(Compositor compositor,
|
||||
IPlatformRenderInterfaceContext context,
|
||||
IExternalObjectsRenderInterfaceContextFeature feature,
|
||||
Func<IPlatformRenderInterfaceImportedImage> importer): base(compositor, context, feature)
|
||||
Func<IPlatformRenderInterfaceImportedImage> importer, IPlatformHandle? handle): base(compositor, context, feature, handle)
|
||||
{
|
||||
_importer = importer;
|
||||
}
|
||||
@@ -128,7 +140,7 @@ class CompositionImportedGpuSemaphore : CompositionGpuImportedObjectBase, ICompo
|
||||
|
||||
public CompositionImportedGpuSemaphore(IPlatformHandle handle,
|
||||
Compositor compositor, IPlatformRenderInterfaceContext context,
|
||||
IExternalObjectsRenderInterfaceContextFeature feature) : base(compositor, context, feature)
|
||||
IExternalObjectsRenderInterfaceContextFeature feature) : base(compositor, context, feature, handle)
|
||||
{
|
||||
_handle = handle;
|
||||
}
|
||||
|
||||
@@ -76,6 +76,19 @@ internal class ServerCompositionDrawingSurface : ServerCompositionSurface, IDisp
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
_bitmap?.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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Avalonia.OpenGL;
|
||||
using Avalonia.Native.Interop;
|
||||
using System.Drawing;
|
||||
using Avalonia.OpenGL.Surfaces;
|
||||
using Avalonia.Platform;
|
||||
using Avalonia.Rendering.Composition;
|
||||
using Avalonia.Threading;
|
||||
|
||||
namespace Avalonia.Native
|
||||
@@ -13,7 +14,7 @@ namespace Avalonia.Native
|
||||
{
|
||||
private readonly IAvnGlDisplay _display;
|
||||
|
||||
public AvaloniaNativeGlPlatformGraphics(IAvnGlDisplay display)
|
||||
public AvaloniaNativeGlPlatformGraphics(IAvnGlDisplay display, IAvaloniaNativeFactory factory)
|
||||
{
|
||||
_display = display;
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -59,12 +60,15 @@ namespace Avalonia.Native
|
||||
{
|
||||
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;
|
||||
SampleCount = sampleCount;
|
||||
StencilSize = stencilSize;
|
||||
Factory = factory;
|
||||
GlInterface = glInterface;
|
||||
MemoryHelper = factory.CreateMemoryManagementHelper();
|
||||
}
|
||||
|
||||
public GlInterface GlInterface { get; }
|
||||
@@ -72,6 +76,9 @@ namespace Avalonia.Native
|
||||
public int SampleCount { get; }
|
||||
|
||||
public int StencilSize { get; }
|
||||
public IAvaloniaNativeFactory Factory { get; }
|
||||
|
||||
public IAvnNativeObjectsMemoryManagement MemoryHelper { get; }
|
||||
|
||||
public void ClearContext() => _display.LegacyClearCurrentContext();
|
||||
|
||||
@@ -84,6 +91,8 @@ namespace Avalonia.Native
|
||||
{
|
||||
private readonly GlDisplay _display;
|
||||
private readonly GlContext _sharedWith;
|
||||
private readonly GpuHandleWrapFeature _handleWrapFeature;
|
||||
private readonly GlExternalObjectsFeature _externalObjects;
|
||||
public IAvnGlContext Context { get; private set; }
|
||||
|
||||
public GlContext(GlDisplay display, GlContext sharedWith, IAvnGlContext context, GlVersion version)
|
||||
@@ -92,6 +101,8 @@ namespace Avalonia.Native
|
||||
_sharedWith = sharedWith;
|
||||
Context = context;
|
||||
Version = version;
|
||||
_handleWrapFeature = new GpuHandleWrapFeature(display.Factory);
|
||||
_externalObjects = new GlExternalObjectsFeature(this, display);
|
||||
}
|
||||
|
||||
public GlVersion Version { get; }
|
||||
@@ -128,7 +139,14 @@ namespace Avalonia.Native
|
||||
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;
|
||||
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
|
||||
{
|
||||
_platformGraphics = new AvaloniaNativeGlPlatformGraphics(_factory.ObtainGlDisplay());
|
||||
_platformGraphics = new AvaloniaNativeGlPlatformGraphics(_factory.ObtainGlDisplay(), _factory);
|
||||
break;
|
||||
}
|
||||
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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Avalonia.Metal;
|
||||
using Avalonia.Native.Interop;
|
||||
using Avalonia.Platform;
|
||||
using Avalonia.Rendering.Composition;
|
||||
using Avalonia.Threading;
|
||||
using Avalonia.Utilities;
|
||||
|
||||
@@ -9,14 +12,16 @@ namespace Avalonia.Native;
|
||||
|
||||
class MetalPlatformGraphics : IPlatformGraphics
|
||||
{
|
||||
private readonly IAvaloniaNativeFactory _factory;
|
||||
private readonly IAvnMetalDisplay _display;
|
||||
|
||||
public MetalPlatformGraphics(IAvaloniaNativeFactory factory)
|
||||
{
|
||||
_factory = factory;
|
||||
_display = factory.ObtainMetalDisplay();
|
||||
}
|
||||
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();
|
||||
}
|
||||
@@ -25,11 +30,15 @@ class MetalDevice : IMetalDevice
|
||||
{
|
||||
public IAvnMetalDevice Native { get; private set; }
|
||||
private DisposableLock _syncRoot = new();
|
||||
private readonly GpuHandleWrapFeature _handleWrapFeature;
|
||||
private readonly MetalExternalObjectsFeature _externalObjectsFeature;
|
||||
|
||||
|
||||
public MetalDevice(IAvnMetalDevice native)
|
||||
public MetalDevice(IAvaloniaNativeFactory factory, IAvnMetalDevice native)
|
||||
{
|
||||
Native = native;
|
||||
_handleWrapFeature = new GpuHandleWrapFeature(factory);
|
||||
_externalObjectsFeature = new MetalExternalObjectsFeature(native);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
@@ -38,7 +47,14 @@ class MetalDevice : IMetalDevice
|
||||
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;
|
||||
|
||||
@@ -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
|
||||
{
|
||||
private IAvnMetalRenderTarget _native;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
@clr-access internal
|
||||
@clr-map bool int
|
||||
@clr-map u_int64_t ulong
|
||||
@clr-map uint64_t ulong
|
||||
@clr-map int64_t long
|
||||
@clr-map long IntPtr
|
||||
@cpp-preamble @@
|
||||
@@ -700,6 +701,8 @@ interface IAvaloniaNativeFactory : IUnknown
|
||||
HRESULT CreatePlatformSettings(IAvnPlatformSettings** ppv);
|
||||
HRESULT CreatePlatformBehaviorInhibition(IAvnPlatformBehaviorInhibition** ppv);
|
||||
HRESULT CreatePlatformRenderTimer(IAvnPlatformRenderTimer** ppv);
|
||||
HRESULT ImportMTLSharedEvent([intptr]void* idMtlSharedEvent, IAvnMTLSharedEvent** ppv);
|
||||
HRESULT CreateMemoryManagementHelper(IAvnNativeObjectsMemoryManagement** ppv);
|
||||
}
|
||||
|
||||
[uuid(233e094f-9b9f-44a3-9a6e-6948bbdd9fb1)]
|
||||
@@ -1043,6 +1046,9 @@ interface IAvnGlContext : IUnknown
|
||||
int GetSampleCount();
|
||||
int GetStencilSize();
|
||||
[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)]
|
||||
@@ -1069,6 +1075,11 @@ interface IAvnMetalDevice : IUnknown
|
||||
{
|
||||
[intptr]void* GetDevice();
|
||||
[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)]
|
||||
@@ -1077,6 +1088,35 @@ interface IAvnMetalRenderTarget : IUnknown
|
||||
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)]
|
||||
interface IAvnMetalRenderingSession : IUnknown
|
||||
{
|
||||
|
||||
@@ -249,6 +249,16 @@ public class ExternalObjectsOpenGlExtensionFeature : IGlContextExternalObjectsFe
|
||||
var dstLayout = 0;
|
||||
_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
|
||||
@@ -286,6 +296,7 @@ public class ExternalObjectsOpenGlExtensionFeature : IGlContextExternalObjectsFe
|
||||
|
||||
public int TextureId { get; }
|
||||
public int InternalFormat => GL_RGBA8;
|
||||
public int TextureType => GL_TEXTURE_2D;
|
||||
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_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_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_2_10_10_10_REV = 0x8368;
|
||||
// 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_TEXTURE_BINDING_BUFFER = 0x8C2C;
|
||||
// public const int GL_TEXTURE_BUFFER_DATA_STORE_BINDING = 0x8C2D;
|
||||
// public const int GL_TEXTURE_RECTANGLE = 0x84F5;
|
||||
// public const int GL_TEXTURE_BINDING_RECTANGLE = 0x84F6;
|
||||
public const int GL_TEXTURE_RECTANGLE = 0x84F5;
|
||||
public const int GL_TEXTURE_BINDING_RECTANGLE = 0x84F6;
|
||||
// public const int GL_PROXY_TEXTURE_RECTANGLE = 0x84F7;
|
||||
// public const int GL_MAX_RECTANGLE_TEXTURE_SIZE = 0x84F8;
|
||||
// public const int GL_R8_SNORM = 0x8F94;
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Avalonia.Metadata;
|
||||
using Avalonia.Platform;
|
||||
using Avalonia.Rendering.Composition;
|
||||
|
||||
namespace Avalonia.OpenGL;
|
||||
|
||||
//TODO12: Make private and expose IBitmapImpl-based import API for composition visuals
|
||||
[NotClientImplementable]
|
||||
public interface IGlContextExternalObjectsFeature
|
||||
{
|
||||
IReadOnlyList<string> SupportedImportableExternalImageTypes { get; }
|
||||
@@ -23,10 +26,14 @@ public interface IGlContextExternalObjectsFeature
|
||||
public byte[]? DeviceUuid { get; }
|
||||
}
|
||||
|
||||
//TODO12: Make private and expose IBitmapImpl-based import API for composition visuals
|
||||
[NotClientImplementable]
|
||||
public interface IGlExternalSemaphore : IDisposable
|
||||
{
|
||||
void WaitSemaphore(IGlExternalImageTexture texture);
|
||||
void SignalSemaphore(IGlExternalImageTexture texture);
|
||||
void WaitTimelineSemaphore(IGlExternalImageTexture texture, ulong value);
|
||||
void SignalTimelineSemaphore(IGlExternalImageTexture texture, ulong value);
|
||||
}
|
||||
|
||||
public interface IGlExportableExternalSemaphore : IGlExternalSemaphore
|
||||
@@ -34,12 +41,17 @@ public interface IGlExportableExternalSemaphore : IGlExternalSemaphore
|
||||
IPlatformHandle GetHandle();
|
||||
}
|
||||
|
||||
[NotClientImplementable]
|
||||
public interface IGlExternalImageTexture : IDisposable
|
||||
{
|
||||
void AcquireKeyedMutex(uint key);
|
||||
void ReleaseKeyedMutex(uint key);
|
||||
int TextureId { get; }
|
||||
int InternalFormat { get; }
|
||||
/// <summary>
|
||||
/// GL_TEXTURE_2D or GL_TEXTURE_RECTANGLE
|
||||
/// </summary>
|
||||
public int TextureType { 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.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using Avalonia.Metal;
|
||||
using Avalonia.Platform;
|
||||
using SkiaSharp;
|
||||
@@ -11,6 +10,9 @@ internal class SkiaMetalGpu : ISkiaGpu, ISkiaGpuWithPlatformGraphicsContext
|
||||
{
|
||||
private GRContext? _context;
|
||||
private readonly IMetalDevice _device;
|
||||
private readonly SkiaMetalExternalObjectsFeature? _externalObjects;
|
||||
|
||||
internal GRContext GrContext => _context ?? throw new ObjectDisposedException(nameof(SkiaMetalGpu));
|
||||
|
||||
public SkiaMetalGpu(IMetalDevice device, long? maxResourceBytes)
|
||||
{
|
||||
@@ -19,6 +21,8 @@ internal class SkiaMetalGpu : ISkiaGpu, ISkiaGpuWithPlatformGraphicsContext
|
||||
new GRContextOptions { AvoidStencilBuffers = true })
|
||||
?? throw new InvalidOperationException("Unable to create GRContext from Metal device.");
|
||||
_device = device;
|
||||
if (device.TryGetFeature<IMetalExternalObjectsFeature>() is { } externalObjects)
|
||||
_externalObjects = new SkiaMetalExternalObjectsFeature(this, externalObjects);
|
||||
if (maxResourceBytes.HasValue)
|
||||
_context.SetResourceCacheLimit(maxResourceBytes.Value);
|
||||
}
|
||||
@@ -29,15 +33,21 @@ internal class SkiaMetalGpu : ISkiaGpu, ISkiaGpuWithPlatformGraphicsContext
|
||||
_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 IDisposable EnsureCurrent() => _device.EnsureCurrent();
|
||||
public IPlatformGraphicsContext? PlatformGraphicsContext => _device;
|
||||
|
||||
public IScopedResource<GRContext> TryGetGrContext() =>
|
||||
ScopedResource<GRContext>.Create(_context ?? throw new ObjectDisposedException(nameof(SkiaMetalGpu)),
|
||||
EnsureCurrent().Dispose);
|
||||
ScopedResource<GRContext>.Create(GrContext, EnsureCurrent().Dispose);
|
||||
|
||||
public ISkiaGpuRenderTarget? TryCreateRenderTarget(IEnumerable<object> surfaces)
|
||||
{
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Avalonia.Media.Imaging;
|
||||
using Avalonia.OpenGL;
|
||||
using Avalonia.Platform;
|
||||
using Avalonia.Rendering.Composition;
|
||||
@@ -108,17 +109,17 @@ internal class GlSkiaImportedImage : IPlatformRenderInterfaceImportedImage
|
||||
_ => 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;
|
||||
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);
|
||||
if (surf != null)
|
||||
return surf;
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -130,16 +131,34 @@ internal class GlSkiaImportedImage : IPlatformRenderInterfaceImportedImage
|
||||
var internalFormat = _image?.InternalFormat ?? _sharedTexture!.InternalFormat;
|
||||
var textureId = _image?.TextureId ?? _sharedTexture!.TextureId;
|
||||
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;
|
||||
using (var surf = TryCreateSurface(textureId, internalFormat, width, height, topLeft))
|
||||
using (var surf = TryCreateSurface(textureType, textureId, internalFormat, width, height, topLeft))
|
||||
{
|
||||
if (surf == null)
|
||||
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();
|
||||
@@ -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()
|
||||
{
|
||||
using (_gpu.EnsureCurrent())
|
||||
|
||||
@@ -173,6 +173,8 @@ namespace Avalonia.Skia
|
||||
return this;
|
||||
if (featureType == typeof(IExternalObjectsRenderInterfaceContextFeature))
|
||||
return _externalObjectsFeature;
|
||||
if (featureType == typeof(IExternalObjectsHandleWrapRenderInterfaceContextFeature))
|
||||
return _glContext.TryGetFeature(featureType);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -108,6 +108,10 @@ internal class VulkanSkiaExternalObjectsFeature : IExternalObjectsRenderInterfac
|
||||
return new ImmutableBitmap(image);
|
||||
}
|
||||
|
||||
public IBitmapImpl SnapshotWithTimelineSemaphores(IPlatformRenderInterfaceImportedSemaphore waitForSemaphore,
|
||||
ulong waitForValue, IPlatformRenderInterfaceImportedSemaphore signalSemaphore, ulong signalValue) =>
|
||||
throw new NotSupportedException();
|
||||
|
||||
public IBitmapImpl SnapshotWithAutomaticSync() => throw new NotSupportedException();
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ namespace Avalonia.Skia
|
||||
{
|
||||
private readonly SKImage _image;
|
||||
private readonly SKBitmap? _bitmap;
|
||||
private readonly Action? _customImageDispose = null;
|
||||
|
||||
/// <summary>
|
||||
/// 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;
|
||||
_customImageDispose = customImageDispose;
|
||||
PixelSize = new PixelSize(image.Width, image.Height);
|
||||
Dpi = new Vector(96, 96);
|
||||
}
|
||||
@@ -156,7 +158,10 @@ namespace Avalonia.Skia
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
_image.Dispose();
|
||||
if (_customImageDispose != null)
|
||||
_customImageDispose();
|
||||
else
|
||||
_image.Dispose();
|
||||
_bitmap?.Dispose();
|
||||
}
|
||||
|
||||
|
||||
@@ -68,6 +68,8 @@ internal class AngleExternalMemoryD3D11Texture2D : IGlExternalImageTexture
|
||||
|
||||
public int TextureId { get; private set; }
|
||||
public int InternalFormat { get; }
|
||||
|
||||
public int TextureType => GL_TEXTURE_2D;
|
||||
public PlatformGraphicsExternalImageProperties Properties { get; }
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user