diff --git a/GSS2.Core/GSS2.Core.csproj b/GSS2.Core/GSS2.Core.csproj index f11cb3f..061e878 100644 --- a/GSS2.Core/GSS2.Core.csproj +++ b/GSS2.Core/GSS2.Core.csproj @@ -21,7 +21,7 @@ - + diff --git a/GSS2.Core/Hardware/CameraService.cs b/GSS2.Core/Hardware/CameraService.cs index 78f27bf..ec53e1f 100644 --- a/GSS2.Core/Hardware/CameraService.cs +++ b/GSS2.Core/Hardware/CameraService.cs @@ -716,7 +716,6 @@ public class CameraService : IDisposable _camera.RequestCompleted += RequestCompleted; result = _camera.QueueRequest(request); - Console.WriteLine(result); if (result < 0) { _camera.RequestCompleted -= RequestCompleted; diff --git a/GSS2.Test/DependencyInjectionHelper.cs b/GSS2.Test/DependencyInjectionHelper.cs index d7755e6..91827dd 100644 --- a/GSS2.Test/DependencyInjectionHelper.cs +++ b/GSS2.Test/DependencyInjectionHelper.cs @@ -11,7 +11,7 @@ public static class DependencyInjectionHelper .AddSingleton() .AddSingleton() .AddSingleton() - .AddSingleton(); - // .AddSingleton() - // .AddSingleton(); + .AddSingleton() + .AddSingleton() + .AddSingleton(); } \ No newline at end of file diff --git a/GSS2.Test/ViewModels/CameraViewModel.cs b/GSS2.Test/ViewModels/CameraViewModel.cs index 1ec2dba..1e11eb6 100644 --- a/GSS2.Test/ViewModels/CameraViewModel.cs +++ b/GSS2.Test/ViewModels/CameraViewModel.cs @@ -13,7 +13,7 @@ public class CameraViewModel : ViewModelBase { _cameraService = cameraService; _logger = logger; - _logger.LogInformation("Initialized"); + _logger.LogInformation("{} initialized", nameof(CameraViewModel)); } public async Task<(FrameBuffer, StreamConfiguration)?> CaptureImage(int bufferId) diff --git a/GSS2.Test/ViewModels/MainViewModel.cs b/GSS2.Test/ViewModels/MainViewModel.cs index 6c598d2..9e3caad 100644 --- a/GSS2.Test/ViewModels/MainViewModel.cs +++ b/GSS2.Test/ViewModels/MainViewModel.cs @@ -8,10 +8,10 @@ public partial class MainViewModel : ViewModelBase [ObservableProperty] private CameraViewModel _cameraViewModel; [ObservableProperty] private ResourcesViewModel _resourcesViewModel; - public MainViewModel(ILogger logger, ResourcesViewModel resourcesViewModel) + public MainViewModel(ILogger logger, CameraViewModel cameraViewModel, ResourcesViewModel resourcesViewModel) { _logger = logger; - // CameraViewModel = cameraViewModel; + CameraViewModel = cameraViewModel; ResourcesViewModel = resourcesViewModel; _logger.LogInformation("{} initialized", nameof(MainViewModel)); } diff --git a/GSS2.Test/ViewModels/ResourcesViewModel.cs b/GSS2.Test/ViewModels/ResourcesViewModel.cs index 9b36a37..dda898c 100644 --- a/GSS2.Test/ViewModels/ResourcesViewModel.cs +++ b/GSS2.Test/ViewModels/ResourcesViewModel.cs @@ -43,7 +43,6 @@ public partial class ResourcesViewModel : ViewModelBase, IDisposable [ObservableProperty] private ObservableCollection _memoryUsageSeriesXAxes; [ObservableProperty] private ObservableCollection _memoryUsagePieSeries; - [ObservableProperty] private int _intervalMs = 500; [ObservableProperty] private int _chartsHistorySize = 120; private readonly System.Timers.Timer _updateTimer; @@ -409,6 +408,7 @@ public partial class ResourcesViewModel : ViewModelBase, IDisposable memoryUsageValues.Add(Math.Round(snapshot.MemoryUsage.UsedMB, 2)); while (memoryUsageValues.Count > ChartsHistorySize) memoryUsageValues.RemoveAt(0); + // _logger.LogInformation("Memory leakage: {} MB/s", (memoryUsageValues.TakeLast(10).Average() - memoryUsageValues.TakeLast(20).Take(10).Average()) / 5.0); } if (MemoryUsageSeriesYAxes.Count() == 1) MemoryUsageSeriesYAxes.First().MaxLimit = Math.Round(snapshot.MemoryUsage.TotalMB); diff --git a/GSS2.Test/Views/CameraView.axaml.cs b/GSS2.Test/Views/CameraView.axaml.cs index f5bef49..fdbd2c8 100644 --- a/GSS2.Test/Views/CameraView.axaml.cs +++ b/GSS2.Test/Views/CameraView.axaml.cs @@ -1,105 +1,128 @@ using System.Runtime.InteropServices; + using Avalonia; using Avalonia.Controls; using Avalonia.LogicalTree; using Avalonia.Platform; using Avalonia.Rendering.Composition; using Avalonia.Threading; + using GSS2.Test.ViewModels; using GSS2.Vulkan; + using LibCameraSharp; + using Silk.NET.Vulkan; using Silk.NET.Vulkan.Extensions.KHR; + using Image = Silk.NET.Vulkan.Image; namespace GSS2.Test.Views; -interface IImage +public class VulkanImageResources { - public (IPlatformHandle handle, PlatformGraphicsExternalImageProperties properties, Silk.NET.Vulkan.Semaphore semaphore) Export(); + public Image ImportImage { get; init; } + public DeviceMemory ImportMemory { get; init; } + public Image ExportImage { get; init; } + public DeviceMemory ExportMemory { get; init; } } -interface IRenderer +class VulkanExportedImage { - public abstract IImage ImportOrUpdate(int fd, int width, int height); -} + public required int ImageExportedSemaphoreFd { get; init; } + public required int ImageRenderedSemaphoreFd { get; init; } + public required int OrignalFd { get; init; } + public required int ImageFd { get; init; } + public required int ImageWidth { get; init; } + public required int ImageHeight { get; init; } + public required ulong ImageMemorySize { get; init; } + public required int WaitSemaphoreFd { get; init; } + public required int SignalSemaphoreFd { get; init; } + public required VulkanImageResources Resources { get; init; } -class VkImage -{ - public int Fd { get; set; } - public int Width { get; set; } - public int Height { get; set; } - public ulong MemorySize { get; set; } - public Silk.NET.Vulkan.Semaphore Semaphore { get; set; } - - public (IPlatformHandle handle, PlatformGraphicsExternalImageProperties properties, Silk.NET.Vulkan.Semaphore semaphore) Export() + public (IPlatformHandle imageHandle, PlatformGraphicsExternalImageProperties imageProperties, IPlatformHandle waitSemaphoreHandle, IPlatformHandle signalSemaphoreHandle) Export() { return ( - new PlatformHandle(new IntPtr(Fd), KnownPlatformGraphicsExternalImageHandleTypes.VulkanOpaquePosixFileDescriptor), + new PlatformHandle(new IntPtr(ImageFd), KnownPlatformGraphicsExternalImageHandleTypes.VulkanOpaquePosixFileDescriptor), new PlatformGraphicsExternalImageProperties { - Width = Width, - Height = Height, + Width = ImageWidth, + Height = ImageHeight, Format = PlatformGraphicsExternalImageFormat.B8G8R8A8UNorm, - MemorySize = MemorySize + MemorySize = ImageMemorySize }, - Semaphore + new PlatformHandle(new IntPtr(WaitSemaphoreFd), KnownPlatformGraphicsExternalSemaphoreHandleTypes.VulkanOpaquePosixFileDescriptor), + new PlatformHandle(new IntPtr(SignalSemaphoreFd), KnownPlatformGraphicsExternalSemaphoreHandleTypes.VulkanOpaquePosixFileDescriptor) ); } } -class VkRenderer +class VulkanRenderer { [DllImport("libc")] private static extern int dup(int fd); - readonly VulkanContext context = new VulkanContext("GSS2.Test", "CameraView"); + [DllImport("libc")] private static extern int close(int fd); + private readonly VulkanContext _context = new VulkanContext("GSS2.Test", "CameraView"); - public IPlatformHandle ExportSemaphore(bool renderFinished) + unsafe public VulkanExportedImage Import(int imageFd, int width, int height, int stride, int? fenceFd, bool initializationFrame) { - int fd = context.ExportSemaphoreFd(renderFinished ? context.RenderFinishedSemaphore : context.ImageAvailableSemaphore); - return new PlatformHandle(new IntPtr(fd), KnownPlatformGraphicsExternalImageHandleTypes.VulkanOpaquePosixFileDescriptor); - } - - unsafe public VkImage ImportOrUpdate(int fd, int width, int height, int stride) - { - var fdDup = dup(fd); + // Console.WriteLine("Import 1"); + var imageFdDuplicate = dup(imageFd); + // Console.WriteLine("Import 2"); try { - FenceCreateInfo fenceInfo = new FenceCreateInfo - { - SType = StructureType.FenceCreateInfo - }; - context.Api.CreateFence(context.Device, &fenceInfo, null, out var fence).ThrowOnError(); + // Console.WriteLine("Import 3"); + var imageExportedSemaphore = _context.ImageExportedSemaphore; + var imageRenderedSemaphore = _context.ImageRenderedSemaphore; + var commandBuffer = _context.CommandBuffer; - var (importImage, importMemoryReqirements, importMemory) = ImportImage(fdDup, width, height, stride); + // Console.WriteLine("Import 7"); + var imageExportedSemaphoreGetFdInfoKHR = new SemaphoreGetFdInfoKHR() + { + SType = StructureType.SemaphoreGetFDInfoKhr, + Semaphore = imageExportedSemaphore, + HandleType = ExternalSemaphoreHandleTypeFlags.OpaqueFDBit + }; + _context.KhrExternalSemaphoreFd.GetSemaphoreF(_context.Device, in imageExportedSemaphoreGetFdInfoKHR, out var imageExportedSemaphoreFd).ThrowOnError(); + + // Console.WriteLine("Import 8"); + var imageRenderedSemaphoreGetFdInfoKHR = new SemaphoreGetFdInfoKHR() + { + SType = StructureType.SemaphoreGetFDInfoKhr, + Semaphore = imageRenderedSemaphore, + HandleType = ExternalSemaphoreHandleTypeFlags.OpaqueFDBit + }; + _context.KhrExternalSemaphoreFd.GetSemaphoreF(_context.Device, in imageRenderedSemaphoreGetFdInfoKHR, out var imageRenderedSemaphoreFd).ThrowOnError(); + + // Silk.NET.Vulkan.Fence imageCapturedFence = new Silk.NET.Vulkan.Fence(null); + // if (fenceFd is not null) + // { + // var importSemaphoreFdInfoKHR = new ImportFenceFdInfoKHR() + // { + // SType = StructureType.ImportSemaphoreFDInfoKhr, + // Fd = fenceFd.Value, + // Flags = FenceImportFlags.TemporaryBit, + // HandleType = ExternalFenceHandleTypeFlags.SyncFDBit + // }; + // _context.KhrExternalFenceFd.ImportFenceF(_context.Device, &importSemaphoreFdInfoKHR); + // } + + // Console.WriteLine("Import 9"); + var (importImage, importMemoryReqirements, importMemory) = ImportImage(imageFdDuplicate, width, height, stride); + // Console.WriteLine("Import 10"); var (exportImage, exportMemoryReqirements, exportMemory) = CreateExportImage(width, height); - var commandPoolCreateInfo = new CommandPoolCreateInfo - { - SType = StructureType.CommandPoolCreateInfo, - Flags = CommandPoolCreateFlags.ResetCommandBufferBit, - QueueFamilyIndex = context.QueueFamilyIndex - }; - context.Api.CreateCommandPool(context.Device, &commandPoolCreateInfo, null, out var commandPool).ThrowOnError(); - - var commandBufferAllocateInfo = new CommandBufferAllocateInfo - { - SType = StructureType.CommandBufferAllocateInfo, - CommandPool = commandPool, - Level = CommandBufferLevel.Primary, - CommandBufferCount = 1 - }; - - context.Api.AllocateCommandBuffers(context.Device, &commandBufferAllocateInfo, out var commandBuffers).ThrowOnError(); - + // Console.WriteLine("Import 11"); var commandBufferBeginInfo = new CommandBufferBeginInfo { SType = StructureType.CommandBufferBeginInfo, Flags = CommandBufferUsageFlags.OneTimeSubmitBit }; - context.Api.BeginCommandBuffer(commandBuffers, &commandBufferBeginInfo).ThrowOnError(); + _context.Api.BeginCommandBuffer(_context.CommandBuffer, &commandBufferBeginInfo).ThrowOnError(); - TransitionImageLayout(commandBuffers, importImage, ImageLayout.Undefined, ImageLayout.TransferSrcOptimal); - TransitionImageLayout(commandBuffers, exportImage, ImageLayout.Undefined, ImageLayout.TransferDstOptimal); + // Console.WriteLine("Import 12"); + TransitionImageLayout(_context.CommandBuffer, importImage, ImageLayout.Undefined, ImageLayout.TransferSrcOptimal); + TransitionImageLayout(_context.CommandBuffer, exportImage, ImageLayout.Undefined, ImageLayout.TransferDstOptimal); - var region = new ImageCopy + // Console.WriteLine("Import 13"); + var imageCopy = new ImageCopy { SrcSubresource = new ImageSubresourceLayers { @@ -119,55 +142,121 @@ class VkRenderer DstOffset = new Offset3D(0, 0, 0), Extent = new Extent3D((uint)width, (uint)height, 1) }; - context.Api.CmdCopyImage(commandBuffers, importImage, ImageLayout.Undefined, exportImage, ImageLayout.Undefined, [region]); + _context.Api.CmdCopyImage(_context.CommandBuffer, importImage, ImageLayout.Undefined, exportImage, ImageLayout.Undefined, [imageCopy]); - TransitionImageLayout(commandBuffers, exportImage, ImageLayout.TransferDstOptimal, ImageLayout.General); + // Console.WriteLine("Import 14"); + TransitionImageLayout(_context.CommandBuffer, exportImage, ImageLayout.TransferDstOptimal, ImageLayout.General); - context.Api.EndCommandBuffer(commandBuffers).ThrowOnError(); + // Console.WriteLine("Import 154"); + _context.Api.EndCommandBuffer(_context.CommandBuffer).ThrowOnError(); - if (context.Api.TryGetDeviceExtension(context.Instance, context.Device, out var khrExternalMemoryFd)) - Console.WriteLine("KhrExternalMemoryFd import success"); - else - throw new Exception("KhrExternalMemoryFd import error"); + // Console.WriteLine("Import 16"); var memoryGetFdInfoKHR = new MemoryGetFdInfoKHR { SType = StructureType.MemoryGetFDInfoKhr, Memory = exportMemory, HandleType = ExternalMemoryHandleTypeFlags.OpaqueFDBit }; - khrExternalMemoryFd.GetMemoryF(context.Device, &memoryGetFdInfoKHR, out var vulkanFd).ThrowOnError(); - Console.WriteLine($"Image export success {vulkanFd}"); + _context.KhrExternalMemoryFd.GetMemoryF(_context.Device, &memoryGetFdInfoKHR, out var vulkanImageFd).ThrowOnError(); - var renderFinished = context.RenderFinishedSemaphore; - var submitInfo = new SubmitInfo + // Console.WriteLine("Import 17"); + // var waitSemaphoreIndex = 0; + // Silk.NET.Vulkan.Semaphore[] waitSemaphoresArray = new Silk.NET.Vulkan.Semaphore[ + // !initializationFrame ? 1 : 0 + // ]; + // PipelineStageFlags[] pipelineStageFlagsArray = new PipelineStageFlags[waitSemaphoresArray.Length]; + // if (!initializationFrame) + // { + // waitSemaphoresArray[waitSemaphoreIndex] = imageRenderedSemaphore; + // pipelineStageFlagsArray[waitSemaphoreIndex++] = PipelineStageFlags.AllGraphicsBit; + // } + // var waitSemaphoresSpan = new ReadOnlySpan(waitSemaphoresArray); + // var pipelineStageFlagsSpan = new ReadOnlySpan(pipelineStageFlagsArray); + + // fixed (PipelineStageFlags* pPipelineStageFlags = pipelineStageFlagsSpan) + // { + // fixed (Silk.NET.Vulkan.Semaphore* pWaitSemaphoresSpan = waitSemaphoresSpan) + // { + // var submitInfo = new SubmitInfo + // { + // SType = StructureType.SubmitInfo, + // CommandBufferCount = 1, + // PCommandBuffers = &commandBuffer, + // PSignalSemaphores = &imageExportedSemaphore, + // SignalSemaphoreCount = 1, + // PWaitSemaphores = pWaitSemaphoresSpan, + // PWaitDstStageMask = pPipelineStageFlags, + // WaitSemaphoreCount = checked((uint)waitSemaphoresArray.Length) + // }; + // // Console.WriteLine("Import 18"); + // _context.Api.QueueSubmit(_context.Queue, 1, &submitInfo, imageCapturedFence).ThrowOnError(); + // } + // } + + ReadOnlySpan pipelineStageFlags = [PipelineStageFlags.AllGraphicsBit]; + fixed (PipelineStageFlags* pPipelineStageFlags = pipelineStageFlags) { - SType = StructureType.SubmitInfo, - CommandBufferCount = 1, - PCommandBuffers = &commandBuffers, - PSignalSemaphores = &renderFinished, - SignalSemaphoreCount = 1 - }; + ReadOnlySpan waitSemaphores = + [ + imageRenderedSemaphore + ]; + var submitInfo = new SubmitInfo + { + SType = StructureType.SubmitInfo, + CommandBufferCount = 1, + PCommandBuffers = &commandBuffer, + PSignalSemaphores = &imageExportedSemaphore, + SignalSemaphoreCount = 1, + PWaitSemaphores = initializationFrame ? null : &imageRenderedSemaphore, + WaitSemaphoreCount = initializationFrame ? 0u : 1u, + PWaitDstStageMask = initializationFrame ? null : pPipelineStageFlags + }; - context.Api.QueueSubmit(context.Queue, 1, &submitInfo, fence).ThrowOnError(); - context.Api.WaitForFences(context.Device, 1, &fence, true, ulong.MaxValue).ThrowOnError(); - context.Api.ResetFences(context.Device, 1, &fence).ThrowOnError(); + // Console.WriteLine("Import 18"); + _context.Api.QueueSubmit(_context.Queue, 1, &submitInfo, new Silk.NET.Vulkan.Fence(UIntPtr.Zero)).ThrowOnError(); + } - return new VkImage() + // Console.WriteLine("Import 19"); + return new VulkanExportedImage { - Width = width, - Height = height, - Fd = vulkanFd, - MemorySize = exportMemoryReqirements.Size + ImageExportedSemaphoreFd = imageExportedSemaphoreFd, + ImageRenderedSemaphoreFd = imageRenderedSemaphoreFd, + OrignalFd = imageFdDuplicate, + ImageWidth = width, + ImageHeight = height, + ImageFd = vulkanImageFd, + ImageMemorySize = exportMemoryReqirements.Size, + WaitSemaphoreFd = imageExportedSemaphoreFd, + SignalSemaphoreFd = imageRenderedSemaphoreFd, + Resources = new VulkanImageResources + { + ImportImage = importImage, + ImportMemory = importMemory, + ExportImage = exportImage, + ExportMemory = exportMemory + } }; } catch (Exception e) { - Console.WriteLine(e.Message); - Console.WriteLine(e.StackTrace); + // Console.WriteLine(e.Message); + // Console.WriteLine(e.StackTrace); throw; } } + unsafe public void ClearResource(VulkanExportedImage image) + { + _context.Api.DestroyImage(_context.Device, image.Resources.ImportImage, null); + _context.Api.FreeMemory(_context.Device, image.Resources.ImportMemory, null); + _context.Api.DestroyImage(_context.Device, image.Resources.ExportImage, null); + _context.Api.FreeMemory(_context.Device, image.Resources.ExportMemory, null); + close(image.OrignalFd); + close(image.ImageFd); + close(image.ImageExportedSemaphoreFd); + close(image.ImageRenderedSemaphoreFd); + } + unsafe public (Image, MemoryRequirements, DeviceMemory) ImportImage(int fd, int width, int height, int stride) { var subresourceLayout = new SubresourceLayout @@ -202,8 +291,8 @@ class VkRenderer SharingMode = SharingMode.Exclusive, InitialLayout = ImageLayout.Undefined }; - context.Api.CreateImage(context.Device, &imageCreateInfo, null, out var image).ThrowOnError(); - context.Api.GetImageMemoryRequirements(context.Device, image, out var memoryReqirements); + _context.Api.CreateImage(_context.Device, &imageCreateInfo, null, out var image).ThrowOnError(); + _context.Api.GetImageMemoryRequirements(_context.Device, image, out var memoryReqirements); var importMemoryFdInfoKHR = new ImportMemoryFdInfoKHR { @@ -218,8 +307,8 @@ class VkRenderer MemoryTypeIndex = checked((uint)FindMemoryType(memoryReqirements.MemoryTypeBits, MemoryPropertyFlags.DeviceLocalBit)), PNext = &importMemoryFdInfoKHR, }; - context.Api.AllocateMemory(context.Device, &memoryAllocateInfo, null, out var memory).ThrowOnError(); - context.Api.BindImageMemory(context.Device, image, memory, 0).ThrowOnError(); + _context.Api.AllocateMemory(_context.Device, &memoryAllocateInfo, null, out var memory).ThrowOnError(); + _context.Api.BindImageMemory(_context.Device, image, memory, 0).ThrowOnError(); return (image, memoryReqirements, memory); } @@ -250,8 +339,8 @@ class VkRenderer SharingMode = SharingMode.Exclusive, InitialLayout = ImageLayout.Undefined, }; - context.Api.CreateImage(context.Device, &imageCreateInfo, null, out var image).ThrowOnError(); - context.Api.GetImageMemoryRequirements(context.Device, image, out var memoryReqirements); + _context.Api.CreateImage(_context.Device, &imageCreateInfo, null, out var image).ThrowOnError(); + _context.Api.GetImageMemoryRequirements(_context.Device, image, out var memoryReqirements); var exportMemoryFdInfoKHR = new ExportMemoryAllocateInfoKHR { @@ -265,15 +354,15 @@ class VkRenderer MemoryTypeIndex = checked((uint)FindMemoryType(memoryReqirements.MemoryTypeBits, MemoryPropertyFlags.DeviceLocalBit)), PNext = &exportMemoryFdInfoKHR, }; - context.Api.AllocateMemory(context.Device, &exportAllocateInfo, null, out var memory).ThrowOnError(); - context.Api.BindImageMemory(context.Device, image, memory, 0).ThrowOnError(); + _context.Api.AllocateMemory(_context.Device, &exportAllocateInfo, null, out var memory).ThrowOnError(); + _context.Api.BindImageMemory(_context.Device, image, memory, 0).ThrowOnError(); return (image, memoryReqirements, memory); } private int FindMemoryType(uint typeFilter, MemoryPropertyFlags properties) { - context!.Api.GetPhysicalDeviceMemoryProperties(context.PhysicalDevice, out var memProps); + _context!.Api.GetPhysicalDeviceMemoryProperties(_context.PhysicalDevice, out var memProps); for (int i = 0; i < memProps.MemoryTypeCount; i++) if ((typeFilter & (1 << i)) != 0 && (memProps.MemoryTypes[i].PropertyFlags & properties) == properties) return i; @@ -299,7 +388,7 @@ class VkRenderer } }; - context.Api.CmdPipelineBarrier( + _context.Api.CmdPipelineBarrier( cmd, PipelineStageFlags.TopOfPipeBit, PipelineStageFlags.TransferBit, @@ -308,77 +397,56 @@ class VkRenderer 0, null, 1, &barrier); } - // unsafe private void TransitionImageLayout(CommandBuffer cmd, Silk.NET.Vulkan.Image image, ImageLayout oldLayout, ImageLayout newLayout) - // { - // var barrier = new ImageMemoryBarrier - // { - // SType = StructureType.ImageMemoryBarrier, - // OldLayout = oldLayout, - // NewLayout = newLayout, - // SrcQueueFamilyIndex = 0, - // DstQueueFamilyIndex = 0, - // Image = image, - // SubresourceRange = new ImageSubresourceRange - // { - // AspectMask = ImageAspectFlags.ColorBit, - // BaseMipLevel = 0, - // LevelCount = 1, - // BaseArrayLayer = 0, - // LayerCount = 1 - // } - // }; - - // PipelineStageFlags srcStage; - // PipelineStageFlags dstStage; - - // if (oldLayout == ImageLayout.Undefined && newLayout == ImageLayout.TransferDstOptimal) - // { - // barrier.SrcAccessMask = 0; - // barrier.DstAccessMask = AccessFlags.TransferWriteBit; - - // srcStage = PipelineStageFlags.TopOfPipeBit; - // dstStage = PipelineStageFlags.TransferBit; - // } - // else if (oldLayout == ImageLayout.Undefined && newLayout == ImageLayout.ShaderReadOnlyOptimal) - // { - // barrier.SrcAccessMask = 0; - // barrier.DstAccessMask = AccessFlags.ShaderReadBit; - - // srcStage = PipelineStageFlags.TopOfPipeBit; - // dstStage = PipelineStageFlags.FragmentShaderBit; - // } - // else if (oldLayout == ImageLayout.TransferDstOptimal && newLayout == ImageLayout.ShaderReadOnlyOptimal) - // { - // barrier.SrcAccessMask = AccessFlags.TransferWriteBit; - // barrier.DstAccessMask = AccessFlags.ShaderReadBit; - - // srcStage = PipelineStageFlags.TransferBit; - // dstStage = PipelineStageFlags.FragmentShaderBit; - // } - // else - // { - // srcStage = PipelineStageFlags.AllCommandsBit; - // dstStage = PipelineStageFlags.AllCommandsBit; - // } - - // context!.Api.CmdPipelineBarrier(cmd, srcStage, dstStage, 0, 0, null, 0, null, 1, &barrier); - // } } public partial class CameraView : Control { + class PresentedFrame + { + public required ICompositionImportedGpuImage Image { get; init; } + public required ICompositionImportedGpuSemaphore Wait { get; init; } + public required ICompositionImportedGpuSemaphore Signal { get; init; } + public required VulkanExportedImage Vulkan { get; init; } + + public async ValueTask DisposeAsync(VulkanRenderer? _renerer) + { + Dispatcher.UIThread.Invoke(() => + { + try + { + Image.DisposeAsync().AsTask(); + Wait.DisposeAsync().AsTask(); + Signal.DisposeAsync().AsTask(); + } + catch (Exception ex) + { + Console.WriteLine($"{ex.Message}\n{ex.StackTrace}"); + } + }); + _renerer?.ClearResource(Vulkan); + } + } + + private readonly ILogger _logger; private bool _initialized = false; + private bool _initializationFrame = true; + private Compositor? _compositor; private CompositionSurfaceVisual? _visual; private ICompositionGpuInterop? _gpuInterop; - private CompositionDrawingSurface? Surface { get; set; } + private CompositionDrawingSurface? _surface; + + private VulkanRenderer? _renerer; + private VulkanExportedImage? _latestImage; + private PresentedFrame? _lastFrame; + private bool _compositionRequested; - private VkRenderer? _renerer; - private VkImage? _latestImage; private System.Timers.Timer? _updateTimer; - public CameraView() - { } + public CameraView(ILogger logger) + { + _logger = logger; + } protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) { @@ -416,7 +484,7 @@ public partial class CameraView : Control { _updateTimer?.Stop(); _updateTimer?.Dispose(); - Surface?.Dispose(); + _surface?.Dispose(); } _initialized = false; @@ -429,17 +497,18 @@ public partial class CameraView : Control var selfVisual = ElementComposition.GetElementVisual(this)!; _compositor = selfVisual.Compositor; - Surface = _compositor.CreateDrawingSurface(); + _surface = _compositor.CreateDrawingSurface(); _visual = _compositor.CreateSurfaceVisual(); - _visual.Surface = Surface; + _visual.Surface = _surface; ElementComposition.SetElementChildVisual(this, _visual); _gpuInterop = await _compositor.TryGetCompositionGpuInterop(); - _renerer = new VkRenderer(); + _renerer = new VulkanRenderer(); _initialized = true; + _logger.LogInformation("{} initialized", nameof(CameraView)); } - catch (Exception e) + catch (Exception ex) { - Console.WriteLine($"Error while initialization: \n{e.Message}\n{e.StackTrace}"); + _logger.LogError(ex, "Error while {} initialization", nameof(CameraView)); } } @@ -453,73 +522,52 @@ public partial class CameraView : Control base.OnPropertyChanged(change); } - private void RenderToSurface(VkImage image) + private void RenderToSurface(VulkanExportedImage image) { - var (handle, properties, semaphore) = image.Export(); + var (handle, properties, waitSemaphore, signalSemaphore) = image.Export(); - var imageRenderedSemaphore = _gpuInterop!.ImportSemaphore(_renerer!.ExportSemaphore(false)); - imageRenderedSemaphore.ImportCompleted.ContinueWith(t => + var imageExportedSemaphore = _gpuInterop!.ImportSemaphore(waitSemaphore); + imageExportedSemaphore.ImportCompleted.ContinueWith(t => { - Console.WriteLine($"imageRenderedSemaphore.ImportCompleted {t.Status}"); - if (t.Exception is not null) - { - Console.WriteLine($"Error: \n{t.Exception.Message}\n{t.Exception.StackTrace} "); - foreach (var e in t.Exception.InnerExceptions) - { - Console.WriteLine(e.Message); - Console.WriteLine(e.StackTrace); - } - } + if (t.IsCompletedSuccessfully) + return; + _logger.LogError(t.Exception, "Error while imageExported semaphore import"); }); - var renderCompletedSemaphore = _gpuInterop!.ImportSemaphore(_renerer!.ExportSemaphore(true)); - renderCompletedSemaphore.ImportCompleted.ContinueWith(t => + var imageRenderedSemaphore = _gpuInterop!.ImportSemaphore(signalSemaphore); + imageExportedSemaphore.ImportCompleted.ContinueWith(t => { - Console.WriteLine($"renderCompletedSemaphore.ImportCompleted {t.Status}"); - if (t.Exception is not null) - { - Console.WriteLine($"Error: \n{t.Exception.Message}\n{t.Exception.StackTrace} "); - foreach (var e in t.Exception.InnerExceptions) - { - Console.WriteLine(e.Message); - Console.WriteLine(e.StackTrace); - } - } + if (t.IsCompletedSuccessfully) + return; + _logger.LogError(t.Exception, "Error while imageRendered semaphore import"); }); - - Console.WriteLine("ImportImage"); var importedImage = _gpuInterop!.ImportImage(handle, properties); - importedImage.ImportCompleted.ContinueWith(t => + imageExportedSemaphore.ImportCompleted.ContinueWith(t => { - Console.WriteLine($"importedImage.ImportCompleted {t.Status}"); - if (t.Exception is not null) - { - Console.WriteLine($"Error: \n{t.Exception.Message}\n{t.Exception.StackTrace} "); - foreach (var e in t.Exception.InnerExceptions) - { - Console.WriteLine(e.Message); - Console.WriteLine(e.StackTrace); - } - } + if (t.IsCompletedSuccessfully) + return; + _logger.LogError(t.Exception, "Error while image import"); }); + var frame = new PresentedFrame + { + Image = importedImage, + Wait = imageExportedSemaphore, + Signal = imageRenderedSemaphore, + Vulkan = image + }; + Dispatcher.UIThread.Invoke(async () => { - await Surface!.UpdateWithSemaphoresAsync(importedImage, renderCompletedSemaphore, imageRenderedSemaphore) - .ContinueWith((t) => - { - Console.WriteLine($"UpdateWithSemaphoresAsync {t.Status}"); - if (t.Exception is not null) - { - Console.WriteLine($"Error while surface update: \n{t.Exception.Message}\n{t.Exception.StackTrace} "); - foreach (var e in t.Exception.InnerExceptions) - { - Console.WriteLine(e.Message); - Console.WriteLine(e.StackTrace); - } - } - }); - } - ); + await _surface!.UpdateWithSemaphoresAsync(importedImage, imageExportedSemaphore, imageRenderedSemaphore).ContinueWith(t => + { + _updateTimer?.Start(); + _lastFrame?.DisposeAsync(_renerer); + _lastFrame = frame; + if (t.IsCompletedSuccessfully) + return; + _logger.LogError(t.Exception, "Error while image update"); + }); + }); } private void OnComposition() { @@ -533,18 +581,32 @@ public partial class CameraView : Control } private void Update(FrameBuffer? frameBuffer = null, StreamConfiguration? streamConfiguration = null) { + // Console.WriteLine("Update 1"); if (!_initialized || _compositor is null) return; - if (frameBuffer is not null && streamConfiguration is not null && _renerer is not null) + if (frameBuffer is not null && + frameBuffer.Planes.Count() == 1 && + streamConfiguration is not null && + _renerer is not null) { - var image = _renerer.ImportOrUpdate(frameBuffer.Planes.First().Fd.Get(), checked((int)streamConfiguration.Size.Width), checked((int)streamConfiguration.Size.Height), checked((int)streamConfiguration.Stride)); + // Console.WriteLine("Update 2"); + // var fenceFd = frameBuffer.ReleaseFence()?.Fd?.Release(); + var image = _renerer.Import( + frameBuffer.Planes.First().Fd.Get(), + checked((int)streamConfiguration.Size.Width), + checked((int)streamConfiguration.Size.Height), + checked((int)streamConfiguration.Stride), + null, // fenceFd, + _initializationFrame); + // _renerer.ClearResource(image); + // Console.WriteLine("Update 3"); + _initializationFrame = false; _latestImage = image; } if (_compositionRequested) return; - _compositionRequested = true; _compositor?.RequestCompositionUpdate(OnComposition); } diff --git a/GSS2.Test/Views/MainView.axaml b/GSS2.Test/Views/MainView.axaml index 0f8bfee..be1af8b 100644 --- a/GSS2.Test/Views/MainView.axaml +++ b/GSS2.Test/Views/MainView.axaml @@ -5,6 +5,8 @@ xmlns:local="using:GSS2.Test.Views" x:DataType="vm:MainViewModel" x:Class="GSS2.Test.Views.MainView"> - - + + + + \ No newline at end of file diff --git a/GSS2.Test/Views/ResourcesView.axaml b/GSS2.Test/Views/ResourcesView.axaml index 98fc4c4..0b41203 100644 --- a/GSS2.Test/Views/ResourcesView.axaml +++ b/GSS2.Test/Views/ResourcesView.axaml @@ -11,7 +11,7 @@ Series="{Binding CpuUsageSeries}" YAxes="{Binding CpuUsageSeriesYAxes}" XAxes="{Binding CpuUsageSeriesXAxes}" - Height="300" + Height="150" ZoomMode="None" EasingFunction="{x:Null}" /> @@ -44,39 +44,39 @@ diff --git a/GSS2.Vulkan/VulkanContext.cs b/GSS2.Vulkan/VulkanContext.cs index f4196c7..c79def2 100644 --- a/GSS2.Vulkan/VulkanContext.cs +++ b/GSS2.Vulkan/VulkanContext.cs @@ -19,8 +19,13 @@ unsafe public class VulkanContext : IDisposable public Device Device { get; init; } public uint QueueFamilyIndex { get; init; } public Queue Queue { get; init; } - public Semaphore ImageAvailableSemaphore { get; init; } - public Semaphore RenderFinishedSemaphore { get; init; } + public Semaphore ImageExportedSemaphore { get; init; } + public Semaphore ImageRenderedSemaphore { get; init; } + public CommandPool CommandPool { get; init; } + public CommandBuffer CommandBuffer { get; init; } + public KhrExternalMemoryFd KhrExternalMemoryFd { get; init; } + public KhrExternalSemaphoreFd KhrExternalSemaphoreFd { get; init; } + public KhrExternalFenceFd KhrExternalFenceFd { get; init; } public VulkanContext(string applicationName, string engineName) { @@ -30,42 +35,42 @@ unsafe public class VulkanContext : IDisposable [ "VK_KHR_get_physical_device_properties2", "VK_KHR_external_memory_capabilities", + "VK_KHR_external_fence_capabilities", "VK_KHR_external_semaphore_capabilities", "VK_EXT_debug_utils" ]; List instanceLayers = [ - + ]; List deviceExtensions = [ "VK_KHR_external_memory", "VK_KHR_external_memory_fd", "VK_KHR_external_semaphore", - "VK_KHR_external_semaphore_fd" + "VK_KHR_external_semaphore_fd", + "VK_KHR_external_fence", + "VK_KHR_external_fence_fd" ]; Instance = CreateInstance(Api, applicationName, engineName, instanceExtensions, instanceLayers); PhysicalDevice = FindPhysicalDevice(Api, Instance, deviceExtensions); (Device, QueueFamilyIndex) = CreateDevice(Api, Instance, PhysicalDevice, deviceExtensions); Queue = CreateQueue(Api, Device, QueueFamilyIndex); - ImageAvailableSemaphore = CreateSemaphore(Api, Device); - RenderFinishedSemaphore = CreateSemaphore(Api, Device); - } + ImageExportedSemaphore = CreateSemaphore(Api, Device); + ImageRenderedSemaphore = CreateSemaphore(Api, Device); + CommandPool = CreateCommandPool(Api, Device, QueueFamilyIndex); + CommandBuffer = CreateCommandBuffer(Api, Device, CommandPool); - public int ExportSemaphoreFd(Semaphore semaphore) - { - if (!Api.TryGetDeviceExtension(Instance, Device, out var extension)) - throw new InvalidOperationException($"No {KhrExternalSemaphoreFd.ExtensionName} device extension"); - - var info = new SemaphoreGetFdInfoKHR() - { - SType = StructureType.SemaphoreGetFDInfoKhr, - Semaphore = semaphore, - HandleType = ExternalSemaphoreHandleTypeFlags.OpaqueFDBit - }; - extension.GetSemaphoreF(Device, in info, out var fd).ThrowOnError(); - return fd; + if (!Api.TryGetDeviceExtension(Instance, Device, out var khrExternalMemoryFd)) + throw new Exception("KhrExternalMemoryFd import error"); + KhrExternalMemoryFd = khrExternalMemoryFd; + if (!Api.TryGetDeviceExtension(Instance, Device, out var khrExternalSemaphoreFd)) + throw new Exception("KhrExternalMemoryFd import error"); + KhrExternalSemaphoreFd = khrExternalSemaphoreFd; + if (!Api.TryGetDeviceExtension(Instance, Device, out var khrExternalFenceFd)) + throw new Exception("KhrExternalFenceFd import error"); + KhrExternalFenceFd = khrExternalFenceFd; } private static Instance CreateInstance(Vk api, string applicationName, string engineName, List extentions, List layers) @@ -186,6 +191,32 @@ unsafe public class VulkanContext : IDisposable api.CreateSemaphore(device, in semaphoreCreateInfo, null, out var semaphore).ThrowOnError(); return semaphore; } + private static CommandPool CreateCommandPool(Vk api, Device device, uint queueFamilyIndex) + { + var commandPoolCreateInfo = new CommandPoolCreateInfo + { + SType = StructureType.CommandPoolCreateInfo, + Flags = CommandPoolCreateFlags.ResetCommandBufferBit, + QueueFamilyIndex = queueFamilyIndex + }; + api.CreateCommandPool(device, &commandPoolCreateInfo, null, out var commandPool).ThrowOnError(); + + return commandPool; + } + private static CommandBuffer CreateCommandBuffer(Vk api, Device device, CommandPool commandPool) + { + var commandBufferAllocateInfo = new CommandBufferAllocateInfo + { + SType = StructureType.CommandBufferAllocateInfo, + CommandPool = commandPool, + Level = CommandBufferLevel.Primary, + CommandBufferCount = 1 + }; + + api.AllocateCommandBuffers(device, &commandBufferAllocateInfo, out var commandBuffer).ThrowOnError(); + + return commandBuffer; + } protected virtual void Dispose(bool disposing) { @@ -195,8 +226,11 @@ unsafe public class VulkanContext : IDisposable { if (Api is null) return; - Api.DestroySemaphore(Device, ImageAvailableSemaphore, null); - Api.DestroySemaphore(Device, RenderFinishedSemaphore, null); + Api.DestroySemaphore(Device, ImageExportedSemaphore, null); + Api.DestroySemaphore(Device, ImageRenderedSemaphore, null); + Api.FreeCommandBuffers(Device, CommandPool, [CommandBuffer]); + Api.DestroyCommandPool(Device, CommandPool, null); + Api.DestroyDevice(Device, null); } _disposedValue = true; }