Compare commits
9 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
4218311e6a | ||
|
e37735ed26 | ||
|
74a18b7c18 | ||
|
74fe814329 | ||
|
d1a093e5ca | ||
|
dfb14a5607 | ||
|
904a5ffcb4 | ||
|
946633276b | ||
|
baf94e0e3e |
68
.github/workflows/flatpak.yml
vendored
68
.github/workflows/flatpak.yml
vendored
@@ -58,33 +58,69 @@ jobs:
|
||||
- name: Generate nuget_sources.json
|
||||
shell: python
|
||||
run: |
|
||||
import hashlib
|
||||
from pathlib import Path
|
||||
import base64
|
||||
import binascii
|
||||
import json
|
||||
import os
|
||||
import urllib.request
|
||||
|
||||
sources = []
|
||||
|
||||
for path in Path(os.environ['NUGET_PACKAGES']).glob('**/*.nupkg.sha512'):
|
||||
name = path.parent.parent.name
|
||||
version = path.parent.name
|
||||
filename = '{}.{}.nupkg'.format(name, version)
|
||||
url = 'https://api.nuget.org/v3-flatcontainer/{}/{}/{}'.format(name, version, filename)
|
||||
|
||||
with path.open() as fp:
|
||||
sha512 = binascii.hexlify(base64.b64decode(fp.read())).decode('ascii')
|
||||
def create_source_from_external(name, version):
|
||||
full_dir_path = Path(os.environ["NUGET_PACKAGES"]).joinpath(name).joinpath(version)
|
||||
os.makedirs(full_dir_path, exist_ok=True)
|
||||
|
||||
sources.append({
|
||||
'type': 'file',
|
||||
'url': url,
|
||||
'sha512': sha512,
|
||||
'dest': os.environ['NUGET_SOURCES_DESTDIR'],
|
||||
'dest-filename': filename,
|
||||
})
|
||||
filename = "{}.{}.nupkg".format(name, version)
|
||||
url = "https://api.nuget.org/v3-flatcontainer/{}/{}/{}".format(
|
||||
name, version, filename
|
||||
)
|
||||
|
||||
with open('flathub/nuget_sources.json', 'w') as fp:
|
||||
json.dump(sources, fp, indent=4)
|
||||
print(f"Processing {url}...")
|
||||
response = urllib.request.urlopen(url)
|
||||
sha512 = hashlib.sha512(response.read()).hexdigest()
|
||||
|
||||
return {
|
||||
"type": "file",
|
||||
"url": url,
|
||||
"sha512": sha512,
|
||||
"dest": os.environ["NUGET_SOURCES_DESTDIR"],
|
||||
"dest-filename": filename,
|
||||
}
|
||||
|
||||
|
||||
has_added_x64_apphost = False
|
||||
|
||||
for path in Path(os.environ["NUGET_PACKAGES"]).glob("**/*.nupkg.sha512"):
|
||||
name = path.parent.parent.name
|
||||
version = path.parent.name
|
||||
filename = "{}.{}.nupkg".format(name, version)
|
||||
url = "https://api.nuget.org/v3-flatcontainer/{}/{}/{}".format(
|
||||
name, version, filename
|
||||
)
|
||||
|
||||
with path.open() as fp:
|
||||
sha512 = binascii.hexlify(base64.b64decode(fp.read())).decode("ascii")
|
||||
|
||||
sources.append(
|
||||
{
|
||||
"type": "file",
|
||||
"url": url,
|
||||
"sha512": sha512,
|
||||
"dest": os.environ["NUGET_SOURCES_DESTDIR"],
|
||||
"dest-filename": filename,
|
||||
}
|
||||
)
|
||||
|
||||
# .NET will not add current installed application host to the list, force inject it here.
|
||||
if not has_added_x64_apphost and name.startswith('microsoft.netcore.app.host'):
|
||||
sources.append(create_source_from_external("microsoft.netcore.app.host.linux-x64", version))
|
||||
has_added_x64_apphost = True
|
||||
|
||||
with open("flathub/nuget_sources.json", "w") as fp:
|
||||
json.dump(sources, fp, indent=4)
|
||||
|
||||
- name: Update flatpak metadata
|
||||
id: metadata
|
||||
|
@@ -333,8 +333,6 @@
|
||||
"DialogUpdaterAddingFilesMessage": "Adding New Update...",
|
||||
"DialogUpdaterCompleteMessage": "Update Complete!",
|
||||
"DialogUpdaterRestartMessage": "Do you want to restart Ryujinx now?",
|
||||
"DialogUpdaterArchNotSupportedMessage": "You are not running a supported system architecture!",
|
||||
"DialogUpdaterArchNotSupportedSubMessage": "(Only x64 systems are supported!)",
|
||||
"DialogUpdaterNoInternetMessage": "You are not connected to the Internet!",
|
||||
"DialogUpdaterNoInternetSubMessage": "Please verify that you have a working Internet connection!",
|
||||
"DialogUpdaterDirtyBuildMessage": "You Cannot update a Dirty build of Ryujinx!",
|
||||
|
@@ -68,7 +68,8 @@ namespace Ryujinx.Modules
|
||||
}
|
||||
else if (OperatingSystem.IsLinux())
|
||||
{
|
||||
_platformExt = "linux_x64.tar.gz";
|
||||
var arch = RuntimeInformation.OSArchitecture == Architecture.Arm64 ? "arm64" : "x64";
|
||||
_platformExt = $"linux_{arch}.tar.gz";
|
||||
}
|
||||
|
||||
Version newVersion;
|
||||
@@ -637,20 +638,6 @@ namespace Ryujinx.Modules
|
||||
public static bool CanUpdate(bool showWarnings)
|
||||
{
|
||||
#if !DISABLE_UPDATER
|
||||
if (RuntimeInformation.OSArchitecture != Architecture.X64 && !OperatingSystem.IsMacOS())
|
||||
{
|
||||
if (showWarnings)
|
||||
{
|
||||
Dispatcher.UIThread.InvokeAsync(() =>
|
||||
ContentDialogHelper.CreateWarningDialog(
|
||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterArchNotSupportedMessage],
|
||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterArchNotSupportedSubMessage])
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!NetworkInterface.GetIsNetworkAvailable())
|
||||
{
|
||||
if (showWarnings)
|
||||
|
@@ -2,6 +2,7 @@ using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Common.Utilities;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.Versioning;
|
||||
|
||||
namespace Ryujinx.Common.Configuration
|
||||
{
|
||||
@@ -95,18 +96,9 @@ namespace Ryujinx.Common.Configuration
|
||||
|
||||
BaseDirPath = Path.GetFullPath(BaseDirPath); // convert relative paths
|
||||
|
||||
// NOTE: Moves the Ryujinx folder in `~/.config` to `~/Library/Application Support` if one is found
|
||||
// and a Ryujinx folder does not already exist in Application Support.
|
||||
// Also creates a symlink from `~/.config/Ryujinx` to `~/Library/Application Support/Ryujinx` to preserve backwards compatibility.
|
||||
// This should be removed in the future.
|
||||
if (OperatingSystem.IsMacOS() && Mode == LaunchMode.UserProfile)
|
||||
if (IsPathSymlink(BaseDirPath))
|
||||
{
|
||||
string oldConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), DefaultBaseDir);
|
||||
if (Path.Exists(oldConfigPath) && !IsPathSymlink(oldConfigPath) && !Path.Exists(BaseDirPath))
|
||||
{
|
||||
FileSystemUtils.MoveDirectory(oldConfigPath, BaseDirPath);
|
||||
Directory.CreateSymbolicLink(oldConfigPath, BaseDirPath);
|
||||
}
|
||||
Logger.Warning?.Print(LogClass.Application, $"Application data directory is a symlink. This may be unintended.");
|
||||
}
|
||||
|
||||
SetupBasePaths();
|
||||
@@ -241,8 +233,91 @@ namespace Ryujinx.Common.Configuration
|
||||
// Should be removed, when the existence of the old directory isn't checked anymore.
|
||||
private static bool IsPathSymlink(string path)
|
||||
{
|
||||
FileAttributes attributes = File.GetAttributes(path);
|
||||
return (attributes & FileAttributes.ReparsePoint) == FileAttributes.ReparsePoint;
|
||||
try
|
||||
{
|
||||
FileAttributes attributes = File.GetAttributes(path);
|
||||
return (attributes & FileAttributes.ReparsePoint) == FileAttributes.ReparsePoint;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
[SupportedOSPlatform("macos")]
|
||||
public static void FixMacOSConfigurationFolders()
|
||||
{
|
||||
string oldConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
|
||||
".config", DefaultBaseDir);
|
||||
if (Path.Exists(oldConfigPath) && !IsPathSymlink(oldConfigPath) && !Path.Exists(BaseDirPath))
|
||||
{
|
||||
FileSystemUtils.MoveDirectory(oldConfigPath, BaseDirPath);
|
||||
Directory.CreateSymbolicLink(oldConfigPath, BaseDirPath);
|
||||
}
|
||||
|
||||
string correctApplicationDataDirectoryPath =
|
||||
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), DefaultBaseDir);
|
||||
if (IsPathSymlink(correctApplicationDataDirectoryPath))
|
||||
{
|
||||
//copy the files somewhere temporarily
|
||||
string tempPath = Path.Combine(Path.GetTempPath(), DefaultBaseDir);
|
||||
try
|
||||
{
|
||||
FileSystemUtils.CopyDirectory(correctApplicationDataDirectoryPath, tempPath, true);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Application,
|
||||
$"Critical error copying Ryujinx application data into the temp folder. {exception}");
|
||||
try
|
||||
{
|
||||
FileSystemInfo resolvedDirectoryInfo =
|
||||
Directory.ResolveLinkTarget(correctApplicationDataDirectoryPath, true);
|
||||
string resolvedPath = resolvedDirectoryInfo.FullName;
|
||||
Logger.Error?.Print(LogClass.Application, $"Please manually move your Ryujinx data from {resolvedPath} to {correctApplicationDataDirectoryPath}, and remove the symlink.");
|
||||
}
|
||||
catch (Exception symlinkException)
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Application, $"Unable to resolve the symlink for Ryujinx application data: {symlinkException}. Follow the symlink at {correctApplicationDataDirectoryPath} and move your data back to the Application Support folder.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
//delete the symlink
|
||||
try
|
||||
{
|
||||
//This will fail if this is an actual directory, so there is no way we can actually delete user data here.
|
||||
File.Delete(correctApplicationDataDirectoryPath);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Application,
|
||||
$"Critical error deleting the Ryujinx application data folder symlink at {correctApplicationDataDirectoryPath}. {exception}");
|
||||
try
|
||||
{
|
||||
FileSystemInfo resolvedDirectoryInfo =
|
||||
Directory.ResolveLinkTarget(correctApplicationDataDirectoryPath, true);
|
||||
string resolvedPath = resolvedDirectoryInfo.FullName;
|
||||
Logger.Error?.Print(LogClass.Application, $"Please manually move your Ryujinx data from {resolvedPath} to {correctApplicationDataDirectoryPath}, and remove the symlink.");
|
||||
}
|
||||
catch (Exception symlinkException)
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Application, $"Unable to resolve the symlink for Ryujinx application data: {symlinkException}. Follow the symlink at {correctApplicationDataDirectoryPath} and move your data back to the Application Support folder.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
//put the files back
|
||||
try
|
||||
{
|
||||
FileSystemUtils.CopyDirectory(tempPath, correctApplicationDataDirectoryPath, true);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Application,
|
||||
$"Critical error copying Ryujinx application data into the correct location. {exception}. Please manually move your application data from {tempPath} to {correctApplicationDataDirectoryPath}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetModsPath() => CustomModsPath ?? Directory.CreateDirectory(Path.Combine(BaseDirPath, DefaultModsDir)).FullName;
|
||||
|
@@ -148,6 +148,7 @@ namespace Ryujinx.Graphics.GAL
|
||||
B8G8R8A8Unorm,
|
||||
B8G8R8A8Srgb,
|
||||
B10G10R10A2Unorm,
|
||||
X8UintD24Unorm,
|
||||
}
|
||||
|
||||
public static class FormatExtensions
|
||||
@@ -269,6 +270,7 @@ namespace Ryujinx.Graphics.GAL
|
||||
case Format.D16Unorm:
|
||||
return 2;
|
||||
case Format.S8UintD24Unorm:
|
||||
case Format.X8UintD24Unorm:
|
||||
case Format.D32Float:
|
||||
case Format.D24UnormS8Uint:
|
||||
return 4;
|
||||
@@ -349,6 +351,7 @@ namespace Ryujinx.Graphics.GAL
|
||||
case Format.D16Unorm:
|
||||
case Format.D24UnormS8Uint:
|
||||
case Format.S8UintD24Unorm:
|
||||
case Format.X8UintD24Unorm:
|
||||
case Format.D32Float:
|
||||
case Format.D32FloatS8Uint:
|
||||
return true;
|
||||
@@ -633,6 +636,7 @@ namespace Ryujinx.Graphics.GAL
|
||||
case Format.D16Unorm:
|
||||
case Format.D24UnormS8Uint:
|
||||
case Format.S8UintD24Unorm:
|
||||
case Format.X8UintD24Unorm:
|
||||
case Format.D32Float:
|
||||
case Format.D32FloatS8Uint:
|
||||
case Format.S8Uint:
|
||||
|
@@ -8,13 +8,13 @@ namespace Ryujinx.Graphics.Gpu.Engine.Types
|
||||
/// </summary>
|
||||
enum ZetaFormat
|
||||
{
|
||||
D32Float = 0xa,
|
||||
D16Unorm = 0x13,
|
||||
D24UnormS8Uint = 0x14,
|
||||
D24Unorm = 0x15,
|
||||
S8UintD24Unorm = 0x16,
|
||||
Zf32 = 0xa,
|
||||
Z16 = 0x13,
|
||||
Z24S8 = 0x14,
|
||||
X8Z24 = 0x15,
|
||||
S8Z24 = 0x16,
|
||||
S8Uint = 0x17,
|
||||
D32FloatS8Uint = 0x19,
|
||||
Zf32X24S8 = 0x19,
|
||||
}
|
||||
|
||||
static class ZetaFormatConverter
|
||||
@@ -29,14 +29,14 @@ namespace Ryujinx.Graphics.Gpu.Engine.Types
|
||||
return format switch
|
||||
{
|
||||
#pragma warning disable IDE0055 // Disable formatting
|
||||
ZetaFormat.D32Float => new FormatInfo(Format.D32Float, 1, 1, 4, 1),
|
||||
ZetaFormat.D16Unorm => new FormatInfo(Format.D16Unorm, 1, 1, 2, 1),
|
||||
ZetaFormat.D24UnormS8Uint => new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4, 2),
|
||||
ZetaFormat.D24Unorm => new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4, 1),
|
||||
ZetaFormat.S8UintD24Unorm => new FormatInfo(Format.S8UintD24Unorm, 1, 1, 4, 2),
|
||||
ZetaFormat.S8Uint => new FormatInfo(Format.S8Uint, 1, 1, 1, 1),
|
||||
ZetaFormat.D32FloatS8Uint => new FormatInfo(Format.D32FloatS8Uint, 1, 1, 8, 2),
|
||||
_ => FormatInfo.Default,
|
||||
ZetaFormat.Zf32 => new FormatInfo(Format.D32Float, 1, 1, 4, 1),
|
||||
ZetaFormat.Z16 => new FormatInfo(Format.D16Unorm, 1, 1, 2, 1),
|
||||
ZetaFormat.Z24S8 => new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4, 2),
|
||||
ZetaFormat.X8Z24 => new FormatInfo(Format.X8UintD24Unorm, 1, 1, 4, 1),
|
||||
ZetaFormat.S8Z24 => new FormatInfo(Format.S8UintD24Unorm, 1, 1, 4, 2),
|
||||
ZetaFormat.S8Uint => new FormatInfo(Format.S8Uint, 1, 1, 1, 1),
|
||||
ZetaFormat.Zf32X24S8 => new FormatInfo(Format.D32FloatS8Uint, 1, 1, 8, 2),
|
||||
_ => FormatInfo.Default,
|
||||
#pragma warning restore IDE0055
|
||||
};
|
||||
}
|
||||
|
@@ -185,6 +185,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
G24R8RUintGUnormBUnormAUnorm = G24R8 | RUint | GUnorm | BUnorm | AUnorm, // 0x24a0e
|
||||
Z24S8RUintGUnormBUnormAUnorm = Z24S8 | RUint | GUnorm | BUnorm | AUnorm, // 0x24a29
|
||||
Z24S8RUintGUnormBUintAUint = Z24S8 | RUint | GUnorm | BUint | AUint, // 0x48a29
|
||||
X8Z24RUnormGUintBUintAUint = X8Z24 | RUnorm | GUint | BUint | AUint, // 0x4912a
|
||||
S8Z24RUnormGUintBUintAUint = S8Z24 | RUnorm | GUint | BUint | AUint, // 0x4912b
|
||||
R32B24G8RFloatGUintBUnormAUnorm = R32B24G8 | RFloat | GUint | BUnorm | AUnorm, // 0x25385
|
||||
Zf32X24S8RFloatGUintBUnormAUnorm = Zf32X24S8 | RFloat | GUint | BUnorm | AUnorm, // 0x253b0
|
||||
@@ -410,6 +411,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
{ TextureFormat.G24R8RUintGUnormBUnormAUnorm, new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4, 2) },
|
||||
{ TextureFormat.Z24S8RUintGUnormBUnormAUnorm, new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4, 2) },
|
||||
{ TextureFormat.Z24S8RUintGUnormBUintAUint, new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4, 2) },
|
||||
{ TextureFormat.X8Z24RUnormGUintBUintAUint, new FormatInfo(Format.X8UintD24Unorm, 1, 1, 4, 2) },
|
||||
{ TextureFormat.S8Z24RUnormGUintBUintAUint, new FormatInfo(Format.S8UintD24Unorm, 1, 1, 4, 2) },
|
||||
{ TextureFormat.R32B24G8RFloatGUintBUnormAUnorm, new FormatInfo(Format.D32FloatS8Uint, 1, 1, 8, 2) },
|
||||
{ TextureFormat.Zf32X24S8RFloatGUintBUnormAUnorm, new FormatInfo(Format.D32FloatS8Uint, 1, 1, 8, 2) },
|
||||
|
@@ -242,7 +242,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
return TextureMatchQuality.FormatAlias;
|
||||
}
|
||||
else if ((lhs.FormatInfo.Format == Format.D24UnormS8Uint ||
|
||||
lhs.FormatInfo.Format == Format.S8UintD24Unorm) && rhs.FormatInfo.Format == Format.B8G8R8A8Unorm)
|
||||
lhs.FormatInfo.Format == Format.S8UintD24Unorm ||
|
||||
lhs.FormatInfo.Format == Format.X8UintD24Unorm) && rhs.FormatInfo.Format == Format.B8G8R8A8Unorm)
|
||||
{
|
||||
return TextureMatchQuality.FormatAlias;
|
||||
}
|
||||
|
@@ -68,6 +68,7 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
Add(Format.S8Uint, new FormatInfo(1, false, false, All.StencilIndex8, PixelFormat.StencilIndex, PixelType.UnsignedByte));
|
||||
Add(Format.D16Unorm, new FormatInfo(1, false, false, All.DepthComponent16, PixelFormat.DepthComponent, PixelType.UnsignedShort));
|
||||
Add(Format.S8UintD24Unorm, new FormatInfo(1, false, false, All.Depth24Stencil8, PixelFormat.DepthStencil, PixelType.UnsignedInt248));
|
||||
Add(Format.X8UintD24Unorm, new FormatInfo(1, false, false, All.DepthComponent24, PixelFormat.DepthComponent, PixelType.UnsignedInt));
|
||||
Add(Format.D32Float, new FormatInfo(1, false, false, All.DepthComponent32f, PixelFormat.DepthComponent, PixelType.Float));
|
||||
Add(Format.D24UnormS8Uint, new FormatInfo(1, false, false, All.Depth24Stencil8, PixelFormat.DepthStencil, PixelType.UnsignedInt248));
|
||||
Add(Format.D32FloatS8Uint, new FormatInfo(1, false, false, All.Depth32fStencil8, PixelFormat.DepthStencil, PixelType.Float32UnsignedInt248Rev));
|
||||
@@ -224,5 +225,17 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
{
|
||||
return _tableImage[(int)format];
|
||||
}
|
||||
|
||||
public static bool IsPackedDepthStencil(Format format)
|
||||
{
|
||||
return format == Format.D24UnormS8Uint ||
|
||||
format == Format.D32FloatS8Uint ||
|
||||
format == Format.S8UintD24Unorm;
|
||||
}
|
||||
|
||||
public static bool IsDepthOnly(Format format)
|
||||
{
|
||||
return format == Format.D16Unorm || format == Format.D32Float || format == Format.X8UintD24Unorm;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -119,11 +119,11 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
|
||||
private static FramebufferAttachment GetAttachment(Format format)
|
||||
{
|
||||
if (IsPackedDepthStencilFormat(format))
|
||||
if (FormatTable.IsPackedDepthStencil(format))
|
||||
{
|
||||
return FramebufferAttachment.DepthStencilAttachment;
|
||||
}
|
||||
else if (IsDepthOnlyFormat(format))
|
||||
else if (FormatTable.IsDepthOnly(format))
|
||||
{
|
||||
return FramebufferAttachment.DepthAttachment;
|
||||
}
|
||||
@@ -133,18 +133,6 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsPackedDepthStencilFormat(Format format)
|
||||
{
|
||||
return format == Format.D24UnormS8Uint ||
|
||||
format == Format.D32FloatS8Uint ||
|
||||
format == Format.S8UintD24Unorm;
|
||||
}
|
||||
|
||||
private static bool IsDepthOnlyFormat(Format format)
|
||||
{
|
||||
return format == Format.D16Unorm || format == Format.D32Float;
|
||||
}
|
||||
|
||||
public int GetColorLayerCount(int index)
|
||||
{
|
||||
return _colors[index]?.Info.GetDepthOrLayers() ?? 0;
|
||||
|
@@ -294,7 +294,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
||||
{
|
||||
return FramebufferAttachment.DepthStencilAttachment;
|
||||
}
|
||||
else if (IsDepthOnly(format))
|
||||
else if (FormatTable.IsDepthOnly(format))
|
||||
{
|
||||
return FramebufferAttachment.DepthAttachment;
|
||||
}
|
||||
@@ -324,11 +324,11 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
||||
|
||||
private static ClearBufferMask GetMask(Format format)
|
||||
{
|
||||
if (format == Format.D24UnormS8Uint || format == Format.D32FloatS8Uint || format == Format.S8UintD24Unorm)
|
||||
if (FormatTable.IsPackedDepthStencil(format))
|
||||
{
|
||||
return ClearBufferMask.DepthBufferBit | ClearBufferMask.StencilBufferBit;
|
||||
}
|
||||
else if (IsDepthOnly(format))
|
||||
else if (FormatTable.IsDepthOnly(format))
|
||||
{
|
||||
return ClearBufferMask.DepthBufferBit;
|
||||
}
|
||||
@@ -342,11 +342,6 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsDepthOnly(Format format)
|
||||
{
|
||||
return format == Format.D16Unorm || format == Format.D32Float;
|
||||
}
|
||||
|
||||
public TextureView BgraSwap(TextureView from)
|
||||
{
|
||||
TextureView to = (TextureView)_renderer.CreateTexture(from.Info);
|
||||
|
@@ -16,6 +16,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
public const int MaxStorageBufferBindings = MaxStorageBuffersPerStage * MaxShaderStages;
|
||||
public const int MaxTextureBindings = MaxTexturesPerStage * MaxShaderStages;
|
||||
public const int MaxImageBindings = MaxImagesPerStage * MaxShaderStages;
|
||||
public const int MaxPushDescriptorBinding = 64;
|
||||
|
||||
public const ulong SparseBufferAlignment = 0x10000;
|
||||
}
|
||||
|
@@ -1,19 +1,32 @@
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using Silk.NET.Vulkan;
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
class DescriptorSetTemplate : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Renderdoc seems to crash when doing a templated uniform update with count > 1 on a push descriptor.
|
||||
/// When this is true, consecutive buffers are always updated individually.
|
||||
/// </summary>
|
||||
private const bool RenderdocPushCountBug = true;
|
||||
|
||||
private readonly VulkanRenderer _gd;
|
||||
private readonly Device _device;
|
||||
|
||||
public readonly DescriptorUpdateTemplate Template;
|
||||
public readonly int Size;
|
||||
|
||||
public unsafe DescriptorSetTemplate(VulkanRenderer gd, Device device, ResourceBindingSegment[] segments, PipelineLayoutCacheEntry plce, PipelineBindPoint pbp, int setIndex)
|
||||
public unsafe DescriptorSetTemplate(
|
||||
VulkanRenderer gd,
|
||||
Device device,
|
||||
ResourceBindingSegment[] segments,
|
||||
PipelineLayoutCacheEntry plce,
|
||||
PipelineBindPoint pbp,
|
||||
int setIndex)
|
||||
{
|
||||
_gd = gd;
|
||||
_device = device;
|
||||
@@ -137,6 +150,93 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
Template = result;
|
||||
}
|
||||
|
||||
public unsafe DescriptorSetTemplate(
|
||||
VulkanRenderer gd,
|
||||
Device device,
|
||||
ResourceDescriptorCollection descriptors,
|
||||
long updateMask,
|
||||
PipelineLayoutCacheEntry plce,
|
||||
PipelineBindPoint pbp,
|
||||
int setIndex)
|
||||
{
|
||||
_gd = gd;
|
||||
_device = device;
|
||||
|
||||
// Create a template from the set usages. Assumes the descriptor set is updated in segment order then binding order.
|
||||
int segmentCount = BitOperations.PopCount((ulong)updateMask);
|
||||
|
||||
DescriptorUpdateTemplateEntry* entries = stackalloc DescriptorUpdateTemplateEntry[segmentCount];
|
||||
int entry = 0;
|
||||
nuint structureOffset = 0;
|
||||
|
||||
void AddBinding(int binding, int count)
|
||||
{
|
||||
entries[entry++] = new DescriptorUpdateTemplateEntry()
|
||||
{
|
||||
DescriptorType = DescriptorType.UniformBuffer,
|
||||
DstBinding = (uint)binding,
|
||||
DescriptorCount = (uint)count,
|
||||
Offset = structureOffset,
|
||||
Stride = (nuint)Unsafe.SizeOf<DescriptorBufferInfo>()
|
||||
};
|
||||
|
||||
structureOffset += (nuint)(Unsafe.SizeOf<DescriptorBufferInfo>() * count);
|
||||
}
|
||||
|
||||
int startBinding = 0;
|
||||
int bindingCount = 0;
|
||||
|
||||
foreach (ResourceDescriptor descriptor in descriptors.Descriptors)
|
||||
{
|
||||
for (int i = 0; i < descriptor.Count; i++)
|
||||
{
|
||||
int binding = descriptor.Binding + i;
|
||||
|
||||
if ((updateMask & (1L << binding)) != 0)
|
||||
{
|
||||
if (bindingCount > 0 && (RenderdocPushCountBug || startBinding + bindingCount != binding))
|
||||
{
|
||||
AddBinding(startBinding, bindingCount);
|
||||
|
||||
bindingCount = 0;
|
||||
}
|
||||
|
||||
if (bindingCount == 0)
|
||||
{
|
||||
startBinding = binding;
|
||||
}
|
||||
|
||||
bindingCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bindingCount > 0)
|
||||
{
|
||||
AddBinding(startBinding, bindingCount);
|
||||
}
|
||||
|
||||
Size = (int)structureOffset;
|
||||
|
||||
var info = new DescriptorUpdateTemplateCreateInfo()
|
||||
{
|
||||
SType = StructureType.DescriptorUpdateTemplateCreateInfo,
|
||||
DescriptorUpdateEntryCount = (uint)entry,
|
||||
PDescriptorUpdateEntries = entries,
|
||||
|
||||
TemplateType = DescriptorUpdateTemplateType.PushDescriptorsKhr,
|
||||
DescriptorSetLayout = plce.DescriptorSetLayouts[setIndex],
|
||||
PipelineBindPoint = pbp,
|
||||
PipelineLayout = plce.PipelineLayout,
|
||||
Set = (uint)setIndex,
|
||||
};
|
||||
|
||||
DescriptorUpdateTemplate result;
|
||||
gd.Api.CreateDescriptorUpdateTemplate(device, &info, null, &result).ThrowOnError();
|
||||
|
||||
Template = result;
|
||||
}
|
||||
|
||||
public unsafe void Dispose()
|
||||
{
|
||||
_gd.Api.DestroyDescriptorUpdateTemplate(_device, Template, null);
|
||||
|
@@ -52,11 +52,23 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
return new DescriptorSetTemplateWriter(new Span<byte>(_data.Pointer, template.Size));
|
||||
}
|
||||
|
||||
public DescriptorSetTemplateWriter Begin(int maxSize)
|
||||
{
|
||||
EnsureSize(maxSize);
|
||||
|
||||
return new DescriptorSetTemplateWriter(new Span<byte>(_data.Pointer, maxSize));
|
||||
}
|
||||
|
||||
public void Commit(VulkanRenderer gd, Device device, DescriptorSet set)
|
||||
{
|
||||
gd.Api.UpdateDescriptorSetWithTemplate(device, set, _activeTemplate.Template, _data.Pointer);
|
||||
}
|
||||
|
||||
public void CommitPushDescriptor(VulkanRenderer gd, CommandBufferScoped cbs, DescriptorSetTemplate template, PipelineLayout layout)
|
||||
{
|
||||
gd.PushDescriptorApi.CmdPushDescriptorSetWithTemplate(cbs.CommandBuffer, template.Template, layout, 0, _data.Pointer);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_data?.Dispose();
|
||||
|
@@ -4,6 +4,7 @@ using Ryujinx.Graphics.Shader;
|
||||
using Silk.NET.Vulkan;
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using CompareOp = Ryujinx.Graphics.GAL.CompareOp;
|
||||
using Format = Ryujinx.Graphics.GAL.Format;
|
||||
using SamplerCreateInfo = Ryujinx.Graphics.GAL.SamplerCreateInfo;
|
||||
@@ -61,6 +62,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
private BitMapStruct<Array2<long>> _storageSet;
|
||||
private BitMapStruct<Array2<long>> _uniformMirrored;
|
||||
private BitMapStruct<Array2<long>> _storageMirrored;
|
||||
private readonly int[] _uniformSetPd;
|
||||
private int _pdSequence = 1;
|
||||
|
||||
private bool _updateDescriptorCacheCbIndex;
|
||||
|
||||
@@ -106,6 +109,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
_bufferTextures = new BufferView[Constants.MaxTexturesPerStage];
|
||||
_bufferImages = new BufferView[Constants.MaxImagesPerStage];
|
||||
|
||||
_uniformSetPd = new int[Constants.MaxUniformBufferBindings];
|
||||
|
||||
var initialImageInfo = new DescriptorImageInfo
|
||||
{
|
||||
ImageLayout = ImageLayout.General,
|
||||
@@ -193,6 +198,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
if (BindingOverlaps(ref info, bindingOffset, offset, size))
|
||||
{
|
||||
_uniformSet.Clear(binding);
|
||||
_uniformSetPd[binding] = 0;
|
||||
SignalDirty(DirtyFlags.Uniform);
|
||||
}
|
||||
}
|
||||
@@ -223,8 +229,30 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
});
|
||||
}
|
||||
|
||||
public void SetProgram(ShaderCollection program)
|
||||
public void AdvancePdSequence()
|
||||
{
|
||||
if (++_pdSequence == 0)
|
||||
{
|
||||
_pdSequence = 1;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetProgram(CommandBufferScoped cbs, ShaderCollection program, bool isBound)
|
||||
{
|
||||
if (!program.HasSameLayout(_program))
|
||||
{
|
||||
// When the pipeline layout changes, push descriptor bindings are invalidated.
|
||||
|
||||
AdvancePdSequence();
|
||||
|
||||
if (_gd.IsNvidiaPreTuring && !program.UsePushDescriptors && _program?.UsePushDescriptors == true && isBound)
|
||||
{
|
||||
// On older nvidia GPUs, we need to clear out the active push descriptor bindings when switching
|
||||
// to normal descriptors. Keeping them bound can prevent buffers from binding properly in future.
|
||||
ClearAndBindUniformBufferPd(cbs);
|
||||
}
|
||||
}
|
||||
|
||||
_program = program;
|
||||
_updateDescriptorCacheCbIndex = true;
|
||||
_dirty = DirtyFlags.All;
|
||||
@@ -402,6 +430,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
if (!currentBufferRef.Equals(newRef) || currentInfo.Range != info.Range)
|
||||
{
|
||||
_uniformSet.Clear(index);
|
||||
_uniformSetPd[index] = 0;
|
||||
|
||||
currentInfo = info;
|
||||
currentBufferRef = newRef;
|
||||
@@ -671,15 +700,19 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void UpdateAndBindUniformBufferPd(CommandBufferScoped cbs, PipelineBindPoint pbp)
|
||||
{
|
||||
int sequence = _pdSequence;
|
||||
var bindingSegments = _program.BindingSegments[PipelineBase.UniformSetIndex];
|
||||
var dummyBuffer = _dummyBuffer?.GetBuffer();
|
||||
|
||||
long updatedBindings = 0;
|
||||
DescriptorSetTemplateWriter writer = _templateUpdater.Begin(32 * Unsafe.SizeOf<DescriptorBufferInfo>());
|
||||
|
||||
foreach (ResourceBindingSegment segment in bindingSegments)
|
||||
{
|
||||
int binding = segment.Binding;
|
||||
int count = segment.Count;
|
||||
|
||||
bool doUpdate = false;
|
||||
ReadOnlySpan<DescriptorBufferInfo> uniformBuffers = _uniformBuffers;
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
@@ -688,17 +721,58 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
if (_uniformSet.Set(index))
|
||||
{
|
||||
ref BufferRef buffer = ref _uniformBufferRefs[index];
|
||||
UpdateBuffer(cbs, ref _uniformBuffers[index], ref buffer, dummyBuffer, true);
|
||||
doUpdate = true;
|
||||
|
||||
bool mirrored = UpdateBuffer(cbs, ref _uniformBuffers[index], ref buffer, dummyBuffer, true);
|
||||
|
||||
_uniformMirrored.Set(index, mirrored);
|
||||
}
|
||||
|
||||
if (_uniformSetPd[index] != sequence)
|
||||
{
|
||||
// Need to set this push descriptor (even if the buffer binding has not changed)
|
||||
|
||||
_uniformSetPd[index] = sequence;
|
||||
updatedBindings |= 1L << index;
|
||||
|
||||
writer.Push(MemoryMarshal.CreateReadOnlySpan(ref _uniformBuffers[index], 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (doUpdate)
|
||||
if (updatedBindings > 0)
|
||||
{
|
||||
DescriptorSetTemplate template = _program.GetPushDescriptorTemplate(updatedBindings);
|
||||
_templateUpdater.CommitPushDescriptor(_gd, cbs, template, _program.PipelineLayout);
|
||||
}
|
||||
}
|
||||
|
||||
private void ClearAndBindUniformBufferPd(CommandBufferScoped cbs)
|
||||
{
|
||||
var bindingSegments = _program.BindingSegments[PipelineBase.UniformSetIndex];
|
||||
|
||||
long updatedBindings = 0;
|
||||
DescriptorSetTemplateWriter writer = _templateUpdater.Begin(32 * Unsafe.SizeOf<DescriptorBufferInfo>());
|
||||
|
||||
foreach (ResourceBindingSegment segment in bindingSegments)
|
||||
{
|
||||
int binding = segment.Binding;
|
||||
int count = segment.Count;
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
ReadOnlySpan<DescriptorBufferInfo> uniformBuffers = _uniformBuffers;
|
||||
UpdateBuffers(cbs, pbp, binding, uniformBuffers.Slice(binding, count), DescriptorType.UniformBuffer);
|
||||
int index = binding + i;
|
||||
updatedBindings |= 1L << index;
|
||||
|
||||
var bufferInfo = new DescriptorBufferInfo();
|
||||
writer.Push(MemoryMarshal.CreateReadOnlySpan(ref bufferInfo, 1));
|
||||
}
|
||||
}
|
||||
|
||||
if (updatedBindings > 0)
|
||||
{
|
||||
DescriptorSetTemplate template = _program.GetPushDescriptorTemplate(updatedBindings);
|
||||
_templateUpdater.CommitPushDescriptor(_gd, cbs, template, _program.PipelineLayout);
|
||||
}
|
||||
}
|
||||
|
||||
private void Initialize(CommandBufferScoped cbs, int setIndex, DescriptorSetCollection dsc)
|
||||
@@ -724,6 +798,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
_uniformSet.Clear();
|
||||
_storageSet.Clear();
|
||||
AdvancePdSequence();
|
||||
}
|
||||
|
||||
private static void SwapBuffer(BufferRef[] list, Auto<DisposableBuffer> from, Auto<DisposableBuffer> to)
|
||||
|
@@ -376,7 +376,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
return format switch
|
||||
{
|
||||
Format.D16Unorm or Format.D32Float => ImageAspectFlags.DepthBit,
|
||||
Format.D16Unorm or Format.D32Float or Format.X8UintD24Unorm => ImageAspectFlags.DepthBit,
|
||||
Format.S8Uint => ImageAspectFlags.StencilBit,
|
||||
Format.D24UnormS8Uint or
|
||||
Format.D32FloatS8Uint or
|
||||
@@ -389,7 +389,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
return format switch
|
||||
{
|
||||
Format.D16Unorm or Format.D32Float => ImageAspectFlags.DepthBit,
|
||||
Format.D16Unorm or Format.D32Float or Format.X8UintD24Unorm => ImageAspectFlags.DepthBit,
|
||||
Format.S8Uint => ImageAspectFlags.StencilBit,
|
||||
Format.D24UnormS8Uint or
|
||||
Format.D32FloatS8Uint or
|
||||
|
@@ -220,7 +220,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
public static bool IsD24S8(Format format)
|
||||
{
|
||||
return format == Format.D24UnormS8Uint || format == Format.S8UintD24Unorm;
|
||||
return format == Format.D24UnormS8Uint || format == Format.S8UintD24Unorm || format == Format.X8UintD24Unorm;
|
||||
}
|
||||
|
||||
private static bool IsRGB16IntFloat(Format format)
|
||||
|
@@ -67,6 +67,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
Add(Format.S8Uint, VkFormat.S8Uint);
|
||||
Add(Format.D16Unorm, VkFormat.D16Unorm);
|
||||
Add(Format.S8UintD24Unorm, VkFormat.D24UnormS8Uint);
|
||||
Add(Format.X8UintD24Unorm, VkFormat.X8D24UnormPack32);
|
||||
Add(Format.D32Float, VkFormat.D32Sfloat);
|
||||
Add(Format.D24UnormS8Uint, VkFormat.D24UnormS8Uint);
|
||||
Add(Format.D32FloatS8Uint, VkFormat.D32SfloatS8Uint);
|
||||
|
@@ -34,6 +34,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
public readonly bool SupportsMultiView;
|
||||
public readonly bool SupportsNullDescriptors;
|
||||
public readonly bool SupportsPushDescriptors;
|
||||
public readonly uint MaxPushDescriptors;
|
||||
public readonly bool SupportsPrimitiveTopologyListRestart;
|
||||
public readonly bool SupportsPrimitiveTopologyPatchListRestart;
|
||||
public readonly bool SupportsTransformFeedback;
|
||||
@@ -71,6 +72,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
bool supportsMultiView,
|
||||
bool supportsNullDescriptors,
|
||||
bool supportsPushDescriptors,
|
||||
uint maxPushDescriptors,
|
||||
bool supportsPrimitiveTopologyListRestart,
|
||||
bool supportsPrimitiveTopologyPatchListRestart,
|
||||
bool supportsTransformFeedback,
|
||||
@@ -107,6 +109,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
SupportsMultiView = supportsMultiView;
|
||||
SupportsNullDescriptors = supportsNullDescriptors;
|
||||
SupportsPushDescriptors = supportsPushDescriptors;
|
||||
MaxPushDescriptors = maxPushDescriptors;
|
||||
SupportsPrimitiveTopologyListRestart = supportsPrimitiveTopologyListRestart;
|
||||
SupportsPrimitiveTopologyPatchListRestart = supportsPrimitiveTopologyPatchListRestart;
|
||||
SupportsTransformFeedback = supportsTransformFeedback;
|
||||
|
@@ -976,7 +976,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
_program = internalProgram;
|
||||
|
||||
_descriptorSetUpdater.SetProgram(internalProgram);
|
||||
_descriptorSetUpdater.SetProgram(Cbs, internalProgram, _currentPipelineHandle != 0);
|
||||
|
||||
_newState.PipelineLayout = internalProgram.PipelineLayout;
|
||||
_newState.StagesCount = (uint)stages.Length;
|
||||
|
@@ -31,6 +31,11 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
private int _dsLastCbIndex;
|
||||
private int _dsLastSubmissionCount;
|
||||
|
||||
private readonly Dictionary<long, DescriptorSetTemplate> _pdTemplates;
|
||||
private readonly ResourceDescriptorCollection _pdDescriptors;
|
||||
private long _lastPdUsage;
|
||||
private DescriptorSetTemplate _lastPdTemplate;
|
||||
|
||||
private PipelineLayoutCacheEntry(VulkanRenderer gd, Device device, int setsCount)
|
||||
{
|
||||
_gd = gd;
|
||||
@@ -72,6 +77,12 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
_consumedDescriptorsPerSet[setIndex] = count;
|
||||
}
|
||||
|
||||
if (usePushDescriptors)
|
||||
{
|
||||
_pdDescriptors = setDescriptors[0];
|
||||
_pdTemplates = new();
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateCommandBufferIndex(int commandBufferIndex)
|
||||
@@ -143,10 +154,39 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
return output[..count];
|
||||
}
|
||||
|
||||
public DescriptorSetTemplate GetPushDescriptorTemplate(PipelineBindPoint pbp, long updateMask)
|
||||
{
|
||||
if (_lastPdUsage == updateMask && _lastPdTemplate != null)
|
||||
{
|
||||
// Most likely result is that it asks to update the same buffers.
|
||||
return _lastPdTemplate;
|
||||
}
|
||||
|
||||
if (!_pdTemplates.TryGetValue(updateMask, out DescriptorSetTemplate template))
|
||||
{
|
||||
template = new DescriptorSetTemplate(_gd, _device, _pdDescriptors, updateMask, this, pbp, 0);
|
||||
|
||||
_pdTemplates.Add(updateMask, template);
|
||||
}
|
||||
|
||||
_lastPdUsage = updateMask;
|
||||
_lastPdTemplate = template;
|
||||
|
||||
return template;
|
||||
}
|
||||
|
||||
protected virtual unsafe void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
if (_pdTemplates != null)
|
||||
{
|
||||
foreach (DescriptorSetTemplate template in _pdTemplates.Values)
|
||||
{
|
||||
template.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < _dsCache.Length; i++)
|
||||
{
|
||||
for (int j = 0; j < _dsCache[i].Length; j++)
|
||||
|
@@ -108,18 +108,25 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
_shaders = internalShaders;
|
||||
|
||||
bool usePushDescriptors = !isMinimal && VulkanConfiguration.UsePushDescriptors && _gd.Capabilities.SupportsPushDescriptors;
|
||||
bool usePushDescriptors = !isMinimal &&
|
||||
VulkanConfiguration.UsePushDescriptors &&
|
||||
_gd.Capabilities.SupportsPushDescriptors &&
|
||||
!IsCompute &&
|
||||
CanUsePushDescriptors(gd, resourceLayout, IsCompute);
|
||||
|
||||
_plce = gd.PipelineLayoutCache.GetOrCreate(gd, device, resourceLayout.Sets, usePushDescriptors);
|
||||
ReadOnlyCollection<ResourceDescriptorCollection> sets = usePushDescriptors ?
|
||||
BuildPushDescriptorSets(gd, resourceLayout.Sets) : resourceLayout.Sets;
|
||||
|
||||
_plce = gd.PipelineLayoutCache.GetOrCreate(gd, device, sets, usePushDescriptors);
|
||||
|
||||
HasMinimalLayout = isMinimal;
|
||||
UsePushDescriptors = usePushDescriptors;
|
||||
|
||||
Stages = stages;
|
||||
|
||||
ClearSegments = BuildClearSegments(resourceLayout.Sets);
|
||||
ClearSegments = BuildClearSegments(sets);
|
||||
BindingSegments = BuildBindingSegments(resourceLayout.SetUsages);
|
||||
Templates = BuildTemplates();
|
||||
Templates = BuildTemplates(usePushDescriptors);
|
||||
|
||||
_compileTask = Task.CompletedTask;
|
||||
_firstBackgroundUse = false;
|
||||
@@ -139,6 +146,76 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
_firstBackgroundUse = !fromCache;
|
||||
}
|
||||
|
||||
private static bool CanUsePushDescriptors(VulkanRenderer gd, ResourceLayout layout, bool isCompute)
|
||||
{
|
||||
// If binding 3 is immediately used, use an alternate set of reserved bindings.
|
||||
ReadOnlyCollection<ResourceUsage> uniformUsage = layout.SetUsages[0].Usages;
|
||||
bool hasBinding3 = uniformUsage.Any(x => x.Binding == 3);
|
||||
int[] reserved = isCompute ? Array.Empty<int>() : gd.GetPushDescriptorReservedBindings(hasBinding3);
|
||||
|
||||
// Can't use any of the reserved usages.
|
||||
for (int i = 0; i < uniformUsage.Count; i++)
|
||||
{
|
||||
var binding = uniformUsage[i].Binding;
|
||||
|
||||
if (reserved.Contains(binding) ||
|
||||
binding >= Constants.MaxPushDescriptorBinding ||
|
||||
binding >= gd.Capabilities.MaxPushDescriptors + reserved.Count(id => id < binding))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static ReadOnlyCollection<ResourceDescriptorCollection> BuildPushDescriptorSets(
|
||||
VulkanRenderer gd,
|
||||
ReadOnlyCollection<ResourceDescriptorCollection> sets)
|
||||
{
|
||||
// The reserved bindings were selected when determining if push descriptors could be used.
|
||||
int[] reserved = gd.GetPushDescriptorReservedBindings(false);
|
||||
|
||||
var result = new ResourceDescriptorCollection[sets.Count];
|
||||
|
||||
for (int i = 0; i < sets.Count; i++)
|
||||
{
|
||||
if (i == 0)
|
||||
{
|
||||
// Push descriptors apply here. Remove reserved bindings.
|
||||
ResourceDescriptorCollection original = sets[i];
|
||||
|
||||
var pdUniforms = new ResourceDescriptor[original.Descriptors.Count];
|
||||
int j = 0;
|
||||
|
||||
foreach (ResourceDescriptor descriptor in original.Descriptors)
|
||||
{
|
||||
if (reserved.Contains(descriptor.Binding))
|
||||
{
|
||||
// If the binding is reserved, set its descriptor count to 0.
|
||||
pdUniforms[j++] = new ResourceDescriptor(
|
||||
descriptor.Binding,
|
||||
0,
|
||||
descriptor.Type,
|
||||
descriptor.Stages);
|
||||
}
|
||||
else
|
||||
{
|
||||
pdUniforms[j++] = descriptor;
|
||||
}
|
||||
}
|
||||
|
||||
result[i] = new ResourceDescriptorCollection(new(pdUniforms));
|
||||
}
|
||||
else
|
||||
{
|
||||
result[i] = sets[i];
|
||||
}
|
||||
}
|
||||
|
||||
return new(result);
|
||||
}
|
||||
|
||||
private static ResourceBindingSegment[][] BuildClearSegments(ReadOnlyCollection<ResourceDescriptorCollection> sets)
|
||||
{
|
||||
ResourceBindingSegment[][] segments = new ResourceBindingSegment[sets.Count][];
|
||||
@@ -243,12 +320,18 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
return segments;
|
||||
}
|
||||
|
||||
private DescriptorSetTemplate[] BuildTemplates()
|
||||
private DescriptorSetTemplate[] BuildTemplates(bool usePushDescriptors)
|
||||
{
|
||||
var templates = new DescriptorSetTemplate[BindingSegments.Length];
|
||||
|
||||
for (int setIndex = 0; setIndex < BindingSegments.Length; setIndex++)
|
||||
{
|
||||
if (usePushDescriptors && setIndex == 0)
|
||||
{
|
||||
// Push descriptors get updated using templates owned by the pipeline layout.
|
||||
continue;
|
||||
}
|
||||
|
||||
ResourceBindingSegment[] segments = BindingSegments[setIndex];
|
||||
|
||||
if (segments != null && segments.Length > 0)
|
||||
@@ -433,6 +516,11 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
return null;
|
||||
}
|
||||
|
||||
public DescriptorSetTemplate GetPushDescriptorTemplate(long updateMask)
|
||||
{
|
||||
return _plce.GetPushDescriptorTemplate(IsCompute ? PipelineBindPoint.Compute : PipelineBindPoint.Graphics, updateMask);
|
||||
}
|
||||
|
||||
public void AddComputePipeline(ref SpecData key, Auto<DisposablePipeline> pipeline)
|
||||
{
|
||||
(_computePipelineCache ??= new()).Add(ref key, pipeline);
|
||||
@@ -493,6 +581,11 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
return _plce.GetNewDescriptorSetCollection(setIndex, out isNew);
|
||||
}
|
||||
|
||||
public bool HasSameLayout(ShaderCollection other)
|
||||
{
|
||||
return other != null && _plce == other._plce;
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
|
@@ -154,9 +154,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
Format.S8Uint => Format.R8Unorm,
|
||||
Format.D16Unorm => Format.R16Unorm,
|
||||
Format.S8UintD24Unorm => Format.R8G8B8A8Unorm,
|
||||
Format.D24UnormS8Uint or Format.S8UintD24Unorm or Format.X8UintD24Unorm => Format.R8G8B8A8Unorm,
|
||||
Format.D32Float => Format.R32Float,
|
||||
Format.D24UnormS8Uint => Format.R8G8B8A8Unorm,
|
||||
Format.D32FloatS8Uint => Format.R32G32Float,
|
||||
_ => throw new ArgumentException($"\"{format}\" is not a supported depth or stencil format."),
|
||||
};
|
||||
|
@@ -20,6 +20,9 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
[GeneratedRegex("Radeon (((HD|R(5|7|9|X)) )?((M?[2-6]\\d{2}(\\D|$))|([7-8]\\d{3}(\\D|$))|Fury|Nano))|(Pro Duo)")]
|
||||
public static partial Regex AmdGcnRegex();
|
||||
|
||||
[GeneratedRegex("NVIDIA GeForce (R|G)?TX? (\\d{3}\\d?)M?")]
|
||||
public static partial Regex NvidiaConsumerClassRegex();
|
||||
|
||||
public static Vendor FromId(uint id)
|
||||
{
|
||||
return id switch
|
||||
|
@@ -4,7 +4,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
public const bool UseFastBufferUpdates = true;
|
||||
public const bool UseUnsafeBlit = true;
|
||||
public const bool UsePushDescriptors = false;
|
||||
public const bool UsePushDescriptors = true;
|
||||
|
||||
public const bool ForceD24S8Unsupported = false;
|
||||
public const bool ForceRGB16IntFloatUnsupported = false;
|
||||
|
@@ -486,20 +486,6 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
pExtendedFeatures = &featuresFragmentShaderInterlock;
|
||||
}
|
||||
|
||||
PhysicalDeviceSubgroupSizeControlFeaturesEXT featuresSubgroupSizeControl;
|
||||
|
||||
if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_subgroup_size_control"))
|
||||
{
|
||||
featuresSubgroupSizeControl = new PhysicalDeviceSubgroupSizeControlFeaturesEXT
|
||||
{
|
||||
SType = StructureType.PhysicalDeviceSubgroupSizeControlFeaturesExt,
|
||||
PNext = pExtendedFeatures,
|
||||
SubgroupSizeControl = true,
|
||||
};
|
||||
|
||||
pExtendedFeatures = &featuresSubgroupSizeControl;
|
||||
}
|
||||
|
||||
PhysicalDeviceCustomBorderColorFeaturesEXT featuresCustomBorderColor;
|
||||
|
||||
if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_custom_border_color") &&
|
||||
|
@@ -76,10 +76,15 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
private readonly Func<string[]> _getRequiredExtensions;
|
||||
private readonly string _preferredGpuId;
|
||||
|
||||
private int[] _pdReservedBindings;
|
||||
private readonly static int[] _pdReservedBindingsNvn = { 3, 18, 21, 36, 30 };
|
||||
private readonly static int[] _pdReservedBindingsOgl = { 17, 18, 34, 35, 36 };
|
||||
|
||||
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 IsNvidiaPreTuring { get; private set; }
|
||||
internal bool IsMoltenVk { get; private set; }
|
||||
internal bool IsTBDR { get; private set; }
|
||||
internal bool IsSharedMemory { get; private set; }
|
||||
@@ -191,6 +196,19 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
SType = StructureType.PhysicalDevicePortabilitySubsetPropertiesKhr,
|
||||
};
|
||||
|
||||
bool supportsPushDescriptors = _physicalDevice.IsDeviceExtensionPresent(KhrPushDescriptor.ExtensionName);
|
||||
|
||||
PhysicalDevicePushDescriptorPropertiesKHR propertiesPushDescriptor = new PhysicalDevicePushDescriptorPropertiesKHR()
|
||||
{
|
||||
SType = StructureType.PhysicalDevicePushDescriptorPropertiesKhr
|
||||
};
|
||||
|
||||
if (supportsPushDescriptors)
|
||||
{
|
||||
propertiesPushDescriptor.PNext = properties2.PNext;
|
||||
properties2.PNext = &propertiesPushDescriptor;
|
||||
}
|
||||
|
||||
PhysicalDeviceFeatures2 features2 = new()
|
||||
{
|
||||
SType = StructureType.PhysicalDeviceFeatures2,
|
||||
@@ -320,7 +338,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
_physicalDevice.IsDeviceExtensionPresent(ExtExtendedDynamicState.ExtensionName),
|
||||
features2.Features.MultiViewport && !(IsMoltenVk && Vendor == Vendor.Amd), // Workaround for AMD on MoltenVK issue
|
||||
featuresRobustness2.NullDescriptor || IsMoltenVk,
|
||||
_physicalDevice.IsDeviceExtensionPresent(KhrPushDescriptor.ExtensionName),
|
||||
supportsPushDescriptors && !IsMoltenVk,
|
||||
propertiesPushDescriptor.MaxPushDescriptors,
|
||||
featuresPrimitiveTopologyListRestart.PrimitiveTopologyListRestart,
|
||||
featuresPrimitiveTopologyListRestart.PrimitiveTopologyPatchListRestart,
|
||||
supportsTransformFeedback,
|
||||
@@ -400,6 +419,25 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
internal int[] GetPushDescriptorReservedBindings(bool isOgl)
|
||||
{
|
||||
// The first call of this method determines what push descriptor layout is used for all shaders on this renderer.
|
||||
// This is chosen to minimize shaders that can't fit their uniforms on the device's max number of push descriptors.
|
||||
if (_pdReservedBindings == null)
|
||||
{
|
||||
if (Capabilities.MaxPushDescriptors <= Constants.MaxUniformBuffersPerStage * 2)
|
||||
{
|
||||
_pdReservedBindings = isOgl ? _pdReservedBindingsOgl : _pdReservedBindingsNvn;
|
||||
}
|
||||
else
|
||||
{
|
||||
_pdReservedBindings = Array.Empty<int>();
|
||||
}
|
||||
}
|
||||
|
||||
return _pdReservedBindings;
|
||||
}
|
||||
|
||||
public BufferHandle CreateBuffer(int size, BufferAccess access)
|
||||
{
|
||||
return BufferManager.CreateWithHandle(this, size, access.HasFlag(BufferAccess.SparseCompatible), access.Convert(), default, access == BufferAccess.Stream);
|
||||
@@ -716,6 +754,20 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
IsAmdGcn = !IsMoltenVk && Vendor == Vendor.Amd && VendorUtils.AmdGcnRegex().IsMatch(GpuRenderer);
|
||||
|
||||
if (Vendor == Vendor.Nvidia)
|
||||
{
|
||||
var match = VendorUtils.NvidiaConsumerClassRegex().Match(GpuRenderer);
|
||||
|
||||
if (match != null && int.TryParse(match.Groups[2].Value, out int gpuNumber))
|
||||
{
|
||||
IsNvidiaPreTuring = gpuNumber < 2000;
|
||||
}
|
||||
else if (GpuDriver.Contains("TITAN") && !GpuDriver.Contains("RTX"))
|
||||
{
|
||||
IsNvidiaPreTuring = true;
|
||||
}
|
||||
}
|
||||
|
||||
Logger.Notice.Print(LogClass.Gpu, $"{GpuVendor} {GpuRenderer} ({GpuVersion})");
|
||||
}
|
||||
|
||||
|
@@ -673,9 +673,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
MemoryState.UnmapProcessCodeMemoryAllowed,
|
||||
KMemoryPermission.None,
|
||||
KMemoryPermission.None,
|
||||
MemoryAttribute.Mask,
|
||||
MemoryAttribute.Mask & ~MemoryAttribute.PermissionLocked,
|
||||
MemoryAttribute.None,
|
||||
MemoryAttribute.IpcAndDeviceMapped | MemoryAttribute.PermissionLocked,
|
||||
MemoryAttribute.IpcAndDeviceMapped,
|
||||
out MemoryState state,
|
||||
out _,
|
||||
out _);
|
||||
|
@@ -50,6 +50,12 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrlGpu
|
||||
case 0x06:
|
||||
result = CallIoctlMethod<GetTpcMasksArguments>(GetTpcMasks, arguments);
|
||||
break;
|
||||
case 0x12:
|
||||
result = CallIoctlMethod<NumVsmsArguments>(NumVsms, arguments);
|
||||
break;
|
||||
case 0x13:
|
||||
result = CallIoctlMethod<VsmsMappingArguments>(VsmsMapping, arguments);
|
||||
break;
|
||||
case 0x14:
|
||||
result = CallIoctlMethod<GetActiveSlotMaskArguments>(GetActiveSlotMask, arguments);
|
||||
break;
|
||||
@@ -76,6 +82,12 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrlGpu
|
||||
case 0x06:
|
||||
result = CallIoctlMethod<GetTpcMasksArguments, int>(GetTpcMasks, arguments, inlineOutBuffer);
|
||||
break;
|
||||
case 0x12:
|
||||
result = CallIoctlMethod<NumVsmsArguments>(NumVsms, arguments);
|
||||
break;
|
||||
case 0x13:
|
||||
result = CallIoctlMethod<VsmsMappingArguments>(VsmsMapping, arguments);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,6 +228,27 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrlGpu
|
||||
return NvInternalResult.Success;
|
||||
}
|
||||
|
||||
private NvInternalResult NumVsms(ref NumVsmsArguments arguments)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceNv);
|
||||
|
||||
arguments.NumVsms = 2;
|
||||
|
||||
return NvInternalResult.Success;
|
||||
}
|
||||
|
||||
private NvInternalResult VsmsMapping(ref VsmsMappingArguments arguments)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceNv);
|
||||
|
||||
arguments.Sm0GpcIndex = 0;
|
||||
arguments.Sm0TpcIndex = 0;
|
||||
arguments.Sm1GpcIndex = 0;
|
||||
arguments.Sm1TpcIndex = 1;
|
||||
|
||||
return NvInternalResult.Success;
|
||||
}
|
||||
|
||||
private NvInternalResult GetActiveSlotMask(ref GetActiveSlotMaskArguments arguments)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceNv);
|
||||
|
@@ -0,0 +1,11 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrlGpu.Types
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
struct NumVsmsArguments
|
||||
{
|
||||
public uint NumVsms;
|
||||
public uint Reserved;
|
||||
}
|
||||
}
|
@@ -0,0 +1,13 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrlGpu.Types
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
struct VsmsMappingArguments
|
||||
{
|
||||
public byte Sm0GpcIndex;
|
||||
public byte Sm0TpcIndex;
|
||||
public byte Sm1GpcIndex;
|
||||
public byte Sm1TpcIndex;
|
||||
}
|
||||
}
|
@@ -15,7 +15,7 @@ namespace Ryujinx.UI.Common.Configuration
|
||||
/// <summary>
|
||||
/// The current version of the file format
|
||||
/// </summary>
|
||||
public const int CurrentVersion = 48;
|
||||
public const int CurrentVersion = 49;
|
||||
|
||||
/// <summary>
|
||||
/// Version of the configuration file format
|
||||
|
@@ -1430,6 +1430,18 @@ namespace Ryujinx.UI.Common.Configuration
|
||||
configurationFileUpdated = true;
|
||||
}
|
||||
|
||||
if (configurationFileFormat.Version < 49)
|
||||
{
|
||||
Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 49.");
|
||||
|
||||
if (OperatingSystem.IsMacOS())
|
||||
{
|
||||
AppDataManager.FixMacOSConfigurationFolders();
|
||||
}
|
||||
|
||||
configurationFileUpdated = true;
|
||||
}
|
||||
|
||||
Logger.EnableFileLog.Value = configurationFileFormat.EnableFileLog;
|
||||
Graphics.ResScale.Value = configurationFileFormat.ResScale;
|
||||
Graphics.ResScaleCustom.Value = configurationFileFormat.ResScaleCustom;
|
||||
|
@@ -78,7 +78,8 @@ namespace Ryujinx.Modules
|
||||
}
|
||||
else if (OperatingSystem.IsLinux())
|
||||
{
|
||||
_platformExt = "linux_x64.tar.gz";
|
||||
var arch = RuntimeInformation.OSArchitecture == Architecture.Arm64 ? "arm64" : "x64";
|
||||
_platformExt = $"linux_{arch}.tar.gz";
|
||||
artifactIndex = 0;
|
||||
}
|
||||
|
||||
@@ -512,16 +513,6 @@ namespace Ryujinx.Modules
|
||||
public static bool CanUpdate(bool showWarnings)
|
||||
{
|
||||
#if !DISABLE_UPDATER
|
||||
if (RuntimeInformation.OSArchitecture != Architecture.X64)
|
||||
{
|
||||
if (showWarnings)
|
||||
{
|
||||
GtkDialog.CreateWarningDialog("You are not running a supported system architecture!", "(Only x64 systems are supported!)");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!NetworkInterface.GetIsNetworkAvailable())
|
||||
{
|
||||
if (showWarnings)
|
||||
|
Reference in New Issue
Block a user