using System; using Avalonia; using Ryujinx.Graphics.Vulkan; using Silk.NET.Vulkan; namespace Ryujinx.Ava.Ui.Vulkan.Surfaces { internal class VulkanSurfaceRenderTarget : IDisposable { private readonly VulkanPlatformInterface _platformInterface; private readonly Format _format; private VulkanCommandBufferPool.VulkanCommandBuffer _commandBuffer; private VulkanImage Image { get; set; } private object _lock = new object(); public uint MipLevels => Image.MipLevels; public VulkanDevice Device { get; } public VulkanSurfaceRenderTarget(VulkanPlatformInterface platformInterface, VulkanSurface surface) { _platformInterface = platformInterface; var device = VulkanInitialization.CreateDevice(platformInterface.Api, platformInterface.PhysicalDevice.InternalHandle, platformInterface.PhysicalDevice.QueueFamilyIndex, VulkanInitialization.GetSupportedExtensions(platformInterface.Api, platformInterface.PhysicalDevice.InternalHandle), platformInterface.PhysicalDevice.QueueCount); Device = new VulkanDevice(device, platformInterface.PhysicalDevice, platformInterface.Api); Display = VulkanDisplay.CreateDisplay( platformInterface.Instance, Device, platformInterface.PhysicalDevice, surface); Surface = surface; // Skia seems to only create surfaces from images with unorm format IsRgba = Display.SurfaceFormat.Format >= Format.R8G8B8A8Unorm && Display.SurfaceFormat.Format <= Format.R8G8B8A8Srgb; _format = IsRgba ? Format.R8G8B8A8Unorm : Format.B8G8R8A8Unorm; } public bool IsRgba { get; } public uint ImageFormat => (uint)_format; public ulong MemorySize => Image.MemorySize; public VulkanDisplay Display { get; private set; } public VulkanSurface Surface { get; private set; } public uint UsageFlags => Image.UsageFlags; public PixelSize Size { get; private set; } public void Dispose() { lock (_lock) { DestroyImage(); Display?.Dispose(); Surface?.Dispose(); Device?.Dispose(); Display = null; Surface = null; } } public VulkanSurfaceRenderingSession BeginDraw(float scaling) { if (Image == null) { RecreateImage(); } _commandBuffer?.WaitForFence(); _commandBuffer = null; var session = new VulkanSurfaceRenderingSession(Display, Device, this, scaling); Image.TransitionLayout(ImageLayout.ColorAttachmentOptimal, AccessFlags.AccessNoneKhr); return session; } public void RecreateImage() { DestroyImage(); CreateImage(); } private void CreateImage() { Size = Display.Size; Image = new VulkanImage(Device, _platformInterface.PhysicalDevice, Display.CommandBufferPool, ImageFormat, Size); } private void DestroyImage() { _commandBuffer?.WaitForFence(); _commandBuffer = null; Image?.Dispose(); Image = null; } public VulkanImage GetImage() { return Image; } public void EndDraw() { lock (_lock) { if (Display == null) { return; } _commandBuffer = Display.StartPresentation(); Display.BlitImageToCurrentImage(this, _commandBuffer.InternalHandle); Display.EndPresentation(_commandBuffer); } } } }