forked from Mirror/Ryujinx
c8f9292bab
* drop split devices, rebase * add fallback to opengl if vulkan is not available * addressed review * ensure present image references are incremented and decremented when necessary * allow changing vsync for vulkan * fix screenshot on avalonia vulkan * save favorite when toggled * improve sync between popups * use separate devices for each new window * fix crash when closing window * addressed review * don't create the main window with immediate mode * change skia vk delegate to method * update vulkan throwonerror * addressed review
165 lines
6.5 KiB
C#
165 lines
6.5 KiB
C#
using System;
|
|
using Avalonia;
|
|
using Silk.NET.Vulkan;
|
|
|
|
namespace Ryujinx.Ava.Ui.Vulkan
|
|
{
|
|
internal class VulkanImage : IDisposable
|
|
{
|
|
private readonly VulkanDevice _device;
|
|
private readonly VulkanPhysicalDevice _physicalDevice;
|
|
private readonly VulkanCommandBufferPool _commandBufferPool;
|
|
private ImageLayout _currentLayout;
|
|
private AccessFlags _currentAccessFlags;
|
|
private ImageUsageFlags _imageUsageFlags { get; }
|
|
private ImageView? _imageView { get; set; }
|
|
private DeviceMemory _imageMemory { get; set; }
|
|
|
|
internal Image? InternalHandle { get; private set; }
|
|
internal Format Format { get; }
|
|
internal ImageAspectFlags AspectFlags { get; private set; }
|
|
|
|
public ulong Handle => InternalHandle?.Handle ?? 0;
|
|
public ulong ViewHandle => _imageView?.Handle ?? 0;
|
|
public uint UsageFlags => (uint)_imageUsageFlags;
|
|
public ulong MemoryHandle => _imageMemory.Handle;
|
|
public uint MipLevels { get; private set; }
|
|
public PixelSize Size { get; }
|
|
public ulong MemorySize { get; private set; }
|
|
public uint CurrentLayout => (uint)_currentLayout;
|
|
|
|
public VulkanImage(
|
|
VulkanDevice device,
|
|
VulkanPhysicalDevice physicalDevice,
|
|
VulkanCommandBufferPool commandBufferPool,
|
|
uint format,
|
|
PixelSize size,
|
|
uint mipLevels = 0)
|
|
{
|
|
_device = device;
|
|
_physicalDevice = physicalDevice;
|
|
_commandBufferPool = commandBufferPool;
|
|
Format = (Format)format;
|
|
Size = size;
|
|
MipLevels = mipLevels;
|
|
_imageUsageFlags =
|
|
ImageUsageFlags.ImageUsageColorAttachmentBit | ImageUsageFlags.ImageUsageTransferDstBit |
|
|
ImageUsageFlags.ImageUsageTransferSrcBit | ImageUsageFlags.ImageUsageSampledBit;
|
|
|
|
Initialize();
|
|
}
|
|
|
|
public unsafe void Initialize()
|
|
{
|
|
if (!InternalHandle.HasValue)
|
|
{
|
|
MipLevels = MipLevels != 0 ? MipLevels : (uint)Math.Floor(Math.Log(Math.Max(Size.Width, Size.Height), 2));
|
|
|
|
var imageCreateInfo = new ImageCreateInfo
|
|
{
|
|
SType = StructureType.ImageCreateInfo,
|
|
ImageType = ImageType.ImageType2D,
|
|
Format = Format,
|
|
Extent = new Extent3D((uint?)Size.Width, (uint?)Size.Height, 1),
|
|
MipLevels = MipLevels,
|
|
ArrayLayers = 1,
|
|
Samples = SampleCountFlags.SampleCount1Bit,
|
|
Tiling = Tiling,
|
|
Usage = _imageUsageFlags,
|
|
SharingMode = SharingMode.Exclusive,
|
|
InitialLayout = ImageLayout.Undefined,
|
|
Flags = ImageCreateFlags.ImageCreateMutableFormatBit
|
|
};
|
|
|
|
_device.Api.CreateImage(_device.InternalHandle, imageCreateInfo, null, out var image).ThrowOnError();
|
|
InternalHandle = image;
|
|
|
|
_device.Api.GetImageMemoryRequirements(_device.InternalHandle, InternalHandle.Value,
|
|
out var memoryRequirements);
|
|
|
|
var memoryAllocateInfo = new MemoryAllocateInfo
|
|
{
|
|
SType = StructureType.MemoryAllocateInfo,
|
|
AllocationSize = memoryRequirements.Size,
|
|
MemoryTypeIndex = (uint)VulkanMemoryHelper.FindSuitableMemoryTypeIndex(
|
|
_physicalDevice,
|
|
memoryRequirements.MemoryTypeBits, MemoryPropertyFlags.MemoryPropertyDeviceLocalBit)
|
|
};
|
|
|
|
_device.Api.AllocateMemory(_device.InternalHandle, memoryAllocateInfo, null,
|
|
out var imageMemory);
|
|
|
|
_imageMemory = imageMemory;
|
|
|
|
_device.Api.BindImageMemory(_device.InternalHandle, InternalHandle.Value, _imageMemory, 0);
|
|
|
|
MemorySize = memoryRequirements.Size;
|
|
|
|
var componentMapping = new ComponentMapping(
|
|
ComponentSwizzle.Identity,
|
|
ComponentSwizzle.Identity,
|
|
ComponentSwizzle.Identity,
|
|
ComponentSwizzle.Identity);
|
|
|
|
AspectFlags = ImageAspectFlags.ImageAspectColorBit;
|
|
|
|
var subresourceRange = new ImageSubresourceRange(AspectFlags, 0, MipLevels, 0, 1);
|
|
|
|
var imageViewCreateInfo = new ImageViewCreateInfo
|
|
{
|
|
SType = StructureType.ImageViewCreateInfo,
|
|
Image = InternalHandle.Value,
|
|
ViewType = ImageViewType.ImageViewType2D,
|
|
Format = Format,
|
|
Components = componentMapping,
|
|
SubresourceRange = subresourceRange
|
|
};
|
|
|
|
_device.Api
|
|
.CreateImageView(_device.InternalHandle, imageViewCreateInfo, null, out var imageView)
|
|
.ThrowOnError();
|
|
|
|
_imageView = imageView;
|
|
|
|
_currentLayout = ImageLayout.Undefined;
|
|
|
|
TransitionLayout(ImageLayout.ColorAttachmentOptimal, AccessFlags.AccessNoneKhr);
|
|
}
|
|
}
|
|
|
|
public ImageTiling Tiling => ImageTiling.Optimal;
|
|
|
|
internal void TransitionLayout(ImageLayout destinationLayout, AccessFlags destinationAccessFlags)
|
|
{
|
|
var commandBuffer = _commandBufferPool.CreateCommandBuffer();
|
|
commandBuffer.BeginRecording();
|
|
|
|
VulkanMemoryHelper.TransitionLayout(_device, commandBuffer.InternalHandle, InternalHandle.Value,
|
|
_currentLayout,
|
|
_currentAccessFlags,
|
|
destinationLayout, destinationAccessFlags,
|
|
MipLevels);
|
|
|
|
commandBuffer.EndRecording();
|
|
|
|
commandBuffer.Submit();
|
|
|
|
_currentLayout = destinationLayout;
|
|
_currentAccessFlags = destinationAccessFlags;
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
if (InternalHandle != null)
|
|
{
|
|
_device.Api.DestroyImageView(_device.InternalHandle, _imageView.Value, Span<AllocationCallbacks>.Empty);
|
|
_device.Api.DestroyImage(_device.InternalHandle, InternalHandle.Value, Span<AllocationCallbacks>.Empty);
|
|
_device.Api.FreeMemory(_device.InternalHandle, _imageMemory, Span<AllocationCallbacks>.Empty);
|
|
|
|
_imageView = default;
|
|
InternalHandle = null;
|
|
_imageMemory = default;
|
|
}
|
|
}
|
|
}
|
|
}
|