A cross-platform UI framework for .NET
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

302 lines
12 KiB

using System;
using System.Collections.Generic;
using Avalonia.Logging;
using Avalonia.Platform;
using Avalonia.Rendering.Composition;
using Avalonia.SourceGenerator;
using static Avalonia.OpenGL.GlConsts;
namespace Avalonia.OpenGL.Features;
unsafe partial class ExternalObjectsInterface
{
public ExternalObjectsInterface(Func<string, IntPtr> getProcAddress)
{
Initialize(getProcAddress);
}
[GetProcAddress("glImportMemoryFdEXT", true)]
public partial void ImportMemoryFdEXT(uint memory, ulong size, int handleType, int fd);
[GetProcAddress("glImportSemaphoreFdEXT", true)]
public partial void ImportSemaphoreFdEXT(uint semaphore,
int handleType,
int fd);
[GetProcAddress("glCreateMemoryObjectsEXT")]
public partial void CreateMemoryObjectsEXT(int n, out uint memoryObjects);
[GetProcAddress("glDeleteMemoryObjectsEXT")]
public partial void DeleteMemoryObjectsEXT(int n, ref uint objects);
[GetProcAddress("glTexStorageMem2DEXT")]
public partial void TexStorageMem2DEXT(int target, int levels, int internalFormat, int width, int height,
uint memory, ulong offset);
[GetProcAddress("glGenSemaphoresEXT")]
public partial void GenSemaphoresEXT(int n, out uint semaphores);
[GetProcAddress("glDeleteSemaphoresEXT")]
public partial void DeleteSemaphoresEXT(int n, ref uint semaphores);
[GetProcAddress("glWaitSemaphoreEXT")]
public partial void WaitSemaphoreEXT(uint semaphore,
uint numBufferBarriers, uint* buffers,
uint numTextureBarriers, int* textures,
int* srcLayouts);
[GetProcAddress("glSignalSemaphoreEXT")]
public partial void SignalSemaphoreEXT(uint semaphore,
uint numBufferBarriers, uint* buffers,
uint numTextureBarriers, int* textures,
int* dstLayouts);
[GetProcAddress("glGetUnsignedBytei_vEXT", true)]
public partial void GetUnsignedBytei_vEXT(int target, uint index, byte* data);
[GetProcAddress("glGetUnsignedBytevEXT", true)]
public partial void GetUnsignedBytevEXT(int target, byte* data);
}
public class ExternalObjectsOpenGlExtensionFeature : IGlContextExternalObjectsFeature
{
private readonly IGlContext _context;
private readonly ExternalObjectsInterface _ext;
private readonly List<string> _imageTypes = new();
private readonly List<string> _semaphoreTypes = new();
public static ExternalObjectsOpenGlExtensionFeature? TryCreate(IGlContext context)
{
var extensions = context.GlInterface.GetExtensions();
if (extensions.Contains("GL_EXT_memory_object") && extensions.Contains("GL_EXT_semaphore"))
{
try
{
return new ExternalObjectsOpenGlExtensionFeature(context, extensions);
}
catch (Exception e)
{
Logger.TryGet(LogEventLevel.Error, "OpenGL")?.Log(nameof(ExternalObjectsOpenGlExtensionFeature),
"Unable to initialize EXT_external_objects extension: " + e);
}
}
return null;
}
private unsafe ExternalObjectsOpenGlExtensionFeature(IGlContext context, List<string> extensions)
{
_context = context;
_ext = new ExternalObjectsInterface(_context.GlInterface.GetProcAddress);
if (_ext.IsGetUnsignedBytei_vEXTAvailable)
{
_context.GlInterface.GetIntegerv(GL_NUM_DEVICE_UUIDS_EXT, out var numUiids);
if (numUiids > 0)
{
DeviceUuid = new byte[16];
fixed (byte* pUuid = DeviceUuid)
_ext.GetUnsignedBytei_vEXT(GL_DEVICE_UUID_EXT, 0, pUuid);
}
}
if (_ext.IsGetUnsignedBytevEXTAvailable)
{
if (extensions.Contains("GL_EXT_memory_object_win32") || extensions.Contains("GL_EXT_semaphore_win32"))
{
DeviceLuid = new byte[8];
fixed (byte* pLuid = DeviceLuid)
_ext.GetUnsignedBytevEXT(GL_DEVICE_LUID_EXT, pLuid);
}
}
if (extensions.Contains("GL_EXT_memory_object_fd")
&& extensions.Contains("GL_EXT_semaphore_fd"))
{
_imageTypes.Add(KnownPlatformGraphicsExternalImageHandleTypes.VulkanOpaquePosixFileDescriptor);
_semaphoreTypes.Add(KnownPlatformGraphicsExternalSemaphoreHandleTypes.VulkanOpaquePosixFileDescriptor);
}
}
public IReadOnlyList<string> SupportedImportableExternalImageTypes => _imageTypes;
public IReadOnlyList<string> SupportedExportableExternalImageTypes { get; } = Array.Empty<string>();
public IReadOnlyList<string> SupportedImportableExternalSemaphoreTypes => _semaphoreTypes;
public IReadOnlyList<string> SupportedExportableExternalSemaphoreTypes { get; } = Array.Empty<string>();
public IReadOnlyList<PlatformGraphicsExternalImageFormat> GetSupportedFormatsForExternalMemoryType(string type)
{
return new[]
{
PlatformGraphicsExternalImageFormat.R8G8B8A8UNorm
};
}
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)
{
var handleDescriptor = handle.HandleDescriptor;
if (string.IsNullOrEmpty(handleDescriptor))
throw new ArgumentException("The handle must have a descriptor", nameof(handle));
if (!_imageTypes.Contains(handleDescriptor))
throw new ArgumentException(handleDescriptor + " is not supported", nameof(handle));
if (handleDescriptor == KnownPlatformGraphicsExternalImageHandleTypes.VulkanOpaquePosixFileDescriptor)
{
while (_context.GlInterface.GetError() != 0)
{
//Skip existing errors
}
_ext.CreateMemoryObjectsEXT(1, out var memoryObject);
_ext.ImportMemoryFdEXT(memoryObject, properties.MemorySize, GL_HANDLE_TYPE_OPAQUE_FD_EXT,
handle.Handle.ToInt32());
var err = _context.GlInterface.GetError();
if (err != 0)
throw OpenGlException.GetFormattedException("glImportMemoryFdEXT", err);
_context.GlInterface.GetIntegerv(GL_TEXTURE_BINDING_2D, out var oldTexture);
var texture = _context.GlInterface.GenTexture();
_context.GlInterface.BindTexture(GL_TEXTURE_2D, texture);
_ext.TexStorageMem2DEXT(GL_TEXTURE_2D, 1, GL_RGBA8, properties.Width, properties.Height,
memoryObject, properties.MemoryOffset);
err = _context.GlInterface.GetError();
_context.GlInterface.BindTexture(GL_TEXTURE_2D, oldTexture);
if (err != 0)
throw OpenGlException.GetFormattedException("glTexStorageMem2DEXT", err);
return new ExternalImageTexture(_context, properties, _ext, memoryObject, texture);
}
throw new ArgumentException(handleDescriptor + " is not supported", nameof(handle));
}
public IGlExternalSemaphore ImportSemaphore(IPlatformHandle handle)
{
var handleDescriptor = handle.HandleDescriptor;
if (string.IsNullOrEmpty(handleDescriptor))
throw new ArgumentException("The handle must have a descriptor", nameof(handle));
if (!_semaphoreTypes.Contains(handleDescriptor))
throw new ArgumentException(handleDescriptor + " is not supported");
if (handleDescriptor == KnownPlatformGraphicsExternalSemaphoreHandleTypes.VulkanOpaquePosixFileDescriptor)
{
_ext.GenSemaphoresEXT(1, out var semaphore);
_ext.ImportSemaphoreFdEXT(semaphore, GL_HANDLE_TYPE_OPAQUE_FD_EXT, handle.Handle.ToInt32());
return new ExternalSemaphore(_context, _ext, semaphore);
}
throw new ArgumentException(handleDescriptor + " is not supported", nameof(handle));
}
public CompositionGpuImportedImageSynchronizationCapabilities GetSynchronizationCapabilities(string imageHandleType)
{
if (imageHandleType == KnownPlatformGraphicsExternalImageHandleTypes.VulkanOpaquePosixFileDescriptor)
return CompositionGpuImportedImageSynchronizationCapabilities.Semaphores;
return default;
}
public byte[]? DeviceLuid { get; }
public byte[]? DeviceUuid { get; }
private unsafe class ExternalSemaphore : IGlExternalSemaphore
{
private readonly IGlContext _context;
private readonly ExternalObjectsInterface _ext;
private uint _semaphore;
public ExternalSemaphore(IGlContext context, ExternalObjectsInterface ext, uint semaphore)
{
_context = context;
_ext = ext;
_semaphore = semaphore;
}
public void Dispose()
{
if(_context.IsLost)
return;
using (_context.EnsureCurrent())
_ext.DeleteSemaphoresEXT(1, ref _semaphore);
_semaphore = 0;
}
public void WaitSemaphore(IGlExternalImageTexture texture)
{
var tex = (ExternalImageTexture)texture;
var texId = tex.TextureId;
var srcLayout = GL_LAYOUT_TRANSFER_SRC_EXT;
_ext.WaitSemaphoreEXT(_semaphore, 0, null, 1, &texId, &srcLayout);
}
public void SignalSemaphore(IGlExternalImageTexture texture)
{
var tex = (ExternalImageTexture)texture;
var texId = tex.TextureId;
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
{
private readonly IGlContext _context;
private readonly ExternalObjectsInterface _ext;
private uint _objectId;
public ExternalImageTexture(IGlContext context,
PlatformGraphicsExternalImageProperties properties,
ExternalObjectsInterface ext, uint objectId, int textureId)
{
Properties = properties;
TextureId = textureId;
_context = context;
_ext = ext;
_objectId = objectId;
}
public void Dispose()
{
if(_context.IsLost)
return;
using (_context.EnsureCurrent())
{
_context.GlInterface.DeleteTexture(TextureId);
_ext.DeleteMemoryObjectsEXT(1, ref _objectId);
_objectId = 0;
}
}
public void AcquireKeyedMutex(uint key) => throw new NotSupportedException();
public void ReleaseKeyedMutex(uint key) => throw new NotSupportedException();
public int TextureId { get; }
public int InternalFormat => GL_RGBA8;
public int TextureType => GL_TEXTURE_2D;
public PlatformGraphicsExternalImageProperties Properties { get; }
}
}