diff options
Diffstat (limited to 'src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs')
-rw-r--r-- | src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs | 820 |
1 files changed, 820 insertions, 0 deletions
diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs new file mode 100644 index 00000000..e7475b6b --- /dev/null +++ b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -0,0 +1,820 @@ +using Ryujinx.Common.Configuration; +using Ryujinx.Common.Logging; +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.Shader; +using Ryujinx.Graphics.Shader.Translation; +using Ryujinx.Graphics.Vulkan.MoltenVK; +using Ryujinx.Graphics.Vulkan.Queries; +using Silk.NET.Vulkan; +using Silk.NET.Vulkan.Extensions.EXT; +using Silk.NET.Vulkan.Extensions.KHR; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; + +namespace Ryujinx.Graphics.Vulkan +{ + public sealed class VulkanRenderer : IRenderer + { + private VulkanInstance _instance; + private SurfaceKHR _surface; + private VulkanPhysicalDevice _physicalDevice; + private Device _device; + private WindowBase _window; + + private bool _initialized; + + internal FormatCapabilities FormatCapabilities { get; private set; } + internal HardwareCapabilities Capabilities; + + internal Vk Api { get; private set; } + internal KhrSurface SurfaceApi { get; private set; } + internal KhrSwapchain SwapchainApi { get; private set; } + internal ExtConditionalRendering ConditionalRenderingApi { get; private set; } + internal ExtExtendedDynamicState ExtendedDynamicStateApi { get; private set; } + internal KhrPushDescriptor PushDescriptorApi { get; private set; } + internal ExtTransformFeedback TransformFeedbackApi { get; private set; } + internal KhrDrawIndirectCount DrawIndirectCountApi { get; private set; } + + internal uint QueueFamilyIndex { get; private set; } + internal Queue Queue { get; private set; } + internal Queue BackgroundQueue { get; private set; } + internal object BackgroundQueueLock { get; private set; } + internal object QueueLock { get; private set; } + + internal MemoryAllocator MemoryAllocator { get; private set; } + internal CommandBufferPool CommandBufferPool { get; private set; } + internal DescriptorSetManager DescriptorSetManager { get; private set; } + internal PipelineLayoutCache PipelineLayoutCache { get; private set; } + internal BackgroundResources BackgroundResources { get; private set; } + internal Action<Action> InterruptAction { get; private set; } + internal SyncManager SyncManager { get; private set; } + + internal BufferManager BufferManager { get; private set; } + + internal HashSet<ShaderCollection> Shaders { get; } + internal HashSet<ITexture> Textures { get; } + internal HashSet<SamplerHolder> Samplers { get; } + + private VulkanDebugMessenger _debugMessenger; + private Counters _counters; + + private PipelineFull _pipeline; + + internal HelperShader HelperShader { get; private set; } + internal PipelineFull PipelineInternal => _pipeline; + + public IPipeline Pipeline => _pipeline; + + public IWindow Window => _window; + + private readonly Func<Instance, Vk, SurfaceKHR> _getSurface; + private readonly Func<string[]> _getRequiredExtensions; + private readonly string _preferredGpuId; + + internal Vendor Vendor { get; private set; } + internal bool IsAmdWindows { get; private set; } + internal bool IsIntelWindows { get; private set; } + internal bool IsAmdGcn { get; private set; } + internal bool IsMoltenVk { get; private set; } + internal bool IsTBDR { get; private set; } + internal bool IsSharedMemory { get; private set; } + public string GpuVendor { get; private set; } + public string GpuRenderer { get; private set; } + public string GpuVersion { get; private set; } + + public bool PreferThreading => true; + + public event EventHandler<ScreenCaptureImageInfo> ScreenCaptured; + + public VulkanRenderer(Func<Instance, Vk, SurfaceKHR> surfaceFunc, Func<string[]> requiredExtensionsFunc, string preferredGpuId) + { + _getSurface = surfaceFunc; + _getRequiredExtensions = requiredExtensionsFunc; + _preferredGpuId = preferredGpuId; + Shaders = new HashSet<ShaderCollection>(); + Textures = new HashSet<ITexture>(); + Samplers = new HashSet<SamplerHolder>(); + + if (OperatingSystem.IsMacOS()) + { + MVKInitialization.Initialize(); + + // Any device running on MacOS is using MoltenVK, even Intel and AMD vendors. + IsMoltenVk = true; + } + } + + private unsafe void LoadFeatures(uint maxQueueCount, uint queueFamilyIndex) + { + FormatCapabilities = new FormatCapabilities(Api, _physicalDevice.PhysicalDevice); + + if (Api.TryGetDeviceExtension(_instance.Instance, _device, out ExtConditionalRendering conditionalRenderingApi)) + { + ConditionalRenderingApi = conditionalRenderingApi; + } + + if (Api.TryGetDeviceExtension(_instance.Instance, _device, out ExtExtendedDynamicState extendedDynamicStateApi)) + { + ExtendedDynamicStateApi = extendedDynamicStateApi; + } + + if (Api.TryGetDeviceExtension(_instance.Instance, _device, out KhrPushDescriptor pushDescriptorApi)) + { + PushDescriptorApi = pushDescriptorApi; + } + + if (Api.TryGetDeviceExtension(_instance.Instance, _device, out ExtTransformFeedback transformFeedbackApi)) + { + TransformFeedbackApi = transformFeedbackApi; + } + + if (Api.TryGetDeviceExtension(_instance.Instance, _device, out KhrDrawIndirectCount drawIndirectCountApi)) + { + DrawIndirectCountApi = drawIndirectCountApi; + } + + if (maxQueueCount >= 2) + { + Api.GetDeviceQueue(_device, queueFamilyIndex, 1, out var backgroundQueue); + BackgroundQueue = backgroundQueue; + BackgroundQueueLock = new object(); + } + + PhysicalDeviceProperties2 properties2 = new PhysicalDeviceProperties2() + { + SType = StructureType.PhysicalDeviceProperties2 + }; + + PhysicalDeviceBlendOperationAdvancedPropertiesEXT propertiesBlendOperationAdvanced = new PhysicalDeviceBlendOperationAdvancedPropertiesEXT() + { + SType = StructureType.PhysicalDeviceBlendOperationAdvancedPropertiesExt + }; + + bool supportsBlendOperationAdvanced = _physicalDevice.IsDeviceExtensionPresent("VK_EXT_blend_operation_advanced"); + + if (supportsBlendOperationAdvanced) + { + propertiesBlendOperationAdvanced.PNext = properties2.PNext; + properties2.PNext = &propertiesBlendOperationAdvanced; + } + + PhysicalDeviceSubgroupSizeControlPropertiesEXT propertiesSubgroupSizeControl = new PhysicalDeviceSubgroupSizeControlPropertiesEXT() + { + SType = StructureType.PhysicalDeviceSubgroupSizeControlPropertiesExt + }; + + bool supportsSubgroupSizeControl = _physicalDevice.IsDeviceExtensionPresent("VK_EXT_subgroup_size_control"); + + if (supportsSubgroupSizeControl) + { + properties2.PNext = &propertiesSubgroupSizeControl; + } + + bool supportsTransformFeedback = _physicalDevice.IsDeviceExtensionPresent(ExtTransformFeedback.ExtensionName); + + PhysicalDeviceTransformFeedbackPropertiesEXT propertiesTransformFeedback = new PhysicalDeviceTransformFeedbackPropertiesEXT() + { + SType = StructureType.PhysicalDeviceTransformFeedbackPropertiesExt + }; + + if (supportsTransformFeedback) + { + propertiesTransformFeedback.PNext = properties2.PNext; + properties2.PNext = &propertiesTransformFeedback; + } + + PhysicalDevicePortabilitySubsetPropertiesKHR propertiesPortabilitySubset = new PhysicalDevicePortabilitySubsetPropertiesKHR() + { + SType = StructureType.PhysicalDevicePortabilitySubsetPropertiesKhr + }; + + PhysicalDeviceFeatures2 features2 = new PhysicalDeviceFeatures2() + { + SType = StructureType.PhysicalDeviceFeatures2 + }; + + PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT featuresPrimitiveTopologyListRestart = new PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT() + { + SType = StructureType.PhysicalDevicePrimitiveTopologyListRestartFeaturesExt + }; + + PhysicalDeviceRobustness2FeaturesEXT featuresRobustness2 = new PhysicalDeviceRobustness2FeaturesEXT() + { + SType = StructureType.PhysicalDeviceRobustness2FeaturesExt + }; + + PhysicalDeviceShaderFloat16Int8FeaturesKHR featuresShaderInt8 = new PhysicalDeviceShaderFloat16Int8FeaturesKHR() + { + SType = StructureType.PhysicalDeviceShaderFloat16Int8Features + }; + + PhysicalDeviceCustomBorderColorFeaturesEXT featuresCustomBorderColor = new PhysicalDeviceCustomBorderColorFeaturesEXT() + { + SType = StructureType.PhysicalDeviceCustomBorderColorFeaturesExt + }; + + PhysicalDevicePortabilitySubsetFeaturesKHR featuresPortabilitySubset = new PhysicalDevicePortabilitySubsetFeaturesKHR() + { + SType = StructureType.PhysicalDevicePortabilitySubsetFeaturesKhr + }; + + if (_physicalDevice.IsDeviceExtensionPresent("VK_EXT_primitive_topology_list_restart")) + { + features2.PNext = &featuresPrimitiveTopologyListRestart; + } + + if (_physicalDevice.IsDeviceExtensionPresent("VK_EXT_robustness2")) + { + featuresRobustness2.PNext = features2.PNext; + features2.PNext = &featuresRobustness2; + } + + if (_physicalDevice.IsDeviceExtensionPresent("VK_KHR_shader_float16_int8")) + { + featuresShaderInt8.PNext = features2.PNext; + features2.PNext = &featuresShaderInt8; + } + + if (_physicalDevice.IsDeviceExtensionPresent("VK_EXT_custom_border_color")) + { + featuresCustomBorderColor.PNext = features2.PNext; + features2.PNext = &featuresCustomBorderColor; + } + + bool usePortability = _physicalDevice.IsDeviceExtensionPresent("VK_KHR_portability_subset"); + + if (usePortability) + { + propertiesPortabilitySubset.PNext = properties2.PNext; + properties2.PNext = &propertiesPortabilitySubset; + + featuresPortabilitySubset.PNext = features2.PNext; + features2.PNext = &featuresPortabilitySubset; + } + + Api.GetPhysicalDeviceProperties2(_physicalDevice.PhysicalDevice, &properties2); + Api.GetPhysicalDeviceFeatures2(_physicalDevice.PhysicalDevice, &features2); + + var portabilityFlags = PortabilitySubsetFlags.None; + uint vertexBufferAlignment = 1; + + if (usePortability) + { + vertexBufferAlignment = propertiesPortabilitySubset.MinVertexInputBindingStrideAlignment; + + portabilityFlags |= featuresPortabilitySubset.TriangleFans ? 0 : PortabilitySubsetFlags.NoTriangleFans; + portabilityFlags |= featuresPortabilitySubset.PointPolygons ? 0 : PortabilitySubsetFlags.NoPointMode; + portabilityFlags |= featuresPortabilitySubset.ImageView2DOn3DImage ? 0 : PortabilitySubsetFlags.No3DImageView; + portabilityFlags |= featuresPortabilitySubset.SamplerMipLodBias ? 0 : PortabilitySubsetFlags.NoLodBias; + } + + bool supportsCustomBorderColor = _physicalDevice.IsDeviceExtensionPresent("VK_EXT_custom_border_color") && + featuresCustomBorderColor.CustomBorderColors && + featuresCustomBorderColor.CustomBorderColorWithoutFormat; + + ref var properties = ref properties2.Properties; + + SampleCountFlags supportedSampleCounts = + properties.Limits.FramebufferColorSampleCounts & + properties.Limits.FramebufferDepthSampleCounts & + properties.Limits.FramebufferStencilSampleCounts; + + Capabilities = new HardwareCapabilities( + _physicalDevice.IsDeviceExtensionPresent("VK_EXT_index_type_uint8"), + supportsCustomBorderColor, + supportsBlendOperationAdvanced, + propertiesBlendOperationAdvanced.AdvancedBlendCorrelatedOverlap, + propertiesBlendOperationAdvanced.AdvancedBlendNonPremultipliedSrcColor, + propertiesBlendOperationAdvanced.AdvancedBlendNonPremultipliedDstColor, + _physicalDevice.IsDeviceExtensionPresent(KhrDrawIndirectCount.ExtensionName), + _physicalDevice.IsDeviceExtensionPresent("VK_EXT_fragment_shader_interlock"), + _physicalDevice.IsDeviceExtensionPresent("VK_NV_geometry_shader_passthrough"), + supportsSubgroupSizeControl, + featuresShaderInt8.ShaderInt8, + _physicalDevice.IsDeviceExtensionPresent("VK_EXT_shader_stencil_export"), + _physicalDevice.IsDeviceExtensionPresent(ExtConditionalRendering.ExtensionName), + _physicalDevice.IsDeviceExtensionPresent(ExtExtendedDynamicState.ExtensionName), + features2.Features.MultiViewport, + featuresRobustness2.NullDescriptor || IsMoltenVk, + _physicalDevice.IsDeviceExtensionPresent(KhrPushDescriptor.ExtensionName), + featuresPrimitiveTopologyListRestart.PrimitiveTopologyListRestart, + featuresPrimitiveTopologyListRestart.PrimitiveTopologyPatchListRestart, + supportsTransformFeedback, + propertiesTransformFeedback.TransformFeedbackQueries, + features2.Features.OcclusionQueryPrecise, + _physicalDevice.PhysicalDeviceFeatures.PipelineStatisticsQuery, + _physicalDevice.PhysicalDeviceFeatures.GeometryShader, + _physicalDevice.IsDeviceExtensionPresent("VK_NV_viewport_array2"), + propertiesSubgroupSizeControl.MinSubgroupSize, + propertiesSubgroupSizeControl.MaxSubgroupSize, + propertiesSubgroupSizeControl.RequiredSubgroupSizeStages, + supportedSampleCounts, + portabilityFlags, + vertexBufferAlignment, + properties.Limits.SubTexelPrecisionBits); + + IsSharedMemory = MemoryAllocator.IsDeviceMemoryShared(_physicalDevice); + + MemoryAllocator = new MemoryAllocator(Api, _physicalDevice, _device); + + CommandBufferPool = new CommandBufferPool(Api, _device, Queue, QueueLock, queueFamilyIndex); + + DescriptorSetManager = new DescriptorSetManager(_device); + + PipelineLayoutCache = new PipelineLayoutCache(); + + BackgroundResources = new BackgroundResources(this, _device); + + BufferManager = new BufferManager(this, _device); + + SyncManager = new SyncManager(this, _device); + _pipeline = new PipelineFull(this, _device); + _pipeline.Initialize(); + + HelperShader = new HelperShader(this, _device); + + _counters = new Counters(this, _device, _pipeline); + } + + private unsafe void SetupContext(GraphicsDebugLevel logLevel) + { + var api = Vk.GetApi(); + + Api = api; + + _instance = VulkanInitialization.CreateInstance(api, logLevel, _getRequiredExtensions()); + _debugMessenger = new VulkanDebugMessenger(api, _instance.Instance, logLevel); + + if (api.TryGetInstanceExtension(_instance.Instance, out KhrSurface surfaceApi)) + { + SurfaceApi = surfaceApi; + } + + _surface = _getSurface(_instance.Instance, api); + _physicalDevice = VulkanInitialization.FindSuitablePhysicalDevice(api, _instance, _surface, _preferredGpuId); + + var queueFamilyIndex = VulkanInitialization.FindSuitableQueueFamily(api, _physicalDevice, _surface, out uint maxQueueCount); + + _device = VulkanInitialization.CreateDevice(api, _physicalDevice, queueFamilyIndex, maxQueueCount); + + if (api.TryGetDeviceExtension(_instance.Instance, _device, out KhrSwapchain swapchainApi)) + { + SwapchainApi = swapchainApi; + } + + api.GetDeviceQueue(_device, queueFamilyIndex, 0, out var queue); + Queue = queue; + QueueLock = new object(); + + LoadFeatures(maxQueueCount, queueFamilyIndex); + + _window = new Window(this, _surface, _physicalDevice.PhysicalDevice, _device); + + _initialized = true; + } + + public BufferHandle CreateBuffer(int size, BufferHandle storageHint) + { + return BufferManager.CreateWithHandle(this, size, BufferAllocationType.Auto, storageHint); + } + + public IProgram CreateProgram(ShaderSource[] sources, ShaderInfo info) + { + bool isCompute = sources.Length == 1 && sources[0].Stage == ShaderStage.Compute; + + if (info.State.HasValue || isCompute) + { + return new ShaderCollection(this, _device, sources, info.State ?? default, info.FromCache); + } + else + { + return new ShaderCollection(this, _device, sources); + } + } + + internal ShaderCollection CreateProgramWithMinimalLayout(ShaderSource[] sources, SpecDescription[] specDescription = null) + { + return new ShaderCollection(this, _device, sources, specDescription: specDescription, isMinimal: true); + } + + public ISampler CreateSampler(GAL.SamplerCreateInfo info) + { + return new SamplerHolder(this, _device, info); + } + + public ITexture CreateTexture(TextureCreateInfo info, float scale) + { + if (info.Target == Target.TextureBuffer) + { + return new TextureBuffer(this, info, scale); + } + + return CreateTextureView(info, scale); + } + + internal TextureView CreateTextureView(TextureCreateInfo info, float scale) + { + // This should be disposed when all views are destroyed. + var storage = CreateTextureStorage(info, scale); + return storage.CreateView(info, 0, 0); + } + + internal TextureStorage CreateTextureStorage(TextureCreateInfo info, float scale) + { + return new TextureStorage(this, _device, info, scale); + } + + public void DeleteBuffer(BufferHandle buffer) + { + BufferManager.Delete(buffer); + } + + internal void FlushAllCommands() + { + _pipeline?.FlushCommandsImpl(); + } + + internal void RegisterFlush() + { + SyncManager.RegisterFlush(); + } + + public PinnedSpan<byte> GetBufferData(BufferHandle buffer, int offset, int size) + { + return BufferManager.GetData(buffer, offset, size); + } + + public unsafe Capabilities GetCapabilities() + { + FormatFeatureFlags compressedFormatFeatureFlags = + FormatFeatureFlags.SampledImageBit | + FormatFeatureFlags.SampledImageFilterLinearBit | + FormatFeatureFlags.BlitSrcBit | + FormatFeatureFlags.TransferSrcBit | + FormatFeatureFlags.TransferDstBit; + + bool supportsBc123CompressionFormat = FormatCapabilities.OptimalFormatsSupport(compressedFormatFeatureFlags, + GAL.Format.Bc1RgbaSrgb, + GAL.Format.Bc1RgbaUnorm, + GAL.Format.Bc2Srgb, + GAL.Format.Bc2Unorm, + GAL.Format.Bc3Srgb, + GAL.Format.Bc3Unorm); + + bool supportsBc45CompressionFormat = FormatCapabilities.OptimalFormatsSupport(compressedFormatFeatureFlags, + GAL.Format.Bc4Snorm, + GAL.Format.Bc4Unorm, + GAL.Format.Bc5Snorm, + GAL.Format.Bc5Unorm); + + bool supportsBc67CompressionFormat = FormatCapabilities.OptimalFormatsSupport(compressedFormatFeatureFlags, + GAL.Format.Bc6HSfloat, + GAL.Format.Bc6HUfloat, + GAL.Format.Bc7Srgb, + GAL.Format.Bc7Unorm); + + bool supportsEtc2CompressionFormat = FormatCapabilities.OptimalFormatsSupport(compressedFormatFeatureFlags, + GAL.Format.Etc2RgbaSrgb, + GAL.Format.Etc2RgbaUnorm, + GAL.Format.Etc2RgbPtaSrgb, + GAL.Format.Etc2RgbPtaUnorm, + GAL.Format.Etc2RgbSrgb, + GAL.Format.Etc2RgbUnorm); + + bool supports5BitComponentFormat = FormatCapabilities.OptimalFormatsSupport(compressedFormatFeatureFlags, + GAL.Format.R5G6B5Unorm, + GAL.Format.R5G5B5A1Unorm, + GAL.Format.R5G5B5X1Unorm, + GAL.Format.B5G6R5Unorm, + GAL.Format.B5G5R5A1Unorm, + GAL.Format.A1B5G5R5Unorm); + + bool supportsR4G4B4A4Format = FormatCapabilities.OptimalFormatsSupport(compressedFormatFeatureFlags, + GAL.Format.R4G4B4A4Unorm); + + bool supportsAstcFormats = FormatCapabilities.OptimalFormatsSupport(compressedFormatFeatureFlags, + GAL.Format.Astc4x4Unorm, + GAL.Format.Astc5x4Unorm, + GAL.Format.Astc5x5Unorm, + GAL.Format.Astc6x5Unorm, + GAL.Format.Astc6x6Unorm, + GAL.Format.Astc8x5Unorm, + GAL.Format.Astc8x6Unorm, + GAL.Format.Astc8x8Unorm, + GAL.Format.Astc10x5Unorm, + GAL.Format.Astc10x6Unorm, + GAL.Format.Astc10x8Unorm, + GAL.Format.Astc10x10Unorm, + GAL.Format.Astc12x10Unorm, + GAL.Format.Astc12x12Unorm, + GAL.Format.Astc4x4Srgb, + GAL.Format.Astc5x4Srgb, + GAL.Format.Astc5x5Srgb, + GAL.Format.Astc6x5Srgb, + GAL.Format.Astc6x6Srgb, + GAL.Format.Astc8x5Srgb, + GAL.Format.Astc8x6Srgb, + GAL.Format.Astc8x8Srgb, + GAL.Format.Astc10x5Srgb, + GAL.Format.Astc10x6Srgb, + GAL.Format.Astc10x8Srgb, + GAL.Format.Astc10x10Srgb, + GAL.Format.Astc12x10Srgb, + GAL.Format.Astc12x12Srgb); + + PhysicalDeviceVulkan12Features featuresVk12 = new PhysicalDeviceVulkan12Features() + { + SType = StructureType.PhysicalDeviceVulkan12Features + }; + + PhysicalDeviceFeatures2 features2 = new PhysicalDeviceFeatures2() + { + SType = StructureType.PhysicalDeviceFeatures2, + PNext = &featuresVk12 + }; + + Api.GetPhysicalDeviceFeatures2(_physicalDevice.PhysicalDevice, &features2); + + var limits = _physicalDevice.PhysicalDeviceProperties.Limits; + + return new Capabilities( + api: TargetApi.Vulkan, + GpuVendor, + hasFrontFacingBug: IsIntelWindows, + hasVectorIndexingBug: Vendor == Vendor.Qualcomm, + needsFragmentOutputSpecialization: IsMoltenVk, + reduceShaderPrecision: IsMoltenVk, + supportsAstcCompression: features2.Features.TextureCompressionAstcLdr && supportsAstcFormats, + supportsBc123Compression: supportsBc123CompressionFormat, + supportsBc45Compression: supportsBc45CompressionFormat, + supportsBc67Compression: supportsBc67CompressionFormat, + supportsEtc2Compression: supportsEtc2CompressionFormat, + supports3DTextureCompression: true, + supportsBgraFormat: true, + supportsR4G4Format: false, + supportsR4G4B4A4Format: supportsR4G4B4A4Format, + supportsSnormBufferTextureFormat: true, + supports5BitComponentFormat: supports5BitComponentFormat, + supportsBlendEquationAdvanced: Capabilities.SupportsBlendEquationAdvanced, + supportsFragmentShaderInterlock: Capabilities.SupportsFragmentShaderInterlock, + supportsFragmentShaderOrderingIntel: false, + supportsGeometryShader: Capabilities.SupportsGeometryShader, + supportsGeometryShaderPassthrough: Capabilities.SupportsGeometryShaderPassthrough, + supportsImageLoadFormatted: features2.Features.ShaderStorageImageReadWithoutFormat, + supportsLayerVertexTessellation: featuresVk12.ShaderOutputLayer, + supportsMismatchingViewFormat: true, + supportsCubemapView: !IsAmdGcn, + supportsNonConstantTextureOffset: false, + supportsShaderBallot: false, + supportsTextureShadowLod: false, + supportsViewportIndexVertexTessellation: featuresVk12.ShaderOutputViewportIndex, + supportsViewportMask: Capabilities.SupportsViewportArray2, + supportsViewportSwizzle: false, + supportsIndirectParameters: true, + maximumUniformBuffersPerStage: Constants.MaxUniformBuffersPerStage, + maximumStorageBuffersPerStage: Constants.MaxStorageBuffersPerStage, + maximumTexturesPerStage: Constants.MaxTexturesPerStage, + maximumImagesPerStage: Constants.MaxImagesPerStage, + maximumComputeSharedMemorySize: (int)limits.MaxComputeSharedMemorySize, + maximumSupportedAnisotropy: (int)limits.MaxSamplerAnisotropy, + storageBufferOffsetAlignment: (int)limits.MinStorageBufferOffsetAlignment, + gatherBiasPrecision: IsIntelWindows || IsAmdWindows ? (int)Capabilities.SubTexelPrecisionBits : 0); + } + + public HardwareInfo GetHardwareInfo() + { + return new HardwareInfo(GpuVendor, GpuRenderer); + } + + public static DeviceInfo[] GetPhysicalDevices() + { + try + { + return VulkanInitialization.GetSuitablePhysicalDevices(Vk.GetApi()); + } + catch (Exception) + { + // If we got an exception here, Vulkan is most likely not supported. + return Array.Empty<DeviceInfo>(); + } + } + + private static string ParseStandardVulkanVersion(uint version) + { + return $"{version >> 22}.{(version >> 12) & 0x3FF}.{version & 0xFFF}"; + } + + private static string ParseDriverVersion(ref PhysicalDeviceProperties properties) + { + uint driverVersionRaw = properties.DriverVersion; + + // NVIDIA differ from the standard here and uses a different format. + if (properties.VendorID == 0x10DE) + { + return $"{(driverVersionRaw >> 22) & 0x3FF}.{(driverVersionRaw >> 14) & 0xFF}.{(driverVersionRaw >> 6) & 0xFF}.{driverVersionRaw & 0x3F}"; + } + else + { + return ParseStandardVulkanVersion(driverVersionRaw); + } + } + + private unsafe void PrintGpuInformation() + { + var properties = _physicalDevice.PhysicalDeviceProperties; + + string vendorName = VendorUtils.GetNameFromId(properties.VendorID); + + Vendor = VendorUtils.FromId(properties.VendorID); + + IsAmdWindows = Vendor == Vendor.Amd && OperatingSystem.IsWindows(); + IsIntelWindows = Vendor == Vendor.Intel && OperatingSystem.IsWindows(); + IsTBDR = IsMoltenVk || + Vendor == Vendor.Qualcomm || + Vendor == Vendor.ARM || + Vendor == Vendor.Broadcom || + Vendor == Vendor.ImgTec; + + GpuVendor = vendorName; + GpuRenderer = Marshal.PtrToStringAnsi((IntPtr)properties.DeviceName); + GpuVersion = $"Vulkan v{ParseStandardVulkanVersion(properties.ApiVersion)}, Driver v{ParseDriverVersion(ref properties)}"; + + IsAmdGcn = !IsMoltenVk && Vendor == Vendor.Amd && VendorUtils.AmdGcnRegex().IsMatch(GpuRenderer); + + Logger.Notice.Print(LogClass.Gpu, $"{GpuVendor} {GpuRenderer} ({GpuVersion})"); + } + + public GAL.PrimitiveTopology TopologyRemap(GAL.PrimitiveTopology topology) + { + return topology switch + { + GAL.PrimitiveTopology.Quads => GAL.PrimitiveTopology.Triangles, + GAL.PrimitiveTopology.QuadStrip => GAL.PrimitiveTopology.TriangleStrip, + GAL.PrimitiveTopology.TriangleFan => Capabilities.PortabilitySubset.HasFlag(PortabilitySubsetFlags.NoTriangleFans) ? GAL.PrimitiveTopology.Triangles : topology, + _ => topology + }; + } + + public bool TopologyUnsupported(GAL.PrimitiveTopology topology) + { + return topology switch + { + GAL.PrimitiveTopology.Quads => true, + GAL.PrimitiveTopology.TriangleFan => Capabilities.PortabilitySubset.HasFlag(PortabilitySubsetFlags.NoTriangleFans), + _ => false + }; + } + + public void Initialize(GraphicsDebugLevel logLevel) + { + SetupContext(logLevel); + + PrintGpuInformation(); + } + + internal bool NeedsVertexBufferAlignment(int attrScalarAlignment, out int alignment) + { + if (Capabilities.VertexBufferAlignment > 1) + { + alignment = (int)Capabilities.VertexBufferAlignment; + + return true; + } + else if (Vendor != Vendor.Nvidia) + { + // Vulkan requires that vertex attributes are globally aligned by their component size, + // so buffer strides that don't divide by the largest scalar element are invalid. + // Guest applications do this, NVIDIA GPUs are OK with it, others are not. + + alignment = attrScalarAlignment; + + return true; + } + + alignment = 1; + + return false; + } + + public void PreFrame() + { + SyncManager.Cleanup(); + } + + public ICounterEvent ReportCounter(CounterType type, EventHandler<ulong> resultHandler, bool hostReserved) + { + return _counters.QueueReport(type, resultHandler, hostReserved); + } + + public void ResetCounter(CounterType type) + { + _counters.QueueReset(type); + } + + public void SetBufferData(BufferHandle buffer, int offset, ReadOnlySpan<byte> data) + { + BufferManager.SetData(buffer, offset, data, _pipeline.CurrentCommandBuffer, _pipeline.EndRenderPass); + } + + public void UpdateCounters() + { + _counters.Update(); + } + + public void ResetCounterPool() + { + _counters.ResetCounterPool(); + } + + public void ResetFutureCounters(CommandBuffer cmd, int count) + { + _counters?.ResetFutureCounters(cmd, count); + } + + public void BackgroundContextAction(Action action, bool alwaysBackground = false) + { + action(); + } + + public void CreateSync(ulong id, bool strict) + { + SyncManager.Create(id, strict); + } + + public IProgram LoadProgramBinary(byte[] programBinary, bool isFragment, ShaderInfo info) + { + throw new NotImplementedException(); + } + + public void WaitSync(ulong id) + { + SyncManager.Wait(id); + } + + public ulong GetCurrentSync() + { + return SyncManager.GetCurrent(); + } + + public void SetInterruptAction(Action<Action> interruptAction) + { + InterruptAction = interruptAction; + } + + public void Screenshot() + { + _window.ScreenCaptureRequested = true; + } + + public void OnScreenCaptured(ScreenCaptureImageInfo bitmap) + { + ScreenCaptured?.Invoke(this, bitmap); + } + + public unsafe void Dispose() + { + if (!_initialized) + { + return; + } + + CommandBufferPool.Dispose(); + BackgroundResources.Dispose(); + _counters.Dispose(); + _window.Dispose(); + HelperShader.Dispose(); + _pipeline.Dispose(); + BufferManager.Dispose(); + DescriptorSetManager.Dispose(); + PipelineLayoutCache.Dispose(); + + MemoryAllocator.Dispose(); + + foreach (var shader in Shaders) + { + shader.Dispose(); + } + + foreach (var texture in Textures) + { + texture.Release(); + } + + foreach (var sampler in Samplers) + { + sampler.Dispose(); + } + + SurfaceApi.DestroySurface(_instance.Instance, _surface, null); + + Api.DestroyDevice(_device, null); + + _debugMessenger.Dispose(); + + // Last step destroy the instance + _instance.Dispose(); + } + } +}
\ No newline at end of file |