diff --git a/GSS2.Test/CameraView.axaml.cs b/GSS2.Test/CameraView.axaml.cs index e8f5400..506d15a 100644 --- a/GSS2.Test/CameraView.axaml.cs +++ b/GSS2.Test/CameraView.axaml.cs @@ -9,16 +9,13 @@ using Avalonia.Threading; using GSS2.Vulkan; -using Iot.Device.FtCommon; - using LibCameraSharp; -using Microsoft.Extensions.Logging.Abstractions; - -using Silk.NET.Core; using Silk.NET.Vulkan; using Silk.NET.Vulkan.Extensions.KHR; +using Image = Silk.NET.Vulkan.Image; + namespace GSS2.Test; interface IImage @@ -46,7 +43,7 @@ class VkImage { Width = Width, Height = Height, - Format = PlatformGraphicsExternalImageFormat.R8G8B8A8UNorm, + Format = PlatformGraphicsExternalImageFormat.B8G8R8A8UNorm, MemorySize = MemorySize }, Semaphore @@ -63,66 +60,22 @@ class VkRenderer return new PlatformHandle(new IntPtr(fd), KnownPlatformGraphicsExternalImageHandleTypes.VulkanOpaquePosixFileDescriptor); } - unsafe public VkImage ImportOrUpdate(int fd, int width, int height) + unsafe public VkImage ImportOrUpdate(int fd, int width, int height, int stride) { try { - var extImageInfo = new ExternalMemoryImageCreateInfo - { - SType = StructureType.ExternalMemoryImageCreateInfo, - HandleTypes = ExternalMemoryHandleTypeFlags.OpaqueFDBit - }; + var (importImage, importMemoryReqirements, importMemory) = ImportImage(fd, width, height, stride); + var (exportImage, exportMemoryReqirements, exportMemory) = CreateExportImage(width, height); - var imageInfo = new ImageCreateInfo - { - SType = StructureType.ImageCreateInfo, - PNext = &extImageInfo, - ImageType = ImageType.Type2D, - Format = Format.R8G8B8A8Unorm, - Extent = new Extent3D - { - Width = checked((uint)width), - Height = checked((uint)height), - Depth = 1 - }, - MipLevels = 1, - ArrayLayers = 1, - Samples = SampleCountFlags.Count1Bit, - Tiling = ImageTiling.Optimal, - Usage = ImageUsageFlags.TransferDstBit | ImageUsageFlags.SampledBit, - SharingMode = SharingMode.Exclusive, - InitialLayout = ImageLayout.Undefined - }; - - context.Api.CreateImage(context.Device, &imageInfo, null, out var image).ThrowOnError(); - context.Api.GetImageMemoryRequirements(context.Device, image, out var memReq); - - var exportAllocInfo = new ExportMemoryAllocateInfo - { - SType = StructureType.ExportMemoryAllocateInfo, - HandleTypes = ExternalMemoryHandleTypeFlags.OpaqueFDBit - }; - - var allocInfo = new MemoryAllocateInfo - { - SType = StructureType.MemoryAllocateInfo, - PNext = &exportAllocInfo, - AllocationSize = memReq.Size, - MemoryTypeIndex = checked((uint)FindMemoryType(memReq.MemoryTypeBits, MemoryPropertyFlags.DeviceLocalBit)) - }; - - context.Api.AllocateMemory(context.Device, &allocInfo, null, out var memory).ThrowOnError(); - context.Api.BindImageMemory(context.Device, image, memory, 0).ThrowOnError(); - - var poolInfo = new CommandPoolCreateInfo + var commandPoolCreateInfo = new CommandPoolCreateInfo { SType = StructureType.CommandPoolCreateInfo, Flags = CommandPoolCreateFlags.ResetCommandBufferBit, QueueFamilyIndex = context.QueueFamilyIndex }; - context.Api.CreateCommandPool(context.Device, &poolInfo, null, out var commandPool).ThrowOnError(); + context.Api.CreateCommandPool(context.Device, &commandPoolCreateInfo, null, out var commandPool).ThrowOnError(); - var cmdBufferAllocInfo = new CommandBufferAllocateInfo + var commandBufferAllocateInfo = new CommandBufferAllocateInfo { SType = StructureType.CommandBufferAllocateInfo, CommandPool = commandPool, @@ -130,65 +83,75 @@ class VkRenderer CommandBufferCount = 1 }; - context.Api.AllocateCommandBuffers(context.Device, &cmdBufferAllocInfo, out var cmd).ThrowOnError(); + context.Api.AllocateCommandBuffers(context.Device, &commandBufferAllocateInfo, out var commandBuffers).ThrowOnError(); - var clearColor = new ClearColorValue((float)Random.Shared.NextDouble(), (float)Random.Shared.NextDouble(), (float)Random.Shared.NextDouble(), (float)Random.Shared.NextDouble()); - var range = new ImageSubresourceRange - { - AspectMask = ImageAspectFlags.ColorBit, - BaseMipLevel = 0, - LevelCount = 1, - BaseArrayLayer = 0, - LayerCount = 1 - }; - - var beginInfo = new CommandBufferBeginInfo + var commandBufferBeginInfo = new CommandBufferBeginInfo { SType = StructureType.CommandBufferBeginInfo, Flags = CommandBufferUsageFlags.OneTimeSubmitBit }; - context.Api.BeginCommandBuffer(cmd, &beginInfo).ThrowOnError(); + context.Api.BeginCommandBuffer(commandBuffers, &commandBufferBeginInfo).ThrowOnError(); - TransitionImageLayout(cmd, image, ImageLayout.Undefined, ImageLayout.TransferDstOptimal); + TransitionImageLayout(commandBuffers, importImage, ImageLayout.Undefined, ImageLayout.TransferSrcOptimal); + TransitionImageLayout(commandBuffers, exportImage, ImageLayout.Undefined, ImageLayout.TransferDstOptimal); - context.Api.CmdClearColorImage(cmd, image, ImageLayout.TransferDstOptimal, &clearColor, 1, &range); + var region = new ImageCopy + { + SrcSubresource = new ImageSubresourceLayers + { + AspectMask = ImageAspectFlags.ColorBit, + MipLevel = 0, + BaseArrayLayer = 0, + LayerCount = 1 + }, + SrcOffset = new Offset3D(0, 0, 0), + DstSubresource = new ImageSubresourceLayers + { + AspectMask = ImageAspectFlags.ColorBit, + MipLevel = 0, + BaseArrayLayer = 0, + LayerCount = 1 + }, + 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]); - TransitionImageLayout(cmd, image, ImageLayout.TransferDstOptimal, ImageLayout.General); + TransitionImageLayout(commandBuffers, exportImage, ImageLayout.TransferDstOptimal, ImageLayout.General); - context.Api.EndCommandBuffer(cmd).ThrowOnError(); + context.Api.EndCommandBuffer(commandBuffers).ThrowOnError(); + + if (context.Api.TryGetDeviceExtension(context.Instance, context.Device, out var khrExternalMemoryFd)) + Console.WriteLine("KhrExternalMemoryFd import success"); + else + throw new Exception("KhrExternalMemoryFd import error"); + 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}"); var renderFinished = context.RenderFinishedSemaphore; - var submitInfo = new SubmitInfo { SType = StructureType.SubmitInfo, CommandBufferCount = 1, - PCommandBuffers = &cmd, + PCommandBuffers = &commandBuffers, PSignalSemaphores = &renderFinished, SignalSemaphoreCount = 1 }; context.Api.QueueSubmit(context.Queue, 1, &submitInfo, default).ThrowOnError(); - var getFdInfo = new MemoryGetFdInfoKHR - { - SType = StructureType.MemoryGetFDInfoKhr, - Memory = memory, - HandleType = ExternalMemoryHandleTypeFlags.OpaqueFDBit - }; - if (context.Api.TryGetDeviceExtension(context.Instance, context.Device, out var khrExternalMemoryFd)) - Console.WriteLine("KhrExternalMemoryFd import success"); - else - throw new Exception("KhrExternalMemoryFd import error"); - khrExternalMemoryFd.GetMemoryF(context.Device, &getFdInfo, out var fd1).ThrowOnError(); - Console.WriteLine($"Image export success {fd1}"); - return new VkImage() { Width = width, Height = height, - Fd = fd1, - MemorySize = memReq.Size + Fd = vulkanFd, + MemorySize = exportMemoryReqirements.Size }; } catch (Exception e) @@ -199,6 +162,109 @@ class VkRenderer } } + unsafe public (Image, MemoryRequirements, DeviceMemory) ImportImage(int fd, int width, int height, int stride) + { + var subresourceLayout = new SubresourceLayout + { + Offset = 0, + RowPitch = checked((ulong)stride), + }; + var imageDrmFormatModifierExplicitCreateInfoEXT = new ImageDrmFormatModifierExplicitCreateInfoEXT + { + SType = StructureType.ImageDrmFormatModifierExplicitCreateInfoExt, + DrmFormatModifier = 0, + DrmFormatModifierPlaneCount = 1, + PPlaneLayouts = &subresourceLayout, + }; + var imageCreateInfo = new ImageCreateInfo + { + SType = StructureType.ImageCreateInfo, + PNext = &imageDrmFormatModifierExplicitCreateInfoEXT, + ImageType = ImageType.Type2D, + Format = Format.B8G8R8A8Unorm, + Extent = new Extent3D + { + Width = checked((uint)width), + Height = checked((uint)height), + Depth = 1 + }, + MipLevels = 1, + ArrayLayers = 1, + Samples = SampleCountFlags.Count1Bit, + Tiling = ImageTiling.DrmFormatModifierExt, + Usage = ImageUsageFlags.TransferSrcBit | ImageUsageFlags.SampledBit, + 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); + + var importMemoryFdInfoKHR = new ImportMemoryFdInfoKHR + { + SType = StructureType.ImportMemoryFDInfoKhr, + HandleType = ExternalMemoryHandleTypeFlags.DmaBufBitExt, + Fd = fd, + }; + var memoryAllocateInfo = new MemoryAllocateInfo + { + SType = StructureType.MemoryAllocateInfo, + AllocationSize = memoryReqirements.Size, + 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(); + + return (image, memoryReqirements, memory); + } + unsafe public (Image, MemoryRequirements, DeviceMemory) CreateExportImage(int width, int height) + { + var externalMemoryImageCreateInfo = new ExternalMemoryImageCreateInfo + { + SType = StructureType.ExternalMemoryImageCreateInfo, + HandleTypes = ExternalMemoryHandleTypeFlags.OpaqueFDBit + }; + var imageCreateInfo = new ImageCreateInfo + { + SType = StructureType.ImageCreateInfo, + PNext = &externalMemoryImageCreateInfo, + ImageType = ImageType.Type2D, + Format = Format.B8G8R8A8Unorm, + Extent = new Extent3D + { + Width = checked((uint)width), + Height = checked((uint)height), + Depth = 1 + }, + MipLevels = 1, + ArrayLayers = 1, + Samples = SampleCountFlags.Count1Bit, + Tiling = ImageTiling.Optimal, + Usage = ImageUsageFlags.TransferDstBit | ImageUsageFlags.SampledBit, + 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); + + var exportMemoryFdInfoKHR = new ExportMemoryAllocateInfoKHR + { + SType = StructureType.ExportMemoryAllocateInfoKhr, + HandleTypes = ExternalMemoryHandleTypeFlags.DmaBufBitExt + }; + var exportAllocateInfo = new MemoryAllocateInfo + { + SType = StructureType.MemoryAllocateInfo, + AllocationSize = memoryReqirements.Size, + 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(); + + return (image, memoryReqirements, memory); + } + private int FindMemoryType(uint typeFilter, MemoryPropertyFlags properties) { context!.Api.GetPhysicalDeviceMemoryProperties(context.PhysicalDevice, out var memProps); @@ -207,15 +273,15 @@ class VkRenderer return i; return -1; } - unsafe private void TransitionImageLayout(CommandBuffer cmd, Silk.NET.Vulkan.Image image, ImageLayout oldLayout, ImageLayout newLayout) + unsafe private void TransitionImageLayout(CommandBuffer cmd, Image image, ImageLayout oldLayout, ImageLayout newLayout) { var barrier = new ImageMemoryBarrier { SType = StructureType.ImageMemoryBarrier, OldLayout = oldLayout, NewLayout = newLayout, - SrcQueueFamilyIndex = 0, - DstQueueFamilyIndex = 0, + SrcQueueFamilyIndex = Vk.QueueFamilyIgnored, + DstQueueFamilyIndex = Vk.QueueFamilyIgnored, Image = image, SubresourceRange = new ImageSubresourceRange { @@ -227,37 +293,70 @@ class VkRenderer } }; - 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.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); + context.Api.CmdPipelineBarrier( + cmd, + PipelineStageFlags.TopOfPipeBit, + PipelineStageFlags.TransferBit, + 0, + 0, null, + 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 @@ -327,16 +426,56 @@ public partial class CameraView : Control var (handle, properties, semaphore) = image.Export(); var imageRenderedSemaphore = _gpuInterop!.ImportSemaphore(_renerer!.ExportSemaphore(false)); + imageRenderedSemaphore.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); + } + } + }); var renderCompletedSemaphore = _gpuInterop!.ImportSemaphore(_renerer!.ExportSemaphore(true)); + renderCompletedSemaphore.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); + } + } + }); + Console.WriteLine("ImportImage"); var importedImage = _gpuInterop!.ImportImage(handle, properties); + importedImage.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); + } + } + }); Dispatcher.UIThread.Invoke(async () => { - await importedImage.ImportCompleted; 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} "); @@ -367,7 +506,7 @@ public partial class CameraView : Control if (frameBuffer is not null && 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)); + var image = _renerer.ImportOrUpdate(frameBuffer.Planes.First().Fd.Get(), checked((int)streamConfiguration.Size.Width), checked((int)streamConfiguration.Size.Height), checked((int)streamConfiguration.Stride)); _latestImage = image; } diff --git a/GSS2.Vulkan/VulkanExtensions.cs b/GSS2.Vulkan/VulkanExtensions.cs index 3a699ac..262441d 100644 --- a/GSS2.Vulkan/VulkanExtensions.cs +++ b/GSS2.Vulkan/VulkanExtensions.cs @@ -1,14 +1,17 @@ // Основан на примере GpuInterop Avalonia // https://github.com/AvaloniaUI/Avalonia/blob/5f3dbae22244e830b464fc680c6c28ca124be41a/samples/GpuInterop/VulkanDemo/VulkanExtensions.cs +using System.Runtime.CompilerServices; + using Silk.NET.Vulkan; namespace GSS2.Vulkan; public static class VulkanExtensions { - public static void ThrowOnError(this Result result) + public static void ThrowOnError(this Result result, [CallerMemberName] string callerMemberName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumber = -1) { + Console.WriteLine($"{callerFilePath} {callerMemberName} {callerLineNumber} {result}"); if (result != Result.Success) throw new Exception($"Unexpected API error \"{result}\""); }