Compare commits

...

4 Commits

Author SHA1 Message Date
Mary
c95be55091 vulkan: Cleanup PhysicalDevice and Instance querying (#4632)
* vulkan: Move most of the properties enumeration to VulkanPhysicalDevice

That clean up a bit of duplicate logic.
Also move to use an hashset for device extensions.

* vulkan: Move instance querying to VulkanInstance

Also cleanup code to use span when possible instead of unsafe pointers.

* Address gdkchan's comments
2023-04-05 14:48:38 -03:00
dependabot[bot]
63dedbda86 nuget: bump System.IdentityModel.Tokens.Jwt from 6.27.0 to 6.28.1 (#4639)
Bumps [System.IdentityModel.Tokens.Jwt](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet) from 6.27.0 to 6.28.1.
- [Release notes](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/releases)
- [Changelog](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/dev/CHANGELOG.md)
- [Commits](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/compare/6.27.0...6.28.1)

---
updated-dependencies:
- dependency-name: System.IdentityModel.Tokens.Jwt
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-05 07:55:57 +02:00
gdkchan
c532118d94 Use index fragment shader output when dual source blend is enabled (#4404)
* Use index fragment shader output when dual source blend is enabled

* Shader cache version bump

* Actually set DualSourceBlendEnabled to true

* Fix XML doc

---------

Co-authored-by: Ac_K <Acoustik666@gmail.com>
2023-04-05 05:25:19 +02:00
TSRBerry
52d6f2e656 hle: Set ProcessResult name from NACP (#4633)
* Extract titleName from nacp

* Address formatting feedback

* Check if the desired language is actually available
2023-04-05 03:34:21 +02:00
20 changed files with 480 additions and 249 deletions

View File

@@ -44,7 +44,7 @@
<PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta11" /> <PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta11" />
<PackageVersion Include="SPB" Version="0.0.4-build28" /> <PackageVersion Include="SPB" Version="0.0.4-build28" />
<PackageVersion Include="System.Drawing.Common" Version="7.0.0" /> <PackageVersion Include="System.Drawing.Common" Version="7.0.0" />
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="6.27.0" /> <PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="6.28.1" />
<PackageVersion Include="System.IO.Hashing" Version="7.0.0" /> <PackageVersion Include="System.IO.Hashing" Version="7.0.0" />
<PackageVersion Include="System.Management" Version="7.0.0" /> <PackageVersion Include="System.Management" Version="7.0.0" />
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" /> <PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" />

View File

@@ -38,4 +38,25 @@ namespace Ryujinx.Graphics.GAL
Src1AlphaGl = 0xc902, Src1AlphaGl = 0xc902,
OneMinusSrc1AlphaGl = 0xc903 OneMinusSrc1AlphaGl = 0xc903
} }
public static class BlendFactorExtensions
{
public static bool IsDualSource(this BlendFactor factor)
{
switch (factor)
{
case BlendFactor.Src1Color:
case BlendFactor.Src1ColorGl:
case BlendFactor.Src1Alpha:
case BlendFactor.Src1AlphaGl:
case BlendFactor.OneMinusSrc1Color:
case BlendFactor.OneMinusSrc1ColorGl:
case BlendFactor.OneMinusSrc1Alpha:
case BlendFactor.OneMinusSrc1AlphaGl:
return true;
}
return false;
}
}
} }

View File

@@ -328,5 +328,19 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
Signal(); Signal();
} }
} }
/// <summary>
/// Sets the dual-source blend enabled state.
/// </summary>
/// <param name="enabled">True if blending is enabled and using dual-source blend</param>
public void SetDualSourceBlendEnabled(bool enabled)
{
if (enabled != _graphics.DualSourceBlendEnable)
{
_graphics.DualSourceBlendEnable = enabled;
Signal();
}
}
} }
} }

View File

@@ -1183,6 +1183,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
bool blendIndependent = _state.State.BlendIndependent; bool blendIndependent = _state.State.BlendIndependent;
ColorF blendConstant = _state.State.BlendConstant; ColorF blendConstant = _state.State.BlendConstant;
bool dualSourceBlendEnabled = false;
if (blendIndependent) if (blendIndependent)
{ {
for (int index = 0; index < Constants.TotalRenderTargets; index++) for (int index = 0; index < Constants.TotalRenderTargets; index++)
@@ -1200,6 +1202,15 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
FilterBlendFactor(blend.AlphaSrcFactor, index), FilterBlendFactor(blend.AlphaSrcFactor, index),
FilterBlendFactor(blend.AlphaDstFactor, index)); FilterBlendFactor(blend.AlphaDstFactor, index));
if (enable &&
(blend.ColorSrcFactor.IsDualSource() ||
blend.ColorDstFactor.IsDualSource() ||
blend.AlphaSrcFactor.IsDualSource() ||
blend.AlphaDstFactor.IsDualSource()))
{
dualSourceBlendEnabled = true;
}
_pipeline.BlendDescriptors[index] = descriptor; _pipeline.BlendDescriptors[index] = descriptor;
_context.Renderer.Pipeline.SetBlendState(index, descriptor); _context.Renderer.Pipeline.SetBlendState(index, descriptor);
} }
@@ -1219,12 +1230,23 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
FilterBlendFactor(blend.AlphaSrcFactor, 0), FilterBlendFactor(blend.AlphaSrcFactor, 0),
FilterBlendFactor(blend.AlphaDstFactor, 0)); FilterBlendFactor(blend.AlphaDstFactor, 0));
if (enable &&
(blend.ColorSrcFactor.IsDualSource() ||
blend.ColorDstFactor.IsDualSource() ||
blend.AlphaSrcFactor.IsDualSource() ||
blend.AlphaDstFactor.IsDualSource()))
{
dualSourceBlendEnabled = true;
}
for (int index = 0; index < Constants.TotalRenderTargets; index++) for (int index = 0; index < Constants.TotalRenderTargets; index++)
{ {
_pipeline.BlendDescriptors[index] = descriptor; _pipeline.BlendDescriptors[index] = descriptor;
_context.Renderer.Pipeline.SetBlendState(index, descriptor); _context.Renderer.Pipeline.SetBlendState(index, descriptor);
} }
} }
_currentSpecState.SetDualSourceBlendEnabled(dualSourceBlendEnabled);
} }
/// <summary> /// <summary>

View File

@@ -141,6 +141,12 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
return _oldSpecState.GraphicsState.HasConstantBufferDrawParameters; return _oldSpecState.GraphicsState.HasConstantBufferDrawParameters;
} }
/// <inheritdoc/>
public bool QueryDualSourceBlendEnable()
{
return _oldSpecState.GraphicsState.DualSourceBlendEnable;
}
/// <inheritdoc/> /// <inheritdoc/>
public InputTopology QueryPrimitiveTopology() public InputTopology QueryPrimitiveTopology()
{ {

View File

@@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMajor = 1;
private const ushort FileFormatVersionMinor = 2; private const ushort FileFormatVersionMinor = 2;
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
private const uint CodeGenVersion = 4368; private const uint CodeGenVersion = 4404;
private const string SharedTocFileName = "shared.toc"; private const string SharedTocFileName = "shared.toc";
private const string SharedDataFileName = "shared.data"; private const string SharedDataFileName = "shared.data";

View File

@@ -157,6 +157,12 @@ namespace Ryujinx.Graphics.Gpu.Shader
return _state.GraphicsState.HasUnalignedStorageBuffer || _state.ComputeState.HasUnalignedStorageBuffer; return _state.GraphicsState.HasUnalignedStorageBuffer || _state.ComputeState.HasUnalignedStorageBuffer;
} }
/// <inheritdoc/>
public bool QueryDualSourceBlendEnable()
{
return _state.GraphicsState.DualSourceBlendEnable;
}
/// <inheritdoc/> /// <inheritdoc/>
public InputTopology QueryPrimitiveTopology() public InputTopology QueryPrimitiveTopology()
{ {

View File

@@ -92,6 +92,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// </summary> /// </summary>
public Array8<AttributeType> FragmentOutputTypes; public Array8<AttributeType> FragmentOutputTypes;
/// <summary>
/// Indicates whether dual source blend is enabled.
/// </summary>
public bool DualSourceBlendEnable;
/// <summary> /// <summary>
/// Creates a new GPU graphics state. /// Creates a new GPU graphics state.
/// </summary> /// </summary>
@@ -111,6 +116,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <param name="hasConstantBufferDrawParameters">Indicates that the draw is writing the base vertex, base instance and draw index to Constant Buffer 0</param> /// <param name="hasConstantBufferDrawParameters">Indicates that the draw is writing the base vertex, base instance and draw index to Constant Buffer 0</param>
/// <param name="hasUnalignedStorageBuffer">Indicates that any storage buffer use is unaligned</param> /// <param name="hasUnalignedStorageBuffer">Indicates that any storage buffer use is unaligned</param>
/// <param name="fragmentOutputTypes">Type of the fragment shader outputs</param> /// <param name="fragmentOutputTypes">Type of the fragment shader outputs</param>
/// <param name="dualSourceBlendEnable">Type of the vertex attributes consumed by the shader</param>
public GpuChannelGraphicsState( public GpuChannelGraphicsState(
bool earlyZForce, bool earlyZForce,
PrimitiveTopology topology, PrimitiveTopology topology,
@@ -127,7 +133,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
ref Array32<AttributeType> attributeTypes, ref Array32<AttributeType> attributeTypes,
bool hasConstantBufferDrawParameters, bool hasConstantBufferDrawParameters,
bool hasUnalignedStorageBuffer, bool hasUnalignedStorageBuffer,
ref Array8<AttributeType> fragmentOutputTypes) ref Array8<AttributeType> fragmentOutputTypes,
bool dualSourceBlendEnable)
{ {
EarlyZForce = earlyZForce; EarlyZForce = earlyZForce;
Topology = topology; Topology = topology;
@@ -145,6 +152,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
HasConstantBufferDrawParameters = hasConstantBufferDrawParameters; HasConstantBufferDrawParameters = hasConstantBufferDrawParameters;
HasUnalignedStorageBuffer = hasUnalignedStorageBuffer; HasUnalignedStorageBuffer = hasUnalignedStorageBuffer;
FragmentOutputTypes = fragmentOutputTypes; FragmentOutputTypes = fragmentOutputTypes;
DualSourceBlendEnable = dualSourceBlendEnable;
} }
} }
} }

