Compare commits
3 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
70d65d3d8e | ||
|
0b58f46266 | ||
|
aa96dcb1be |
@@ -651,9 +651,35 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
/// <returns>True if the format is valid, false otherwise</returns>
|
||||
public static bool TryGetTextureFormat(uint encoded, bool isSrgb, out FormatInfo format)
|
||||
{
|
||||
encoded |= (isSrgb ? 1u << 19 : 0u);
|
||||
bool isPacked = (encoded & 0x80000000u) != 0;
|
||||
if (isPacked)
|
||||
{
|
||||
encoded &= ~0x80000000u;
|
||||
}
|
||||
|
||||
return _textureFormats.TryGetValue((TextureFormat)encoded, out format);
|
||||
encoded |= isSrgb ? 1u << 19 : 0u;
|
||||
|
||||
bool found = _textureFormats.TryGetValue((TextureFormat)encoded, out format);
|
||||
|
||||
if (found && isPacked && !format.Format.IsDepthOrStencil())
|
||||
{
|
||||
// If the packed flag is set, then the components of the pixel are tightly packed into the
|
||||
// GPU registers on the shader.
|
||||
// We can get the same behaviour by aliasing the texture as a format with the same amount of
|
||||
// bytes per pixel, but only a single or the lowest possible number of components.
|
||||
|
||||
format = format.BytesPerPixel switch
|
||||
{
|
||||
1 => new FormatInfo(Format.R8Unorm, 1, 1, 1, 1),
|
||||
2 => new FormatInfo(Format.R16Unorm, 1, 1, 2, 1),
|
||||
4 => new FormatInfo(Format.R32Float, 1, 1, 4, 1),
|
||||
8 => new FormatInfo(Format.R32G32Float, 1, 1, 8, 2),
|
||||
16 => new FormatInfo(Format.R32G32B32A32Float, 1, 1, 16, 4),
|
||||
_ => format,
|
||||
};
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@@ -2,6 +2,8 @@ using Ryujinx.Common;
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.Texture;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Numerics;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
@@ -339,7 +341,20 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
|
||||
if (lhs.FormatInfo.BytesPerPixel != rhs.FormatInfo.BytesPerPixel && IsIncompatibleFormatAliasingAllowed(lhs.FormatInfo, rhs.FormatInfo))
|
||||
{
|
||||
alignedWidthMatches = lhsSize.Width * lhs.FormatInfo.BytesPerPixel == rhsSize.Width * rhs.FormatInfo.BytesPerPixel;
|
||||
// If the formats are incompatible, but the texture strides match,
|
||||
// we might allow them to be copy compatible depending on the format.
|
||||
// The strides are aligned because the format with higher bytes per pixel
|
||||
// might need a bit of padding at the end due to one width not being a multiple of the other.
|
||||
|
||||
Debug.Assert((1 << BitOperations.Log2((uint)lhs.FormatInfo.BytesPerPixel)) == lhs.FormatInfo.BytesPerPixel);
|
||||
Debug.Assert((1 << BitOperations.Log2((uint)rhs.FormatInfo.BytesPerPixel)) == rhs.FormatInfo.BytesPerPixel);
|
||||
|
||||
int alignment = Math.Max(lhs.FormatInfo.BytesPerPixel, rhs.FormatInfo.BytesPerPixel);
|
||||
|
||||
int lhsStride = BitUtils.AlignUp(lhsSize.Width * lhs.FormatInfo.BytesPerPixel, alignment);
|
||||
int rhsStride = BitUtils.AlignUp(rhsSize.Width * rhs.FormatInfo.BytesPerPixel, alignment);
|
||||
|
||||
alignedWidthMatches = lhsStride == rhsStride;
|
||||
}
|
||||
|
||||
TextureViewCompatibility result = TextureViewCompatibility.Full;
|
||||
@@ -718,7 +733,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
(lhsFormat, rhsFormat) = (rhsFormat, lhsFormat);
|
||||
}
|
||||
|
||||
return lhsFormat.Format == Format.R8Unorm && rhsFormat.Format == Format.R8G8B8A8Unorm;
|
||||
return (lhsFormat.Format == Format.R8G8B8A8Unorm && rhsFormat.Format == Format.R32G32B32A32Float) ||
|
||||
(lhsFormat.Format == Format.R8Unorm && rhsFormat.Format == Format.R8G8B8A8Unorm);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@@ -430,7 +430,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
|
||||
if (!FormatTable.TryGetTextureFormat(format, srgb, out FormatInfo formatInfo))
|
||||
{
|
||||
if (gpuVa != 0 && (int)format > 0)
|
||||
if (gpuVa != 0 && format != 0)
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Gpu, $"Invalid texture format 0x{format:X} (sRGB: {srgb}).");
|
||||
}
|
||||
|
@@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
||||
private const ushort FileFormatVersionMajor = 1;
|
||||
private const ushort FileFormatVersionMinor = 2;
|
||||
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
|
||||
private const uint CodeGenVersion = 5791;
|
||||
private const uint CodeGenVersion = 5957;
|
||||
|
||||
private const string SharedTocFileName = "shared.toc";
|
||||
private const string SharedDataFileName = "shared.data";
|
||||
|
@@ -55,7 +55,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
||||
continue;
|
||||
}
|
||||
|
||||
if (bindlessHandle.AsgOp is not Operation handleCombineOp)
|
||||
if (!TryGetOperation(bindlessHandle.AsgOp, out Operation handleCombineOp))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -199,9 +199,64 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TryGetOperation(INode asgOp, out Operation outOperation)
|
||||
{
|
||||
if (asgOp is PhiNode phi)
|
||||
{
|
||||
// If we have a phi, let's check if all inputs are effectively the same value.
|
||||
// If so, we can "see through" the phi and pick any of the inputs (since they are all the same).
|
||||
|
||||
Operand firstSrc = phi.GetSource(0);
|
||||
|
||||
for (int index = 1; index < phi.SourcesCount; index++)
|
||||
{
|
||||
if (!IsSameOperand(firstSrc, phi.GetSource(index)))
|
||||
{
|
||||
outOperation = null;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
asgOp = firstSrc.AsgOp;
|
||||
}
|
||||
|
||||
if (asgOp is Operation operation)
|
||||
{
|
||||
outOperation = operation;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
outOperation = null;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool IsSameOperand(Operand x, Operand y)
|
||||
{
|
||||
if (x.Type == y.Type && x.Type == OperandType.LocalVariable)
|
||||
{
|
||||
return x.AsgOp is Operation xOp &&
|
||||
y.AsgOp is Operation yOp &&
|
||||
xOp.Inst == Instruction.BitwiseOr &&
|
||||
yOp.Inst == Instruction.BitwiseOr &&
|
||||
AreBothEqualConstantBuffers(xOp.GetSource(0), yOp.GetSource(0)) &&
|
||||
AreBothEqualConstantBuffers(xOp.GetSource(1), yOp.GetSource(1));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool AreBothEqualConstantBuffers(Operand x, Operand y)
|
||||
{
|
||||
return x.Type == y.Type && x.Value == y.Value && x.Type == OperandType.ConstantBuffer;
|
||||
}
|
||||
|
||||
private static Operand GetSourceForMaskedHandle(Operation asgOp, uint mask)
|
||||
{
|
||||
// Assume it was already checked that the operation is bitwise AND.
|
||||
|
||||
Operand src0 = asgOp.GetSource(0);
|
||||
Operand src1 = asgOp.GetSource(1);
|
||||
|
||||
@@ -210,6 +265,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
||||
// We can't check if the mask matches here as both operands are from a constant buffer.
|
||||
// Be optimistic and assume it matches. Avoid constant buffer 1 as official drivers
|
||||
// uses this one to store compiler constants.
|
||||
|
||||
return src0.GetCbufSlot() == 1 ? src1 : src0;
|
||||
}
|
||||
else if (src0.Type == OperandType.ConstantBuffer && src1.Type == OperandType.Constant)
|
||||
|
@@ -1,9 +0,0 @@
|
||||
namespace Ryujinx.Ui.Common.Configuration
|
||||
{
|
||||
public enum ConfigurationLoadResult
|
||||
{
|
||||
Success = 0,
|
||||
NotLoaded = 1,
|
||||
MigratedFromPreVulkan = 1 << 8,
|
||||
}
|
||||
}
|
@@ -5,6 +5,7 @@ using Ryujinx.Common.Configuration.Hid.Controller;
|
||||
using Ryujinx.Common.Configuration.Hid.Keyboard;
|
||||
using Ryujinx.Common.Configuration.Multiplayer;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Graphics.Vulkan;
|
||||
using Ryujinx.Ui.Common.Configuration.System;
|
||||
using Ryujinx.Ui.Common.Configuration.Ui;
|
||||
using Ryujinx.Ui.Common.Helper;
|
||||
@@ -763,7 +764,7 @@ namespace Ryujinx.Ui.Common.Configuration
|
||||
Graphics.ResScaleCustom.Value = 1.0f;
|
||||
Graphics.MaxAnisotropy.Value = -1.0f;
|
||||
Graphics.AspectRatio.Value = AspectRatio.Fixed16x9;
|
||||
Graphics.GraphicsBackend.Value = OperatingSystem.IsMacOS() ? GraphicsBackend.Vulkan : GraphicsBackend.OpenGl;
|
||||
Graphics.GraphicsBackend.Value = DefaultGraphicsBackend();
|
||||
Graphics.PreferredGpu.Value = "";
|
||||
Graphics.ShadersDumpPath.Value = "";
|
||||
Logger.EnableDebug.Value = false;
|
||||
@@ -907,7 +908,7 @@ namespace Ryujinx.Ui.Common.Configuration
|
||||
};
|
||||
}
|
||||
|
||||
public ConfigurationLoadResult Load(ConfigurationFileFormat configurationFileFormat, string configurationFilePath)
|
||||
public void Load(ConfigurationFileFormat configurationFileFormat, string configurationFilePath)
|
||||
{
|
||||
bool configurationFileUpdated = false;
|
||||
|
||||
@@ -916,12 +917,8 @@ namespace Ryujinx.Ui.Common.Configuration
|
||||
Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Unsupported configuration version {configurationFileFormat.Version}, loading default.");
|
||||
|
||||
LoadDefault();
|
||||
|
||||
return ConfigurationLoadResult.NotLoaded;
|
||||
}
|
||||
|
||||
ConfigurationLoadResult result = ConfigurationLoadResult.Success;
|
||||
|
||||
if (configurationFileFormat.Version < 2)
|
||||
{
|
||||
Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 2.");
|
||||
@@ -1336,8 +1333,6 @@ namespace Ryujinx.Ui.Common.Configuration
|
||||
|
||||
configurationFileFormat.GraphicsBackend = GraphicsBackend.OpenGl;
|
||||
|
||||
result |= ConfigurationLoadResult.MigratedFromPreVulkan;
|
||||
|
||||
configurationFileUpdated = true;
|
||||
}
|
||||
|
||||
@@ -1535,8 +1530,18 @@ namespace Ryujinx.Ui.Common.Configuration
|
||||
|
||||
Ryujinx.Common.Logging.Logger.Notice.Print(LogClass.Application, $"Configuration file updated to version {ConfigurationFileFormat.CurrentVersion}");
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
private static GraphicsBackend DefaultGraphicsBackend()
|
||||
{
|
||||
// Any system running macOS or returning any amount of valid Vulkan devices should default to Vulkan.
|
||||
// Checks for if the Vulkan version and featureset is compatible should be performed within VulkanRenderer.
|
||||
if (OperatingSystem.IsMacOS() || VulkanRenderer.GetPhysicalDevices().Length > 0)
|
||||
{
|
||||
return GraphicsBackend.Vulkan;
|
||||
}
|
||||
|
||||
return GraphicsBackend.OpenGl;
|
||||
}
|
||||
|
||||
private static void LogValueChange<T>(ReactiveEventArgs<T> eventArgs, string valueName)
|
||||
|
@@ -62,6 +62,7 @@
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.HLE\Ryujinx.HLE.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Graphics.Vulkan\Ryujinx.Graphics.Vulkan.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
@@ -177,8 +177,6 @@ namespace Ryujinx
|
||||
? appDataConfigurationPath
|
||||
: null;
|
||||
|
||||
bool showVulkanPrompt = false;
|
||||
|
||||
if (ConfigurationPath == null)
|
||||
{
|
||||
// No configuration, we load the default values and save it to disk
|
||||
@@ -186,26 +184,17 @@ namespace Ryujinx
|
||||
|
||||
ConfigurationState.Instance.LoadDefault();
|
||||
ConfigurationState.Instance.ToFileFormat().SaveConfig(ConfigurationPath);
|
||||
|
||||
showVulkanPrompt = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ConfigurationFileFormat.TryLoad(ConfigurationPath, out ConfigurationFileFormat configurationFileFormat))
|
||||
{
|
||||
ConfigurationLoadResult result = ConfigurationState.Instance.Load(configurationFileFormat, ConfigurationPath);
|
||||
|
||||
if ((result & ConfigurationLoadResult.MigratedFromPreVulkan) != 0)
|
||||
{
|
||||
showVulkanPrompt = true;
|
||||
}
|
||||
ConfigurationState.Instance.Load(configurationFileFormat, ConfigurationPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
ConfigurationState.Instance.LoadDefault();
|
||||
|
||||
showVulkanPrompt = true;
|
||||
|
||||
Logger.Warning?.PrintMsg(LogClass.Application, $"Failed to load config! Loading the default config instead.\nFailed config location {ConfigurationPath}");
|
||||
}
|
||||
}
|
||||
@@ -216,12 +205,10 @@ namespace Ryujinx
|
||||
if (CommandLineState.OverrideGraphicsBackend.ToLower() == "opengl")
|
||||
{
|
||||
ConfigurationState.Instance.Graphics.GraphicsBackend.Value = GraphicsBackend.OpenGl;
|
||||
showVulkanPrompt = false;
|
||||
}
|
||||
else if (CommandLineState.OverrideGraphicsBackend.ToLower() == "vulkan")
|
||||
{
|
||||
ConfigurationState.Instance.Graphics.GraphicsBackend.Value = GraphicsBackend.Vulkan;
|
||||
showVulkanPrompt = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -343,35 +330,6 @@ namespace Ryujinx
|
||||
}, TaskContinuationOptions.OnlyOnFaulted);
|
||||
}
|
||||
|
||||
if (showVulkanPrompt)
|
||||
{
|
||||
var buttonTexts = new Dictionary<int, string>()
|
||||
{
|
||||
{ 0, "Yes (Vulkan)" },
|
||||
{ 1, "No (OpenGL)" },
|
||||
};
|
||||
|
||||
ResponseType response = GtkDialog.CreateCustomDialog(
|
||||
"Ryujinx - Default graphics backend",
|
||||
"Use Vulkan as default graphics backend?",
|
||||
"Ryujinx now supports the Vulkan API. " +
|
||||
"Vulkan greatly improves shader compilation performance, " +
|
||||
"and fixes some graphical glitches; however, since it is a new feature, " +
|
||||
"you may experience some issues that did not occur with OpenGL.\n\n" +
|
||||
"Note that you will also lose any existing shader cache the first time you start a game " +
|
||||
"on version 1.1.200 onwards, because Vulkan required changes to the shader cache that makes it incompatible with previous versions.\n\n" +
|
||||
"Would you like to set Vulkan as the default graphics backend? " +
|
||||
"You can change this at any time on the settings window.",
|
||||
buttonTexts,
|
||||
MessageType.Question);
|
||||
|
||||
ConfigurationState.Instance.Graphics.GraphicsBackend.Value = response == 0
|
||||
? GraphicsBackend.Vulkan
|
||||
: GraphicsBackend.OpenGl;
|
||||
|
||||
ConfigurationState.Instance.ToFileFormat().SaveConfig(ConfigurationPath);
|
||||
}
|
||||
|
||||
Application.Run();
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user