View File

@@ -535,6 +535,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
return false; return false;
} }
if (graphicsState.DualSourceBlendEnable != GraphicsState.DualSourceBlendEnable)
{
return false;
}
return Matches(channel, ref poolState, checkTextures, isCompute: false); return Matches(channel, ref poolState, checkTextures, isCompute: false);
} }

View File

@@ -833,31 +833,13 @@ namespace Ryujinx.Graphics.OpenGL
(BlendingFactorSrc)blend.AlphaSrcFactor.Convert(), (BlendingFactorSrc)blend.AlphaSrcFactor.Convert(),
(BlendingFactorDest)blend.AlphaDstFactor.Convert()); (BlendingFactorDest)blend.AlphaDstFactor.Convert());
static bool IsDualSource(BlendFactor factor)
{
switch (factor)
{
case BlendFactor.Src1Color:
case BlendFactor.Src1ColorGl:
case BlendFactor.Src1Alpha:
case BlendFactor.Src1AlphaGl:
case BlendFactor.OneMinusSrc1Color:
case BlendFactor.OneMinusSrc1ColorGl:
case BlendFactor.OneMinusSrc1Alpha:
case BlendFactor.OneMinusSrc1AlphaGl:
return true;
}
return false;
}
EnsureFramebuffer(); EnsureFramebuffer();
_framebuffer.SetDualSourceBlend( _framebuffer.SetDualSourceBlend(
IsDualSource(blend.ColorSrcFactor) || blend.ColorSrcFactor.IsDualSource() ||
IsDualSource(blend.ColorDstFactor) || blend.ColorDstFactor.IsDualSource() ||
IsDualSource(blend.AlphaSrcFactor) || blend.AlphaSrcFactor.IsDualSource() ||
IsDualSource(blend.AlphaDstFactor)); blend.AlphaDstFactor.IsDualSource());
if (_blendConstant != blend.BlendConstant) if (_blendConstant != blend.BlendConstant)
{ {

View File

@@ -612,6 +612,19 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
else else
{ {
int usedAttributes = context.Config.UsedOutputAttributes; int usedAttributes = context.Config.UsedOutputAttributes;
if (context.Config.Stage == ShaderStage.Fragment && context.Config.GpuAccessor.QueryDualSourceBlendEnable())
{
int firstOutput = BitOperations.TrailingZeroCount(usedAttributes);
int mask = 3 << firstOutput;
if ((usedAttributes & mask) == mask)
{
usedAttributes &= ~mask;
DeclareOutputDualSourceBlendAttribute(context, firstOutput);
}
}
while (usedAttributes != 0) while (usedAttributes != 0)
{ {
int index = BitOperations.TrailingZeroCount(usedAttributes); int index = BitOperations.TrailingZeroCount(usedAttributes);
@@ -690,6 +703,15 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
} }
} }
private static void DeclareOutputDualSourceBlendAttribute(CodeGenContext context, int attr)
{
string name = $"{DefaultNames.OAttributePrefix}{attr}";
string name2 = $"{DefaultNames.OAttributePrefix}{(attr + 1)}";
context.AppendLine($"layout (location = {attr}, index = 0) out vec4 {name};");
context.AppendLine($"layout (location = {attr}, index = 1) out vec4 {name2};");
}
private static void DeclareUsedOutputAttributesPerPatch(CodeGenContext context, HashSet<int> attrs) private static void DeclareUsedOutputAttributesPerPatch(CodeGenContext context, HashSet<int> attrs)
{ {
foreach (int attr in attrs.Order()) foreach (int attr in attrs.Order())

View File

@@ -6,6 +6,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Numerics;
using static Spv.Specification; using static Spv.Specification;
namespace Ryujinx.Graphics.Shader.CodeGen.Spirv namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
@@ -622,8 +623,28 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
else if (attr >= AttributeConsts.FragmentOutputColorBase && attr < AttributeConsts.FragmentOutputColorEnd) else if (attr >= AttributeConsts.FragmentOutputColorBase && attr < AttributeConsts.FragmentOutputColorEnd)
{ {
int location = (attr - AttributeConsts.FragmentOutputColorBase) / 16; int location = (attr - AttributeConsts.FragmentOutputColorBase) / 16;
if (context.Config.Stage == ShaderStage.Fragment && context.Config.GpuAccessor.QueryDualSourceBlendEnable())
{
int firstLocation = BitOperations.TrailingZeroCount(context.Config.UsedOutputAttributes);
int index = location - firstLocation;
int mask = 3 << firstLocation;
if ((uint)index < 2 && (context.Config.UsedOutputAttributes & mask) == mask)
{
context.Decorate(spvVar, Decoration.Location, (LiteralInteger)firstLocation);
context.Decorate(spvVar, Decoration.Index, (LiteralInteger)index);
}
else
{
context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location); context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location);
} }
}
else
{
context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location);
}
}
if (!isOutAttr) if (!isOutAttr)
{ {

View File

@@ -205,6 +205,15 @@ namespace Ryujinx.Graphics.Shader
return false; return false;
} }
/// <summary>
/// Queries dual source blend state.
/// </summary>
/// <returns>True if blending is enabled with a dual source blend equation, false otherwise</returns>
bool QueryDualSourceBlendEnable()
{
return false;
}
/// <summary> /// <summary>
/// Queries host about the presence of the FrontFacing built-in variable bug. /// Queries host about the presence of the FrontFacing built-in variable bug.
/// </summary> /// </summary>

View File

@@ -9,21 +9,18 @@ namespace Ryujinx.Graphics.Vulkan
private ulong MaxDeviceMemoryUsageEstimate = 16UL * 1024 * 1024 * 1024; private ulong MaxDeviceMemoryUsageEstimate = 16UL * 1024 * 1024 * 1024;
private readonly Vk _api; private readonly Vk _api;
private readonly PhysicalDevice _physicalDevice; private readonly VulkanPhysicalDevice _physicalDevice;
private readonly Device _device; private readonly Device _device;
private readonly List<MemoryAllocatorBlockList> _blockLists; private readonly List<MemoryAllocatorBlockList> _blockLists;
private readonly int _blockAlignment; private readonly int _blockAlignment;
private readonly PhysicalDeviceMemoryProperties _physicalDeviceMemoryProperties;
public MemoryAllocator(Vk api, PhysicalDevice physicalDevice, Device device, uint maxMemoryAllocationCount) public MemoryAllocator(Vk api, VulkanPhysicalDevice physicalDevice, Device device)
{ {
_api = api; _api = api;
_physicalDevice = physicalDevice; _physicalDevice = physicalDevice;
_device = device; _device = device;
_blockLists = new List<MemoryAllocatorBlockList>(); _blockLists = new List<MemoryAllocatorBlockList>();
_blockAlignment = (int)Math.Min(int.MaxValue, MaxDeviceMemoryUsageEstimate / (ulong)maxMemoryAllocationCount); _blockAlignment = (int)Math.Min(int.MaxValue, MaxDeviceMemoryUsageEstimate / (ulong)_physicalDevice.PhysicalDeviceProperties.Limits.MaxMemoryAllocationCount);
_api.GetPhysicalDeviceMemoryProperties(_physicalDevice, out _physicalDeviceMemoryProperties);
} }
public MemoryAllocation AllocateDeviceMemory( public MemoryAllocation AllocateDeviceMemory(
@@ -64,9 +61,9 @@ namespace Ryujinx.Graphics.Vulkan
uint memoryTypeBits, uint memoryTypeBits,
MemoryPropertyFlags flags) MemoryPropertyFlags flags)
{ {
for (int i = 0; i < _physicalDeviceMemoryProperties.MemoryTypeCount; i++) for (int i = 0; i < _physicalDevice.PhysicalDeviceMemoryProperties.MemoryTypeCount; i++)
{ {
var type = _physicalDeviceMemoryProperties.MemoryTypes[i]; var type = _physicalDevice.PhysicalDeviceMemoryProperties.MemoryTypes[i];
if ((memoryTypeBits & (1 << i)) != 0) if ((memoryTypeBits & (1 << i)) != 0)
{ {
@@ -80,15 +77,11 @@ namespace Ryujinx.Graphics.Vulkan
return -1; return -1;
} }
public static bool IsDeviceMemoryShared(Vk api, PhysicalDevice physicalDevice) public static bool IsDeviceMemoryShared(VulkanPhysicalDevice physicalDevice)
{ {
// The device is regarded as having shared memory if all heaps have the device local bit. for (int i = 0; i < physicalDevice.PhysicalDeviceMemoryProperties.MemoryHeapCount; i++)
api.GetPhysicalDeviceMemoryProperties(physicalDevice, out var properties);
for (int i = 0; i < properties.MemoryHeapCount; i++)
{ {
if (!properties.MemoryHeaps[i].Flags.HasFlag(MemoryHeapFlags.DeviceLocalBit)) if (!physicalDevice.PhysicalDeviceMemoryProperties.MemoryHeaps[i].Flags.HasFlag(MemoryHeapFlags.DeviceLocalBit))
{ {
return false; return false;
} }

View File

@@ -47,36 +47,24 @@ namespace Ryujinx.Graphics.Vulkan
KhrSwapchain.ExtensionName KhrSwapchain.ExtensionName
}; };
internal static Instance CreateInstance(Vk api, GraphicsDebugLevel logLevel, string[] requiredExtensions) internal static VulkanInstance CreateInstance(Vk api, GraphicsDebugLevel logLevel, string[] requiredExtensions)
{ {
var enabledLayers = new List<string>(); var enabledLayers = new List<string>();
var instanceExtensions = VulkanInstance.GetInstanceExtensions(api);
var instanceLayers = VulkanInstance.GetInstanceLayers(api);
void AddAvailableLayer(string layerName) void AddAvailableLayer(string layerName)
{ {
uint layerPropertiesCount; if (instanceLayers.Contains(layerName))
api.EnumerateInstanceLayerProperties(&layerPropertiesCount, null).ThrowOnError();
LayerProperties[] layerProperties = new LayerProperties[layerPropertiesCount];
fixed (LayerProperties* pLayerProperties = layerProperties)
{
api.EnumerateInstanceLayerProperties(&layerPropertiesCount, layerProperties).ThrowOnError();
for (int i = 0; i < layerPropertiesCount; i++)
{
string currentLayerName = Marshal.PtrToStringAnsi((IntPtr)pLayerProperties[i].LayerName);
if (currentLayerName == layerName)
{ {
enabledLayers.Add(layerName); enabledLayers.Add(layerName);
return;
} }
} else
} {
Logger.Warning?.Print(LogClass.Gpu, $"Missing layer {layerName}"); Logger.Warning?.Print(LogClass.Gpu, $"Missing layer {layerName}");
} }
}
if (logLevel != GraphicsDebugLevel.None) if (logLevel != GraphicsDebugLevel.None)
{ {
@@ -85,7 +73,7 @@ namespace Ryujinx.Graphics.Vulkan
var enabledExtensions = requiredExtensions; var enabledExtensions = requiredExtensions;
if (api.IsInstanceExtensionPresent("VK_EXT_debug_utils")) if (instanceExtensions.Contains("VK_EXT_debug_utils"))
{ {
enabledExtensions = enabledExtensions.Append(ExtDebugUtils.ExtensionName).ToArray(); enabledExtensions = enabledExtensions.Append(ExtDebugUtils.ExtensionName).ToArray();
} }
@@ -124,7 +112,7 @@ namespace Ryujinx.Graphics.Vulkan
EnabledLayerCount = (uint)enabledLayers.Count EnabledLayerCount = (uint)enabledLayers.Count
}; };
api.CreateInstance(in instanceCreateInfo, null, out var instance).ThrowOnError(); Result result = VulkanInstance.Create(api, ref instanceCreateInfo, out var instance);
Marshal.FreeHGlobal(appName); Marshal.FreeHGlobal(appName);
@@ -138,21 +126,14 @@ namespace Ryujinx.Graphics.Vulkan
Marshal.FreeHGlobal(ppEnabledLayers[i]); Marshal.FreeHGlobal(ppEnabledLayers[i]);
} }
result.ThrowOnError();
return instance; return instance;
} }
internal static PhysicalDevice FindSuitablePhysicalDevice(Vk api, Instance instance, SurfaceKHR surface, string preferredGpuId) internal static VulkanPhysicalDevice FindSuitablePhysicalDevice(Vk api, VulkanInstance instance, SurfaceKHR surface, string preferredGpuId)
{ {
uint physicalDeviceCount; instance.EnumeratePhysicalDevices(out var physicalDevices).ThrowOnError();
api.EnumeratePhysicalDevices(instance, &physicalDeviceCount, null).ThrowOnError();
PhysicalDevice[] physicalDevices = new PhysicalDevice[physicalDeviceCount];
fixed (PhysicalDevice* pPhysicalDevices = physicalDevices)
{
api.EnumeratePhysicalDevices(instance, &physicalDeviceCount, pPhysicalDevices).ThrowOnError();
}
// First we try to pick the the user preferred GPU. // First we try to pick the the user preferred GPU.
for (int i = 0; i < physicalDevices.Length; i++) for (int i = 0; i < physicalDevices.Length; i++)
@@ -198,76 +179,41 @@ namespace Ryujinx.Graphics.Vulkan
EnabledLayerCount = 0 EnabledLayerCount = 0
}; };
api.CreateInstance(in instanceCreateInfo, null, out var instance).ThrowOnError(); Result result = VulkanInstance.Create(api, ref instanceCreateInfo, out var rawInstance);
// We ensure that vkEnumerateInstanceVersion is present (added in 1.1).
// If the instance doesn't support it, no device is going to be 1.1 compatible.
if (api.GetInstanceProcAddr(instance, "vkEnumerateInstanceVersion") == IntPtr.Zero)
{
api.DestroyInstance(instance, null);
return Array.Empty<DeviceInfo>();
}
// We currently assume that the instance is compatible with Vulkan 1.2
// TODO: Remove this once we relax our initialization codepaths.
uint instanceApiVerison = 0;
api.EnumerateInstanceVersion(ref instanceApiVerison).ThrowOnError();
if (instanceApiVerison < MinimalInstanceVulkanVersion)
{
api.DestroyInstance(instance, null);
return Array.Empty<DeviceInfo>();
}
Marshal.FreeHGlobal(appName); Marshal.FreeHGlobal(appName);
uint physicalDeviceCount; result.ThrowOnError();
api.EnumeratePhysicalDevices(instance, &physicalDeviceCount, null).ThrowOnError(); using VulkanInstance instance = rawInstance;
PhysicalDevice[] physicalDevices = new PhysicalDevice[physicalDeviceCount]; // We currently assume that the instance is compatible with Vulkan 1.2
// TODO: Remove this once we relax our initialization codepaths.
fixed (PhysicalDevice* pPhysicalDevices = physicalDevices) if (instance.InstanceVersion < MinimalInstanceVulkanVersion)
{ {
api.EnumeratePhysicalDevices(instance, &physicalDeviceCount, pPhysicalDevices).ThrowOnError(); return Array.Empty<DeviceInfo>();
} }
DeviceInfo[] devices = new DeviceInfo[physicalDevices.Length]; instance.EnumeratePhysicalDevices(out VulkanPhysicalDevice[] physicalDevices).ThrowOnError();
for (int i = 0; i < physicalDevices.Length; i++) List<DeviceInfo> deviceInfos = new List<DeviceInfo>();
foreach (VulkanPhysicalDevice physicalDevice in physicalDevices)
{ {
var physicalDevice = physicalDevices[i]; if (physicalDevice.PhysicalDeviceProperties.ApiVersion < MinimalVulkanVersion)
api.GetPhysicalDeviceProperties(physicalDevice, out var properties);
if (properties.ApiVersion < MinimalVulkanVersion)
{ {
continue; continue;
} }
devices[i] = new DeviceInfo( deviceInfos.Add(physicalDevice.ToDeviceInfo());
StringFromIdPair(properties.VendorID, properties.DeviceID),
VendorUtils.GetNameFromId(properties.VendorID),
Marshal.PtrToStringAnsi((IntPtr)properties.DeviceName),
properties.DeviceType == PhysicalDeviceType.DiscreteGpu);
} }
api.DestroyInstance(instance, null); return deviceInfos.ToArray();
return devices;
} }
public static string StringFromIdPair(uint vendorId, uint deviceId) private static bool IsPreferredAndSuitableDevice(Vk api, VulkanPhysicalDevice physicalDevice, SurfaceKHR surface, string preferredGpuId)
{ {
return $"0x{vendorId:X}_0x{deviceId:X}"; if (physicalDevice.Id != preferredGpuId)
}
private static bool IsPreferredAndSuitableDevice(Vk api, PhysicalDevice physicalDevice, SurfaceKHR surface, string preferredGpuId)
{
api.GetPhysicalDeviceProperties(physicalDevice, out var properties);
if (StringFromIdPair(properties.VendorID, properties.DeviceID) != preferredGpuId)
{ {
return false; return false;
} }
@@ -275,68 +221,47 @@ namespace Ryujinx.Graphics.Vulkan
return IsSuitableDevice(api, physicalDevice, surface); return IsSuitableDevice(api, physicalDevice, surface);
} }
private static bool IsSuitableDevice(Vk api, PhysicalDevice physicalDevice, SurfaceKHR surface) private static bool IsSuitableDevice(Vk api, VulkanPhysicalDevice physicalDevice, SurfaceKHR surface)
{ {
int extensionMatches = 0; int extensionMatches = 0;
uint propertiesCount;
api.EnumerateDeviceExtensionProperties(physicalDevice, (byte*)null, &propertiesCount, null).ThrowOnError(); foreach (string requiredExtension in _requiredExtensions)
ExtensionProperties[] extensionProperties = new ExtensionProperties[propertiesCount];
fixed (ExtensionProperties* pExtensionProperties = extensionProperties)
{ {
api.EnumerateDeviceExtensionProperties(physicalDevice, (byte*)null, &propertiesCount, pExtensionProperties).ThrowOnError(); if (physicalDevice.IsDeviceExtensionPresent(requiredExtension))
for (int i = 0; i < propertiesCount; i++)
{
string extensionName = Marshal.PtrToStringAnsi((IntPtr)pExtensionProperties[i].ExtensionName);
if (_requiredExtensions.Contains(extensionName))
{ {
extensionMatches++; extensionMatches++;
} }
} }
}
return extensionMatches == _requiredExtensions.Length && FindSuitableQueueFamily(api, physicalDevice, surface, out _) != InvalidIndex; return extensionMatches == _requiredExtensions.Length && FindSuitableQueueFamily(api, physicalDevice, surface, out _) != InvalidIndex;
} }
internal static uint FindSuitableQueueFamily(Vk api, PhysicalDevice physicalDevice, SurfaceKHR surface, out uint queueCount) internal static uint FindSuitableQueueFamily(Vk api, VulkanPhysicalDevice physicalDevice, SurfaceKHR surface, out uint queueCount)
{ {
const QueueFlags RequiredFlags = QueueFlags.GraphicsBit | QueueFlags.ComputeBit; const QueueFlags RequiredFlags = QueueFlags.GraphicsBit | QueueFlags.ComputeBit;
var khrSurface = new KhrSurface(api.Context); var khrSurface = new KhrSurface(api.Context);
uint propertiesCount; for (uint index = 0; index < physicalDevice.QueueFamilyProperties.Length; index++)
api.GetPhysicalDeviceQueueFamilyProperties(physicalDevice, &propertiesCount, null);
QueueFamilyProperties[] properties = new QueueFamilyProperties[propertiesCount];
fixed (QueueFamilyProperties* pProperties = properties)
{ {
api.GetPhysicalDeviceQueueFamilyProperties(physicalDevice, &propertiesCount, pProperties); ref QueueFamilyProperties property = ref physicalDevice.QueueFamilyProperties[index];
}
for (uint index = 0; index < propertiesCount; index++) khrSurface.GetPhysicalDeviceSurfaceSupport(physicalDevice.PhysicalDevice, index, surface, out var surfaceSupported).ThrowOnError();
if (property.QueueFlags.HasFlag(RequiredFlags) && surfaceSupported)
{ {
var queueFlags = properties[index].QueueFlags; queueCount = property.QueueCount;
khrSurface.GetPhysicalDeviceSurfaceSupport(physicalDevice, index, surface, out var surfaceSupported).ThrowOnError();
if (queueFlags.HasFlag(RequiredFlags) && surfaceSupported)
{
queueCount = properties[index].QueueCount;
return index; return index;
} }
} }
queueCount = 0; queueCount = 0;
return InvalidIndex; return InvalidIndex;
} }
public static Device CreateDevice(Vk api, PhysicalDevice physicalDevice, uint queueFamilyIndex, string[] supportedExtensions, uint queueCount) internal static Device CreateDevice(Vk api, VulkanPhysicalDevice physicalDevice, uint queueFamilyIndex, uint queueCount)
{ {
if (queueCount > QueuesCount) if (queueCount > QueuesCount)
{ {
@@ -358,8 +283,7 @@ namespace Ryujinx.Graphics.Vulkan
PQueuePriorities = queuePriorities PQueuePriorities = queuePriorities
}; };
api.GetPhysicalDeviceProperties(physicalDevice, out var properties); bool useRobustBufferAccess = VendorUtils.FromId(physicalDevice.PhysicalDeviceProperties.VendorID) == Vendor.Nvidia;
bool useRobustBufferAccess = VendorUtils.FromId(properties.VendorID) == Vendor.Nvidia;
PhysicalDeviceFeatures2 features2 = new PhysicalDeviceFeatures2() PhysicalDeviceFeatures2 features2 = new PhysicalDeviceFeatures2()
{ {
@@ -380,7 +304,7 @@ namespace Ryujinx.Graphics.Vulkan
PNext = features2.PNext PNext = features2.PNext
}; };
if (supportedExtensions.Contains("VK_EXT_custom_border_color")) if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_custom_border_color"))
{ {
features2.PNext = &supportedFeaturesCustomBorderColor; features2.PNext = &supportedFeaturesCustomBorderColor;
} }
@@ -391,7 +315,7 @@ namespace Ryujinx.Graphics.Vulkan
PNext = features2.PNext PNext = features2.PNext
}; };
if (supportedExtensions.Contains("VK_EXT_primitive_topology_list_restart")) if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_primitive_topology_list_restart"))
{ {
features2.PNext = &supportedFeaturesPrimitiveTopologyListRestart; features2.PNext = &supportedFeaturesPrimitiveTopologyListRestart;
} }
@@ -402,7 +326,7 @@ namespace Ryujinx.Graphics.Vulkan
PNext = features2.PNext PNext = features2.PNext
}; };
if (supportedExtensions.Contains(ExtTransformFeedback.ExtensionName)) if (physicalDevice.IsDeviceExtensionPresent(ExtTransformFeedback.ExtensionName))
{ {
features2.PNext = &supportedFeaturesTransformFeedback; features2.PNext = &supportedFeaturesTransformFeedback;
} }
@@ -412,14 +336,14 @@ namespace Ryujinx.Graphics.Vulkan
SType = StructureType.PhysicalDeviceRobustness2FeaturesExt SType = StructureType.PhysicalDeviceRobustness2FeaturesExt
}; };
if (supportedExtensions.Contains("VK_EXT_robustness2")) if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_robustness2"))
{ {
supportedFeaturesRobustness2.PNext = features2.PNext; supportedFeaturesRobustness2.PNext = features2.PNext;
features2.PNext = &supportedFeaturesRobustness2; features2.PNext = &supportedFeaturesRobustness2;
} }
api.GetPhysicalDeviceFeatures2(physicalDevice, &features2); api.GetPhysicalDeviceFeatures2(physicalDevice.PhysicalDevice, &features2);
var supportedFeatures = features2.Features; var supportedFeatures = features2.Features;
@@ -452,7 +376,7 @@ namespace Ryujinx.Graphics.Vulkan
PhysicalDeviceTransformFeedbackFeaturesEXT featuresTransformFeedback; PhysicalDeviceTransformFeedbackFeaturesEXT featuresTransformFeedback;
if (supportedExtensions.Contains(ExtTransformFeedback.ExtensionName)) if (physicalDevice.IsDeviceExtensionPresent(ExtTransformFeedback.ExtensionName))
{ {
featuresTransformFeedback = new PhysicalDeviceTransformFeedbackFeaturesEXT() featuresTransformFeedback = new PhysicalDeviceTransformFeedbackFeaturesEXT()
{ {
@@ -466,7 +390,7 @@ namespace Ryujinx.Graphics.Vulkan
PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT featuresPrimitiveTopologyListRestart; PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT featuresPrimitiveTopologyListRestart;
if (supportedExtensions.Contains("VK_EXT_primitive_topology_list_restart")) if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_primitive_topology_list_restart"))
{ {
featuresPrimitiveTopologyListRestart = new PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT() featuresPrimitiveTopologyListRestart = new PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT()
{ {
@@ -481,7 +405,7 @@ namespace Ryujinx.Graphics.Vulkan
PhysicalDeviceRobustness2FeaturesEXT featuresRobustness2; PhysicalDeviceRobustness2FeaturesEXT featuresRobustness2;
if (supportedExtensions.Contains("VK_EXT_robustness2")) if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_robustness2"))
{ {
featuresRobustness2 = new PhysicalDeviceRobustness2FeaturesEXT() featuresRobustness2 = new PhysicalDeviceRobustness2FeaturesEXT()
{ {
@@ -497,7 +421,7 @@ namespace Ryujinx.Graphics.Vulkan
{ {
SType = StructureType.PhysicalDeviceExtendedDynamicStateFeaturesExt, SType = StructureType.PhysicalDeviceExtendedDynamicStateFeaturesExt,
PNext = pExtendedFeatures, PNext = pExtendedFeatures,
ExtendedDynamicState = supportedExtensions.Contains(ExtExtendedDynamicState.ExtensionName) ExtendedDynamicState = physicalDevice.IsDeviceExtensionPresent(ExtExtendedDynamicState.ExtensionName)
}; };
pExtendedFeatures = &featuresExtendedDynamicState; pExtendedFeatures = &featuresExtendedDynamicState;
@@ -515,16 +439,16 @@ namespace Ryujinx.Graphics.Vulkan
{ {
SType = StructureType.PhysicalDeviceVulkan12Features, SType = StructureType.PhysicalDeviceVulkan12Features,
PNext = pExtendedFeatures, PNext = pExtendedFeatures,
DescriptorIndexing = supportedExtensions.Contains("VK_EXT_descriptor_indexing"), DescriptorIndexing = physicalDevice.IsDeviceExtensionPresent("VK_EXT_descriptor_indexing"),
DrawIndirectCount = supportedExtensions.Contains(KhrDrawIndirectCount.ExtensionName), DrawIndirectCount = physicalDevice.IsDeviceExtensionPresent(KhrDrawIndirectCount.ExtensionName),
UniformBufferStandardLayout = supportedExtensions.Contains("VK_KHR_uniform_buffer_standard_layout") UniformBufferStandardLayout = physicalDevice.IsDeviceExtensionPresent("VK_KHR_uniform_buffer_standard_layout")
}; };
pExtendedFeatures = &featuresVk12; pExtendedFeatures = &featuresVk12;
PhysicalDeviceIndexTypeUint8FeaturesEXT featuresIndexU8; PhysicalDeviceIndexTypeUint8FeaturesEXT featuresIndexU8;
if (supportedExtensions.Contains("VK_EXT_index_type_uint8")) if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_index_type_uint8"))
{ {
featuresIndexU8 = new PhysicalDeviceIndexTypeUint8FeaturesEXT() featuresIndexU8 = new PhysicalDeviceIndexTypeUint8FeaturesEXT()
{ {
@@ -538,7 +462,7 @@ namespace Ryujinx.Graphics.Vulkan
PhysicalDeviceFragmentShaderInterlockFeaturesEXT featuresFragmentShaderInterlock; PhysicalDeviceFragmentShaderInterlockFeaturesEXT featuresFragmentShaderInterlock;
if (supportedExtensions.Contains("VK_EXT_fragment_shader_interlock")) if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_fragment_shader_interlock"))
{ {
featuresFragmentShaderInterlock = new PhysicalDeviceFragmentShaderInterlockFeaturesEXT() featuresFragmentShaderInterlock = new PhysicalDeviceFragmentShaderInterlockFeaturesEXT()
{ {
@@ -552,7 +476,7 @@ namespace Ryujinx.Graphics.Vulkan
PhysicalDeviceSubgroupSizeControlFeaturesEXT featuresSubgroupSizeControl; PhysicalDeviceSubgroupSizeControlFeaturesEXT featuresSubgroupSizeControl;
if (supportedExtensions.Contains("VK_EXT_subgroup_size_control")) if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_subgroup_size_control"))
{ {
featuresSubgroupSizeControl = new PhysicalDeviceSubgroupSizeControlFeaturesEXT() featuresSubgroupSizeControl = new PhysicalDeviceSubgroupSizeControlFeaturesEXT()
{ {
@@ -566,7 +490,7 @@ namespace Ryujinx.Graphics.Vulkan
PhysicalDeviceCustomBorderColorFeaturesEXT featuresCustomBorderColor; PhysicalDeviceCustomBorderColorFeaturesEXT featuresCustomBorderColor;
if (supportedExtensions.Contains("VK_EXT_custom_border_color") && if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_custom_border_color") &&
supportedFeaturesCustomBorderColor.CustomBorderColors && supportedFeaturesCustomBorderColor.CustomBorderColors &&
supportedFeaturesCustomBorderColor.CustomBorderColorWithoutFormat) supportedFeaturesCustomBorderColor.CustomBorderColorWithoutFormat)
{ {
@@ -581,7 +505,7 @@ namespace Ryujinx.Graphics.Vulkan
pExtendedFeatures = &featuresCustomBorderColor; pExtendedFeatures = &featuresCustomBorderColor;
} }
var enabledExtensions = _requiredExtensions.Union(_desirableExtensions.Intersect(supportedExtensions)).ToArray(); var enabledExtensions = _requiredExtensions.Union(_desirableExtensions.Intersect(physicalDevice.DeviceExtensions)).ToArray();
IntPtr* ppEnabledExtensions = stackalloc IntPtr[enabledExtensions.Length]; IntPtr* ppEnabledExtensions = stackalloc IntPtr[enabledExtensions.Length];
@@ -601,7 +525,7 @@ namespace Ryujinx.Graphics.Vulkan
PEnabledFeatures = &features PEnabledFeatures = &features
}; };
api.CreateDevice(physicalDevice, in deviceCreateInfo, null, out var device).ThrowOnError(); api.CreateDevice(physicalDevice.PhysicalDevice, in deviceCreateInfo, null, out var device).ThrowOnError();
for (int i = 0; i < enabledExtensions.Length; i++) for (int i = 0; i < enabledExtensions.Length; i++)
{ {
@@ -610,21 +534,5 @@ namespace Ryujinx.Graphics.Vulkan
return device; return device;
} }
public static string[] GetSupportedExtensions(Vk api, PhysicalDevice physicalDevice)
{
uint propertiesCount;
api.EnumerateDeviceExtensionProperties(physicalDevice, (byte*)null, &propertiesCount, null).ThrowOnError();
ExtensionProperties[] extensionProperties = new ExtensionProperties[propertiesCount];
fixed (ExtensionProperties* pExtensionProperties = extensionProperties)
{
api.EnumerateDeviceExtensionProperties(physicalDevice, (byte*)null, &propertiesCount, pExtensionProperties).ThrowOnError();
}
return extensionProperties.Select(x => Marshal.PtrToStringAnsi((IntPtr)x.ExtensionName)).ToArray();
}
} }
} }

View File

@@ -0,0 +1,127 @@
using Ryujinx.Common.Utilities;
using Silk.NET.Core;
using Silk.NET.Vulkan;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Runtime.InteropServices;
namespace Ryujinx.Graphics.Vulkan
{
class VulkanInstance : IDisposable
{
private readonly Vk _api;
public readonly Instance Instance;
public readonly Version32 InstanceVersion;
private bool _disposed;
private VulkanInstance(Vk api, Instance instance)
{
_api = api;
Instance = instance;
if (api.GetInstanceProcAddr(instance, "vkEnumerateInstanceVersion") == IntPtr.Zero)
{
InstanceVersion = Vk.Version10;
}
else
{
uint rawInstanceVersion = 0;
if (api.EnumerateInstanceVersion(ref rawInstanceVersion) != Result.Success)
{
rawInstanceVersion = Vk.Version11.Value;
}
InstanceVersion = (Version32)rawInstanceVersion;
}
}
public static Result Create(Vk api, ref InstanceCreateInfo createInfo, out VulkanInstance instance)
{
instance = null;
Instance rawInstance = default;
Result result = api.CreateInstance(SpanHelpers.AsReadOnlySpan(ref createInfo), ReadOnlySpan<AllocationCallbacks>.Empty, SpanHelpers.AsSpan(ref rawInstance));
if (result == Result.Success)
{
instance = new VulkanInstance(api, rawInstance);
}
return result;
}
public Result EnumeratePhysicalDevices(out VulkanPhysicalDevice[] physicalDevices)
{
physicalDevices = null;
uint physicalDeviceCount = 0;
Result result = _api.EnumeratePhysicalDevices(Instance, SpanHelpers.AsSpan(ref physicalDeviceCount), Span<PhysicalDevice>.Empty);
if (result != Result.Success)
{
return result;
}
PhysicalDevice[] rawPhysicalDevices = new PhysicalDevice[physicalDeviceCount];
result = _api.EnumeratePhysicalDevices(Instance, SpanHelpers.AsSpan(ref physicalDeviceCount), rawPhysicalDevices);
if (result != Result.Success)
{
return result;
}
physicalDevices = rawPhysicalDevices.Select(x => new VulkanPhysicalDevice(_api, x)).ToArray();
return Result.Success;
}
public static IReadOnlySet<string> GetInstanceExtensions(Vk api)
{
uint propertiesCount = 0;
api.EnumerateInstanceExtensionProperties(ReadOnlySpan<byte>.Empty, SpanHelpers.AsSpan(ref propertiesCount), Span<ExtensionProperties>.Empty).ThrowOnError();
ExtensionProperties[] extensionProperties = new ExtensionProperties[propertiesCount];
api.EnumerateInstanceExtensionProperties(ReadOnlySpan<byte>.Empty, SpanHelpers.AsSpan(ref propertiesCount), extensionProperties).ThrowOnError();
unsafe
{
return extensionProperties.Select(x => Marshal.PtrToStringAnsi((IntPtr)x.ExtensionName)).ToImmutableHashSet();
}
}
public static IReadOnlySet<string> GetInstanceLayers(Vk api)
{
uint propertiesCount = 0;
api.EnumerateInstanceLayerProperties(SpanHelpers.AsSpan(ref propertiesCount), Span<LayerProperties>.Empty).ThrowOnError();
LayerProperties[] layerProperties = new LayerProperties[propertiesCount];
api.EnumerateInstanceLayerProperties(SpanHelpers.AsSpan(ref propertiesCount), layerProperties).ThrowOnError();
unsafe
{
return layerProperties.Select(x => Marshal.PtrToStringAnsi((IntPtr)x.LayerName)).ToImmutableHashSet();
}
}
public void Dispose()
{
if (!_disposed)
{
_api.DestroyInstance(Instance, ReadOnlySpan<AllocationCallbacks>.Empty);
_disposed = true;
}
}
}
}

View File

@@ -0,0 +1,70 @@
using Ryujinx.Common.Utilities;
using Ryujinx.Graphics.GAL;
using Silk.NET.Vulkan;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Runtime.InteropServices;
namespace Ryujinx.Graphics.Vulkan
{
readonly struct VulkanPhysicalDevice
{
public readonly PhysicalDevice PhysicalDevice;
public readonly PhysicalDeviceFeatures PhysicalDeviceFeatures;
public readonly PhysicalDeviceProperties PhysicalDeviceProperties;
public readonly PhysicalDeviceMemoryProperties PhysicalDeviceMemoryProperties;
public readonly QueueFamilyProperties[] QueueFamilyProperties;
public readonly string DeviceName;
public readonly IReadOnlySet<string> DeviceExtensions;
public VulkanPhysicalDevice(Vk api, PhysicalDevice physicalDevice)
{
PhysicalDevice = physicalDevice;
PhysicalDeviceFeatures = api.GetPhysicalDeviceFeature(PhysicalDevice);
api.GetPhysicalDeviceProperties(PhysicalDevice, out var physicalDeviceProperties);
PhysicalDeviceProperties = physicalDeviceProperties;
api.GetPhysicalDeviceMemoryProperties(PhysicalDevice, out PhysicalDeviceMemoryProperties);
unsafe
{
DeviceName = Marshal.PtrToStringAnsi((IntPtr)physicalDeviceProperties.DeviceName);
}
uint propertiesCount = 0;
api.GetPhysicalDeviceQueueFamilyProperties(physicalDevice, SpanHelpers.AsSpan(ref propertiesCount), Span<QueueFamilyProperties>.Empty);
QueueFamilyProperties = new QueueFamilyProperties[propertiesCount];
api.GetPhysicalDeviceQueueFamilyProperties(physicalDevice, SpanHelpers.AsSpan(ref propertiesCount), QueueFamilyProperties);
api.EnumerateDeviceExtensionProperties(PhysicalDevice, Span<byte>.Empty, SpanHelpers.AsSpan(ref propertiesCount), Span<ExtensionProperties>.Empty).ThrowOnError();
ExtensionProperties[] extensionProperties = new ExtensionProperties[propertiesCount];
api.EnumerateDeviceExtensionProperties(PhysicalDevice, Span<byte>.Empty, SpanHelpers.AsSpan(ref propertiesCount), extensionProperties).ThrowOnError();
unsafe
{
DeviceExtensions = extensionProperties.Select(x => Marshal.PtrToStringAnsi((IntPtr)x.ExtensionName)).ToImmutableHashSet();
}
}
public string Id => $"0x{PhysicalDeviceProperties.VendorID:X}_0x{PhysicalDeviceProperties.DeviceID:X}";
public bool IsDeviceExtensionPresent(string extension) => DeviceExtensions.Contains(extension);
public DeviceInfo ToDeviceInfo()
{
return new DeviceInfo(
Id,
VendorUtils.GetNameFromId(PhysicalDeviceProperties.VendorID),
DeviceName,
PhysicalDeviceProperties.DeviceType == PhysicalDeviceType.DiscreteGpu);
}
}
}

View File

@@ -17,9 +17,9 @@ namespace Ryujinx.Graphics.Vulkan
{ {
public sealed class VulkanRenderer : IRenderer public sealed class VulkanRenderer : IRenderer
{ {
private Instance _instance; private VulkanInstance _instance;
private SurfaceKHR _surface; private SurfaceKHR _surface;
private PhysicalDevice _physicalDevice; private VulkanPhysicalDevice _physicalDevice;
private Device _device; private Device _device;
private WindowBase _window; private WindowBase _window;
@@ -106,33 +106,31 @@ namespace Ryujinx.Graphics.Vulkan
} }
} }
private unsafe void LoadFeatures(string[] supportedExtensions, uint maxQueueCount, uint queueFamilyIndex) private unsafe void LoadFeatures(uint maxQueueCount, uint queueFamilyIndex)
{ {
FormatCapabilities = new FormatCapabilities(Api, _physicalDevice); FormatCapabilities = new FormatCapabilities(Api, _physicalDevice.PhysicalDevice);
var supportedFeatures = Api.GetPhysicalDeviceFeature(_physicalDevice); if (Api.TryGetDeviceExtension(_instance.Instance, _device, out ExtConditionalRendering conditionalRenderingApi))
if (Api.TryGetDeviceExtension(_instance, _device, out ExtConditionalRendering conditionalRenderingApi))
{ {
ConditionalRenderingApi = conditionalRenderingApi; ConditionalRenderingApi = conditionalRenderingApi;
} }
if (Api.TryGetDeviceExtension(_instance, _device, out ExtExtendedDynamicState extendedDynamicStateApi)) if (Api.TryGetDeviceExtension(_instance.Instance, _device, out ExtExtendedDynamicState extendedDynamicStateApi))
{ {
ExtendedDynamicStateApi = extendedDynamicStateApi; ExtendedDynamicStateApi = extendedDynamicStateApi;
} }
if (Api.TryGetDeviceExtension(_instance, _device, out KhrPushDescriptor pushDescriptorApi)) if (Api.TryGetDeviceExtension(_instance.Instance, _device, out KhrPushDescriptor pushDescriptorApi))
{ {
PushDescriptorApi = pushDescriptorApi; PushDescriptorApi = pushDescriptorApi;
} }
if (Api.TryGetDeviceExtension(_instance, _device, out ExtTransformFeedback transformFeedbackApi)) if (Api.TryGetDeviceExtension(_instance.Instance, _device, out ExtTransformFeedback transformFeedbackApi))
{ {
TransformFeedbackApi = transformFeedbackApi; TransformFeedbackApi = transformFeedbackApi;
} }
if (Api.TryGetDeviceExtension(_instance, _device, out KhrDrawIndirectCount drawIndirectCountApi)) if (Api.TryGetDeviceExtension(_instance.Instance, _device, out KhrDrawIndirectCount drawIndirectCountApi))
{ {
DrawIndirectCountApi = drawIndirectCountApi; DrawIndirectCountApi = drawIndirectCountApi;
} }
@@ -154,7 +152,7 @@ namespace Ryujinx.Graphics.Vulkan
SType = StructureType.PhysicalDeviceBlendOperationAdvancedPropertiesExt SType = StructureType.PhysicalDeviceBlendOperationAdvancedPropertiesExt
}; };
bool supportsBlendOperationAdvanced = supportedExtensions.Contains("VK_EXT_blend_operation_advanced"); bool supportsBlendOperationAdvanced = _physicalDevice.IsDeviceExtensionPresent("VK_EXT_blend_operation_advanced");
if (supportsBlendOperationAdvanced) if (supportsBlendOperationAdvanced)
{ {
@@ -167,14 +165,14 @@ namespace Ryujinx.Graphics.Vulkan
SType = StructureType.PhysicalDeviceSubgroupSizeControlPropertiesExt SType = StructureType.PhysicalDeviceSubgroupSizeControlPropertiesExt
}; };
bool supportsSubgroupSizeControl = supportedExtensions.Contains("VK_EXT_subgroup_size_control"); bool supportsSubgroupSizeControl = _physicalDevice.IsDeviceExtensionPresent("VK_EXT_subgroup_size_control");
if (supportsSubgroupSizeControl) if (supportsSubgroupSizeControl)
{ {
properties2.PNext = &propertiesSubgroupSizeControl; properties2.PNext = &propertiesSubgroupSizeControl;
} }
bool supportsTransformFeedback = supportedExtensions.Contains(ExtTransformFeedback.ExtensionName); bool supportsTransformFeedback = _physicalDevice.IsDeviceExtensionPresent(ExtTransformFeedback.ExtensionName);
PhysicalDeviceTransformFeedbackPropertiesEXT propertiesTransformFeedback = new PhysicalDeviceTransformFeedbackPropertiesEXT() PhysicalDeviceTransformFeedbackPropertiesEXT propertiesTransformFeedback = new PhysicalDeviceTransformFeedbackPropertiesEXT()
{ {
@@ -222,30 +220,30 @@ namespace Ryujinx.Graphics.Vulkan
SType = StructureType.PhysicalDevicePortabilitySubsetFeaturesKhr SType = StructureType.PhysicalDevicePortabilitySubsetFeaturesKhr
}; };
if (supportedExtensions.Contains("VK_EXT_primitive_topology_list_restart")) if (_physicalDevice.IsDeviceExtensionPresent("VK_EXT_primitive_topology_list_restart"))
{ {
features2.PNext = &featuresPrimitiveTopologyListRestart; features2.PNext = &featuresPrimitiveTopologyListRestart;
} }
if (supportedExtensions.Contains("VK_EXT_robustness2")) if (_physicalDevice.IsDeviceExtensionPresent("VK_EXT_robustness2"))
{ {
featuresRobustness2.PNext = features2.PNext; featuresRobustness2.PNext = features2.PNext;
features2.PNext = &featuresRobustness2; features2.PNext = &featuresRobustness2;
} }
if (supportedExtensions.Contains("VK_KHR_shader_float16_int8")) if (_physicalDevice.IsDeviceExtensionPresent("VK_KHR_shader_float16_int8"))
{ {
featuresShaderInt8.PNext = features2.PNext; featuresShaderInt8.PNext = features2.PNext;
features2.PNext = &featuresShaderInt8; features2.PNext = &featuresShaderInt8;
} }
if (supportedExtensions.Contains("VK_EXT_custom_border_color")) if (_physicalDevice.IsDeviceExtensionPresent("VK_EXT_custom_border_color"))
{ {
featuresCustomBorderColor.PNext = features2.PNext; featuresCustomBorderColor.PNext = features2.PNext;
features2.PNext = &featuresCustomBorderColor; features2.PNext = &featuresCustomBorderColor;
} }
bool usePortability = supportedExtensions.Contains("VK_KHR_portability_subset"); bool usePortability = _physicalDevice.IsDeviceExtensionPresent("VK_KHR_portability_subset");
if (usePortability) if (usePortability)
{ {
@@ -256,8 +254,8 @@ namespace Ryujinx.Graphics.Vulkan
features2.PNext = &featuresPortabilitySubset; features2.PNext = &featuresPortabilitySubset;
} }
Api.GetPhysicalDeviceProperties2(_physicalDevice, &properties2); Api.GetPhysicalDeviceProperties2(_physicalDevice.PhysicalDevice, &properties2);
Api.GetPhysicalDeviceFeatures2(_physicalDevice, &features2); Api.GetPhysicalDeviceFeatures2(_physicalDevice.PhysicalDevice, &features2);
var portabilityFlags = PortabilitySubsetFlags.None; var portabilityFlags = PortabilitySubsetFlags.None;
uint vertexBufferAlignment = 1; uint vertexBufferAlignment = 1;
@@ -272,7 +270,7 @@ namespace Ryujinx.Graphics.Vulkan
portabilityFlags |= featuresPortabilitySubset.SamplerMipLodBias ? 0 : PortabilitySubsetFlags.NoLodBias; portabilityFlags |= featuresPortabilitySubset.SamplerMipLodBias ? 0 : PortabilitySubsetFlags.NoLodBias;
} }
bool supportsCustomBorderColor = supportedExtensions.Contains("VK_EXT_custom_border_color") && bool supportsCustomBorderColor = _physicalDevice.IsDeviceExtensionPresent("VK_EXT_custom_border_color") &&
featuresCustomBorderColor.CustomBorderColors && featuresCustomBorderColor.CustomBorderColors &&
featuresCustomBorderColor.CustomBorderColorWithoutFormat; featuresCustomBorderColor.CustomBorderColorWithoutFormat;
@@ -284,30 +282,30 @@ namespace Ryujinx.Graphics.Vulkan
properties.Limits.FramebufferStencilSampleCounts; properties.Limits.FramebufferStencilSampleCounts;
Capabilities = new HardwareCapabilities( Capabilities = new HardwareCapabilities(
supportedExtensions.Contains("VK_EXT_index_type_uint8"), _physicalDevice.IsDeviceExtensionPresent("VK_EXT_index_type_uint8"),
supportsCustomBorderColor, supportsCustomBorderColor,
supportsBlendOperationAdvanced, supportsBlendOperationAdvanced,
propertiesBlendOperationAdvanced.AdvancedBlendCorrelatedOverlap, propertiesBlendOperationAdvanced.AdvancedBlendCorrelatedOverlap,
propertiesBlendOperationAdvanced.AdvancedBlendNonPremultipliedSrcColor, propertiesBlendOperationAdvanced.AdvancedBlendNonPremultipliedSrcColor,
propertiesBlendOperationAdvanced.AdvancedBlendNonPremultipliedDstColor, propertiesBlendOperationAdvanced.AdvancedBlendNonPremultipliedDstColor,
supportedExtensions.Contains(KhrDrawIndirectCount.ExtensionName), _physicalDevice.IsDeviceExtensionPresent(KhrDrawIndirectCount.ExtensionName),
supportedExtensions.Contains("VK_EXT_fragment_shader_interlock"), _physicalDevice.IsDeviceExtensionPresent("VK_EXT_fragment_shader_interlock"),
supportedExtensions.Contains("VK_NV_geometry_shader_passthrough"), _physicalDevice.IsDeviceExtensionPresent("VK_NV_geometry_shader_passthrough"),
supportsSubgroupSizeControl, supportsSubgroupSizeControl,
featuresShaderInt8.ShaderInt8, featuresShaderInt8.ShaderInt8,
supportedExtensions.Contains("VK_EXT_shader_stencil_export"), _physicalDevice.IsDeviceExtensionPresent("VK_EXT_shader_stencil_export"),
supportedExtensions.Contains(ExtConditionalRendering.ExtensionName), _physicalDevice.IsDeviceExtensionPresent(ExtConditionalRendering.ExtensionName),
supportedExtensions.Contains(ExtExtendedDynamicState.ExtensionName), _physicalDevice.IsDeviceExtensionPresent(ExtExtendedDynamicState.ExtensionName),
features2.Features.MultiViewport, features2.Features.MultiViewport,
featuresRobustness2.NullDescriptor || IsMoltenVk, featuresRobustness2.NullDescriptor || IsMoltenVk,
supportedExtensions.Contains(KhrPushDescriptor.ExtensionName), _physicalDevice.IsDeviceExtensionPresent(KhrPushDescriptor.ExtensionName),
featuresPrimitiveTopologyListRestart.PrimitiveTopologyListRestart, featuresPrimitiveTopologyListRestart.PrimitiveTopologyListRestart,
featuresPrimitiveTopologyListRestart.PrimitiveTopologyPatchListRestart, featuresPrimitiveTopologyListRestart.PrimitiveTopologyPatchListRestart,
supportsTransformFeedback, supportsTransformFeedback,
propertiesTransformFeedback.TransformFeedbackQueries, propertiesTransformFeedback.TransformFeedbackQueries,
features2.Features.OcclusionQueryPrecise, features2.Features.OcclusionQueryPrecise,
supportedFeatures.PipelineStatisticsQuery, _physicalDevice.PhysicalDeviceFeatures.PipelineStatisticsQuery,
supportedFeatures.GeometryShader, _physicalDevice.PhysicalDeviceFeatures.GeometryShader,
propertiesSubgroupSizeControl.MinSubgroupSize, propertiesSubgroupSizeControl.MinSubgroupSize,
propertiesSubgroupSizeControl.MaxSubgroupSize, propertiesSubgroupSizeControl.MaxSubgroupSize,
propertiesSubgroupSizeControl.RequiredSubgroupSizeStages, propertiesSubgroupSizeControl.RequiredSubgroupSizeStages,
@@ -315,9 +313,9 @@ namespace Ryujinx.Graphics.Vulkan
portabilityFlags, portabilityFlags,
vertexBufferAlignment); vertexBufferAlignment);
IsSharedMemory = MemoryAllocator.IsDeviceMemoryShared(Api, _physicalDevice); IsSharedMemory = MemoryAllocator.IsDeviceMemoryShared(_physicalDevice);
MemoryAllocator = new MemoryAllocator(Api, _physicalDevice, _device, properties.Limits.MaxMemoryAllocationCount); MemoryAllocator = new MemoryAllocator(Api, _physicalDevice, _device);
CommandBufferPool = new CommandBufferPool(Api, _device, Queue, QueueLock, queueFamilyIndex); CommandBufferPool = new CommandBufferPool(Api, _device, Queue, QueueLock, queueFamilyIndex);
@@ -345,22 +343,21 @@ namespace Ryujinx.Graphics.Vulkan
Api = api; Api = api;
_instance = VulkanInitialization.CreateInstance(api, logLevel, _getRequiredExtensions()); _instance = VulkanInitialization.CreateInstance(api, logLevel, _getRequiredExtensions());
_debugMessenger = new VulkanDebugMessenger(api, _instance, logLevel); _debugMessenger = new VulkanDebugMessenger(api, _instance.Instance, logLevel);
if (api.TryGetInstanceExtension(_instance, out KhrSurface surfaceApi)) if (api.TryGetInstanceExtension(_instance.Instance, out KhrSurface surfaceApi))
{ {
SurfaceApi = surfaceApi; SurfaceApi = surfaceApi;
} }
_surface = _getSurface(_instance, api); _surface = _getSurface(_instance.Instance, api);
_physicalDevice = VulkanInitialization.FindSuitablePhysicalDevice(api, _instance, _surface, _preferredGpuId); _physicalDevice = VulkanInitialization.FindSuitablePhysicalDevice(api, _instance, _surface, _preferredGpuId);
var queueFamilyIndex = VulkanInitialization.FindSuitableQueueFamily(api, _physicalDevice, _surface, out uint maxQueueCount); var queueFamilyIndex = VulkanInitialization.FindSuitableQueueFamily(api, _physicalDevice, _surface, out uint maxQueueCount);
var supportedExtensions = VulkanInitialization.GetSupportedExtensions(api, _physicalDevice);
_device = VulkanInitialization.CreateDevice(api, _physicalDevice, queueFamilyIndex, supportedExtensions, maxQueueCount); _device = VulkanInitialization.CreateDevice(api, _physicalDevice, queueFamilyIndex, maxQueueCount);
if (api.TryGetDeviceExtension(_instance, _device, out KhrSwapchain swapchainApi)) if (api.TryGetDeviceExtension(_instance.Instance, _device, out KhrSwapchain swapchainApi))
{ {
SwapchainApi = swapchainApi; SwapchainApi = swapchainApi;
} }
@@ -369,9 +366,9 @@ namespace Ryujinx.Graphics.Vulkan
Queue = queue; Queue = queue;
QueueLock = new object(); QueueLock = new object();
LoadFeatures(supportedExtensions, maxQueueCount, queueFamilyIndex); LoadFeatures(maxQueueCount, queueFamilyIndex);
_window = new Window(this, _surface, _physicalDevice, _device); _window = new Window(this, _surface, _physicalDevice.PhysicalDevice, _device);
_initialized = true; _initialized = true;
} }
@@ -536,10 +533,9 @@ namespace Ryujinx.Graphics.Vulkan
PNext = &featuresVk12 PNext = &featuresVk12
}; };
Api.GetPhysicalDeviceFeatures2(_physicalDevice, &features2); Api.GetPhysicalDeviceFeatures2(_physicalDevice.PhysicalDevice, &features2);
Api.GetPhysicalDeviceProperties(_physicalDevice, out var properties);
var limits = properties.Limits; var limits = _physicalDevice.PhysicalDeviceProperties.Limits;
return new Capabilities( return new Capabilities(
api: TargetApi.Vulkan, api: TargetApi.Vulkan,
@@ -623,7 +619,7 @@ namespace Ryujinx.Graphics.Vulkan
private unsafe void PrintGpuInformation() private unsafe void PrintGpuInformation()
{ {
Api.GetPhysicalDeviceProperties(_physicalDevice, out var properties); var properties = _physicalDevice.PhysicalDeviceProperties;
string vendorName = VendorUtils.GetNameFromId(properties.VendorID); string vendorName = VendorUtils.GetNameFromId(properties.VendorID);
@@ -807,14 +803,14 @@ namespace Ryujinx.Graphics.Vulkan
sampler.Dispose(); sampler.Dispose();
} }
SurfaceApi.DestroySurface(_instance, _surface, null); SurfaceApi.DestroySurface(_instance.Instance, _surface, null);
Api.DestroyDevice(_device, null); Api.DestroyDevice(_device, null);
_debugMessenger.Dispose(); _debugMessenger.Dispose();
// Last step destroy the instance // Last step destroy the instance
Api.DestroyInstance(_instance, null); _instance.Dispose();
} }
} }
} }

View File

@@ -405,7 +405,16 @@ namespace Ryujinx.HLE.Loaders.Processes
// Once everything is loaded, we can load cheats. // Once everything is loaded, we can load cheats.
device.Configuration.VirtualFileSystem.ModLoader.LoadCheats(programId, tamperInfo, device.TamperMachine); device.Configuration.VirtualFileSystem.ModLoader.LoadCheats(programId, tamperInfo, device.TamperMachine);
return new ProcessResult(metaLoader, applicationControlProperties, diskCacheEnabled, allowCodeMemoryForJit, processContextFactory.DiskCacheLoadState, process.Pid, meta.MainThreadPriority, meta.MainThreadStackSize); return new ProcessResult(
metaLoader,
applicationControlProperties,
diskCacheEnabled,
allowCodeMemoryForJit,
processContextFactory.DiskCacheLoadState,
process.Pid,
meta.MainThreadPriority,
meta.MainThreadStackSize,
device.System.State.DesiredTitleLanguage);
} }
public static Result LoadIntoMemory(KProcess process, IExecutable image, ulong baseAddress) public static Result LoadIntoMemory(KProcess process, IExecutable image, ulong baseAddress)

View File

@@ -2,6 +2,7 @@
using LibHac.Ns; using LibHac.Ns;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Cpu; using Ryujinx.Cpu;
using Ryujinx.HLE.HOS.SystemState;
using Ryujinx.HLE.Loaders.Processes.Extensions; using Ryujinx.HLE.Loaders.Processes.Extensions;
using Ryujinx.Horizon.Common; using Ryujinx.Horizon.Common;
@@ -9,7 +10,7 @@ namespace Ryujinx.HLE.Loaders.Processes
{ {
public struct ProcessResult public struct ProcessResult
{ {
public static ProcessResult Failed => new(null, new ApplicationControlProperty(), false, false, null, 0, 0, 0); public static ProcessResult Failed => new(null, new ApplicationControlProperty(), false, false, null, 0, 0, 0, TitleLanguage.AmericanEnglish);
private readonly byte _mainThreadPriority; private readonly byte _mainThreadPriority;
private readonly uint _mainThreadStackSize; private readonly uint _mainThreadStackSize;
@@ -35,7 +36,8 @@ namespace Ryujinx.HLE.Loaders.Processes
IDiskCacheLoadState diskCacheLoadState, IDiskCacheLoadState diskCacheLoadState,
ulong pid, ulong pid,
byte mainThreadPriority, byte mainThreadPriority,
uint mainThreadStackSize) uint mainThreadStackSize,
TitleLanguage titleLanguage)
{ {
_mainThreadPriority = mainThreadPriority; _mainThreadPriority = mainThreadPriority;
_mainThreadStackSize = mainThreadStackSize; _mainThreadStackSize = mainThreadStackSize;
@@ -50,7 +52,17 @@ namespace Ryujinx.HLE.Loaders.Processes
{ {
ulong programId = metaLoader.GetProgramId(); ulong programId = metaLoader.GetProgramId();
if (ApplicationControlProperties.Title.ItemsRo.Length > 0)
{
var langIndex = ApplicationControlProperties.Title.ItemsRo.Length > (int)titleLanguage ? (int)titleLanguage : 0;
Name = ApplicationControlProperties.Title[langIndex].NameString.ToString();
}
else
{
Name = metaLoader.GetProgramName(); Name = metaLoader.GetProgramName();
}
ProgramId = programId; ProgramId = programId;
ProgramIdText = $"{programId:x16}"; ProgramIdText = $"{programId:x16}";
Is64Bit = metaLoader.IsProgram64Bit(); Is64Bit = metaLoader.IsProgram64Bit();