Compare commits

..

8 Commits

Author SHA1 Message Date
Emmanuel Hansen
02714a1291 Avalonia - Add source generator for locale items (#3999)
* Add source generator for locale keys

* use locale keys in Ui subdir
2023-01-03 19:45:08 +01:00
Mary-nyan
09c9686498 misc: Use official names for NVDEC registers (#4192)
* misc: Uses official names for NVDEC registers

* Address gdkchan's comment

* Address comments
2023-01-02 14:48:46 +00:00
Mary-nyan
b6614c6ad5 chore: Update tests dependencies (#3978)
* chore: Update tests dependencies

* Apply TSR Berry suggestion to add a GC.SuppressFinalize in MemoryBlock.cs

* Ensure we wait for the test thread to be dead on PartialUnmap

* Use platform attribute for os specific tests

* Make P/Invoke methods private

* Downgrade NUnit3TestAdapter to 4.1.0

* test: Disable warning about platform compat for ThreadLocalMap()

Co-authored-by: TSR Berry <20988865+TSRBerry@users.noreply.github.com>
2023-01-01 17:35:29 +01:00
Emmanuel Hansen
b1d4b174a6 fix typo in left joycon sl binding (#4195) 2023-01-01 15:46:02 +01:00
Andrew Glaze
2b23463daa Filter hidden game files from the Game List (#4051)
* Filter “._” files from the game list

* Filter all hidden files from the game list

* Fix style

Co-authored-by: gdkchan <gab.dark.100@gmail.com>

* merge OR expression into a pattern

* migrate from GetFiles/Directories to Enumerate

* Remove GetFilesInDirectory()

* Update Ryujinx.Ui.Common/App/ApplicationLibrary.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* add error handeling

* code cleanup

Co-authored-by: gdkchan <gab.dark.100@gmail.com>
Co-authored-by: Ac_K <Acoustik666@gmail.com>
2022-12-29 16:52:30 +01:00
gdkchan
9dfe81770a Use vector outputs for texture operations (#3939)
* Change AggregateType to include vector type counts

* Replace VariableType uses with AggregateType and delete VariableType

* Support new local vector types on SPIR-V and GLSL

* Start using vector outputs for texture operations

* Use vectors on more texture operations

* Use vector output for ImageLoad operations

* Replace all uses of single destination texture constructors with multi destination ones

* Update textureGatherOffsets replacement to split vector operations

* Shader cache version bump

Co-authored-by: Ac_K <Acoustik666@gmail.com>
2022-12-29 16:09:34 +01:00
Luminoso-256
52c115a1f8 HLE: Add basic stubs to get Labo VR booting to title screen. (#4007)
* HLE: Add basic stubs to get Labo VR booting to title screen.

* Address code review

* Apply suggestions from code review (pt. 2)

Co-authored-by: gdkchan <gab.dark.100@gmail.com>

* Apply suggestions from code review (pt. 3)

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>
Co-authored-by: gdkchan <gab.dark.100@gmail.com>

* Formatting: final batch?

Co-authored-by: Ac_K <Acoustik666@gmail.com>
Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Address last? bit of formatting

Co-authored-by: gdkchan <gab.dark.100@gmail.com>
Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>
Co-authored-by: Ac_K <Acoustik666@gmail.com>
2022-12-29 15:57:35 +01:00
riperiperi
e20abbf9cc Vulkan: Don't flush commands when creating most sync (#4087)
* Vulkan: Don't flush commands when creating most sync

When the WaitForIdle method is called, we create sync as some internal GPU method may read back written buffer data. Some games randomly intersperse compute dispatch into their render passes, which result in this happening an unbounded number of times depending on how many times they run compute.

Creating sync in Vulkan is expensive, as we need to flush the current command buffer so that it can be waited on. We have a limited number of active command buffers due to how we track resource usage, so submitting too many command buffers will force us to wait for them to return to the pool.

This PR allows less "important" sync (things which are less likely to be waited on) to wait on a command buffer's result without submitting it, instead relying on AutoFlush or another, more important sync to flush it later on.

Because of the possibility of us waiting for a command buffer that hasn't submitted yet, any thread needs to be able to force the active command buffer to submit. The ability to do this has been added to the backend multithreading via an "Interrupt", though it is not supported without multithreading.

OpenGL drivers should already be doing something similar so they don't blow up when creating lots of sync, which is why this hasn't been a problem for these games over there.

Improves Vulkan performance on Xenoblade DE, Pokemon Scarlet/Violet, and Zelda BOTW (still another large issue here)

* Add strict argument

This is technically a separate concern from whether the sync is a host syncpoint.

* Remove _interrupted variable

* Actually wait for the invoke

This is required by AMD GPUs, and also may have caused some issues on other GPUs.

* Remove unused using.

* I don't know why it added these ones.

* Address Feedback

* Fix typo
2022-12-29 15:39:04 +01:00
103 changed files with 1730 additions and 1236 deletions

View File

@@ -25,7 +25,7 @@
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
<PackageVersion Include="MsgPack.Cli" Version="1.0.1" />
<PackageVersion Include="NUnit" Version="3.13.3" />
<PackageVersion Include="NUnit3TestAdapter" Version="3.17.0" />
<PackageVersion Include="NUnit3TestAdapter" Version="4.1.0" />
<PackageVersion Include="OpenTK.Core" Version="4.7.5" />
<PackageVersion Include="OpenTK.Graphics" Version="4.7.5" />
<PackageVersion Include="OpenTK.OpenAL" Version="4.7.5" />
@@ -50,5 +50,7 @@
<PackageVersion Include="System.Net.NameResolution" Version="4.3.0" />
<PackageVersion Include="System.Threading.ThreadPool" Version="4.3.0" />
<PackageVersion Include="XamlNameReferenceGenerator" Version="1.5.1" />
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.3"/>
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.4.0" />
</ItemGroup>
</Project>

View File

@@ -59,11 +59,11 @@ namespace Ryujinx.Ava
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
var result = await ContentDialogHelper.CreateConfirmationDialog(
LocaleManager.Instance["DialogThemeRestartMessage"],
LocaleManager.Instance["DialogThemeRestartSubMessage"],
LocaleManager.Instance["InputDialogYes"],
LocaleManager.Instance["InputDialogNo"],
LocaleManager.Instance["DialogRestartRequiredMessage"]);
LocaleManager.Instance[LocaleKeys.DialogThemeRestartMessage],
LocaleManager.Instance[LocaleKeys.DialogThemeRestartSubMessage],
LocaleManager.Instance[LocaleKeys.InputDialogYes],
LocaleManager.Instance[LocaleKeys.InputDialogNo],
LocaleManager.Instance[LocaleKeys.DialogRestartRequiredMessage]);
if (result == UserResult.Yes)
{

View File

@@ -432,10 +432,10 @@ namespace Ryujinx.Ava
if (userError == UserError.NoFirmware)
{
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(
LocaleManager.Instance["DialogFirmwareNoFirmwareInstalledMessage"],
string.Format(LocaleManager.Instance["DialogFirmwareInstallEmbeddedMessage"], firmwareVersion.VersionString),
LocaleManager.Instance["InputDialogYes"],
LocaleManager.Instance["InputDialogNo"],
LocaleManager.Instance[LocaleKeys.DialogFirmwareNoFirmwareInstalledMessage],
string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallEmbeddedMessage], firmwareVersion.VersionString),
LocaleManager.Instance[LocaleKeys.InputDialogYes],
LocaleManager.Instance[LocaleKeys.InputDialogNo],
"");
if (result != UserResult.Yes)
@@ -463,11 +463,11 @@ namespace Ryujinx.Ava
_parent.RefreshFirmwareStatus();
await ContentDialogHelper.CreateInfoDialog(
string.Format(LocaleManager.Instance["DialogFirmwareInstalledMessage"], firmwareVersion.VersionString),
string.Format(LocaleManager.Instance["DialogFirmwareInstallEmbeddedSuccessMessage"], firmwareVersion.VersionString),
LocaleManager.Instance["InputDialogOk"],
string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstalledMessage], firmwareVersion.VersionString),
string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallEmbeddedSuccessMessage], firmwareVersion.VersionString),
LocaleManager.Instance[LocaleKeys.InputDialogOk],
"",
LocaleManager.Instance["RyujinxInfo"]);
LocaleManager.Instance[LocaleKeys.RyujinxInfo]);
}
}
else
@@ -869,7 +869,7 @@ namespace Ryujinx.Ava
public void UpdateStatus()
{
// Run a status update only when a frame is to be drawn. This prevents from updating the ui and wasting a render when no frame is queued
string dockedMode = ConfigurationState.Instance.System.EnableDockedMode ? LocaleManager.Instance["Docked"] : LocaleManager.Instance["Handheld"];
string dockedMode = ConfigurationState.Instance.System.EnableDockedMode ? LocaleManager.Instance[LocaleKeys.Docked] : LocaleManager.Instance[LocaleKeys.Handheld];
float scale = GraphicsConfig.ResScale;
if (scale != 1)
@@ -879,11 +879,11 @@ namespace Ryujinx.Ava
StatusUpdatedEvent?.Invoke(this, new StatusUpdatedEventArgs(
Device.EnableDeviceVsync,
LocaleManager.Instance["VolumeShort"] + $": {(int)(Device.GetVolume() * 100)}%",
LocaleManager.Instance[LocaleKeys.VolumeShort] + $": {(int)(Device.GetVolume() * 100)}%",
Renderer.IsVulkan ? "Vulkan" : "OpenGL",
dockedMode,
ConfigurationState.Instance.Graphics.AspectRatio.Value.ToText(),
LocaleManager.Instance["Game"] + $": {Device.Statistics.GetGameFrameRate():00.00} FPS ({Device.Statistics.GetGameFrameTime():00.00} ms)",
LocaleManager.Instance[LocaleKeys.Game] + $": {Device.Statistics.GetGameFrameRate():00.00} FPS ({Device.Statistics.GetGameFrameTime():00.00} ms)",
$"FIFO: {Device.Statistics.GetFifoPercent():00.00} %",
$"GPU: {_renderer.GetHardwareInfo().GpuVendor}"));
}

View File

@@ -81,7 +81,7 @@ namespace Ryujinx.Ava.Common
Dispatcher.UIThread.Post(async () =>
{
await ContentDialogHelper.CreateErrorDialog(
string.Format(LocaleManager.Instance["DialogMessageCreateSaveErrorMessage"], result.ToStringWithName()));
string.Format(LocaleManager.Instance[LocaleKeys.DialogMessageCreateSaveErrorMessage], result.ToStringWithName()));
});
return false;
@@ -100,7 +100,7 @@ namespace Ryujinx.Ava.Common
Dispatcher.UIThread.Post(async () =>
{
await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance["DialogMessageFindSaveErrorMessage"], result.ToStringWithName()));
await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance[LocaleKeys.DialogMessageFindSaveErrorMessage], result.ToStringWithName()));
});
return false;
@@ -151,7 +151,7 @@ namespace Ryujinx.Ava.Common
public static async Task ExtractSection(NcaSectionType ncaSectionType, string titleFilePath,
int programIndex = 0)
{
OpenFolderDialog folderDialog = new() { Title = LocaleManager.Instance["FolderDialogExtractTitle"] };
OpenFolderDialog folderDialog = new() { Title = LocaleManager.Instance[LocaleKeys.FolderDialogExtractTitle] };
string destination = await folderDialog.ShowAsync(_owner);
@@ -164,11 +164,11 @@ namespace Ryujinx.Ava.Common
Dispatcher.UIThread.Post(async () =>
{
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(
string.Format(LocaleManager.Instance["DialogNcaExtractionMessage"], ncaSectionType, Path.GetFileName(titleFilePath)),
string.Format(LocaleManager.Instance[LocaleKeys.DialogNcaExtractionMessage], ncaSectionType, Path.GetFileName(titleFilePath)),
"",
"",
LocaleManager.Instance["InputDialogCancel"],
LocaleManager.Instance["DialogNcaExtractionTitle"]);
LocaleManager.Instance[LocaleKeys.InputDialogCancel],
LocaleManager.Instance[LocaleKeys.DialogNcaExtractionTitle]);
if (result == UserResult.Cancel)
{
@@ -234,7 +234,7 @@ namespace Ryujinx.Ava.Common
"Extraction failure. The main NCA was not present in the selected file");
Dispatcher.UIThread.InvokeAsync(async () =>
{
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance["DialogNcaExtractionMainNcaNotFoundErrorMessage"]);
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogNcaExtractionMainNcaNotFoundErrorMessage]);
});
return;
}
@@ -275,7 +275,7 @@ namespace Ryujinx.Ava.Common
$"LibHac returned error code: {resultCode.Value.ErrorCode}");
Dispatcher.UIThread.InvokeAsync(async () =>
{
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance["DialogNcaExtractionCheckLogErrorMessage"]);
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogNcaExtractionCheckLogErrorMessage]);
});
}
else if (resultCode.Value.IsSuccess())
@@ -283,11 +283,11 @@ namespace Ryujinx.Ava.Common
Dispatcher.UIThread.InvokeAsync(async () =>
{
await ContentDialogHelper.CreateInfoDialog(
LocaleManager.Instance["DialogNcaExtractionSuccessMessage"],
LocaleManager.Instance[LocaleKeys.DialogNcaExtractionSuccessMessage],
"",
LocaleManager.Instance["InputDialogOk"],
LocaleManager.Instance[LocaleKeys.InputDialogOk],
"",
LocaleManager.Instance["DialogNcaExtractionTitle"]);
LocaleManager.Instance[LocaleKeys.DialogNcaExtractionTitle]);
});
}
}

View File

@@ -7,16 +7,16 @@ namespace Ryujinx.Ava.Common.Locale
{
internal class LocaleExtension : MarkupExtension
{
public LocaleExtension(string key)
public LocaleExtension(LocaleKeys key)
{
Key = key;
}
public string Key { get; }
public LocaleKeys Key { get; }
public override object ProvideValue(IServiceProvider serviceProvider)
{
string keyToUse = Key;
LocaleKeys keyToUse = Key;
ReflectionBindingExtension binding = new($"[{keyToUse}]")
{

View File

@@ -2,6 +2,7 @@
using Ryujinx.Common;
using Ryujinx.Common.Utilities;
using Ryujinx.Ui.Common.Configuration;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Globalization;
@@ -13,17 +14,17 @@ namespace Ryujinx.Ava.Common.Locale
{
private const string DefaultLanguageCode = "en_US";
private Dictionary<string, string> _localeStrings;
private ConcurrentDictionary<string, object[]> _dynamicValues;
private Dictionary<LocaleKeys, string> _localeStrings;
private ConcurrentDictionary<LocaleKeys, object[]> _dynamicValues;
public static LocaleManager Instance { get; } = new LocaleManager();
public Dictionary<string, string> LocaleStrings { get => _localeStrings; set => _localeStrings = value; }
public Dictionary<LocaleKeys, string> LocaleStrings { get => _localeStrings; set => _localeStrings = value; }
public LocaleManager()
{
_localeStrings = new Dictionary<string, string>();
_dynamicValues = new ConcurrentDictionary<string, object[]>();
_localeStrings = new Dictionary<LocaleKeys, string>();
_dynamicValues = new ConcurrentDictionary<LocaleKeys, object[]>();
Load();
}
@@ -49,7 +50,7 @@ namespace Ryujinx.Ava.Common.Locale
}
}
public string this[string key]
public string this[LocaleKeys key]
{
get
{
@@ -63,7 +64,7 @@ namespace Ryujinx.Ava.Common.Locale
return value;
}
return key;
return key.ToString();
}
set
{
@@ -73,7 +74,7 @@ namespace Ryujinx.Ava.Common.Locale
}
}
public void UpdateDynamicValue(string key, params object[] values)
public void UpdateDynamicValue(LocaleKeys key, params object[] values)
{
_dynamicValues[key] = values;
@@ -98,7 +99,10 @@ namespace Ryujinx.Ava.Common.Locale
foreach (var item in strings)
{
this[item.Key] = item.Value;
if (Enum.TryParse<LocaleKeys>(item.Key, out var key))
{
this[key] = item.Value;
}
}
if (Program.PreviewerDetached)

View File

@@ -56,7 +56,7 @@ namespace Ryujinx.Ava.Input
return null;
}
return new AvaloniaKeyboard(this, _keyboardIdentifers[0], LocaleManager.Instance["AllKeyboards"]);
return new AvaloniaKeyboard(this, _keyboardIdentifers[0], LocaleManager.Instance[LocaleKeys.AllKeyboards]);
}
protected virtual void Dispose(bool disposing)

View File

@@ -84,7 +84,7 @@ namespace Ryujinx.Modules
Logger.Error?.Print(LogClass.Application, "Failed to convert the current Ryujinx version!");
Dispatcher.UIThread.Post(async () =>
{
await ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance["DialogUpdaterConvertFailedMessage"], LocaleManager.Instance["DialogUpdaterCancelUpdateMessage"]);
await ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterConvertFailedMessage], LocaleManager.Instance[LocaleKeys.DialogUpdaterCancelUpdateMessage]);
});
return;
@@ -119,7 +119,7 @@ namespace Ryujinx.Modules
{
Dispatcher.UIThread.Post(async () =>
{
await ContentDialogHelper.CreateUpdaterInfoDialog(LocaleManager.Instance["DialogUpdaterAlreadyOnLatestVersionMessage"], "");
await ContentDialogHelper.CreateUpdaterInfoDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterAlreadyOnLatestVersionMessage], "");
});
}
@@ -137,7 +137,7 @@ namespace Ryujinx.Modules
{
Dispatcher.UIThread.Post(async () =>
{
await ContentDialogHelper.CreateUpdaterInfoDialog(LocaleManager.Instance["DialogUpdaterAlreadyOnLatestVersionMessage"], "");
await ContentDialogHelper.CreateUpdaterInfoDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterAlreadyOnLatestVersionMessage], "");
});
}
@@ -150,7 +150,7 @@ namespace Ryujinx.Modules
Logger.Error?.Print(LogClass.Application, exception.Message);
Dispatcher.UIThread.Post(async () =>
{
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance["DialogUpdaterFailedToGetVersionMessage"]);
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterFailedToGetVersionMessage]);
});
return;
@@ -165,7 +165,7 @@ namespace Ryujinx.Modules
Logger.Error?.Print(LogClass.Application, "Failed to convert the received Ryujinx version from Github!");
Dispatcher.UIThread.Post(async () =>
{
await ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance["DialogUpdaterConvertFailedGithubMessage"], LocaleManager.Instance["DialogUpdaterCancelUpdateMessage"]);
await ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterConvertFailedGithubMessage], LocaleManager.Instance[LocaleKeys.DialogUpdaterCancelUpdateMessage]);
});
return;
@@ -177,7 +177,7 @@ namespace Ryujinx.Modules
{
Dispatcher.UIThread.Post(async () =>
{
await ContentDialogHelper.CreateUpdaterInfoDialog(LocaleManager.Instance["DialogUpdaterAlreadyOnLatestVersionMessage"], "");
await ContentDialogHelper.CreateUpdaterInfoDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterAlreadyOnLatestVersionMessage], "");
});
}
@@ -210,8 +210,8 @@ namespace Ryujinx.Modules
Dispatcher.UIThread.Post(async () =>
{
// Show a message asking the user if they want to update
var shouldUpdate = await ContentDialogHelper.CreateChoiceDialog(LocaleManager.Instance["RyujinxUpdater"],
LocaleManager.Instance["RyujinxUpdaterMessage"],
var shouldUpdate = await ContentDialogHelper.CreateChoiceDialog(LocaleManager.Instance[LocaleKeys.RyujinxUpdater],
LocaleManager.Instance[LocaleKeys.RyujinxUpdaterMessage],
$"{Program.Version} -> {newVersion}");
if (shouldUpdate)
@@ -247,8 +247,8 @@ namespace Ryujinx.Modules
var taskDialog = new TaskDialog()
{
Header = LocaleManager.Instance["RyujinxUpdater"],
SubHeader = LocaleManager.Instance["UpdaterDownloading"],
Header = LocaleManager.Instance[LocaleKeys.RyujinxUpdater],
SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterDownloading],
IconSource = new SymbolIconSource { Symbol = Symbol.Download },
Buttons = { },
ShowProgressBar = true
@@ -272,9 +272,9 @@ namespace Ryujinx.Modules
if (UpdateSuccessful)
{
var shouldRestart = await ContentDialogHelper.CreateChoiceDialog(LocaleManager.Instance["RyujinxUpdater"],
LocaleManager.Instance["DialogUpdaterCompleteMessage"],
LocaleManager.Instance["DialogUpdaterRestartMessage"]);
var shouldRestart = await ContentDialogHelper.CreateChoiceDialog(LocaleManager.Instance[LocaleKeys.RyujinxUpdater],
LocaleManager.Instance[LocaleKeys.DialogUpdaterCompleteMessage],
LocaleManager.Instance[LocaleKeys.DialogUpdaterRestartMessage]);
if (shouldRestart)
{
@@ -478,7 +478,7 @@ namespace Ryujinx.Modules
private static async void InstallUpdate(TaskDialog taskDialog, string updateFile)
{
// Extract Update
taskDialog.SubHeader = LocaleManager.Instance["UpdaterExtracting"];
taskDialog.SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterExtracting];
taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal);
if (OperatingSystem.IsLinux())
@@ -556,7 +556,7 @@ namespace Ryujinx.Modules
List<string> allFiles = EnumerateFilesToDelete().ToList();
taskDialog.SubHeader = LocaleManager.Instance["UpdaterRenaming"];
taskDialog.SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterRenaming];
taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal);
// Replace old files
@@ -577,13 +577,13 @@ namespace Ryujinx.Modules
}
catch
{
Logger.Warning?.Print(LogClass.Application, string.Format(LocaleManager.Instance["UpdaterRenameFailed"], file));
Logger.Warning?.Print(LogClass.Application, string.Format(LocaleManager.Instance[LocaleKeys.UpdaterRenameFailed], file));
}
}
Dispatcher.UIThread.Post(() =>
{
taskDialog.SubHeader = LocaleManager.Instance["UpdaterAddingFiles"];
taskDialog.SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterAddingFiles];
taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal);
});
@@ -607,8 +607,8 @@ namespace Ryujinx.Modules
{
if (showWarnings)
{
ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance["DialogUpdaterArchNotSupportedMessage"],
LocaleManager.Instance["DialogUpdaterArchNotSupportedSubMessage"]);
ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterArchNotSupportedMessage],
LocaleManager.Instance[LocaleKeys.DialogUpdaterArchNotSupportedSubMessage]);
}
return false;
@@ -618,8 +618,8 @@ namespace Ryujinx.Modules
{
if (showWarnings)
{
ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance["DialogUpdaterNoInternetMessage"],
LocaleManager.Instance["DialogUpdaterNoInternetSubMessage"]);
ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterNoInternetMessage],
LocaleManager.Instance[LocaleKeys.DialogUpdaterNoInternetSubMessage]);
}
return false;
@@ -629,8 +629,8 @@ namespace Ryujinx.Modules
{
if (showWarnings)
{
ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance["DialogUpdaterDirtyBuildMessage"],
LocaleManager.Instance["DialogUpdaterDirtyBuildSubMessage"]);
ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterDirtyBuildMessage],
LocaleManager.Instance[LocaleKeys.DialogUpdaterDirtyBuildSubMessage]);
}
return false;
@@ -642,11 +642,11 @@ namespace Ryujinx.Modules
{
if (ReleaseInformations.IsFlatHubBuild())
{
ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance["UpdaterDisabledWarningTitle"], LocaleManager.Instance["DialogUpdaterFlatpakNotSupportedMessage"]);
ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance[LocaleKeys.UpdaterDisabledWarningTitle], LocaleManager.Instance[LocaleKeys.DialogUpdaterFlatpakNotSupportedMessage]);
}
else
{
ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance["UpdaterDisabledWarningTitle"], LocaleManager.Instance["DialogUpdaterDirtyBuildSubMessage"]);
ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance[LocaleKeys.UpdaterDisabledWarningTitle], LocaleManager.Instance[LocaleKeys.DialogUpdaterDirtyBuildSubMessage]);
}
}

View File

@@ -58,6 +58,7 @@
<ProjectReference Include="..\Ryujinx.Graphics.OpenGL\Ryujinx.Graphics.OpenGL.csproj" />
<ProjectReference Include="..\Ryujinx.Graphics.Gpu\Ryujinx.Graphics.Gpu.csproj" />
<ProjectReference Include="..\Ryujinx.Ui.Common\Ryujinx.Ui.Common.csproj" />
<ProjectReference Include="..\Ryujinx.Ui.LocaleGenerator\Ryujinx.Ui.LocaleGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
</ItemGroup>
<ItemGroup>
@@ -158,4 +159,7 @@
<EmbeddedResource Include="Assets\Locales\zh_TW.json" />
<EmbeddedResource Include="Assets\Styles\Styles.xaml" />
</ItemGroup>
<ItemGroup>
<AdditionalFiles Include="Assets\Locales\en_US.json" />
</ItemGroup>
</Project>

View File

@@ -33,15 +33,15 @@ namespace Ryujinx.Ava.UI.Applet
? args.PlayerCountMin.ToString()
: $"{args.PlayerCountMin}-{args.PlayerCountMax}";
string key = args.PlayerCountMin == args.PlayerCountMax ? "DialogControllerAppletMessage" : "DialogControllerAppletMessagePlayerRange";
LocaleKeys key = args.PlayerCountMin == args.PlayerCountMax ? LocaleKeys.DialogControllerAppletMessage : LocaleKeys.DialogControllerAppletMessagePlayerRange;
string message = string.Format(LocaleManager.Instance[key],
playerCount,
args.SupportedStyles,
string.Join(", ", args.SupportedPlayers),
args.IsDocked ? LocaleManager.Instance["DialogControllerAppletDockModeSet"] : "");
args.IsDocked ? LocaleManager.Instance[LocaleKeys.DialogControllerAppletDockModeSet] : "");
return DisplayMessageDialog(LocaleManager.Instance["DialogControllerAppletTitle"], message);
return DisplayMessageDialog(LocaleManager.Instance[LocaleKeys.DialogControllerAppletTitle], message);
}
public bool DisplayMessageDialog(string title, string message)
@@ -62,9 +62,9 @@ namespace Ryujinx.Ava.UI.Applet
title,
message,
"",
LocaleManager.Instance["DialogOpenSettingsWindowLabel"],
LocaleManager.Instance[LocaleKeys.DialogOpenSettingsWindowLabel],
"",
LocaleManager.Instance["SettingsButtonClose"],
LocaleManager.Instance[LocaleKeys.SettingsButtonClose],
(int)Symbol.Important,
deferEvent,
async (window) =>
@@ -92,7 +92,7 @@ namespace Ryujinx.Ava.UI.Applet
}
catch (Exception ex)
{
await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance["DialogMessageDialogErrorExceptionMessage"], ex));
await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance[LocaleKeys.DialogMessageDialogErrorExceptionMessage], ex));
dialogCloseEvent.Set();
}
@@ -115,7 +115,7 @@ namespace Ryujinx.Ava.UI.Applet
{
try
{
var response = await SwkbdAppletDialog.ShowInputDialog(_parent, LocaleManager.Instance["SoftwareKeyboard"], args);
var response = await SwkbdAppletDialog.ShowInputDialog(_parent, LocaleManager.Instance[LocaleKeys.SoftwareKeyboard], args);
if (response.Result == UserResult.Ok)
{
@@ -126,7 +126,7 @@ namespace Ryujinx.Ava.UI.Applet
catch (Exception ex)
{
error = true;
await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance["DialogSoftwareKeyboardErrorExceptionMessage"], ex));
await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance[LocaleKeys.DialogSoftwareKeyboardErrorExceptionMessage], ex));
}
finally
{
@@ -181,7 +181,7 @@ namespace Ryujinx.Ava.UI.Applet
catch (Exception ex)
{
dialogCloseEvent.Set();
await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance["DialogErrorAppletErrorExceptionMessage"], ex));
await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance[LocaleKeys.DialogErrorAppletErrorExceptionMessage], ex));
}
});

View File

@@ -34,7 +34,7 @@ namespace Ryujinx.Ava.UI.Applet
}
else
{
AddButton(LocaleManager.Instance["InputDialogOk"], 0);
AddButton(LocaleManager.Instance[LocaleKeys.InputDialogOk], 0);
}
}

View File

@@ -86,7 +86,7 @@ namespace Ryujinx.Ava.UI.Controls
contentDialog.PrimaryButtonText = args.SubmitText;
contentDialog.IsPrimaryButtonEnabled = content._checkLength(content.Message.Length);
contentDialog.SecondaryButtonText = "";
contentDialog.CloseButtonText = LocaleManager.Instance["InputDialogCancel"];
contentDialog.CloseButtonText = LocaleManager.Instance[LocaleKeys.InputDialogCancel];
contentDialog.Content = content;
TypedEventHandler<ContentDialog, ContentDialogClosedEventArgs> handler = (sender, eventArgs) =>
@@ -139,14 +139,14 @@ namespace Ryujinx.Ava.UI.Controls
else if (_inputMin > 0 && _inputMax == int.MaxValue)
{
Error.IsVisible = true;
Error.Text = string.Format(LocaleManager.Instance["SwkbdMinCharacters"], _inputMin);
Error.Text = string.Format(LocaleManager.Instance[LocaleKeys.SwkbdMinCharacters], _inputMin);
_checkLength = length => _inputMin <= length;
}
else
{
Error.IsVisible = true;
Error.Text = string.Format(LocaleManager.Instance["SwkbdMinRangeCharacters"], _inputMin, _inputMax);
Error.Text = string.Format(LocaleManager.Instance[LocaleKeys.SwkbdMinRangeCharacters], _inputMin, _inputMax);
_checkLength = length => _inputMin <= length && length <= _inputMax;
}

View File

@@ -39,9 +39,9 @@ namespace Ryujinx.Ava.UI.Controls
ContentDialog contentDialog = new ContentDialog
{
Title = title,
PrimaryButtonText = LocaleManager.Instance["InputDialogOk"],
PrimaryButtonText = LocaleManager.Instance[LocaleKeys.InputDialogOk],
SecondaryButtonText = "",
CloseButtonText = LocaleManager.Instance["InputDialogCancel"],
CloseButtonText = LocaleManager.Instance[LocaleKeys.InputDialogCancel],
Content = content,
PrimaryButtonCommand = MiniCommand.Create(() =>
{

View File

@@ -65,10 +65,10 @@ namespace Ryujinx.Ava.UI.Controls
var content = new NavigationDialogHost(ownerAccountManager, ownerContentManager, ownerVirtualFileSystem, ownerHorizonClient);
ContentDialog contentDialog = new ContentDialog
{
Title = LocaleManager.Instance["UserProfileWindowTitle"],
Title = LocaleManager.Instance[LocaleKeys.UserProfileWindowTitle],
PrimaryButtonText = "",
SecondaryButtonText = "",
CloseButtonText = LocaleManager.Instance["UserProfilesClose"],
CloseButtonText = LocaleManager.Instance[LocaleKeys.UserProfilesClose],
Content = content,
Padding = new Thickness(0)
};

View File

@@ -55,7 +55,7 @@ namespace Ryujinx.Ava.UI.Controls
OpenFileDialog dialog = new();
dialog.Filters.Add(new FileDialogFilter
{
Name = LocaleManager.Instance["AllSupportedFormats"],
Name = LocaleManager.Instance[LocaleKeys.AllSupportedFormats],
Extensions = { "jpg", "jpeg", "png", "bmp" }
});
dialog.Filters.Add(new FileDialogFilter { Name = "JPEG", Extensions = { "jpg", "jpeg" } });

View File

@@ -62,7 +62,7 @@
<Label
VerticalAlignment="Center"
HorizontalContentAlignment="Left"
Content="{locale:Locale Descending}" />
Content="{locale:Locale OrderDescending}" />
</ComboBoxItem>
</ComboBox>
</StackPanel>

View File

@@ -65,14 +65,14 @@ namespace Ryujinx.Ava.UI.Controls
if (string.IsNullOrWhiteSpace(TempProfile.Name))
{
DataValidationErrors.SetError(NameBox, new DataValidationException(LocaleManager.Instance["UserProfileEmptyNameError"]));
DataValidationErrors.SetError(NameBox, new DataValidationException(LocaleManager.Instance[LocaleKeys.UserProfileEmptyNameError]));
isInvalid = true;
}
if (TempProfile.Image == null)
{
await ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance["UserProfileNoImageError"], "");
await ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance[LocaleKeys.UserProfileNoImageError], "");
isInvalid = true;
}

View File

@@ -170,7 +170,7 @@ namespace Ryujinx.Ava.UI.Helpers
secondaryButton,
closeButton,
iconSymbol,
primaryButton == LocaleManager.Instance["InputDialogYes"] ? UserResult.Yes : UserResult.Ok,
primaryButton == LocaleManager.Instance[LocaleKeys.InputDialogYes] ? UserResult.Yes : UserResult.Ok,
deferResetEvent,
doWhileDeferred,
DeferClose);
@@ -188,7 +188,7 @@ namespace Ryujinx.Ava.UI.Helpers
var deferral = args.GetDeferral();
result = primaryButton == LocaleManager.Instance["InputDialogYes"] ? UserResult.Yes : UserResult.Ok;
result = primaryButton == LocaleManager.Instance[LocaleKeys.InputDialogYes] ? UserResult.Yes : UserResult.Ok;
sender.PrimaryButtonClick -= DeferClose;
@@ -281,7 +281,7 @@ namespace Ryujinx.Ava.UI.Helpers
UserResult primaryButtonResult = UserResult.Yes)
{
return await ShowContentDialog(
string.IsNullOrWhiteSpace(title) ? LocaleManager.Instance["DialogConfirmationTitle"] : title,
string.IsNullOrWhiteSpace(title) ? LocaleManager.Instance[LocaleKeys.DialogConfirmationTitle] : title,
primaryText,
secondaryText,
acceptButtonText,
@@ -299,24 +299,24 @@ namespace Ryujinx.Ava.UI.Helpers
internal static async Task CreateUpdaterInfoDialog(string primary, string secondaryText)
{
await ShowContentDialog(
LocaleManager.Instance["DialogUpdaterTitle"],
LocaleManager.Instance[LocaleKeys.DialogUpdaterTitle],
primary,
secondaryText,
"",
"",
LocaleManager.Instance["InputDialogOk"],
LocaleManager.Instance[LocaleKeys.InputDialogOk],
(int)Symbol.Important);
}
internal static async Task CreateWarningDialog(string primary, string secondaryText)
{
await ShowContentDialog(
LocaleManager.Instance["DialogWarningTitle"],
LocaleManager.Instance[LocaleKeys.DialogWarningTitle],
primary,
secondaryText,
"",
"",
LocaleManager.Instance["InputDialogOk"],
LocaleManager.Instance[LocaleKeys.InputDialogOk],
(int)Symbol.Important);
}
@@ -325,12 +325,12 @@ namespace Ryujinx.Ava.UI.Helpers
Logger.Error?.Print(LogClass.Application, errorMessage);
await ShowContentDialog(
LocaleManager.Instance["DialogErrorTitle"],
LocaleManager.Instance["DialogErrorMessage"],
LocaleManager.Instance[LocaleKeys.DialogErrorTitle],
LocaleManager.Instance[LocaleKeys.DialogErrorMessage],
errorMessage,
secondaryErrorMessage,
"",
LocaleManager.Instance["InputDialogOk"],
LocaleManager.Instance[LocaleKeys.InputDialogOk],
(int)Symbol.Dismiss);
}
@@ -348,9 +348,9 @@ namespace Ryujinx.Ava.UI.Helpers
title,
primary,
secondaryText,
LocaleManager.Instance["InputDialogYes"],
LocaleManager.Instance[LocaleKeys.InputDialogYes],
"",
LocaleManager.Instance["InputDialogNo"],
LocaleManager.Instance[LocaleKeys.InputDialogNo],
(int)Symbol.Help,
UserResult.Yes);
@@ -362,17 +362,17 @@ namespace Ryujinx.Ava.UI.Helpers
internal static async Task<bool> CreateExitDialog()
{
return await CreateChoiceDialog(
LocaleManager.Instance["DialogExitTitle"],
LocaleManager.Instance["DialogExitMessage"],
LocaleManager.Instance["DialogExitSubMessage"]);
LocaleManager.Instance[LocaleKeys.DialogExitTitle],
LocaleManager.Instance[LocaleKeys.DialogExitMessage],
LocaleManager.Instance[LocaleKeys.DialogExitSubMessage]);
}
internal static async Task<bool> CreateStopEmulationDialog()
{
return await CreateChoiceDialog(
LocaleManager.Instance["DialogStopEmulationTitle"],
LocaleManager.Instance["DialogStopEmulationMessage"],
LocaleManager.Instance["DialogExitSubMessage"]);
LocaleManager.Instance[LocaleKeys.DialogStopEmulationTitle],
LocaleManager.Instance[LocaleKeys.DialogStopEmulationMessage],
LocaleManager.Instance[LocaleKeys.DialogExitSubMessage]);
}
internal static async Task<string> CreateInputDialog(

View File

@@ -19,12 +19,12 @@ namespace Ryujinx.Ava.UI.Helpers
{
return error switch
{
UserError.NoKeys => LocaleManager.Instance["UserErrorNoKeys"],
UserError.NoFirmware => LocaleManager.Instance["UserErrorNoFirmware"],
UserError.FirmwareParsingFailed => LocaleManager.Instance["UserErrorFirmwareParsingFailed"],
UserError.ApplicationNotFound => LocaleManager.Instance["UserErrorApplicationNotFound"],
UserError.Unknown => LocaleManager.Instance["UserErrorUnknown"],
_ => LocaleManager.Instance["UserErrorUndefined"]
UserError.NoKeys => LocaleManager.Instance[LocaleKeys.UserErrorNoKeys],
UserError.NoFirmware => LocaleManager.Instance[LocaleKeys.UserErrorNoFirmware],
UserError.FirmwareParsingFailed => LocaleManager.Instance[LocaleKeys.UserErrorFirmwareParsingFailed],
UserError.ApplicationNotFound => LocaleManager.Instance[LocaleKeys.UserErrorApplicationNotFound],
UserError.Unknown => LocaleManager.Instance[LocaleKeys.UserErrorUnknown],
_ => LocaleManager.Instance[LocaleKeys.UserErrorUndefined]
};
}
@@ -32,12 +32,12 @@ namespace Ryujinx.Ava.UI.Helpers
{
return error switch
{
UserError.NoKeys => LocaleManager.Instance["UserErrorNoKeysDescription"],
UserError.NoFirmware => LocaleManager.Instance["UserErrorNoFirmwareDescription"],
UserError.FirmwareParsingFailed => LocaleManager.Instance["UserErrorFirmwareParsingFailedDescription"],
UserError.ApplicationNotFound => LocaleManager.Instance["UserErrorApplicationNotFoundDescription"],
UserError.Unknown => LocaleManager.Instance["UserErrorUnknownDescription"],
_ => LocaleManager.Instance["UserErrorUndefinedDescription"]
UserError.NoKeys => LocaleManager.Instance[LocaleKeys.UserErrorNoKeysDescription],
UserError.NoFirmware => LocaleManager.Instance[LocaleKeys.UserErrorNoFirmwareDescription],
UserError.FirmwareParsingFailed => LocaleManager.Instance[LocaleKeys.UserErrorFirmwareParsingFailedDescription],
UserError.ApplicationNotFound => LocaleManager.Instance[LocaleKeys.UserErrorApplicationNotFoundDescription],
UserError.Unknown => LocaleManager.Instance[LocaleKeys.UserErrorUnknownDescription],
_ => LocaleManager.Instance[LocaleKeys.UserErrorUndefinedDescription]
};
}
@@ -73,14 +73,14 @@ namespace Ryujinx.Ava.UI.Helpers
bool isInSetupGuide = IsCoveredBySetupGuide(error);
string setupButtonLabel = isInSetupGuide ? LocaleManager.Instance["OpenSetupGuideMessage"] : "";
string setupButtonLabel = isInSetupGuide ? LocaleManager.Instance[LocaleKeys.OpenSetupGuideMessage] : "";
var result = await ContentDialogHelper.CreateInfoDialog(
string.Format(LocaleManager.Instance["DialogUserErrorDialogMessage"], errorCode, GetErrorTitle(error)),
string.Format(LocaleManager.Instance[LocaleKeys.DialogUserErrorDialogMessage], errorCode, GetErrorTitle(error)),
GetErrorDescription(error) + (isInSetupGuide
? LocaleManager.Instance["DialogUserErrorDialogInfoMessage"]
: ""), setupButtonLabel, LocaleManager.Instance["InputDialogOk"],
string.Format(LocaleManager.Instance["DialogUserErrorDialogTitle"], errorCode));
? LocaleManager.Instance[LocaleKeys.DialogUserErrorDialogInfoMessage]
: ""), setupButtonLabel, LocaleManager.Instance[LocaleKeys.InputDialogOk],
string.Format(LocaleManager.Instance[LocaleKeys.DialogUserErrorDialogTitle], errorCode));
if (result == UserResult.Ok)
{

View File

@@ -17,12 +17,12 @@ namespace Ryujinx.Ava.UI.Models.Generic
string aValue = x.LastPlayed;
string bValue = y.LastPlayed;
if (aValue == LocaleManager.Instance["Never"])
if (aValue == LocaleManager.Instance[LocaleKeys.Never])
{
aValue = DateTime.UnixEpoch.ToString();
}
if (bValue == LocaleManager.Instance["Never"])
if (bValue == LocaleManager.Instance[LocaleKeys.Never])
{
bValue = DateTime.UnixEpoch.ToString();
}

View File

@@ -107,10 +107,10 @@ namespace Ryujinx.Ava.UI.Models
public async void Delete()
{
var result = await ContentDialogHelper.CreateConfirmationDialog(LocaleManager.Instance["DeleteUserSave"],
LocaleManager.Instance["IrreversibleActionNote"],
LocaleManager.Instance["InputDialogYes"],
LocaleManager.Instance["InputDialogNo"], "");
var result = await ContentDialogHelper.CreateConfirmationDialog(LocaleManager.Instance[LocaleKeys.DeleteUserSave],
LocaleManager.Instance[LocaleKeys.IrreversibleActionNote],
LocaleManager.Instance[LocaleKeys.InputDialogYes],
LocaleManager.Instance[LocaleKeys.InputDialogNo], "");
if (result == UserResult.Yes)
{

View File

@@ -11,8 +11,8 @@ namespace Ryujinx.Ava.UI.Models
public string Path { get; }
public string Label => IsNoUpdate
? LocaleManager.Instance["NoUpdate"]
: string.Format(LocaleManager.Instance["TitleUpdateVersionLabel"], Control.DisplayVersionString.ToString(),
? LocaleManager.Instance[LocaleKeys.NoUpdate]
: string.Format(LocaleManager.Instance[LocaleKeys.TitleUpdateVersionLabel], Control.DisplayVersionString.ToString(),
Path);
public TitleUpdateModel(ApplicationControlProperty control, string path, bool isNoUpdate = false)

View File

@@ -344,10 +344,10 @@ namespace Ryujinx.Ava.UI.ViewModels
if (usageString.Length == 0)
{
usageString = LocaleManager.Instance["Unknown"] + ".";
usageString = LocaleManager.Instance[LocaleKeys.Unknown] + ".";
}
Usage = $"{LocaleManager.Instance["Usage"]} {(writable ? $" ({LocaleManager.Instance["Writable"]})" : "")} : {usageString}";
Usage = $"{LocaleManager.Instance[LocaleKeys.Usage]} {(writable ? $" ({LocaleManager.Instance[LocaleKeys.Writable]})" : "")} : {usageString}";
}
}
@@ -392,11 +392,11 @@ namespace Ryujinx.Ava.UI.ViewModels
return amiiboJsonString;
}
await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance["DialogAmiiboApiTitle"],
LocaleManager.Instance["DialogAmiiboApiFailFetchMessage"],
LocaleManager.Instance["InputDialogOk"],
await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogAmiiboApiTitle],
LocaleManager.Instance[LocaleKeys.DialogAmiiboApiFailFetchMessage],
LocaleManager.Instance[LocaleKeys.InputDialogOk],
"",
LocaleManager.Instance["RyujinxInfo"]);
LocaleManager.Instance[LocaleKeys.RyujinxInfo]);
Close();
@@ -442,11 +442,11 @@ namespace Ryujinx.Ava.UI.ViewModels
private async void ShowInfoDialog()
{
await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance["DialogAmiiboApiTitle"],
LocaleManager.Instance["DialogAmiiboApiConnectErrorMessage"],
LocaleManager.Instance["InputDialogOk"],
await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogAmiiboApiTitle],
LocaleManager.Instance[LocaleKeys.DialogAmiiboApiConnectErrorMessage],
LocaleManager.Instance[LocaleKeys.InputDialogOk],
"",
LocaleManager.Instance["RyujinxInfo"]);
LocaleManager.Instance[LocaleKeys.RyujinxInfo]);
}
}
}

View File

@@ -267,15 +267,15 @@ namespace Ryujinx.Ava.UI.ViewModels
ControllerImage = ProControllerResource;
PlayerIndexes.Add(new(PlayerIndex.Player1, LocaleManager.Instance["ControllerSettingsPlayer1"]));
PlayerIndexes.Add(new(PlayerIndex.Player2, LocaleManager.Instance["ControllerSettingsPlayer2"]));
PlayerIndexes.Add(new(PlayerIndex.Player3, LocaleManager.Instance["ControllerSettingsPlayer3"]));
PlayerIndexes.Add(new(PlayerIndex.Player4, LocaleManager.Instance["ControllerSettingsPlayer4"]));
PlayerIndexes.Add(new(PlayerIndex.Player5, LocaleManager.Instance["ControllerSettingsPlayer5"]));
PlayerIndexes.Add(new(PlayerIndex.Player6, LocaleManager.Instance["ControllerSettingsPlayer6"]));
PlayerIndexes.Add(new(PlayerIndex.Player7, LocaleManager.Instance["ControllerSettingsPlayer7"]));
PlayerIndexes.Add(new(PlayerIndex.Player8, LocaleManager.Instance["ControllerSettingsPlayer8"]));
PlayerIndexes.Add(new(PlayerIndex.Handheld, LocaleManager.Instance["ControllerSettingsHandheld"]));
PlayerIndexes.Add(new(PlayerIndex.Player1, LocaleManager.Instance[LocaleKeys.ControllerSettingsPlayer1]));
PlayerIndexes.Add(new(PlayerIndex.Player2, LocaleManager.Instance[LocaleKeys.ControllerSettingsPlayer2]));
PlayerIndexes.Add(new(PlayerIndex.Player3, LocaleManager.Instance[LocaleKeys.ControllerSettingsPlayer3]));
PlayerIndexes.Add(new(PlayerIndex.Player4, LocaleManager.Instance[LocaleKeys.ControllerSettingsPlayer4]));
PlayerIndexes.Add(new(PlayerIndex.Player5, LocaleManager.Instance[LocaleKeys.ControllerSettingsPlayer5]));
PlayerIndexes.Add(new(PlayerIndex.Player6, LocaleManager.Instance[LocaleKeys.ControllerSettingsPlayer6]));
PlayerIndexes.Add(new(PlayerIndex.Player7, LocaleManager.Instance[LocaleKeys.ControllerSettingsPlayer7]));
PlayerIndexes.Add(new(PlayerIndex.Player8, LocaleManager.Instance[LocaleKeys.ControllerSettingsPlayer8]));
PlayerIndexes.Add(new(PlayerIndex.Handheld, LocaleManager.Instance[LocaleKeys.ControllerSettingsHandheld]));
}
private void LoadConfiguration(InputConfig inputConfig = null)
@@ -406,16 +406,16 @@ namespace Ryujinx.Ava.UI.ViewModels
if (_playerId == PlayerIndex.Handheld)
{
Controllers.Add(new(ControllerType.Handheld, LocaleManager.Instance["ControllerSettingsControllerTypeHandheld"]));
Controllers.Add(new(ControllerType.Handheld, LocaleManager.Instance[LocaleKeys.ControllerSettingsControllerTypeHandheld]));
Controller = 0;
}
else
{
Controllers.Add(new(ControllerType.ProController, LocaleManager.Instance["ControllerSettingsControllerTypeProController"]));
Controllers.Add(new(ControllerType.JoyconPair, LocaleManager.Instance["ControllerSettingsControllerTypeJoyConPair"]));
Controllers.Add(new(ControllerType.JoyconLeft, LocaleManager.Instance["ControllerSettingsControllerTypeJoyConLeft"]));
Controllers.Add(new(ControllerType.JoyconRight, LocaleManager.Instance["ControllerSettingsControllerTypeJoyConRight"]));
Controllers.Add(new(ControllerType.ProController, LocaleManager.Instance[LocaleKeys.ControllerSettingsControllerTypeProController]));
Controllers.Add(new(ControllerType.JoyconPair, LocaleManager.Instance[LocaleKeys.ControllerSettingsControllerTypeJoyConPair]));
Controllers.Add(new(ControllerType.JoyconLeft, LocaleManager.Instance[LocaleKeys.ControllerSettingsControllerTypeJoyConLeft]));
Controllers.Add(new(ControllerType.JoyconRight, LocaleManager.Instance[LocaleKeys.ControllerSettingsControllerTypeJoyConRight]));
if (Config != null && Controllers.ToList().FindIndex(x => x.Type == Config.ControllerType) != -1)
{
@@ -455,7 +455,7 @@ namespace Ryujinx.Ava.UI.ViewModels
{
Devices.Clear();
DeviceList.Clear();
Devices.Add((DeviceType.None, Disabled, LocaleManager.Instance["ControllerSettingsDeviceDisabled"]));
Devices.Add((DeviceType.None, Disabled, LocaleManager.Instance[LocaleKeys.ControllerSettingsDeviceDisabled]));
foreach (string id in _mainWindow.InputManager.KeyboardDriver.GamepadsIds)
{
@@ -517,7 +517,7 @@ namespace Ryujinx.Ava.UI.ViewModels
Directory.CreateDirectory(basePath);
}
ProfilesList.Add((LocaleManager.Instance["ControllerSettingsProfileDefault"]));
ProfilesList.Add((LocaleManager.Instance[LocaleKeys.ControllerSettingsProfileDefault]));
foreach (string profile in Directory.GetFiles(basePath, "*.json", SearchOption.AllDirectories))
{
@@ -526,7 +526,7 @@ namespace Ryujinx.Ava.UI.ViewModels
if (string.IsNullOrWhiteSpace(ProfileName))
{
ProfileName = LocaleManager.Instance["ControllerSettingsProfileDefault"];
ProfileName = LocaleManager.Instance[LocaleKeys.ControllerSettingsProfileDefault];
}
}
@@ -687,7 +687,7 @@ namespace Ryujinx.Ava.UI.ViewModels
return;
}
if (ProfileName == LocaleManager.Instance["ControllerSettingsProfileDefault"])
if (ProfileName == LocaleManager.Instance[LocaleKeys.ControllerSettingsProfileDefault])
{
config = LoadDefaultConfiguration();
}
@@ -717,7 +717,7 @@ namespace Ryujinx.Ava.UI.ViewModels
{
Logger.Error?.Print(LogClass.Configuration, $"Profile {ProfileName} is incompatible with the current input configuration system.");
await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance["DialogProfileInvalidProfileErrorMessage"], ProfileName));
await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance[LocaleKeys.DialogProfileInvalidProfileErrorMessage], ProfileName));
return;
}
@@ -749,9 +749,9 @@ namespace Ryujinx.Ava.UI.ViewModels
return;
}
if (ProfileName == LocaleManager.Instance["ControllerSettingsProfileDefault"])
if (ProfileName == LocaleManager.Instance[LocaleKeys.ControllerSettingsProfileDefault])
{
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance["DialogProfileDefaultProfileOverwriteErrorMessage"]);
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogProfileDefaultProfileOverwriteErrorMessage]);
return;
}
@@ -784,24 +784,24 @@ namespace Ryujinx.Ava.UI.ViewModels
}
else
{
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance["DialogProfileInvalidProfileNameErrorMessage"]);
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogProfileInvalidProfileNameErrorMessage]);
}
}
}
public async void RemoveProfile()
{
if (Device == 0 || ProfileName == LocaleManager.Instance["ControllerSettingsProfileDefault"] || ProfilesList.IndexOf(ProfileName) == -1)
if (Device == 0 || ProfileName == LocaleManager.Instance[LocaleKeys.ControllerSettingsProfileDefault] || ProfilesList.IndexOf(ProfileName) == -1)
{
return;
}
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(
LocaleManager.Instance["DialogProfileDeleteProfileTitle"],
LocaleManager.Instance["DialogProfileDeleteProfileMessage"],
LocaleManager.Instance["InputDialogYes"],
LocaleManager.Instance["InputDialogNo"],
LocaleManager.Instance["RyujinxConfirm"]);
LocaleManager.Instance[LocaleKeys.DialogProfileDeleteProfileTitle],
LocaleManager.Instance[LocaleKeys.DialogProfileDeleteProfileMessage],
LocaleManager.Instance[LocaleKeys.InputDialogYes],
LocaleManager.Instance[LocaleKeys.InputDialogNo],
LocaleManager.Instance[LocaleKeys.RyujinxConfirm]);
if (result == UserResult.Yes)
{

View File

@@ -635,14 +635,14 @@ namespace Ryujinx.Ava.UI.ViewModels
{
return SortMode switch
{
ApplicationSort.Title => LocaleManager.Instance["GameListHeaderApplication"],
ApplicationSort.Developer => LocaleManager.Instance["GameListHeaderDeveloper"],
ApplicationSort.LastPlayed => LocaleManager.Instance["GameListHeaderLastPlayed"],
ApplicationSort.TotalTimePlayed => LocaleManager.Instance["GameListHeaderTimePlayed"],
ApplicationSort.FileType => LocaleManager.Instance["GameListHeaderFileExtension"],
ApplicationSort.FileSize => LocaleManager.Instance["GameListHeaderFileSize"],
ApplicationSort.Path => LocaleManager.Instance["GameListHeaderPath"],
ApplicationSort.Favorite => LocaleManager.Instance["CommonFavorite"],
ApplicationSort.Title => LocaleManager.Instance[LocaleKeys.GameListHeaderApplication],
ApplicationSort.Developer => LocaleManager.Instance[LocaleKeys.GameListHeaderDeveloper],
ApplicationSort.LastPlayed => LocaleManager.Instance[LocaleKeys.GameListHeaderLastPlayed],
ApplicationSort.TotalTimePlayed => LocaleManager.Instance[LocaleKeys.GameListHeaderTimePlayed],
ApplicationSort.FileType => LocaleManager.Instance[LocaleKeys.GameListHeaderFileExtension],
ApplicationSort.FileSize => LocaleManager.Instance[LocaleKeys.GameListHeaderFileSize],
ApplicationSort.Path => LocaleManager.Instance[LocaleKeys.GameListHeaderPath],
ApplicationSort.Favorite => LocaleManager.Instance[LocaleKeys.CommonFavorite],
_ => string.Empty,
};
}
@@ -771,7 +771,7 @@ namespace Ryujinx.Ava.UI.ViewModels
StatusBarProgressValue = e.NumAppsLoaded;
StatusBarProgressMaximum = e.NumAppsFound;
LocaleManager.Instance.UpdateDynamicValue("StatusBarGamesLoaded", StatusBarProgressValue, StatusBarProgressMaximum);
LocaleManager.Instance.UpdateDynamicValue(LocaleKeys.StatusBarGamesLoaded, StatusBarProgressValue, StatusBarProgressMaximum);
Dispatcher.UIThread.Post(() =>
{
@@ -805,7 +805,7 @@ namespace Ryujinx.Ava.UI.ViewModels
StatusBarProgressMaximum = 0;
StatusBarProgressValue = 0;
LocaleManager.Instance.UpdateDynamicValue("StatusBarGamesLoaded", 0, 0);
LocaleManager.Instance.UpdateDynamicValue(LocaleKeys.StatusBarGamesLoaded, 0, 0);
});
ReloadGameList();
@@ -835,12 +835,12 @@ namespace Ryujinx.Ava.UI.ViewModels
{
OpenFileDialog dialog = new()
{
Title = LocaleManager.Instance["OpenFileDialogTitle"]
Title = LocaleManager.Instance[LocaleKeys.OpenFileDialogTitle]
};
dialog.Filters.Add(new FileDialogFilter
{
Name = LocaleManager.Instance["AllSupportedFormats"],
Name = LocaleManager.Instance[LocaleKeys.AllSupportedFormats],
Extensions =
{
"nsp",
@@ -871,7 +871,7 @@ namespace Ryujinx.Ava.UI.ViewModels
{
OpenFolderDialog dialog = new()
{
Title = LocaleManager.Instance["OpenFolderDialogTitle"]
Title = LocaleManager.Instance[LocaleKeys.OpenFolderDialogTitle]
};
string folder = await dialog.ShowAsync(_owner);
@@ -1039,11 +1039,11 @@ namespace Ryujinx.Ava.UI.ViewModels
{
case PtcLoadingState.Start:
case PtcLoadingState.Loading:
LoadHeading = LocaleManager.Instance["CompilingPPTC"];
LoadHeading = LocaleManager.Instance[LocaleKeys.CompilingPPTC];
IsLoadingIndeterminate = false;
break;
case PtcLoadingState.Loaded:
LoadHeading = string.Format(LocaleManager.Instance["LoadingHeading"], TitleName);
LoadHeading = string.Format(LocaleManager.Instance[LocaleKeys.LoadingHeading], TitleName);
IsLoadingIndeterminate = true;
CacheLoadStatus = "";
break;
@@ -1055,11 +1055,11 @@ namespace Ryujinx.Ava.UI.ViewModels
{
case ShaderCacheLoadingState.Start:
case ShaderCacheLoadingState.Loading:
LoadHeading = LocaleManager.Instance["CompilingShaders"];
LoadHeading = LocaleManager.Instance[LocaleKeys.CompilingShaders];
IsLoadingIndeterminate = false;
break;
case ShaderCacheLoadingState.Loaded:
LoadHeading = string.Format(LocaleManager.Instance["LoadingHeading"], TitleName);
LoadHeading = string.Format(LocaleManager.Instance[LocaleKeys.LoadingHeading], TitleName);
IsLoadingIndeterminate = true;
CacheLoadStatus = "";
break;
@@ -1083,7 +1083,7 @@ namespace Ryujinx.Ava.UI.ViewModels
{
Dispatcher.UIThread.Post(async () =>
{
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance["DialogRyujinxErrorMessage"], LocaleManager.Instance["DialogInvalidTitleIdErrorMessage"]);
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogRyujinxErrorMessage], LocaleManager.Instance[LocaleKeys.DialogInvalidTitleIdErrorMessage]);
});
return;
@@ -1166,11 +1166,11 @@ namespace Ryujinx.Ava.UI.ViewModels
DirectoryInfo backupDir = new(Path.Combine(AppDataManager.GamesDirPath, selection.TitleId, "cache", "cpu", "1"));
// FIXME: Found a way to reproduce the bold effect on the title name (fork?).
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(LocaleManager.Instance["DialogWarning"],
string.Format(LocaleManager.Instance["DialogPPTCDeletionMessage"], selection.TitleName),
LocaleManager.Instance["InputDialogYes"],
LocaleManager.Instance["InputDialogNo"],
LocaleManager.Instance["RyujinxConfirm"]);
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(LocaleManager.Instance[LocaleKeys.DialogWarning],
string.Format(LocaleManager.Instance[LocaleKeys.DialogPPTCDeletionMessage], selection.TitleName),
LocaleManager.Instance[LocaleKeys.InputDialogYes],
LocaleManager.Instance[LocaleKeys.InputDialogNo],
LocaleManager.Instance[LocaleKeys.RyujinxConfirm]);
List<FileInfo> cacheFiles = new();
@@ -1194,7 +1194,7 @@ namespace Ryujinx.Ava.UI.ViewModels
}
catch (Exception e)
{
await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance["DialogPPTCDeletionErrorMessage"], file.Name, e));
await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance[LocaleKeys.DialogPPTCDeletionErrorMessage], file.Name, e));
}
}
}
@@ -1230,11 +1230,11 @@ namespace Ryujinx.Ava.UI.ViewModels
DirectoryInfo shaderCacheDir = new(Path.Combine(AppDataManager.GamesDirPath, selection.TitleId, "cache", "shader"));
// FIXME: Found a way to reproduce the bold effect on the title name (fork?).
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(LocaleManager.Instance["DialogWarning"],
string.Format(LocaleManager.Instance["DialogShaderDeletionMessage"], selection.TitleName),
LocaleManager.Instance["InputDialogYes"],
LocaleManager.Instance["InputDialogNo"],
LocaleManager.Instance["RyujinxConfirm"]);
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(LocaleManager.Instance[LocaleKeys.DialogWarning],
string.Format(LocaleManager.Instance[LocaleKeys.DialogShaderDeletionMessage], selection.TitleName),
LocaleManager.Instance[LocaleKeys.InputDialogYes],
LocaleManager.Instance[LocaleKeys.InputDialogNo],
LocaleManager.Instance[LocaleKeys.RyujinxConfirm]);
List<DirectoryInfo> oldCacheDirectories = new();
List<FileInfo> newCacheFiles = new();
@@ -1256,7 +1256,7 @@ namespace Ryujinx.Ava.UI.ViewModels
}
catch (Exception e)
{
await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance["DialogPPTCDeletionErrorMessage"], directory.Name, e));
await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance[LocaleKeys.DialogPPTCDeletionErrorMessage], directory.Name, e));
}
}
}
@@ -1269,7 +1269,7 @@ namespace Ryujinx.Ava.UI.ViewModels
}
catch (Exception e)
{
await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance["ShaderCachePurgeError"], file.Name, e));
await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance[LocaleKeys.ShaderCachePurgeError], file.Name, e));
}
}
}
@@ -1337,7 +1337,7 @@ namespace Ryujinx.Ava.UI.ViewModels
{
Dispatcher.UIThread.Post(async () =>
{
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance["DialogRyujinxErrorMessage"], LocaleManager.Instance["DialogInvalidTitleIdErrorMessage"]);
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogRyujinxErrorMessage], LocaleManager.Instance[LocaleKeys.DialogInvalidTitleIdErrorMessage]);
});
return;
@@ -1360,7 +1360,7 @@ namespace Ryujinx.Ava.UI.ViewModels
{
Dispatcher.UIThread.Post(async () =>
{
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance["DialogRyujinxErrorMessage"], LocaleManager.Instance["DialogInvalidTitleIdErrorMessage"]);
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogRyujinxErrorMessage], LocaleManager.Instance[LocaleKeys.DialogInvalidTitleIdErrorMessage]);
});
return;
@@ -1417,32 +1417,32 @@ namespace Ryujinx.Ava.UI.ViewModels
if (firmwareVersion == null)
{
await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance["DialogFirmwareInstallerFirmwareNotFoundErrorMessage"], filename));
await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallerFirmwareNotFoundErrorMessage], filename));
return;
}
string dialogTitle = string.Format(LocaleManager.Instance["DialogFirmwareInstallerFirmwareInstallTitle"], firmwareVersion.VersionString);
string dialogTitle = string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallerFirmwareInstallTitle], firmwareVersion.VersionString);
SystemVersion currentVersion = _owner.ContentManager.GetCurrentFirmwareVersion();
string dialogMessage = string.Format(LocaleManager.Instance["DialogFirmwareInstallerFirmwareInstallMessage"], firmwareVersion.VersionString);
string dialogMessage = string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallerFirmwareInstallMessage], firmwareVersion.VersionString);
if (currentVersion != null)
{
dialogMessage += string.Format(LocaleManager.Instance["DialogFirmwareInstallerFirmwareInstallSubMessage"], currentVersion.VersionString);
dialogMessage += string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallerFirmwareInstallSubMessage], currentVersion.VersionString);
}
dialogMessage += LocaleManager.Instance["DialogFirmwareInstallerFirmwareInstallConfirmMessage"];
dialogMessage += LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallerFirmwareInstallConfirmMessage];
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(
dialogTitle,
dialogMessage,
LocaleManager.Instance["InputDialogYes"],
LocaleManager.Instance["InputDialogNo"],
LocaleManager.Instance["RyujinxConfirm"]);
LocaleManager.Instance[LocaleKeys.InputDialogYes],
LocaleManager.Instance[LocaleKeys.InputDialogNo],
LocaleManager.Instance[LocaleKeys.RyujinxConfirm]);
UpdateWaitWindow waitingDialog = ContentDialogHelper.CreateWaitingDialog(dialogTitle, LocaleManager.Instance["DialogFirmwareInstallerFirmwareInstallWaitMessage"]);
UpdateWaitWindow waitingDialog = ContentDialogHelper.CreateWaitingDialog(dialogTitle, LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallerFirmwareInstallWaitMessage]);
if (result == UserResult.Yes)
{
@@ -1463,9 +1463,9 @@ namespace Ryujinx.Ava.UI.ViewModels
{
waitingDialog.Close();
string message = string.Format(LocaleManager.Instance["DialogFirmwareInstallerFirmwareInstallSuccessMessage"], firmwareVersion.VersionString);
string message = string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallerFirmwareInstallSuccessMessage], firmwareVersion.VersionString);
await ContentDialogHelper.CreateInfoDialog(dialogTitle, message, LocaleManager.Instance["InputDialogOk"], "", LocaleManager.Instance["RyujinxInfo"]);
await ContentDialogHelper.CreateInfoDialog(dialogTitle, message, LocaleManager.Instance[LocaleKeys.InputDialogOk], "", LocaleManager.Instance[LocaleKeys.RyujinxInfo]);
Logger.Info?.Print(LogClass.Application, message);
@@ -1513,7 +1513,7 @@ namespace Ryujinx.Ava.UI.ViewModels
public async void InstallFirmwareFromFile()
{
OpenFileDialog dialog = new() { AllowMultiple = false };
dialog.Filters.Add(new FileDialogFilter { Name = LocaleManager.Instance["FileDialogAllTypes"], Extensions = { "xci", "zip" } });
dialog.Filters.Add(new FileDialogFilter { Name = LocaleManager.Instance[LocaleKeys.FileDialogAllTypes], Extensions = { "xci", "zip" } });
dialog.Filters.Add(new FileDialogFilter { Name = "XCI", Extensions = { "xci" } });
dialog.Filters.Add(new FileDialogFilter { Name = "ZIP", Extensions = { "zip" } });

View File

@@ -73,11 +73,11 @@ namespace Ryujinx.Ava.UI.ViewModels
{
Dispatcher.UIThread.Post(async () =>
{
await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance["DialogSettingsBackendThreadingWarningMessage"],
await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogSettingsBackendThreadingWarningMessage],
"",
"",
LocaleManager.Instance["InputDialogOk"],
LocaleManager.Instance["DialogSettingsBackendThreadingWarningTitle"]);
LocaleManager.Instance[LocaleKeys.InputDialogOk],
LocaleManager.Instance[LocaleKeys.DialogSettingsBackendThreadingWarningTitle]);
});
}
}
@@ -306,11 +306,11 @@ namespace Ryujinx.Ava.UI.ViewModels
{
var dialog = new OpenFileDialog()
{
Title = LocaleManager.Instance["SettingsSelectThemeFileDialogTitle"],
Title = LocaleManager.Instance[LocaleKeys.SettingsSelectThemeFileDialogTitle],
AllowMultiple = false
};
dialog.Filters.Add(new FileDialogFilter() { Extensions = { "xaml" }, Name = LocaleManager.Instance["SettingsXamlThemeFile"] });
dialog.Filters.Add(new FileDialogFilter() { Extensions = { "xaml" }, Name = LocaleManager.Instance[LocaleKeys.SettingsXamlThemeFile] });
var file = await dialog.ShowAsync(_owner);

View File

@@ -149,10 +149,10 @@ namespace Ryujinx.Ava.UI.ViewModels
ContentDialog contentDialog = new ContentDialog
{
Title = string.Format(LocaleManager.Instance["SaveManagerHeading"], userProfile.Name),
Title = string.Format(LocaleManager.Instance[LocaleKeys.SaveManagerHeading], userProfile.Name),
PrimaryButtonText = "",
SecondaryButtonText = "",
CloseButtonText = LocaleManager.Instance["UserProfilesClose"],
CloseButtonText = LocaleManager.Instance[LocaleKeys.UserProfilesClose],
Content = manager,
Padding = new Thickness(0)
};
@@ -180,7 +180,7 @@ namespace Ryujinx.Ava.UI.ViewModels
{
Dispatcher.UIThread.Post(async () =>
{
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance["DialogUserProfileDeletionWarningMessage"]);
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUserProfileDeletionWarningMessage]);
});
return;
@@ -190,8 +190,8 @@ namespace Ryujinx.Ava.UI.ViewModels
}
var result =
await ContentDialogHelper.CreateConfirmationDialog(LocaleManager.Instance["DialogUserProfileDeletionConfirmMessage"], "",
LocaleManager.Instance["InputDialogYes"], LocaleManager.Instance["InputDialogNo"], "");
await ContentDialogHelper.CreateConfirmationDialog(LocaleManager.Instance[LocaleKeys.DialogUserProfileDeletionConfirmMessage], "",
LocaleManager.Instance[LocaleKeys.InputDialogYes], LocaleManager.Instance[LocaleKeys.InputDialogNo], "");
if (result == UserResult.Yes)
{

View File

@@ -17,7 +17,7 @@ namespace Ryujinx.Ava.UI.Windows
{
if (Program.PreviewerDetached)
{
Title = $"Ryujinx {Program.Version} - " + LocaleManager.Instance["MenuBarHelpAbout"];
Title = $"Ryujinx {Program.Version} - " + LocaleManager.Instance[LocaleKeys.MenuBarHelpAbout];
}
Version = Program.Version;
@@ -32,7 +32,7 @@ namespace Ryujinx.Ava.UI.Windows
public string Supporters { get; set; }
public string Version { get; set; }
public string Developers => string.Format(LocaleManager.Instance["AboutPageDeveloperListMore"], "gdkchan, Ac_K, Thog, rip in peri peri, LDj3SNuD, emmaus, Thealexbarney, Xpl0itR, GoffyDude, »jD«");
public string Developers => string.Format(LocaleManager.Instance[LocaleKeys.AboutPageDeveloperListMore], "gdkchan, Ac_K, Thog, rip in peri peri, LDj3SNuD, emmaus, Thealexbarney, Xpl0itR, GoffyDude, »jD«");
private void Button_OnClick(object sender, RoutedEventArgs e)
{
@@ -46,7 +46,7 @@ namespace Ryujinx.Ava.UI.Windows
{
if (!NetworkInterface.GetIsNetworkAvailable())
{
Supporters = LocaleManager.Instance["ConnectionError"];
Supporters = LocaleManager.Instance[LocaleKeys.ConnectionError];
return;
}
@@ -61,7 +61,7 @@ namespace Ryujinx.Ava.UI.Windows
}
catch
{
Supporters = LocaleManager.Instance["ApiError"];
Supporters = LocaleManager.Instance[LocaleKeys.ApiError];
}
await Dispatcher.UIThread.InvokeAsync(() => SupportersTextBlock.Text = Supporters);

View File

@@ -17,7 +17,7 @@ namespace Ryujinx.Ava.UI.Windows
InitializeComponent();
Title = $"Ryujinx {Program.Version} - " + LocaleManager.Instance["Amiibo"];
Title = $"Ryujinx {Program.Version} - " + LocaleManager.Instance[LocaleKeys.Amiibo];
}
public AmiiboWindow()
@@ -30,7 +30,7 @@ namespace Ryujinx.Ava.UI.Windows
if (Program.PreviewerDetached)
{
Title = $"Ryujinx {Program.Version} - " + LocaleManager.Instance["Amiibo"];
Title = $"Ryujinx {Program.Version} - " + LocaleManager.Instance[LocaleKeys.Amiibo];
}
}

View File

@@ -24,14 +24,14 @@ namespace Ryujinx.Ava.UI.Windows
InitializeComponent();
Title = $"Ryujinx {Program.Version} - " + LocaleManager.Instance["CheatWindowTitle"];
Title = $"Ryujinx {Program.Version} - " + LocaleManager.Instance[LocaleKeys.CheatWindowTitle];
}
public CheatWindow(VirtualFileSystem virtualFileSystem, string titleId, string titleName)
{
LoadedCheats = new AvaloniaList<CheatsList>();
Heading = string.Format(LocaleManager.Instance["CheatWindowHeading"], titleName, titleId.ToUpper());
Heading = string.Format(LocaleManager.Instance[LocaleKeys.CheatWindowHeading], titleName, titleId.ToUpper());
InitializeComponent();
@@ -86,7 +86,7 @@ namespace Ryujinx.Ava.UI.Windows
DataContext = this;
Title = $"Ryujinx {Program.Version} - " + LocaleManager.Instance["CheatWindowTitle"];
Title = $"Ryujinx {Program.Version} - " + LocaleManager.Instance[LocaleKeys.CheatWindowTitle];
}
public void Save()

View File

@@ -639,14 +639,14 @@
Width="20"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="{locale:Locale ControllerSettingsRightSL}"
Text="{locale:Locale ControllerSettingsLeftSL}"
TextAlignment="Center" />
<ToggleButton
Width="90"
Height="27"
HorizontalAlignment="Stretch">
<TextBlock
Text="{Binding Configuration.RightButtonSl, Mode=TwoWay, Converter={StaticResource Key}}"
Text="{Binding Configuration.LeftButtonSl, Mode=TwoWay, Converter={StaticResource Key}}"
TextAlignment="Center" />
</ToggleButton>
</StackPanel>

View File

@@ -148,11 +148,11 @@ namespace Ryujinx.Ava.UI.Windows
_dialogOpen = true;
var result = await ContentDialogHelper.CreateConfirmationDialog(
LocaleManager.Instance["DialogControllerSettingsModifiedConfirmMessage"],
LocaleManager.Instance["DialogControllerSettingsModifiedConfirmSubMessage"],
LocaleManager.Instance["InputDialogYes"],
LocaleManager.Instance["InputDialogNo"],
LocaleManager.Instance["RyujinxConfirm"]);
LocaleManager.Instance[LocaleKeys.DialogControllerSettingsModifiedConfirmMessage],
LocaleManager.Instance[LocaleKeys.DialogControllerSettingsModifiedConfirmSubMessage],
LocaleManager.Instance[LocaleKeys.InputDialogYes],
LocaleManager.Instance[LocaleKeys.InputDialogNo],
LocaleManager.Instance[LocaleKeys.RyujinxConfirm]);
if (result == UserResult.Yes)
{

View File

@@ -43,7 +43,7 @@ namespace Ryujinx.Ava.UI.Windows
InitializeComponent();
Title = $"Ryujinx {Program.Version} - {LocaleManager.Instance["DlcWindowTitle"]} - {_titleName} ({_titleId:X16})";
Title = $"Ryujinx {Program.Version} - {LocaleManager.Instance[LocaleKeys.DlcWindowTitle]} - {_titleName} ({_titleId:X16})";
}
public DownloadableContentManagerWindow(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName)
@@ -73,7 +73,7 @@ namespace Ryujinx.Ava.UI.Windows
DlcDataGrid.SelectionChanged += DlcDataGrid_SelectionChanged;
Title = $"Ryujinx {Program.Version} - {LocaleManager.Instance["DlcWindowTitle"]} - {_titleName} ({_titleId:X16})";
Title = $"Ryujinx {Program.Version} - {LocaleManager.Instance[LocaleKeys.DlcWindowTitle]} - {_titleName} ({_titleId:X16})";
LoadDownloadableContents();
PrintHeading();
@@ -86,7 +86,7 @@ namespace Ryujinx.Ava.UI.Windows
private void PrintHeading()
{
Heading.Text = string.Format(LocaleManager.Instance["DlcWindowHeading"], _downloadableContents.Count, _titleName, _titleId.ToString("X16"));
Heading.Text = string.Format(LocaleManager.Instance[LocaleKeys.DlcWindowHeading], _downloadableContents.Count, _titleName, _titleId.ToString("X16"));
}
private void LoadDownloadableContents()
@@ -133,7 +133,7 @@ namespace Ryujinx.Ava.UI.Windows
{
Dispatcher.UIThread.InvokeAsync(async () =>
{
await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance["DialogDlcLoadNcaErrorMessage"], ex.Message, containerPath));
await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance[LocaleKeys.DialogDlcLoadNcaErrorMessage], ex.Message, containerPath));
});
}
@@ -181,7 +181,7 @@ namespace Ryujinx.Ava.UI.Windows
if (!containsDownloadableContent)
{
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance["DialogDlcNoDlcErrorMessage"]);
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogDlcNoDlcErrorMessage]);
}
}
@@ -241,7 +241,7 @@ namespace Ryujinx.Ava.UI.Windows
{
OpenFileDialog dialog = new OpenFileDialog()
{
Title = LocaleManager.Instance["SelectDlcDialogTitle"],
Title = LocaleManager.Instance[LocaleKeys.SelectDlcDialogTitle],
AllowMultiple = true
};

View File

@@ -179,12 +179,12 @@ namespace Ryujinx.Ava.UI.Windows
{
if (ConfigurationState.Instance.Logger.EnableTrace.Value)
{
string mainMessage = LocaleManager.Instance["DialogPerformanceCheckLoggingEnabledMessage"];
string secondaryMessage = LocaleManager.Instance["DialogPerformanceCheckLoggingEnabledConfirmMessage"];
string mainMessage = LocaleManager.Instance[LocaleKeys.DialogPerformanceCheckLoggingEnabledMessage];
string secondaryMessage = LocaleManager.Instance[LocaleKeys.DialogPerformanceCheckLoggingEnabledConfirmMessage];
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(mainMessage, secondaryMessage,
LocaleManager.Instance["InputDialogYes"], LocaleManager.Instance["InputDialogNo"],
LocaleManager.Instance["RyujinxConfirm"]);
LocaleManager.Instance[LocaleKeys.InputDialogYes], LocaleManager.Instance[LocaleKeys.InputDialogNo],
LocaleManager.Instance[LocaleKeys.RyujinxConfirm]);
if (result != UserResult.Yes)
{
@@ -196,13 +196,13 @@ namespace Ryujinx.Ava.UI.Windows
if (!string.IsNullOrWhiteSpace(ConfigurationState.Instance.Graphics.ShadersDumpPath.Value))
{
string mainMessage = LocaleManager.Instance["DialogPerformanceCheckShaderDumpEnabledMessage"];
string mainMessage = LocaleManager.Instance[LocaleKeys.DialogPerformanceCheckShaderDumpEnabledMessage];
string secondaryMessage =
LocaleManager.Instance["DialogPerformanceCheckShaderDumpEnabledConfirmMessage"];
LocaleManager.Instance[LocaleKeys.DialogPerformanceCheckShaderDumpEnabledConfirmMessage];
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(mainMessage, secondaryMessage,
LocaleManager.Instance["InputDialogYes"], LocaleManager.Instance["InputDialogNo"],
LocaleManager.Instance["RyujinxConfirm"]);
LocaleManager.Instance[LocaleKeys.InputDialogYes], LocaleManager.Instance[LocaleKeys.InputDialogNo],
LocaleManager.Instance[LocaleKeys.RyujinxConfirm]);
if (result != UserResult.Yes)
{
@@ -227,11 +227,11 @@ namespace Ryujinx.Ava.UI.Windows
if (AppHost != null)
{
await ContentDialogHelper.CreateInfoDialog(
LocaleManager.Instance["DialogLoadAppGameAlreadyLoadedMessage"],
LocaleManager.Instance["DialogLoadAppGameAlreadyLoadedSubMessage"],
LocaleManager.Instance["InputDialogOk"],
LocaleManager.Instance[LocaleKeys.DialogLoadAppGameAlreadyLoadedMessage],
LocaleManager.Instance[LocaleKeys.DialogLoadAppGameAlreadyLoadedSubMessage],
LocaleManager.Instance[LocaleKeys.InputDialogOk],
"",
LocaleManager.Instance["RyujinxInfo"]);
LocaleManager.Instance[LocaleKeys.RyujinxInfo]);
return;
}
@@ -274,7 +274,7 @@ namespace Ryujinx.Ava.UI.Windows
}
CanUpdate = false;
ViewModel.LoadHeading = string.IsNullOrWhiteSpace(titleName) ? string.Format(LocaleManager.Instance["LoadingHeading"], AppHost.Device.Application.TitleName) : titleName;
ViewModel.LoadHeading = string.IsNullOrWhiteSpace(titleName) ? string.Format(LocaleManager.Instance[LocaleKeys.LoadingHeading], AppHost.Device.Application.TitleName) : titleName;
ViewModel.TitleName = string.IsNullOrWhiteSpace(titleName) ? AppHost.Device.Application.TitleName : titleName;
SwitchToGameControl(startFullscreen);
@@ -500,14 +500,14 @@ namespace Ryujinx.Ava.UI.Windows
if (version != null)
{
LocaleManager.Instance.UpdateDynamicValue("StatusBarSystemVersion",
LocaleManager.Instance.UpdateDynamicValue(LocaleKeys.StatusBarSystemVersion,
version.VersionString);
hasApplet = version.Major > 3;
}
else
{
LocaleManager.Instance.UpdateDynamicValue("StatusBarSystemVersion", "0.0");
LocaleManager.Instance.UpdateDynamicValue(LocaleKeys.StatusBarSystemVersion, "0.0");
}
ViewModel.IsAppletMenuActive = hasApplet;

View File

@@ -45,10 +45,10 @@ namespace Ryujinx.Ava.UI.Windows
ContentDialog contentDialog = new ContentDialog
{
Title = LocaleManager.Instance["ControllerMotionTitle"],
PrimaryButtonText = LocaleManager.Instance["ControllerSettingsSave"],
Title = LocaleManager.Instance[LocaleKeys.ControllerMotionTitle],
PrimaryButtonText = LocaleManager.Instance[LocaleKeys.ControllerSettingsSave],
SecondaryButtonText = "",
CloseButtonText = LocaleManager.Instance["ControllerSettingsClose"],
CloseButtonText = LocaleManager.Instance[LocaleKeys.ControllerSettingsClose],
Content = content
};
contentDialog.PrimaryButtonClick += (sender, args) =>

View File

@@ -37,10 +37,10 @@ namespace Ryujinx.Ava.UI.Windows
ContentDialog contentDialog = new ContentDialog
{
Title = LocaleManager.Instance["ControllerRumbleTitle"],
PrimaryButtonText = LocaleManager.Instance["ControllerSettingsSave"],
Title = LocaleManager.Instance[LocaleKeys.ControllerRumbleTitle],
PrimaryButtonText = LocaleManager.Instance[LocaleKeys.ControllerSettingsSave],
SecondaryButtonText = "",
CloseButtonText = LocaleManager.Instance["ControllerSettingsClose"],
CloseButtonText = LocaleManager.Instance[LocaleKeys.ControllerSettingsClose],
Content = content,
};

View File

@@ -29,7 +29,7 @@ namespace Ryujinx.Ava.UI.Windows
public SettingsWindow(VirtualFileSystem virtualFileSystem, ContentManager contentManager)
{
Title = $"Ryujinx {Program.Version} - {LocaleManager.Instance["Settings"]}";
Title = $"Ryujinx {Program.Version} - {LocaleManager.Instance[LocaleKeys.Settings]}";
ViewModel = new SettingsViewModel(virtualFileSystem, contentManager, this);
DataContext = ViewModel;

View File

@@ -43,7 +43,7 @@ namespace Ryujinx.Ava.UI.Windows
InitializeComponent();
Title = $"Ryujinx {Program.Version} - {LocaleManager.Instance["UpdateWindowTitle"]} - {_titleName} ({_titleId:X16})";
Title = $"Ryujinx {Program.Version} - {LocaleManager.Instance[LocaleKeys.UpdateWindowTitle]} - {_titleName} ({_titleId:X16})";
}
public TitleUpdateWindow(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName)
@@ -73,7 +73,7 @@ namespace Ryujinx.Ava.UI.Windows
InitializeComponent();
Title = $"Ryujinx {Program.Version} - {LocaleManager.Instance["UpdateWindowTitle"]} - {_titleName} ({_titleId:X16})";
Title = $"Ryujinx {Program.Version} - {LocaleManager.Instance[LocaleKeys.UpdateWindowTitle]} - {_titleName} ({_titleId:X16})";
LoadUpdates();
PrintHeading();
@@ -81,7 +81,7 @@ namespace Ryujinx.Ava.UI.Windows
private void PrintHeading()
{
Heading.Text = string.Format(LocaleManager.Instance["GameUpdateWindowHeading"], _titleUpdates.Count, _titleName, _titleId.ToString("X16"));
Heading.Text = string.Format(LocaleManager.Instance[LocaleKeys.GameUpdateWindowHeading], _titleUpdates.Count, _titleName, _titleId.ToString("X16"));
}
private void LoadUpdates()
@@ -148,7 +148,7 @@ namespace Ryujinx.Ava.UI.Windows
{
Dispatcher.UIThread.Post(async () =>
{
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance["DialogUpdateAddUpdateErrorMessage"]);
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUpdateAddUpdateErrorMessage]);
});
}
}
@@ -156,7 +156,7 @@ namespace Ryujinx.Ava.UI.Windows
{
Dispatcher.UIThread.Post(async () =>
{
await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance["DialogDlcLoadNcaErrorMessage"], ex.Message, path));
await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance[LocaleKeys.DialogDlcLoadNcaErrorMessage], ex.Message, path));
});
}
}
@@ -193,7 +193,7 @@ namespace Ryujinx.Ava.UI.Windows
{
OpenFileDialog dialog = new()
{
Title = LocaleManager.Instance["SelectUpdateDialogTitle"],
Title = LocaleManager.Instance[LocaleKeys.SelectUpdateDialogTitle],
AllowMultiple = true
};

View File

@@ -27,7 +27,7 @@ namespace Ryujinx.Common.Memory.PartialUnmaps
[SupportedOSPlatform("windows")]
[LibraryImport("kernel32.dll")]
public static partial int GetCurrentThreadId();
private static partial int GetCurrentThreadId();
[SupportedOSPlatform("windows")]
[LibraryImport("kernel32.dll", SetLastError = true)]
@@ -36,7 +36,7 @@ namespace Ryujinx.Common.Memory.PartialUnmaps
[SupportedOSPlatform("windows")]
[LibraryImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs (UnmanagedType.Bool)]
public static partial bool CloseHandle(IntPtr hObject);
private static partial bool CloseHandle(IntPtr hObject);
[SupportedOSPlatform("windows")]
[LibraryImport("kernel32.dll", SetLastError = true)]
@@ -160,4 +160,4 @@ namespace Ryujinx.Common.Memory.PartialUnmaps
}
}
}
}
}

View File

@@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.GAL
ISampler CreateSampler(SamplerCreateInfo info);
ITexture CreateTexture(TextureCreateInfo info, float scale);
void CreateSync(ulong id);
void CreateSync(ulong id, bool strict);
void DeleteBuffer(BufferHandle buffer);
@@ -53,6 +53,8 @@ namespace Ryujinx.Graphics.GAL
void Initialize(GraphicsDebugLevel logLevel);
void SetInterruptAction(Action<Action> interruptAction);
void Screenshot();
}
}

View File

@@ -4,15 +4,17 @@
{
public CommandType CommandType => CommandType.CreateSync;
private ulong _id;
private bool _strict;
public void Set(ulong id)
public void Set(ulong id, bool strict)
{
_id = id;
_strict = strict;
}
public static void Run(ref CreateSyncCommand command, ThreadedRenderer threaded, IRenderer renderer)
{
renderer.CreateSync(command._id);
renderer.CreateSync(command._id, command._strict);
threaded.Sync.AssignSync(command._id);
}

View File

@@ -29,6 +29,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
private int _elementSize;
private IRenderer _baseRenderer;
private Thread _gpuThread;
private Thread _backendThread;
private bool _disposed;
private bool _running;
@@ -38,6 +39,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
private CircularSpanPool _spanPool;
private ManualResetEventSlim _invokeRun;
private AutoResetEvent _interruptRun;
private bool _lastSampleCounterClear = true;
@@ -54,6 +56,8 @@ namespace Ryujinx.Graphics.GAL.Multithreading
private int _refProducerPtr;
private int _refConsumerPtr;
private Action _interruptAction;
public event EventHandler<ScreenCaptureImageInfo> ScreenCaptured;
internal BufferMap Buffers { get; }
@@ -73,6 +77,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
_baseRenderer = renderer;
renderer.ScreenCaptured += (sender, info) => ScreenCaptured?.Invoke(this, info);
renderer.SetInterruptAction(Interrupt);
Pipeline = new ThreadedPipeline(this, renderer.Pipeline);
Window = new ThreadedWindow(this, renderer);
@@ -82,6 +87,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
_galWorkAvailable = new ManualResetEventSlim(false);
_invokeRun = new ManualResetEventSlim();
_interruptRun = new AutoResetEvent(false);
_spanPool = new CircularSpanPool(this, SpanPoolBytes);
SpanPool = _spanPool;
@@ -95,6 +101,8 @@ namespace Ryujinx.Graphics.GAL.Multithreading
{
_running = true;
_backendThread = Thread.CurrentThread;
_gpuThread = new Thread(() => {
gpuLoop();
_running = false;
@@ -116,10 +124,18 @@ namespace Ryujinx.Graphics.GAL.Multithreading
_galWorkAvailable.Wait();
_galWorkAvailable.Reset();
if (Volatile.Read(ref _interruptAction) != null)
{
_interruptAction();
_interruptRun.Set();
Interlocked.Exchange(ref _interruptAction, null);
}
// The other thread can only increase the command count.
// We can assume that if it is above 0, it will stay there or get higher.
while (_commandCount > 0)
while (_commandCount > 0 && Volatile.Read(ref _interruptAction) == null)
{
int commandPtr = _consumerPtr;
@@ -281,10 +297,10 @@ namespace Ryujinx.Graphics.GAL.Multithreading
return sampler;
}
public void CreateSync(ulong id)
public void CreateSync(ulong id, bool strict)
{
Sync.CreateSyncHandle(id);
New<CreateSyncCommand>().Set(id);
New<CreateSyncCommand>().Set(id, strict);
QueueCommand();
}
@@ -421,6 +437,30 @@ namespace Ryujinx.Graphics.GAL.Multithreading
_baseRenderer.WaitSync(id);
}
private void Interrupt(Action action)
{
// Interrupt the backend thread from any external thread and invoke the given action.
if (Thread.CurrentThread == _backendThread)
{
// If this is called from the backend thread, the action can run immediately.
action();
}
else
{
while (Interlocked.CompareExchange(ref _interruptAction, action, null) != null) { }
_galWorkAvailable.Set();
_interruptRun.WaitOne();
}
}
public void SetInterruptAction(Action<Action> interruptAction)
{
// Threaded renderer ignores given interrupt action, as it provides its own to the child renderer.
}
public void Dispose()
{
// Dispose must happen from the render thread, after all commands have completed.
@@ -440,6 +480,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
_frameComplete.Dispose();
_galWorkAvailable.Dispose();
_invokeRun.Dispose();
_interruptRun.Dispose();
Sync.Dispose();
}

View File

@@ -59,7 +59,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
if (_createSyncPending)
{
_createSyncPending = false;
_context.CreateHostSyncIfNeeded(false);
_context.CreateHostSyncIfNeeded(false, false);
}
}
@@ -157,7 +157,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
}
else if (operation == SyncpointbOperation.Incr)
{
_context.CreateHostSyncIfNeeded(true);
_context.CreateHostSyncIfNeeded(true, true);
_context.Synchronization.IncrementSyncpoint(syncpointId);
}
@@ -184,7 +184,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
{
_context.Renderer.Pipeline.CommandBufferBarrier();
_context.CreateHostSyncIfNeeded(false);
_context.CreateHostSyncIfNeeded(false, true);
}
/// <summary>

View File

@@ -250,7 +250,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
uint syncpointId = (uint)argument & 0xFFFF;
_context.AdvanceSequence();
_context.CreateHostSyncIfNeeded(true);
_context.CreateHostSyncIfNeeded(true, true);
_context.Renderer.UpdateCounters(); // Poll the query counters, the game may want an updated result.
_context.Synchronization.IncrementSyncpoint(syncpointId);
}

View File

@@ -316,7 +316,8 @@ namespace Ryujinx.Graphics.Gpu
/// If no actions are present, a host sync object is not created.
/// </summary>
/// <param name="syncpoint">True if host sync is being created by a syncpoint</param>
public void CreateHostSyncIfNeeded(bool syncpoint)
/// <param name="strict">True if the sync should signal as soon as possible</param>
public void CreateHostSyncIfNeeded(bool syncpoint, bool strict)
{
if (BufferMigrations.Count > 0)
{
@@ -337,7 +338,7 @@ namespace Ryujinx.Graphics.Gpu
if (_pendingSync || (syncpoint && SyncpointActions.Count > 0))
{
Renderer.CreateSync(SyncNumber);
Renderer.CreateSync(SyncNumber, strict);
SyncNumber++;

View File

@@ -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 = 4106;
private const uint CodeGenVersion = 3939;
private const string SharedTocFileName = "shared.toc";
private const string SharedDataFileName = "shared.data";

View File

@@ -106,7 +106,7 @@ namespace Ryujinx.Graphics.Host1x
private void Method1(int data)
{
_commandQueue.Add(new MethodCallAction(_currentContextId, (int)_state.State.Method0 * 4, data));
_commandQueue.Add(new MethodCallAction(_currentContextId, (int)_state.State.Method0 * sizeof(uint), data));
}
private void Process(CommandAction cmdAction)

View File

@@ -1,6 +1,6 @@
namespace Ryujinx.Graphics.Nvdec
{
public enum CodecId
public enum ApplicationId
{
Mpeg = 1,
Vc1 = 2,
@@ -8,6 +8,7 @@
Mpeg4 = 4,
Vp8 = 5,
Hevc = 7,
Vp9 = 9
Vp9 = 9,
HevcParser = 12,
}
}

View File

@@ -1,16 +0,0 @@
namespace Ryujinx.Graphics.Nvdec
{
public readonly struct FrameDecodedEventArgs
{
public CodecId CodecId { get; }
public uint LumaOffset { get; }
public uint ChromaOffset { get; }
internal FrameDecodedEventArgs(CodecId codecId, uint lumaOffset, uint chromaOffset)
{
CodecId = codecId;
LumaOffset = lumaOffset;
ChromaOffset = chromaOffset;
}
}
}

View File

@@ -12,18 +12,18 @@ namespace Ryujinx.Graphics.Nvdec
public static void Decode(NvdecDecoderContext context, ResourceManager rm, ref NvdecRegisters state)
{
PictureInfo pictureInfo = rm.Gmm.DeviceRead<PictureInfo>(state.SetPictureInfoOffset);
PictureInfo pictureInfo = rm.Gmm.DeviceRead<PictureInfo>(state.SetDrvPicSetupOffset);
H264PictureInfo info = pictureInfo.Convert();
ReadOnlySpan<byte> bitstream = rm.Gmm.DeviceGetSpan(state.SetBitstreamOffset, (int)pictureInfo.BitstreamSize);
ReadOnlySpan<byte> bitstream = rm.Gmm.DeviceGetSpan(state.SetInBufBaseOffset, (int)pictureInfo.BitstreamSize);
int width = (int)pictureInfo.PicWidthInMbs * MbSizeInPixels;
int height = (int)pictureInfo.PicHeightInMbs * MbSizeInPixels;
int surfaceIndex = (int)pictureInfo.OutputSurfaceIndex;
uint lumaOffset = state.SetSurfaceLumaOffset[surfaceIndex];
uint chromaOffset = state.SetSurfaceChromaOffset[surfaceIndex];
uint lumaOffset = state.SetPictureLumaOffset[surfaceIndex];
uint chromaOffset = state.SetPictureChromaOffset[surfaceIndex];
Decoder decoder = context.GetH264Decoder();

View File

@@ -58,24 +58,24 @@ namespace Ryujinx.Graphics.Nvdec
private void Execute(int data)
{
Decode((CodecId)_state.State.SetCodecID);
Decode((ApplicationId)_state.State.SetApplicationId);
}
private void Decode(CodecId codecId)
private void Decode(ApplicationId applicationId)
{
switch (codecId)
switch (applicationId)
{
case CodecId.H264:
case ApplicationId.H264:
H264Decoder.Decode(_currentContext, _rm, ref _state.State);
break;
case CodecId.Vp8:
case ApplicationId.Vp8:
Vp8Decoder.Decode(_currentContext, _rm, ref _state.State);
break;
case CodecId.Vp9:
case ApplicationId.Vp9:
Vp9Decoder.Decode(_rm, ref _state.State);
break;
default:
Logger.Error?.Print(LogClass.Nvdec, $"Unsupported codec \"{codecId}\".");
Logger.Error?.Print(LogClass.Nvdec, $"Unsupported codec \"{applicationId}\".");
break;
}
}

View File

@@ -2,43 +2,62 @@
namespace Ryujinx.Graphics.Nvdec
{
// Note: Most of those names are not official.
struct NvdecRegisters
{
#pragma warning disable CS0649
public Array64<uint> Reserved0;
public Array64<uint> Reserved100;
public uint SetCodecID;
public Array63<uint> Reserved204;
public uint Nop;
public Array63<uint> Reserved104;
public uint SetApplicationId;
public uint SetWatchdogTimer;
public Array14<uint> Reserved208;
public uint SemaphoreA;
public uint SemaphoreB;
public uint SemaphoreC;
public uint CtxSaveArea;
public Array44<uint> Reserved254;
public uint Execute;
public Array63<uint> Reserved304;
public uint SetPlatformID;
public uint SetPictureInfoOffset;
public uint SetBitstreamOffset;
public uint SetFrameNumber;
public uint SetH264SliceDataOffsetsOffset; // Also used by VC1
public uint SetH264MvDumpOffset; // Also used by VC1
public uint Unknown418; // Used by VC1
public uint Unknown41C;
public uint Unknown420; // Used by VC1
public uint SetFrameStatsOffset;
public uint SetH264LastSurfaceLumaOffset;
public uint SetH264LastSurfaceChromaOffset;
public Array17<uint> SetSurfaceLumaOffset;
public Array17<uint> SetSurfaceChromaOffset;
public uint Unknown4B8;
public uint Unknown4BC;
public uint SemaphoreD;
public Array62<uint> Reserved308;
public uint SetControlParams;
public uint SetDrvPicSetupOffset;
public uint SetInBufBaseOffset;
public uint SetPictureIndex;
public uint SetSliceOffsetsBufOffset; // Also used by VC1
public uint SetColocDataOffset; // Also used by VC1
public uint SetHistoryOffset; // Used by VC1
public uint SetDisplayBufSize;
public uint SetHistogramOffset; // Used by VC1
public uint SetNvDecStatusOffset;
public uint SetDisplayBufLumaOffset;
public uint SetDisplayBufChromaOffset;
public Array17<uint> SetPictureLumaOffset;
public Array17<uint> SetPictureChromaOffset;
public uint SetPicScratchBufOffset;
public uint SetExternalMvBufferOffset;
public uint SetCryptoData0Offset;
public uint SetCryptoData1Offset;
public Array62<uint> Unknown4C8;
public uint SetVp9EntropyProbsOffset;
public uint SetVp9BackwardUpdatesOffset;
public uint SetVp9LastFrameSegMapOffset;
public uint SetVp9CurrFrameSegMapOffset;
public uint Unknown5D0;
public uint SetVp9LastFrameMvsOffset;
public uint SetVp9CurrFrameMvsOffset;
public uint Unknown5DC;
public Array14<uint> Unknown4C8;
public uint H264SetMbHistBufOffset;
public Array15<uint> Unknown504;
public uint Vp8SetProbDataOffset;
public uint Vp8SetHeaderPartitionBufBaseOffset;
public Array14<uint> Unknown548;
public uint HevcSetScalingListOffset;
public uint HevcSetTileSizesOffset;
public uint HevcSetFilterBufferOffset;
public uint HevcSetSaoBufferOffset;
public uint HevcSetSliceInfoBufferOffset;
public uint HevcSetSliceGroupIndex;
public Array10<uint> Unknown598;
public uint Vp9SetProbTabBufOffset;
public uint Vp9SetCtxCounterBufOffset;
public uint Vp9SetSegmentReadBufOffset;
public uint Vp9SetSegmentWriteBufOffset;
public uint Vp9SetTileSizeBufOffset;
public uint Vp9SetColMvWriteBufOffset;
public uint Vp9SetColMvReadBufOffset;
public uint Vp9SetFilterBufferOffset;
#pragma warning restore CS0649
}
}

View File

@@ -10,8 +10,8 @@ namespace Ryujinx.Graphics.Nvdec
{
public static void Decode(NvdecDecoderContext context, ResourceManager rm, ref NvdecRegisters state)
{
PictureInfo pictureInfo = rm.Gmm.DeviceRead<PictureInfo>(state.SetPictureInfoOffset);
ReadOnlySpan<byte> bitstream = rm.Gmm.DeviceGetSpan(state.SetBitstreamOffset, (int)pictureInfo.VLDBufferSize);
PictureInfo pictureInfo = rm.Gmm.DeviceRead<PictureInfo>(state.SetDrvPicSetupOffset);
ReadOnlySpan<byte> bitstream = rm.Gmm.DeviceGetSpan(state.SetInBufBaseOffset, (int)pictureInfo.VLDBufferSize);
Decoder decoder = context.GetVp8Decoder();
@@ -19,8 +19,8 @@ namespace Ryujinx.Graphics.Nvdec
Vp8PictureInfo info = pictureInfo.Convert();
uint lumaOffset = state.SetSurfaceLumaOffset[3];
uint chromaOffset = state.SetSurfaceChromaOffset[3];
uint lumaOffset = state.SetPictureLumaOffset[3];
uint chromaOffset = state.SetPictureChromaOffset[3];
if (decoder.Decode(ref info, outputSurface, bitstream))
{

View File

@@ -17,18 +17,18 @@ namespace Ryujinx.Graphics.Nvdec
public unsafe static void Decode(ResourceManager rm, ref NvdecRegisters state)
{
PictureInfo pictureInfo = rm.Gmm.DeviceRead<PictureInfo>(state.SetPictureInfoOffset);
EntropyProbs entropy = rm.Gmm.DeviceRead<EntropyProbs>(state.SetVp9EntropyProbsOffset);
PictureInfo pictureInfo = rm.Gmm.DeviceRead<PictureInfo>(state.SetDrvPicSetupOffset);
EntropyProbs entropy = rm.Gmm.DeviceRead<EntropyProbs>(state.Vp9SetProbTabBufOffset);
ISurface Rent(uint lumaOffset, uint chromaOffset, FrameSize size)
{
return rm.Cache.Get(_decoder, lumaOffset, chromaOffset, size.Width, size.Height);
}
ISurface lastSurface = Rent(state.SetSurfaceLumaOffset[0], state.SetSurfaceChromaOffset[0], pictureInfo.LastFrameSize);
ISurface goldenSurface = Rent(state.SetSurfaceLumaOffset[1], state.SetSurfaceChromaOffset[1], pictureInfo.GoldenFrameSize);
ISurface altSurface = Rent(state.SetSurfaceLumaOffset[2], state.SetSurfaceChromaOffset[2], pictureInfo.AltFrameSize);
ISurface currentSurface = Rent(state.SetSurfaceLumaOffset[3], state.SetSurfaceChromaOffset[3], pictureInfo.CurrentFrameSize);
ISurface lastSurface = Rent(state.SetPictureLumaOffset[0], state.SetPictureChromaOffset[0], pictureInfo.LastFrameSize);
ISurface goldenSurface = Rent(state.SetPictureLumaOffset[1], state.SetPictureChromaOffset[1], pictureInfo.GoldenFrameSize);
ISurface altSurface = Rent(state.SetPictureLumaOffset[2], state.SetPictureChromaOffset[2], pictureInfo.AltFrameSize);
ISurface currentSurface = Rent(state.SetPictureLumaOffset[3], state.SetPictureChromaOffset[3], pictureInfo.CurrentFrameSize);
Vp9PictureInfo info = pictureInfo.Convert();
@@ -38,31 +38,31 @@ namespace Ryujinx.Graphics.Nvdec
entropy.Convert(ref info.Entropy);
ReadOnlySpan<byte> bitstream = rm.Gmm.DeviceGetSpan(state.SetBitstreamOffset, (int)pictureInfo.BitstreamSize);
ReadOnlySpan<byte> bitstream = rm.Gmm.DeviceGetSpan(state.SetInBufBaseOffset, (int)pictureInfo.BitstreamSize);
ReadOnlySpan<Vp9MvRef> mvsIn = ReadOnlySpan<Vp9MvRef>.Empty;
if (info.UsePrevInFindMvRefs)
{
mvsIn = GetMvsInput(rm.Gmm, pictureInfo.CurrentFrameSize, state.SetVp9LastFrameMvsOffset);
mvsIn = GetMvsInput(rm.Gmm, pictureInfo.CurrentFrameSize, state.Vp9SetColMvReadBufOffset);
}
int miCols = BitUtils.DivRoundUp(pictureInfo.CurrentFrameSize.Width, 8);
int miRows = BitUtils.DivRoundUp(pictureInfo.CurrentFrameSize.Height, 8);
using var mvsRegion = rm.Gmm.GetWritableRegion(ExtendOffset(state.SetVp9CurrFrameMvsOffset), miRows * miCols * 16);
using var mvsRegion = rm.Gmm.GetWritableRegion(ExtendOffset(state.Vp9SetColMvWriteBufOffset), miRows * miCols * 16);
Span<Vp9MvRef> mvsOut = MemoryMarshal.Cast<byte, Vp9MvRef>(mvsRegion.Memory.Span);
uint lumaOffset = state.SetSurfaceLumaOffset[3];
uint chromaOffset = state.SetSurfaceChromaOffset[3];
uint lumaOffset = state.SetPictureLumaOffset[3];
uint chromaOffset = state.SetPictureChromaOffset[3];
if (_decoder.Decode(ref info, currentSurface, bitstream, mvsIn, mvsOut))
{
SurfaceWriter.Write(rm.Gmm, currentSurface, lumaOffset, chromaOffset);
}
WriteBackwardUpdates(rm.Gmm, state.SetVp9BackwardUpdatesOffset, ref info.BackwardUpdateCounts);
WriteBackwardUpdates(rm.Gmm, state.Vp9SetCtxCounterBufOffset, ref info.BackwardUpdateCounts);
rm.Cache.Put(lastSurface);
rm.Cache.Put(goldenSurface);

View File

@@ -232,7 +232,7 @@ namespace Ryujinx.Graphics.OpenGL
return new Program(programBinary, hasFragmentShader, info.FragmentOutputMap);
}
public void CreateSync(ulong id)
public void CreateSync(ulong id, bool strict)
{
_sync.Create(id);
}
@@ -247,6 +247,11 @@ namespace Ryujinx.Graphics.OpenGL
return _sync.GetCurrent();
}
public void SetInterruptAction(Action<Action> interruptAction)
{
// Currently no need for an interrupt action.
}
public void Screenshot()
{
_window.ScreenCaptureRequested = true;

View File

@@ -1,4 +1,3 @@
using Ryujinx.Graphics.Shader.StructuredIr;
using Ryujinx.Graphics.Shader.Translation;
using System;
@@ -25,17 +24,6 @@ namespace Ryujinx.Graphics.Shader
};
}
public static VariableType ToVariableType(this AttributeType type)
{
return type switch
{
AttributeType.Float => VariableType.F32,
AttributeType.Sint => VariableType.S32,
AttributeType.Uint => VariableType.U32,
_ => throw new ArgumentException($"Invalid attribute type \"{type}\".")
};
}
public static AggregateType ToAggregateType(this AttributeType type)
{
return type switch

View File

@@ -350,19 +350,33 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
}
}
public static string GetVarTypeName(VariableType type)
public static string GetVarTypeName(AggregateType type, bool precise = true)
{
switch (type)
return type switch
{
case VariableType.Bool: return "bool";
case VariableType.F32: return "precise float";
case VariableType.F64: return "double";
case VariableType.None: return "void";
case VariableType.S32: return "int";
case VariableType.U32: return "uint";
}
throw new ArgumentException($"Invalid variable type \"{type}\".");
AggregateType.Void => "void",
AggregateType.Bool => "bool",
AggregateType.FP32 => precise ? "precise float" : "float",
AggregateType.FP64 => "double",
AggregateType.S32 => "int",
AggregateType.U32 => "uint",
AggregateType.Vector2 | AggregateType.Bool => "bvec2",
AggregateType.Vector2 | AggregateType.FP32 => precise ? "precise vec2" : "vec2",
AggregateType.Vector2 | AggregateType.FP64 => "dvec2",
AggregateType.Vector2 | AggregateType.S32 => "ivec2",
AggregateType.Vector2 | AggregateType.U32 => "uvec2",
AggregateType.Vector3 | AggregateType.Bool => "bvec3",
AggregateType.Vector3 | AggregateType.FP32 => precise ? "precise vec3" : "vec3",
AggregateType.Vector3 | AggregateType.FP64 => "dvec3",
AggregateType.Vector3 | AggregateType.S32 => "ivec3",
AggregateType.Vector3 | AggregateType.U32 => "uvec3",
AggregateType.Vector4 | AggregateType.Bool => "bvec4",
AggregateType.Vector4 | AggregateType.FP32 => precise ? "precise vec4" : "vec4",
AggregateType.Vector4 | AggregateType.FP64 => "dvec4",
AggregateType.Vector4 | AggregateType.S32 => "ivec4",
AggregateType.Vector4 | AggregateType.U32 => "uvec4",
_ => throw new ArgumentException($"Invalid variable type \"{type}\".")
};
}
private static void DeclareUniforms(CodeGenContext context, BufferDescriptor[] descriptors)

View File

@@ -126,8 +126,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
}
else if (node is AstAssignment assignment)
{
VariableType srcType = OperandManager.GetNodeDestType(context, assignment.Source);
VariableType dstType = OperandManager.GetNodeDestType(context, assignment.Destination, isAsgDest: true);
AggregateType srcType = OperandManager.GetNodeDestType(context, assignment.Source);
AggregateType dstType = OperandManager.GetNodeDestType(context, assignment.Destination, isAsgDest: true);
string dest;
@@ -158,9 +158,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
private static string GetCondExpr(CodeGenContext context, IAstNode cond)
{
VariableType srcType = OperandManager.GetNodeDestType(context, cond);
AggregateType srcType = OperandManager.GetNodeDestType(context, cond);
return ReinterpretCast(context, cond, srcType, VariableType.Bool);
return ReinterpretCast(context, cond, srcType, AggregateType.Bool);
}
}
}

View File

@@ -1,5 +1,6 @@
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
using Ryujinx.Graphics.Shader.StructuredIr;
using Ryujinx.Graphics.Shader.Translation;
using System;
using static Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions.InstGenBallot;
@@ -8,6 +9,7 @@ using static Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions.InstGenFSI;
using static Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions.InstGenHelper;
using static Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions.InstGenMemory;
using static Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions.InstGenPacking;
using static Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions.InstGenVector;
using static Ryujinx.Graphics.Shader.StructuredIr.InstructionInfo;
namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
@@ -32,12 +34,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
{
IAstNode src = operation.GetSource(0);
VariableType type = GetSrcVarType(operation.Inst, 0);
AggregateType type = GetSrcVarType(operation.Inst, 0);
string srcExpr = GetSoureExpr(context, src, type);
string zero;
if (type == VariableType.F64)
if (type == AggregateType.FP64)
{
zero = "0.0";
}
@@ -95,7 +97,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
}
else
{
VariableType dstType = GetSrcVarType(inst, argIndex);
AggregateType dstType = GetSrcVarType(inst, argIndex);
args += GetSoureExpr(context, operation.GetSource(argIndex), dstType);
}
@@ -226,6 +228,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
case Instruction.UnpackHalf2x16:
return UnpackHalf2x16(context, operation);
case Instruction.VectorExtract:
return VectorExtract(context, operation);
}
}

View File

@@ -1,4 +1,5 @@
using Ryujinx.Graphics.Shader.StructuredIr;
using Ryujinx.Graphics.Shader.Translation;
using static Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions.InstGenHelper;
using static Ryujinx.Graphics.Shader.StructuredIr.InstructionInfo;
@@ -9,7 +10,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
{
public static string Ballot(CodeGenContext context, AstOperation operation)
{
VariableType dstType = GetSrcVarType(operation.Inst, 0);
AggregateType dstType = GetSrcVarType(operation.Inst, 0);
string arg = GetSoureExpr(context, operation.GetSource(0), dstType);

View File

@@ -1,5 +1,6 @@
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
using Ryujinx.Graphics.Shader.StructuredIr;
using Ryujinx.Graphics.Shader.Translation;
using static Ryujinx.Graphics.Shader.CodeGen.Glsl.TypeConversion;
@@ -7,11 +8,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
{
static class InstGenHelper
{
private static readonly InstInfo[] InfoTable;
private static readonly InstInfo[] _infoTable;
static InstGenHelper()
{
InfoTable = new InstInfo[(int)Instruction.Count];
_infoTable = new InstInfo[(int)Instruction.Count];
Add(Instruction.AtomicAdd, InstType.AtomicBinary, "atomicAdd");
Add(Instruction.AtomicAnd, InstType.AtomicBinary, "atomicAnd");
@@ -132,6 +133,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
Add(Instruction.Truncate, InstType.CallUnary, "trunc");
Add(Instruction.UnpackDouble2x32, InstType.Special);
Add(Instruction.UnpackHalf2x16, InstType.Special);
Add(Instruction.VectorExtract, InstType.Special);
Add(Instruction.VoteAll, InstType.CallUnary, "allInvocationsARB");
Add(Instruction.VoteAllEqual, InstType.CallUnary, "allInvocationsEqualARB");
Add(Instruction.VoteAny, InstType.CallUnary, "anyInvocationARB");
@@ -139,15 +141,15 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
private static void Add(Instruction inst, InstType flags, string opName = null, int precedence = 0)
{
InfoTable[(int)inst] = new InstInfo(flags, opName, precedence);
_infoTable[(int)inst] = new InstInfo(flags, opName, precedence);
}
public static InstInfo GetInstructionInfo(Instruction inst)
{
return InfoTable[(int)(inst & Instruction.Mask)];
return _infoTable[(int)(inst & Instruction.Mask)];
}
public static string GetSoureExpr(CodeGenContext context, IAstNode node, VariableType dstType)
public static string GetSoureExpr(CodeGenContext context, IAstNode node, AggregateType dstType)
{
return ReinterpretCast(context, node, OperandManager.GetNodeDestType(context, node), dstType);
}
@@ -191,7 +193,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
return false;
}
InstInfo info = InfoTable[(int)(operation.Inst & Instruction.Mask)];
InstInfo info = _infoTable[(int)(operation.Inst & Instruction.Mask)];
if ((info.Type & (InstType.Call | InstType.Special)) != 0)
{

View File

@@ -1,5 +1,6 @@
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
using Ryujinx.Graphics.Shader.StructuredIr;
using Ryujinx.Graphics.Shader.Translation;
using System;
using static Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions.InstGenHelper;
@@ -23,7 +24,17 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
case Instruction.ImageStore:
return "// imageStore(bindless)";
case Instruction.ImageLoad:
NumberFormatter.TryFormat(0, texOp.Format.GetComponentType(), out string imageConst);
AggregateType componentType = texOp.Format.GetComponentType();
NumberFormatter.TryFormat(0, componentType, out string imageConst);
AggregateType outputType = texOp.GetVectorType(componentType);
if ((outputType & AggregateType.ElementCountMask) != 0)
{
return $"{Declarations.GetVarTypeName(outputType, precise: false)}({imageConst})";
}
return imageConst;
default:
return NumberFormatter.FormatInt(0);
@@ -58,7 +69,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
int srcIndex = isBindless ? 1 : 0;
string Src(VariableType type)
string Src(AggregateType type)
{
return GetSoureExpr(context, texOp.GetSource(srcIndex++), type);
}
@@ -67,7 +78,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
if (isIndexed)
{
indexExpr = Src(VariableType.S32);
indexExpr = Src(AggregateType.S32);
}
string imageName = OperandManager.GetImageName(context.Config.Stage, texOp, indexExpr);
@@ -113,19 +124,19 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
for (int index = 0; index < pCount; index++)
{
elems[index] = Src(VariableType.S32);
elems[index] = Src(AggregateType.S32);
}
Append(ApplyScaling("ivec" + pCount + "(" + string.Join(", ", elems) + ")"));
}
else
{
Append(Src(VariableType.S32));
Append(Src(AggregateType.S32));
}
if (texOp.Inst == Instruction.ImageStore)
{
VariableType type = texOp.Format.GetComponentType();
AggregateType type = texOp.Format.GetComponentType();
string[] cElems = new string[4];
@@ -139,8 +150,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
{
cElems[index] = type switch
{
VariableType.S32 => NumberFormatter.FormatInt(0),
VariableType.U32 => NumberFormatter.FormatUint(0),
AggregateType.S32 => NumberFormatter.FormatInt(0),
AggregateType.U32 => NumberFormatter.FormatUint(0),
_ => NumberFormatter.FormatFloat(0)
};
}
@@ -148,8 +159,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
string prefix = type switch
{
VariableType.S32 => "i",
VariableType.U32 => "u",
AggregateType.S32 => "i",
AggregateType.U32 => "u",
_ => string.Empty
};
@@ -158,7 +169,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
if (texOp.Inst == Instruction.ImageAtomic)
{
VariableType type = texOp.Format.GetComponentType();
AggregateType type = texOp.Format.GetComponentType();
if ((texOp.Flags & TextureFlags.AtomicMask) == TextureFlags.CAS)
{
@@ -176,14 +187,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
texCall += ")";
if (type != VariableType.S32)
if (type != AggregateType.S32)
{
texCall = "int(" + texCall + ")";
}
}
else
{
texCall += ")" + (texOp.Inst == Instruction.ImageLoad ? GetMask(texOp.Index) : "");
texCall += ")" + (texOp.Inst == Instruction.ImageLoad ? GetMaskMultiDest(texOp.Index) : "");
}
return texCall;
@@ -288,7 +299,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
if (isIndexed)
{
indexExpr = GetSoureExpr(context, texOp.GetSource(0), VariableType.S32);
indexExpr = GetSoureExpr(context, texOp.GetSource(0), AggregateType.S32);
}
string samplerName = OperandManager.GetSamplerName(context.Config.Stage, texOp, indexExpr);
@@ -303,14 +314,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
for (int index = 0; index < coordsCount; index++)
{
elems[index] = GetSoureExpr(context, texOp.GetSource(coordsIndex + index), VariableType.F32);
elems[index] = GetSoureExpr(context, texOp.GetSource(coordsIndex + index), AggregateType.FP32);
}
coordsExpr = "vec" + coordsCount + "(" + string.Join(", ", elems) + ")";
}
else
{
coordsExpr = GetSoureExpr(context, texOp.GetSource(coordsIndex), VariableType.F32);
coordsExpr = GetSoureExpr(context, texOp.GetSource(coordsIndex), AggregateType.FP32);
}
return $"textureQueryLod({samplerName}, {coordsExpr}){GetMask(texOp.Index)}";
@@ -362,9 +373,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
string offsetExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0));
VariableType srcType = OperandManager.GetNodeDestType(context, src2);
AggregateType srcType = OperandManager.GetNodeDestType(context, src2);
string src = TypeConversion.ReinterpretCast(context, src2, srcType, VariableType.U32);
string src = TypeConversion.ReinterpretCast(context, src2, srcType, AggregateType.U32);
return $"{arrayName}[{offsetExpr}] = {src}";
}
@@ -376,9 +387,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
string offsetExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0));
VariableType srcType = OperandManager.GetNodeDestType(context, src2);
AggregateType srcType = OperandManager.GetNodeDestType(context, src2);
string src = TypeConversion.ReinterpretCast(context, src2, srcType, VariableType.U32);
string src = TypeConversion.ReinterpretCast(context, src2, srcType, AggregateType.U32);
return $"{HelperFunctionNames.StoreShared16}({offsetExpr}, {src})";
}
@@ -390,9 +401,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
string offsetExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0));
VariableType srcType = OperandManager.GetNodeDestType(context, src2);
AggregateType srcType = OperandManager.GetNodeDestType(context, src2);
string src = TypeConversion.ReinterpretCast(context, src2, srcType, VariableType.U32);
string src = TypeConversion.ReinterpretCast(context, src2, srcType, AggregateType.U32);
return $"{HelperFunctionNames.StoreShared8}({offsetExpr}, {src})";
}
@@ -406,9 +417,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
string indexExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0));
string offsetExpr = GetSoureExpr(context, src2, GetSrcVarType(operation.Inst, 1));
VariableType srcType = OperandManager.GetNodeDestType(context, src3);
AggregateType srcType = OperandManager.GetNodeDestType(context, src3);
string src = TypeConversion.ReinterpretCast(context, src3, srcType, VariableType.U32);
string src = TypeConversion.ReinterpretCast(context, src3, srcType, AggregateType.U32);
string sb = GetStorageBufferAccessor(indexExpr, offsetExpr, context.Config.Stage);
@@ -424,9 +435,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
string indexExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0));
string offsetExpr = GetSoureExpr(context, src2, GetSrcVarType(operation.Inst, 1));
VariableType srcType = OperandManager.GetNodeDestType(context, src3);
AggregateType srcType = OperandManager.GetNodeDestType(context, src3);
string src = TypeConversion.ReinterpretCast(context, src3, srcType, VariableType.U32);
string src = TypeConversion.ReinterpretCast(context, src3, srcType, AggregateType.U32);
string sb = GetStorageBufferAccessor(indexExpr, offsetExpr, context.Config.Stage);
@@ -442,9 +453,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
string indexExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0));
string offsetExpr = GetSoureExpr(context, src2, GetSrcVarType(operation.Inst, 1));
VariableType srcType = OperandManager.GetNodeDestType(context, src3);
AggregateType srcType = OperandManager.GetNodeDestType(context, src3);
string src = TypeConversion.ReinterpretCast(context, src3, srcType, VariableType.U32);
string src = TypeConversion.ReinterpretCast(context, src3, srcType, AggregateType.U32);
string sb = GetStorageBufferAccessor(indexExpr, offsetExpr, context.Config.Stage);
@@ -469,6 +480,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
bool isMultisample = (texOp.Type & SamplerType.Multisample) != 0;
bool isShadow = (texOp.Type & SamplerType.Shadow) != 0;
bool colorIsVector = isGather || !isShadow;
SamplerType type = texOp.Type & SamplerType.Mask;
bool is2D = type == SamplerType.Texture2D;
@@ -492,7 +505,19 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
// TODO: Bindless texture support. For now we just return 0.
if (isBindless)
{
return NumberFormatter.FormatFloat(0);
string scalarValue = NumberFormatter.FormatFloat(0);
if (colorIsVector)
{
AggregateType outputType = texOp.GetVectorType(AggregateType.FP32);
if ((outputType & AggregateType.ElementCountMask) != 0)
{
return $"{Declarations.GetVarTypeName(outputType, precise: false)}({scalarValue})";
}
}
return scalarValue;
}
string texCall = intCoords ? "texelFetch" : "texture";
@@ -521,7 +546,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
int srcIndex = isBindless ? 1 : 0;
string Src(VariableType type)
string Src(AggregateType type)
{
return GetSoureExpr(context, texOp.GetSource(srcIndex++), type);
}
@@ -530,7 +555,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
if (isIndexed)
{
indexExpr = Src(VariableType.S32);
indexExpr = Src(AggregateType.S32);
}
string samplerName = OperandManager.GetSamplerName(context.Config.Stage, texOp, indexExpr);
@@ -578,7 +603,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
texCall += ", " + str;
}
VariableType coordType = intCoords ? VariableType.S32 : VariableType.F32;
AggregateType coordType = intCoords ? AggregateType.S32 : AggregateType.FP32;
string AssemblePVector(int count)
{
@@ -590,7 +615,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
{
if (arrayIndexElem == index)
{
elems[index] = Src(VariableType.S32);
elems[index] = Src(AggregateType.S32);
if (!intCoords)
{
@@ -652,20 +677,20 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
for (int index = 0; index < count; index++)
{
elems[index] = Src(VariableType.F32);
elems[index] = Src(AggregateType.FP32);
}
return "vec" + count + "(" + string.Join(", ", elems) + ")";
}
else
{
return Src(VariableType.F32);
return Src(AggregateType.FP32);
}
}
if (hasExtraCompareArg)
{
Append(Src(VariableType.F32));
Append(Src(AggregateType.FP32));
}
if (hasDerivatives)
@@ -676,7 +701,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
if (isMultisample)
{
Append(Src(VariableType.S32));
Append(Src(AggregateType.S32));
}
else if (hasLodLevel)
{
@@ -691,14 +716,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
for (int index = 0; index < count; index++)
{
elems[index] = Src(VariableType.S32);
elems[index] = Src(AggregateType.S32);
}
return "ivec" + count + "(" + string.Join(", ", elems) + ")";
}
else
{
return Src(VariableType.S32);
return Src(AggregateType.S32);
}
}
@@ -718,17 +743,17 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
if (hasLodBias)
{
Append(Src(VariableType.F32));
Append(Src(AggregateType.FP32));
}
// textureGather* optional extra component index,
// not needed for shadow samplers.
if (isGather && !isShadow)
{
Append(Src(VariableType.S32));
Append(Src(AggregateType.S32));
}
texCall += ")" + (isGather || !isShadow ? GetMask(texOp.Index) : "");
texCall += ")" + (colorIsVector ? GetMaskMultiDest(texOp.Index) : "");
return texCall;
}
@@ -751,7 +776,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
if (isIndexed)
{
indexExpr = GetSoureExpr(context, texOp.GetSource(0), VariableType.S32);
indexExpr = GetSoureExpr(context, texOp.GetSource(0), AggregateType.S32);
}
string samplerName = OperandManager.GetSamplerName(context.Config.Stage, texOp, indexExpr);
@@ -804,5 +829,20 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
{
return '.' + "rgba".Substring(index, 1);
}
private static string GetMaskMultiDest(int mask)
{
string swizzle = ".";
for (int i = 0; i < 4; i++)
{
if ((mask & (1 << i)) != 0)
{
swizzle += "xyzw"[i];
}
}
return swizzle;
}
}
}

View File

@@ -0,0 +1,32 @@
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
using Ryujinx.Graphics.Shader.StructuredIr;
using static Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions.InstGenHelper;
using static Ryujinx.Graphics.Shader.StructuredIr.InstructionInfo;
namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
{
static class InstGenVector
{
public static string VectorExtract(CodeGenContext context, AstOperation operation)
{
IAstNode vector = operation.GetSource(0);
IAstNode index = operation.GetSource(1);
string vectorExpr = GetSoureExpr(context, vector, OperandManager.GetNodeDestType(context, vector));
if (index is AstOperand indexOperand && indexOperand.Type == OperandType.Constant)
{
char elem = "xyzw"[indexOperand.Value];
return $"{vectorExpr}.{elem}";
}
else
{
string indexExpr = GetSoureExpr(context, index, GetSrcVarType(operation.Inst, 1));
return $"{vectorExpr}[{indexExpr}]";
}
}
}
}

View File

@@ -1,4 +1,4 @@
using Ryujinx.Graphics.Shader.StructuredIr;
using Ryujinx.Graphics.Shader.Translation;
using System;
using System.Globalization;
@@ -8,21 +8,21 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
{
private const int MaxDecimal = 256;
public static bool TryFormat(int value, VariableType dstType, out string formatted)
public static bool TryFormat(int value, AggregateType dstType, out string formatted)
{
if (dstType == VariableType.F32)
if (dstType == AggregateType.FP32)
{
return TryFormatFloat(BitConverter.Int32BitsToSingle(value), out formatted);
}
else if (dstType == VariableType.S32)
else if (dstType == AggregateType.S32)
{
formatted = FormatInt(value);
}
else if (dstType == VariableType.U32)
else if (dstType == AggregateType.U32)
{
formatted = FormatUint((uint)value);
}
else if (dstType == VariableType.Bool)
else if (dstType == AggregateType.Bool)
{
formatted = value != 0 ? "true" : "false";
}
@@ -65,13 +65,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
return true;
}
public static string FormatInt(int value, VariableType dstType)
public static string FormatInt(int value, AggregateType dstType)
{
if (dstType == VariableType.S32)
if (dstType == AggregateType.S32)
{
return FormatInt(value);
}
else if (dstType == VariableType.U32)
else if (dstType == AggregateType.U32)
{
return FormatUint((uint)value);
}

View File

@@ -4,6 +4,7 @@ using Ryujinx.Graphics.Shader.Translation;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Numerics;
using static Ryujinx.Graphics.Shader.StructuredIr.InstructionInfo;
@@ -17,9 +18,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
{
public string Name { get; }
public VariableType Type { get; }
public AggregateType Type { get; }
public BuiltInAttribute(string name, VariableType type)
public BuiltInAttribute(string name, AggregateType type)
{
Name = name;
Type = type;
@@ -28,64 +29,64 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
private static Dictionary<int, BuiltInAttribute> _builtInAttributes = new Dictionary<int, BuiltInAttribute>()
{
{ AttributeConsts.Layer, new BuiltInAttribute("gl_Layer", VariableType.S32) },
{ AttributeConsts.PointSize, new BuiltInAttribute("gl_PointSize", VariableType.F32) },
{ AttributeConsts.PositionX, new BuiltInAttribute("gl_Position.x", VariableType.F32) },
{ AttributeConsts.PositionY, new BuiltInAttribute("gl_Position.y", VariableType.F32) },
{ AttributeConsts.PositionZ, new BuiltInAttribute("gl_Position.z", VariableType.F32) },
{ AttributeConsts.PositionW, new BuiltInAttribute("gl_Position.w", VariableType.F32) },
{ AttributeConsts.ClipDistance0, new BuiltInAttribute("gl_ClipDistance[0]", VariableType.F32) },
{ AttributeConsts.ClipDistance1, new BuiltInAttribute("gl_ClipDistance[1]", VariableType.F32) },
{ AttributeConsts.ClipDistance2, new BuiltInAttribute("gl_ClipDistance[2]", VariableType.F32) },
{ AttributeConsts.ClipDistance3, new BuiltInAttribute("gl_ClipDistance[3]", VariableType.F32) },
{ AttributeConsts.ClipDistance4, new BuiltInAttribute("gl_ClipDistance[4]", VariableType.F32) },
{ AttributeConsts.ClipDistance5, new BuiltInAttribute("gl_ClipDistance[5]", VariableType.F32) },
{ AttributeConsts.ClipDistance6, new BuiltInAttribute("gl_ClipDistance[6]", VariableType.F32) },
{ AttributeConsts.ClipDistance7, new BuiltInAttribute("gl_ClipDistance[7]", VariableType.F32) },
{ AttributeConsts.PointCoordX, new BuiltInAttribute("gl_PointCoord.x", VariableType.F32) },
{ AttributeConsts.PointCoordY, new BuiltInAttribute("gl_PointCoord.y", VariableType.F32) },
{ AttributeConsts.TessCoordX, new BuiltInAttribute("gl_TessCoord.x", VariableType.F32) },
{ AttributeConsts.TessCoordY, new BuiltInAttribute("gl_TessCoord.y", VariableType.F32) },
{ AttributeConsts.InstanceId, new BuiltInAttribute("gl_InstanceID", VariableType.S32) },
{ AttributeConsts.VertexId, new BuiltInAttribute("gl_VertexID", VariableType.S32) },
{ AttributeConsts.BaseInstance, new BuiltInAttribute("gl_BaseInstanceARB", VariableType.S32) },
{ AttributeConsts.BaseVertex, new BuiltInAttribute("gl_BaseVertexARB", VariableType.S32) },
{ AttributeConsts.InstanceIndex, new BuiltInAttribute("gl_InstanceIndex", VariableType.S32) },
{ AttributeConsts.VertexIndex, new BuiltInAttribute("gl_VertexIndex", VariableType.S32) },
{ AttributeConsts.DrawIndex, new BuiltInAttribute("gl_DrawIDARB", VariableType.S32) },
{ AttributeConsts.FrontFacing, new BuiltInAttribute("gl_FrontFacing", VariableType.Bool) },
{ AttributeConsts.Layer, new BuiltInAttribute("gl_Layer", AggregateType.S32) },
{ AttributeConsts.PointSize, new BuiltInAttribute("gl_PointSize", AggregateType.FP32) },
{ AttributeConsts.PositionX, new BuiltInAttribute("gl_Position.x", AggregateType.FP32) },
{ AttributeConsts.PositionY, new BuiltInAttribute("gl_Position.y", AggregateType.FP32) },
{ AttributeConsts.PositionZ, new BuiltInAttribute("gl_Position.z", AggregateType.FP32) },
{ AttributeConsts.PositionW, new BuiltInAttribute("gl_Position.w", AggregateType.FP32) },
{ AttributeConsts.ClipDistance0, new BuiltInAttribute("gl_ClipDistance[0]", AggregateType.FP32) },
{ AttributeConsts.ClipDistance1, new BuiltInAttribute("gl_ClipDistance[1]", AggregateType.FP32) },
{ AttributeConsts.ClipDistance2, new BuiltInAttribute("gl_ClipDistance[2]", AggregateType.FP32) },
{ AttributeConsts.ClipDistance3, new BuiltInAttribute("gl_ClipDistance[3]", AggregateType.FP32) },
{ AttributeConsts.ClipDistance4, new BuiltInAttribute("gl_ClipDistance[4]", AggregateType.FP32) },
{ AttributeConsts.ClipDistance5, new BuiltInAttribute("gl_ClipDistance[5]", AggregateType.FP32) },
{ AttributeConsts.ClipDistance6, new BuiltInAttribute("gl_ClipDistance[6]", AggregateType.FP32) },
{ AttributeConsts.ClipDistance7, new BuiltInAttribute("gl_ClipDistance[7]", AggregateType.FP32) },
{ AttributeConsts.PointCoordX, new BuiltInAttribute("gl_PointCoord.x", AggregateType.FP32) },
{ AttributeConsts.PointCoordY, new BuiltInAttribute("gl_PointCoord.y", AggregateType.FP32) },
{ AttributeConsts.TessCoordX, new BuiltInAttribute("gl_TessCoord.x", AggregateType.FP32) },
{ AttributeConsts.TessCoordY, new BuiltInAttribute("gl_TessCoord.y", AggregateType.FP32) },
{ AttributeConsts.InstanceId, new BuiltInAttribute("gl_InstanceID", AggregateType.S32) },
{ AttributeConsts.VertexId, new BuiltInAttribute("gl_VertexID", AggregateType.S32) },
{ AttributeConsts.BaseInstance, new BuiltInAttribute("gl_BaseInstanceARB", AggregateType.S32) },
{ AttributeConsts.BaseVertex, new BuiltInAttribute("gl_BaseVertexARB", AggregateType.S32) },
{ AttributeConsts.InstanceIndex, new BuiltInAttribute("gl_InstanceIndex", AggregateType.S32) },
{ AttributeConsts.VertexIndex, new BuiltInAttribute("gl_VertexIndex", AggregateType.S32) },
{ AttributeConsts.DrawIndex, new BuiltInAttribute("gl_DrawIDARB", AggregateType.S32) },
{ AttributeConsts.FrontFacing, new BuiltInAttribute("gl_FrontFacing", AggregateType.Bool) },
// Special.
{ AttributeConsts.FragmentOutputDepth, new BuiltInAttribute("gl_FragDepth", VariableType.F32) },
{ AttributeConsts.ThreadKill, new BuiltInAttribute("gl_HelperInvocation", VariableType.Bool) },
{ AttributeConsts.ThreadIdX, new BuiltInAttribute("gl_LocalInvocationID.x", VariableType.U32) },
{ AttributeConsts.ThreadIdY, new BuiltInAttribute("gl_LocalInvocationID.y", VariableType.U32) },
{ AttributeConsts.ThreadIdZ, new BuiltInAttribute("gl_LocalInvocationID.z", VariableType.U32) },
{ AttributeConsts.CtaIdX, new BuiltInAttribute("gl_WorkGroupID.x", VariableType.U32) },
{ AttributeConsts.CtaIdY, new BuiltInAttribute("gl_WorkGroupID.y", VariableType.U32) },
{ AttributeConsts.CtaIdZ, new BuiltInAttribute("gl_WorkGroupID.z", VariableType.U32) },
{ AttributeConsts.LaneId, new BuiltInAttribute(null, VariableType.U32) },
{ AttributeConsts.InvocationId, new BuiltInAttribute("gl_InvocationID", VariableType.S32) },
{ AttributeConsts.PrimitiveId, new BuiltInAttribute("gl_PrimitiveID", VariableType.S32) },
{ AttributeConsts.PatchVerticesIn, new BuiltInAttribute("gl_PatchVerticesIn", VariableType.S32) },
{ AttributeConsts.EqMask, new BuiltInAttribute(null, VariableType.U32) },
{ AttributeConsts.GeMask, new BuiltInAttribute(null, VariableType.U32) },
{ AttributeConsts.GtMask, new BuiltInAttribute(null, VariableType.U32) },
{ AttributeConsts.LeMask, new BuiltInAttribute(null, VariableType.U32) },
{ AttributeConsts.LtMask, new BuiltInAttribute(null, VariableType.U32) },
{ AttributeConsts.FragmentOutputDepth, new BuiltInAttribute("gl_FragDepth", AggregateType.FP32) },
{ AttributeConsts.ThreadKill, new BuiltInAttribute("gl_HelperInvocation", AggregateType.Bool) },
{ AttributeConsts.ThreadIdX, new BuiltInAttribute("gl_LocalInvocationID.x", AggregateType.U32) },
{ AttributeConsts.ThreadIdY, new BuiltInAttribute("gl_LocalInvocationID.y", AggregateType.U32) },
{ AttributeConsts.ThreadIdZ, new BuiltInAttribute("gl_LocalInvocationID.z", AggregateType.U32) },
{ AttributeConsts.CtaIdX, new BuiltInAttribute("gl_WorkGroupID.x", AggregateType.U32) },
{ AttributeConsts.CtaIdY, new BuiltInAttribute("gl_WorkGroupID.y", AggregateType.U32) },
{ AttributeConsts.CtaIdZ, new BuiltInAttribute("gl_WorkGroupID.z", AggregateType.U32) },
{ AttributeConsts.LaneId, new BuiltInAttribute(null, AggregateType.U32) },
{ AttributeConsts.InvocationId, new BuiltInAttribute("gl_InvocationID", AggregateType.S32) },
{ AttributeConsts.PrimitiveId, new BuiltInAttribute("gl_PrimitiveID", AggregateType.S32) },
{ AttributeConsts.PatchVerticesIn, new BuiltInAttribute("gl_PatchVerticesIn", AggregateType.S32) },
{ AttributeConsts.EqMask, new BuiltInAttribute(null, AggregateType.U32) },
{ AttributeConsts.GeMask, new BuiltInAttribute(null, AggregateType.U32) },
{ AttributeConsts.GtMask, new BuiltInAttribute(null, AggregateType.U32) },
{ AttributeConsts.LeMask, new BuiltInAttribute(null, AggregateType.U32) },
{ AttributeConsts.LtMask, new BuiltInAttribute(null, AggregateType.U32) },
// Support uniforms.
{ AttributeConsts.FragmentOutputIsBgraBase + 0, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[0]", VariableType.Bool) },
{ AttributeConsts.FragmentOutputIsBgraBase + 4, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[1]", VariableType.Bool) },
{ AttributeConsts.FragmentOutputIsBgraBase + 8, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[2]", VariableType.Bool) },
{ AttributeConsts.FragmentOutputIsBgraBase + 12, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[3]", VariableType.Bool) },
{ AttributeConsts.FragmentOutputIsBgraBase + 16, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[4]", VariableType.Bool) },
{ AttributeConsts.FragmentOutputIsBgraBase + 20, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[5]", VariableType.Bool) },
{ AttributeConsts.FragmentOutputIsBgraBase + 24, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[6]", VariableType.Bool) },
{ AttributeConsts.FragmentOutputIsBgraBase + 28, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[7]", VariableType.Bool) },
{ AttributeConsts.FragmentOutputIsBgraBase + 0, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[0]", AggregateType.Bool) },
{ AttributeConsts.FragmentOutputIsBgraBase + 4, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[1]", AggregateType.Bool) },
{ AttributeConsts.FragmentOutputIsBgraBase + 8, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[2]", AggregateType.Bool) },
{ AttributeConsts.FragmentOutputIsBgraBase + 12, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[3]", AggregateType.Bool) },
{ AttributeConsts.FragmentOutputIsBgraBase + 16, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[4]", AggregateType.Bool) },
{ AttributeConsts.FragmentOutputIsBgraBase + 20, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[5]", AggregateType.Bool) },
{ AttributeConsts.FragmentOutputIsBgraBase + 24, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[6]", AggregateType.Bool) },
{ AttributeConsts.FragmentOutputIsBgraBase + 28, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[7]", AggregateType.Bool) },
{ AttributeConsts.SupportBlockViewInverseX, new BuiltInAttribute($"{DefaultNames.SupportBlockViewportInverse}.x", VariableType.F32) },
{ AttributeConsts.SupportBlockViewInverseY, new BuiltInAttribute($"{DefaultNames.SupportBlockViewportInverse}.y", VariableType.F32) }
{ AttributeConsts.SupportBlockViewInverseX, new BuiltInAttribute($"{DefaultNames.SupportBlockViewportInverse}.x", AggregateType.FP32) },
{ AttributeConsts.SupportBlockViewInverseY, new BuiltInAttribute($"{DefaultNames.SupportBlockViewportInverse}.y", AggregateType.FP32) }
};
private Dictionary<AstOperand, string> _locals;
@@ -329,7 +330,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
{
if (cbIndexable)
{
return GetUbName(stage, NumberFormatter.FormatInt(slot, VariableType.S32));
return GetUbName(stage, NumberFormatter.FormatInt(slot, AggregateType.S32));
}
return $"{GetShaderStagePrefix(stage)}_{DefaultNames.UniformNamePrefix}{slot}_{DefaultNames.UniformNameSuffix}";
@@ -404,7 +405,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
return $"{DefaultNames.ArgumentNamePrefix}{argIndex}";
}
public static VariableType GetNodeDestType(CodeGenContext context, IAstNode node, bool isAsgDest = false)
public static AggregateType GetNodeDestType(CodeGenContext context, IAstNode node, bool isAsgDest = false)
{
if (node is AstOperation operation)
{
@@ -431,12 +432,22 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
return context.GetFunction(funcId.Value).ReturnType;
}
else if (operation is AstTextureOperation texOp &&
(texOp.Inst == Instruction.ImageLoad ||
texOp.Inst == Instruction.ImageStore ||
texOp.Inst == Instruction.ImageAtomic))
else if (operation.Inst == Instruction.VectorExtract)
{
return texOp.Format.GetComponentType();
return GetNodeDestType(context, operation.GetSource(0)) & ~AggregateType.ElementCountMask;
}
else if (operation is AstTextureOperation texOp)
{
if (texOp.Inst == Instruction.ImageLoad ||
texOp.Inst == Instruction.ImageStore ||
texOp.Inst == Instruction.ImageAtomic)
{
return texOp.GetVectorType(texOp.Format.GetComponentType());
}
else if (texOp.Inst == Instruction.TextureSample)
{
return texOp.GetVectorType(GetDestVarType(operation.Inst));
}
}
return GetDestVarType(operation.Inst);
@@ -458,7 +469,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
}
}
private static VariableType GetOperandVarType(CodeGenContext context, AstOperand operand, bool isAsgDest = false)
private static AggregateType GetOperandVarType(CodeGenContext context, AstOperand operand, bool isAsgDest = false)
{
if (operand.Type == OperandType.Attribute)
{
@@ -474,7 +485,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
AttributeType type = context.Config.GpuAccessor.QueryAttributeType(location);
return type.ToVariableType();
return type.ToAggregateType();
}
}

View File

@@ -1,6 +1,7 @@
using Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions;
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
using Ryujinx.Graphics.Shader.StructuredIr;
using Ryujinx.Graphics.Shader.Translation;
using System;
namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
@@ -10,8 +11,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
public static string ReinterpretCast(
CodeGenContext context,
IAstNode node,
VariableType srcType,
VariableType dstType)
AggregateType srcType,
AggregateType dstType)
{
if (node is AstOperand operand && operand.Type == OperandType.Constant)
{
@@ -26,46 +27,46 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
return ReinterpretCast(expr, node, srcType, dstType);
}
private static string ReinterpretCast(string expr, IAstNode node, VariableType srcType, VariableType dstType)
private static string ReinterpretCast(string expr, IAstNode node, AggregateType srcType, AggregateType dstType)
{
if (srcType == dstType)
{
return expr;
}
if (srcType == VariableType.F32)
if (srcType == AggregateType.FP32)
{
switch (dstType)
{
case VariableType.Bool: return $"(floatBitsToInt({expr}) != 0)";
case VariableType.S32: return $"floatBitsToInt({expr})";
case VariableType.U32: return $"floatBitsToUint({expr})";
case AggregateType.Bool: return $"(floatBitsToInt({expr}) != 0)";
case AggregateType.S32: return $"floatBitsToInt({expr})";
case AggregateType.U32: return $"floatBitsToUint({expr})";
}
}
else if (dstType == VariableType.F32)
else if (dstType == AggregateType.FP32)
{
switch (srcType)
{
case VariableType.Bool: return $"intBitsToFloat({ReinterpretBoolToInt(expr, node, VariableType.S32)})";
case VariableType.S32: return $"intBitsToFloat({expr})";
case VariableType.U32: return $"uintBitsToFloat({expr})";
case AggregateType.Bool: return $"intBitsToFloat({ReinterpretBoolToInt(expr, node, AggregateType.S32)})";
case AggregateType.S32: return $"intBitsToFloat({expr})";
case AggregateType.U32: return $"uintBitsToFloat({expr})";
}
}
else if (srcType == VariableType.Bool)
else if (srcType == AggregateType.Bool)
{
return ReinterpretBoolToInt(expr, node, dstType);
}
else if (dstType == VariableType.Bool)
else if (dstType == AggregateType.Bool)
{
expr = InstGenHelper.Enclose(expr, node, Instruction.CompareNotEqual, isLhs: true);
return $"({expr} != 0)";
}
else if (dstType == VariableType.S32)
else if (dstType == AggregateType.S32)
{
return $"int({expr})";
}
else if (dstType == VariableType.U32)
else if (dstType == AggregateType.U32)
{
return $"uint({expr})";
}
@@ -73,7 +74,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
throw new ArgumentException($"Invalid reinterpret cast from \"{srcType}\" to \"{dstType}\".");
}
private static string ReinterpretBoolToInt(string expr, IAstNode node, VariableType dstType)
private static string ReinterpretBoolToInt(string expr, IAstNode node, AggregateType dstType)
{
string trueExpr = NumberFormatter.FormatInt(IrConsts.True, dstType);
string falseExpr = NumberFormatter.FormatInt(IrConsts.False, dstType);

View File

@@ -241,6 +241,29 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
throw new NotImplementedException(node.GetType().Name);
}
public Instruction GetWithType(IAstNode node, out AggregateType type)
{
if (node is AstOperation operation)
{
var opResult = Instructions.Generate(this, operation);
type = opResult.Type;
return opResult.Value;
}
else if (node is AstOperand operand)
{
switch (operand.Type)
{
case IrOperandType.LocalVariable:
type = operand.VarType;
return GetLocal(type, operand);
default:
throw new ArgumentException($"Invalid operand type \"{operand.Type}\".");
}
}
throw new NotImplementedException(node.GetType().Name);
}
private Instruction GetUndefined(AggregateType type)
{
return type switch
@@ -325,7 +348,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
if (components > 1)
{
attrOffset &= ~0xf;
type = AggregateType.Vector | AggregateType.FP32;
type = components switch
{
2 => AggregateType.Vector2 | AggregateType.FP32,
3 => AggregateType.Vector3 | AggregateType.FP32,
4 => AggregateType.Vector4 | AggregateType.FP32,
_ => AggregateType.FP32
};
attrInfo = new AttributeInfo(attrOffset, (attr - attrOffset) / 4, components, type, false);
}
}
@@ -335,7 +365,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
bool isIndexed = AttributeInfo.IsArrayAttributeSpirv(Config.Stage, isOutAttr) && (!attrInfo.IsBuiltin || AttributeInfo.IsArrayBuiltIn(attr));
if ((type & (AggregateType.Array | AggregateType.Vector)) == 0)
if ((type & (AggregateType.Array | AggregateType.ElementCountMask)) == 0)
{
if (invocationId != null)
{
@@ -452,7 +482,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
elemType = attrInfo.Type & AggregateType.ElementTypeMask;
if ((attrInfo.Type & (AggregateType.Array | AggregateType.Vector)) == 0)
if ((attrInfo.Type & (AggregateType.Array | AggregateType.ElementCountMask)) == 0)
{
return ioVariable;
}
@@ -533,13 +563,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
public Instruction GetLocal(AggregateType dstType, AstOperand local)
{
var srcType = local.VarType.Convert();
var srcType = local.VarType;
return BitcastIfNeeded(dstType, srcType, Load(GetType(srcType), GetLocalPointer(local)));
}
public Instruction GetArgument(AggregateType dstType, AstOperand funcArg)
{
var srcType = funcArg.VarType.Convert();
var srcType = funcArg.VarType;
return BitcastIfNeeded(dstType, srcType, Load(GetType(srcType), GetArgumentPointer(funcArg)));
}
@@ -550,13 +580,21 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
public Instruction GetType(AggregateType type, int length = 1)
{
if (type.HasFlag(AggregateType.Array))
if ((type & AggregateType.Array) != 0)
{
return TypeArray(GetType(type & ~AggregateType.Array), Constant(TypeU32(), length));
}
else if (type.HasFlag(AggregateType.Vector))
else if ((type & AggregateType.ElementCountMask) != 0)
{
return TypeVector(GetType(type & ~AggregateType.Vector), length);
int vectorLength = (type & AggregateType.ElementCountMask) switch
{
AggregateType.Vector2 => 2,
AggregateType.Vector3 => 3,
AggregateType.Vector4 => 4,
_ => 1
};
return TypeVector(GetType(type & ~AggregateType.ElementCountMask), vectorLength);
}
return type switch

View File

@@ -23,11 +23,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
DeclareParameters(context, function.OutArguments, function.InArguments.Length);
}
private static void DeclareParameters(CodeGenContext context, IEnumerable<VariableType> argTypes, int argIndex)
private static void DeclareParameters(CodeGenContext context, IEnumerable<AggregateType> argTypes, int argIndex)
{
foreach (var argType in argTypes)
{
var argPointerType = context.TypePointer(StorageClass.Function, context.GetType(argType.Convert()));
var argPointerType = context.TypePointer(StorageClass.Function, context.GetType(argType));
var spvArg = context.FunctionParameter(argPointerType);
context.DeclareArgument(argIndex++, spvArg);
@@ -38,7 +38,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
{
foreach (AstOperand local in function.Locals)
{
var localPointerType = context.TypePointer(StorageClass.Function, context.GetType(local.VarType.Convert()));
var localPointerType = context.TypePointer(StorageClass.Function, context.GetType(local.VarType));
var spvLocal = context.Variable(localPointerType, StorageClass.Function);
context.AddLocalVariable(spvLocal);
@@ -62,7 +62,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
for (int i = 0; i < function.InArguments.Length; i++)
{
var type = function.GetArgumentType(i).Convert();
var type = function.GetArgumentType(i);
var localPointerType = context.TypePointer(StorageClass.Function, context.GetType(type));
var spvLocal = context.Variable(localPointerType, StorageClass.Function);
@@ -303,7 +303,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
var dim = GetDim(descriptor.Type);
var imageType = context.TypeImage(
context.GetType(meta.Format.GetComponentType().Convert()),
context.GetType(meta.Format.GetComponentType()),
dim,
descriptor.Type.HasFlag(SamplerType.Shadow),
descriptor.Type.HasFlag(SamplerType.Array),
@@ -652,7 +652,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
if (components > 1)
{
attr &= ~0xf;
type = AggregateType.Vector | AggregateType.FP32;
type = components switch
{
2 => AggregateType.Vector2 | AggregateType.FP32,
3 => AggregateType.Vector3 | AggregateType.FP32,
4 => AggregateType.Vector4 | AggregateType.FP32,
_ => AggregateType.FP32
};
hasComponent = false;
}
}

View File

@@ -1,5 +1,4 @@
using Ryujinx.Graphics.Shader.StructuredIr;
using Ryujinx.Graphics.Shader.Translation;
using Ryujinx.Graphics.Shader.Translation;
using System;
using static Spv.Specification;
@@ -7,20 +6,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
{
static class EnumConversion
{
public static AggregateType Convert(this VariableType type)
{
return type switch
{
VariableType.None => AggregateType.Void,
VariableType.Bool => AggregateType.Bool,
VariableType.F32 => AggregateType.FP32,
VariableType.F64 => AggregateType.FP64,
VariableType.S32 => AggregateType.S32,
VariableType.U32 => AggregateType.U32,
_ => throw new ArgumentException($"Invalid variable type \"{type}\".")
};
}
public static ExecutionModel Convert(this ShaderStage stage)
{
return stage switch

View File

@@ -4,6 +4,7 @@ using Ryujinx.Graphics.Shader.Translation;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Numerics;
using static Spv.Specification;
namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
@@ -146,6 +147,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
Add(Instruction.Truncate, GenerateTruncate);
Add(Instruction.UnpackDouble2x32, GenerateUnpackDouble2x32);
Add(Instruction.UnpackHalf2x16, GenerateUnpackHalf2x16);
Add(Instruction.VectorExtract, GenerateVectorExtract);
Add(Instruction.VoteAll, GenerateVoteAll);
Add(Instruction.VoteAllEqual, GenerateVoteAllEqual);
Add(Instruction.VoteAny, GenerateVoteAny);
@@ -317,7 +319,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
}
else
{
var type = function.GetArgumentType(i).Convert();
var type = function.GetArgumentType(i);
var value = context.Get(type, operand);
var spvLocal = spvLocals[i];
@@ -327,7 +329,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
}
}
var retType = function.ReturnType.Convert();
var retType = function.ReturnType;
var result = context.FunctionCall(context.GetType(retType), spvFunc, args);
return new OperationResult(retType, result);
}
@@ -604,10 +606,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
// TODO: Bindless texture support. For now we just return 0/do nothing.
if (isBindless)
{
return new OperationResult(componentType.Convert(), componentType switch
return new OperationResult(componentType, componentType switch
{
VariableType.S32 => context.Constant(context.TypeS32(), 0),
VariableType.U32 => context.Constant(context.TypeU32(), 0u),
AggregateType.S32 => context.Constant(context.TypeS32(), 0),
AggregateType.U32 => context.Constant(context.TypeU32(), 0u),
_ => context.Constant(context.TypeFP32(), 0f),
});
}
@@ -652,13 +654,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
pCoords = Src(AggregateType.S32);
}
SpvInstruction value = Src(componentType.Convert());
SpvInstruction value = Src(componentType);
(var imageType, var imageVariable) = context.Images[new TextureMeta(texOp.CbufSlot, texOp.Handle, texOp.Format)];
var image = context.Load(imageType, imageVariable);
SpvInstruction resultType = context.GetType(componentType.Convert());
SpvInstruction resultType = context.GetType(componentType);
SpvInstruction imagePointerType = context.TypePointer(StorageClass.Image, resultType);
var pointer = context.ImageTexelPointer(imagePointerType, imageVariable, pCoords, context.Constant(context.TypeU32(), 0));
@@ -668,10 +670,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
var result = (texOp.Flags & TextureFlags.AtomicMask) switch
{
TextureFlags.Add => context.AtomicIAdd(resultType, pointer, one, zero, value),
TextureFlags.Minimum => componentType == VariableType.S32
TextureFlags.Minimum => componentType == AggregateType.S32
? context.AtomicSMin(resultType, pointer, one, zero, value)
: context.AtomicUMin(resultType, pointer, one, zero, value),
TextureFlags.Maximum => componentType == VariableType.S32
TextureFlags.Maximum => componentType == AggregateType.S32
? context.AtomicSMax(resultType, pointer, one, zero, value)
: context.AtomicUMax(resultType, pointer, one, zero, value),
TextureFlags.Increment => context.AtomicIIncrement(resultType, pointer, one, zero),
@@ -680,11 +682,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
TextureFlags.BitwiseOr => context.AtomicOr(resultType, pointer, one, zero, value),
TextureFlags.BitwiseXor => context.AtomicXor(resultType, pointer, one, zero, value),
TextureFlags.Swap => context.AtomicExchange(resultType, pointer, one, zero, value),
TextureFlags.CAS => context.AtomicCompareExchange(resultType, pointer, one, zero, zero, Src(componentType.Convert()), value),
TextureFlags.CAS => context.AtomicCompareExchange(resultType, pointer, one, zero, zero, Src(componentType), value),
_ => context.AtomicIAdd(resultType, pointer, one, zero, value),
};
return new OperationResult(componentType.Convert(), result);
return new OperationResult(componentType, result);
}
private static OperationResult GenerateImageLoad(CodeGenContext context, AstOperation operation)
@@ -698,14 +700,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
// TODO: Bindless texture support. For now we just return 0/do nothing.
if (isBindless)
{
var zero = componentType switch
{
VariableType.S32 => context.Constant(context.TypeS32(), 0),
VariableType.U32 => context.Constant(context.TypeU32(), 0u),
_ => context.Constant(context.TypeFP32(), 0f),
};
return new OperationResult(componentType.Convert(), zero);
return GetZeroOperationResult(context, texOp, componentType, isVector: true);
}
bool isArray = (texOp.Type & SamplerType.Array) != 0;
@@ -753,12 +748,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
(var imageType, var imageVariable) = context.Images[new TextureMeta(texOp.CbufSlot, texOp.Handle, texOp.Format)];
var image = context.Load(imageType, imageVariable);
var imageComponentType = context.GetType(componentType.Convert());
var imageComponentType = context.GetType(componentType);
var swizzledResultType = texOp.GetVectorType(componentType);
var texel = context.ImageRead(context.TypeVector(imageComponentType, 4), image, pCoords, ImageOperandsMask.MaskNone);
var result = context.CompositeExtract(imageComponentType, texel, (SpvLiteralInteger)texOp.Index);
var result = GetSwizzledResult(context, texel, swizzledResultType, texOp.Index);
return new OperationResult(componentType.Convert(), result);
return new OperationResult(componentType, result);
}
private static OperationResult GenerateImageStore(CodeGenContext context, AstOperation operation)
@@ -823,20 +819,20 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
{
if (srcIndex < texOp.SourcesCount)
{
cElems[i] = Src(componentType.Convert());
cElems[i] = Src(componentType);
}
else
{
cElems[i] = componentType switch
{
VariableType.S32 => context.Constant(context.TypeS32(), 0),
VariableType.U32 => context.Constant(context.TypeU32(), 0u),
AggregateType.S32 => context.Constant(context.TypeS32(), 0),
AggregateType.U32 => context.Constant(context.TypeU32(), 0u),
_ => context.Constant(context.TypeFP32(), 0f),
};
}
}
var texel = context.CompositeConstruct(context.TypeVector(context.GetType(componentType.Convert()), ComponentsCount), cElems);
var texel = context.CompositeConstruct(context.TypeVector(context.GetType(componentType), ComponentsCount), cElems);
(var imageType, var imageVariable) = context.Images[new TextureMeta(texOp.CbufSlot, texOp.Handle, texOp.Format)];
@@ -1238,7 +1234,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
var validLocal = (AstOperand)operation.GetSource(3);
context.Store(context.GetLocalPointer(validLocal), context.BitcastIfNeeded(validLocal.VarType.Convert(), AggregateType.Bool, valid));
context.Store(context.GetLocalPointer(validLocal), context.BitcastIfNeeded(validLocal.VarType, AggregateType.Bool, valid));
return new OperationResult(AggregateType.FP32, result);
}
@@ -1268,7 +1264,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
var validLocal = (AstOperand)operation.GetSource(3);
context.Store(context.GetLocalPointer(validLocal), context.BitcastIfNeeded(validLocal.VarType.Convert(), AggregateType.Bool, valid));
context.Store(context.GetLocalPointer(validLocal), context.BitcastIfNeeded(validLocal.VarType, AggregateType.Bool, valid));
return new OperationResult(AggregateType.FP32, result);
}
@@ -1294,7 +1290,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
var validLocal = (AstOperand)operation.GetSource(3);
context.Store(context.GetLocalPointer(validLocal), context.BitcastIfNeeded(validLocal.VarType.Convert(), AggregateType.Bool, valid));
context.Store(context.GetLocalPointer(validLocal), context.BitcastIfNeeded(validLocal.VarType, AggregateType.Bool, valid));
return new OperationResult(AggregateType.FP32, result);
}
@@ -1324,7 +1320,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
var validLocal = (AstOperand)operation.GetSource(3);
context.Store(context.GetLocalPointer(validLocal), context.BitcastIfNeeded(validLocal.VarType.Convert(), AggregateType.Bool, valid));
context.Store(context.GetLocalPointer(validLocal), context.BitcastIfNeeded(validLocal.VarType, AggregateType.Bool, valid));
return new OperationResult(AggregateType.FP32, result);
}
@@ -1485,10 +1481,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
bool isMultisample = (texOp.Type & SamplerType.Multisample) != 0;
bool isShadow = (texOp.Type & SamplerType.Shadow) != 0;
bool colorIsVector = isGather || !isShadow;
// TODO: Bindless texture support. For now we just return 0.
if (isBindless)
{
return new OperationResult(AggregateType.FP32, context.Constant(context.TypeFP32(), 0f));
return GetZeroOperationResult(context, texOp, AggregateType.FP32, colorIsVector);
}
// This combination is valid, but not available on GLSL.
@@ -1705,7 +1703,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
operandsList.Add(sample);
}
bool colorIsVector = isGather || !isShadow;
var resultType = colorIsVector ? context.TypeVector(context.TypeFP32(), 4) : context.TypeFP32();
var meta = new TextureMeta(texOp.CbufSlot, texOp.Handle, texOp.Format);
@@ -1758,12 +1755,16 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
result = context.ImageSampleImplicitLod(resultType, image, pCoords, operandsMask, operands);
}
var swizzledResultType = AggregateType.FP32;
if (colorIsVector)
{
result = context.CompositeExtract(context.TypeFP32(), result, (SpvLiteralInteger)texOp.Index);
swizzledResultType = texOp.GetVectorType(swizzledResultType);
result = GetSwizzledResult(context, result, swizzledResultType, texOp.Index);
}
return new OperationResult(AggregateType.FP32, result);
return new OperationResult(swizzledResultType, result);
}
private static OperationResult GenerateTextureSize(CodeGenContext context, AstOperation operation)
@@ -1862,6 +1863,26 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
return new OperationResult(AggregateType.FP32, result);
}
private static OperationResult GenerateVectorExtract(CodeGenContext context, AstOperation operation)
{
var vector = context.GetWithType(operation.GetSource(0), out AggregateType vectorType);
var scalarType = vectorType & ~AggregateType.ElementCountMask;
var resultType = context.GetType(scalarType);
SpvInstruction result;
if (operation.GetSource(1) is AstOperand indexOperand && indexOperand.Type == OperandType.Constant)
{
result = context.CompositeExtract(resultType, vector, (SpvLiteralInteger)indexOperand.Value);
}
else
{
var index = context.Get(AggregateType.S32, operation.GetSource(1));
result = context.VectorExtractDynamic(resultType, vector, index);
}
return new OperationResult(scalarType, result);
}
private static OperationResult GenerateVoteAll(CodeGenContext context, AstOperation operation)
{
var execution = context.Constant(context.TypeU32(), Scope.Subgroup);
@@ -2044,6 +2065,64 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
context.AddLabel(loopEnd);
}
private static OperationResult GetZeroOperationResult(
CodeGenContext context,
AstTextureOperation texOp,
AggregateType scalarType,
bool isVector)
{
var zero = scalarType switch
{
AggregateType.S32 => context.Constant(context.TypeS32(), 0),
AggregateType.U32 => context.Constant(context.TypeU32(), 0u),
_ => context.Constant(context.TypeFP32(), 0f),
};
if (isVector)
{
AggregateType outputType = texOp.GetVectorType(scalarType);
if ((outputType & AggregateType.ElementCountMask) != 0)
{
int componentsCount = BitOperations.PopCount((uint)texOp.Index);
SpvInstruction[] values = new SpvInstruction[componentsCount];
values.AsSpan().Fill(zero);
return new OperationResult(outputType, context.ConstantComposite(context.GetType(outputType), values));
}
}
return new OperationResult(scalarType, zero);
}
private static SpvInstruction GetSwizzledResult(CodeGenContext context, SpvInstruction vector, AggregateType swizzledResultType, int mask)
{
if ((swizzledResultType & AggregateType.ElementCountMask) != 0)
{
SpvLiteralInteger[] components = new SpvLiteralInteger[BitOperations.PopCount((uint)mask)];
int componentIndex = 0;
for (int i = 0; i < 4; i++)
{
if ((mask & (1 << i)) != 0)
{
components[componentIndex++] = i;
}
}
return context.VectorShuffle(context.GetType(swizzledResultType), vector, vector, components);
}
else
{
int componentIndex = (int)BitOperations.TrailingZeroCount(mask);
return context.CompositeExtract(context.GetType(swizzledResultType), vector, (SpvLiteralInteger)componentIndex);
}
}
private static SpvInstruction GetStorageElemPointer(CodeGenContext context, AstOperation operation)
{
var sbVariable = context.StorageBuffersArray;

View File

@@ -104,13 +104,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
for (int funcIndex = 0; funcIndex < info.Functions.Count; funcIndex++)
{
var function = info.Functions[funcIndex];
var retType = context.GetType(function.ReturnType.Convert());
var retType = context.GetType(function.ReturnType);
var funcArgs = new SpvInstruction[function.InArguments.Length + function.OutArguments.Length];
for (int argIndex = 0; argIndex < funcArgs.Length; argIndex++)
{
var argType = context.GetType(function.GetArgumentType(argIndex).Convert());
var argType = context.GetType(function.GetArgumentType(argIndex));
var argPointerType = context.TypePointer(StorageClass.Function, argType);
funcArgs[argIndex] = argPointerType;
}
@@ -387,7 +387,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
if (dest.Type == OperandType.LocalVariable)
{
var source = context.Get(dest.VarType.Convert(), assignment.Source);
var source = context.Get(dest.VarType, assignment.Source);
context.Store(context.GetLocalPointer(dest), source);
}
else if (dest.Type == OperandType.Attribute || dest.Type == OperandType.AttributePerPatch)
@@ -407,7 +407,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
}
else if (dest.Type == OperandType.Argument)
{
var source = context.Get(dest.VarType.Convert(), assignment.Source);
var source = context.Get(dest.VarType, assignment.Source);
context.Store(context.GetArgumentPointer(dest), source);
}
else

View File

@@ -1,7 +1,9 @@
using Ryujinx.Graphics.Shader.Decoders;
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
using Ryujinx.Graphics.Shader.Translation;
using System;
using System.Collections.Generic;
using System.Numerics;
using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper;
using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
@@ -217,15 +219,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
return context.Copy(Register(srcB++, RegisterType.Gpr));
}
Operand GetDest()
{
if (dest >= RegisterConsts.RegisterZeroIndex)
{
return null;
}
return Register(dest++, RegisterType.Gpr);
}
Operand destOperand = dest != RegisterConsts.RegisterZeroIndex ? Register(dest, RegisterType.Gpr) : null;
List<Operand> sourcesList = new List<Operand>();
@@ -291,7 +285,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
flags,
imm,
0,
GetDest(),
new[] { destOperand },
sources);
context.Add(operation);
@@ -371,36 +365,40 @@ namespace Ryujinx.Graphics.Shader.Instructions
if (useComponents)
{
for (int compMask = (int)componentMask, compIndex = 0; compMask != 0; compMask >>= 1, compIndex++)
{
if ((compMask & 1) == 0)
{
continue;
}
Operand[] dests = new Operand[BitOperations.PopCount((uint)componentMask)];
if (srcB == RegisterConsts.RegisterZeroIndex)
int outputIndex = 0;
for (int i = 0; i < dests.Length; i++)
{
if (srcB + i >= RegisterConsts.RegisterZeroIndex)
{
break;
}
Operand rd = Register(srcB++, RegisterType.Gpr);
TextureOperation operation = context.CreateTextureOperation(
Instruction.ImageLoad,
type,
flags,
handle,
compIndex,
rd,
sources);
if (!isBindless)
{
operation.Format = context.Config.GetTextureFormat(handle);
}
context.Add(operation);
dests[outputIndex++] = Register(srcB + i, RegisterType.Gpr);
}
if (outputIndex != dests.Length)
{
Array.Resize(ref dests, outputIndex);
}
TextureOperation operation = context.CreateTextureOperation(
Instruction.ImageLoad,
type,
flags,
handle,
(int)componentMask,
dests,
sources);
if (!isBindless)
{
operation.Format = context.Config.GetTextureFormat(handle);
}
context.Add(operation);
}
else
{
@@ -412,35 +410,45 @@ namespace Ryujinx.Graphics.Shader.Instructions
}
int components = GetComponents(size);
int compMask = (1 << components) - 1;
for (int compIndex = 0; compIndex < components; compIndex++)
Operand[] dests = new Operand[components];
int outputIndex = 0;
for (int i = 0; i < dests.Length; i++)
{
if (srcB == RegisterConsts.RegisterZeroIndex)
if (srcB + i >= RegisterConsts.RegisterZeroIndex)
{
break;
}
Operand rd = Register(srcB++, RegisterType.Gpr);
dests[outputIndex++] = Register(srcB + i, RegisterType.Gpr);
}
TextureOperation operation = context.CreateTextureOperation(
Instruction.ImageLoad,
type,
GetTextureFormat(size),
flags,
handle,
compIndex,
rd,
sources);
if (outputIndex != dests.Length)
{
Array.Resize(ref dests, outputIndex);
}
context.Add(operation);
TextureOperation operation = context.CreateTextureOperation(
Instruction.ImageLoad,
type,
GetTextureFormat(size),
flags,
handle,
compMask,
dests,
sources);
switch (size)
{
case SuSize.U8: context.Copy(rd, ZeroExtendTo32(context, rd, 8)); break;
case SuSize.U16: context.Copy(rd, ZeroExtendTo32(context, rd, 16)); break;
case SuSize.S8: context.Copy(rd, SignExtendTo32(context, rd, 8)); break;
case SuSize.S16: context.Copy(rd, SignExtendTo32(context, rd, 16)); break;
}
context.Add(operation);
switch (size)
{
case SuSize.U8: context.Copy(dests[0], ZeroExtendTo32(context, dests[0], 8)); break;
case SuSize.U16: context.Copy(dests[0], ZeroExtendTo32(context, dests[0], 16)); break;
case SuSize.S8: context.Copy(dests[0], SignExtendTo32(context, dests[0], 8)); break;
case SuSize.S16: context.Copy(dests[0], SignExtendTo32(context, dests[0], 16)); break;
}
}
}

View File

@@ -3,6 +3,7 @@ using Ryujinx.Graphics.Shader.IntermediateRepresentation;
using Ryujinx.Graphics.Shader.Translation;
using System;
using System.Collections.Generic;
using System.Numerics;
using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
@@ -303,42 +304,37 @@ namespace Ryujinx.Graphics.Shader.Instructions
}
Operand[] sources = sourcesList.ToArray();
Operand[] dests = new Operand[BitOperations.PopCount((uint)componentMask)];
Operand GetDest()
int outputIndex = 0;
for (int i = 0; i < dests.Length; i++)
{
if (rdIndex >= RegisterConsts.RegisterZeroIndex)
if (rdIndex + i >= RegisterConsts.RegisterZeroIndex)
{
return null;
break;
}
return Register(rdIndex++, RegisterType.Gpr);
dests[outputIndex++] = Register(rdIndex + i, RegisterType.Gpr);
}
if (outputIndex != dests.Length)
{
Array.Resize(ref dests, outputIndex);
}
int handle = !isBindless ? imm : 0;
for (int compMask = componentMask, compIndex = 0; compMask != 0; compMask >>= 1, compIndex++)
{
if ((compMask & 1) != 0)
{
Operand dest = GetDest();
TextureOperation operation = context.CreateTextureOperation(
Instruction.TextureSample,
type,
flags,
handle,
componentMask,
dests,
sources);
if (dest == null)
{
break;
}
TextureOperation operation = context.CreateTextureOperation(
Instruction.TextureSample,
type,
flags,
handle,
compIndex,
dest,
sources);
context.Add(operation);
}
}
context.Add(operation);
}
private static void EmitTexs(
@@ -624,18 +620,23 @@ namespace Ryujinx.Graphics.Shader.Instructions
Operand[] rd0 = new Operand[2] { ConstF(0), ConstF(0) };
Operand[] rd1 = new Operand[2] { ConstF(0), ConstF(0) };
int destIncrement = 0;
int handle = imm;
int componentMask = _maskLut[dest2 == RegisterConsts.RegisterZeroIndex ? 0 : 1, writeMask];
Operand GetDest()
int componentsCount = BitOperations.PopCount((uint)componentMask);
Operand[] dests = new Operand[componentsCount];
int outputIndex = 0;
for (int i = 0; i < componentsCount; i++)
{
int high = destIncrement >> 1;
int low = destIncrement & 1;
destIncrement++;
int high = i >> 1;
int low = i & 1;
if (isF16)
{
return high != 0
dests[outputIndex++] = high != 0
? (rd1[low] = Local())
: (rd0[low] = Local());
}
@@ -648,30 +649,26 @@ namespace Ryujinx.Graphics.Shader.Instructions
rdIndex += low;
}
return Register(rdIndex, RegisterType.Gpr);
dests[outputIndex++] = Register(rdIndex, RegisterType.Gpr);
}
}
int handle = imm;
int componentMask = _maskLut[dest2 == RegisterConsts.RegisterZeroIndex ? 0 : 1, writeMask];
for (int compMask = componentMask, compIndex = 0; compMask != 0; compMask >>= 1, compIndex++)
if (outputIndex != dests.Length)
{
if ((compMask & 1) != 0)
{
TextureOperation operation = context.CreateTextureOperation(
Instruction.TextureSample,
type,
flags,
handle,
compIndex,
GetDest(),
sources);
context.Add(operation);
}
Array.Resize(ref dests, outputIndex);
}
TextureOperation operation = context.CreateTextureOperation(
Instruction.TextureSample,
type,
flags,
handle,
componentMask,
dests,
sources);
context.Add(operation);
if (isF16)
{
context.Copy(Register(dest, RegisterType.Gpr), context.PackHalf2x16(rd0[0], rd0[1]));
@@ -797,42 +794,37 @@ namespace Ryujinx.Graphics.Shader.Instructions
sourcesList.Add(Const((int)component));
Operand[] sources = sourcesList.ToArray();
Operand[] dests = new Operand[BitOperations.PopCount((uint)componentMask)];
Operand GetDest()
int outputIndex = 0;
for (int i = 0; i < dests.Length; i++)
{
if (dest >= RegisterConsts.RegisterZeroIndex)
if (dest + i >= RegisterConsts.RegisterZeroIndex)
{
return null;
break;
}
return Register(dest++, RegisterType.Gpr);
dests[outputIndex++] = Register(dest + i, RegisterType.Gpr);
}
if (outputIndex != dests.Length)
{
Array.Resize(ref dests, outputIndex);
}
int handle = imm;
for (int compMask = componentMask, compIndex = 0; compMask != 0; compMask >>= 1, compIndex++)
{
if ((compMask & 1) != 0)
{
Operand destOperand = GetDest();
TextureOperation operation = context.CreateTextureOperation(
Instruction.TextureSample,
type,
flags,
handle,
componentMask,
dests,
sources);
if (destOperand == null)
{
break;
}
TextureOperation operation = context.CreateTextureOperation(
Instruction.TextureSample,
type,
flags,
handle,
compIndex,
destOperand,
sources);
context.Add(operation);
}
}
context.Add(operation);
}
private static void EmitTmml(
@@ -951,7 +943,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
flags,
handle,
compIndex ^ 1, // The instruction component order is the inverse of GLSL's.
tempDest,
new[] { tempDest },
sources);
context.Add(operation);
@@ -1071,42 +1063,37 @@ namespace Ryujinx.Graphics.Shader.Instructions
}
Operand[] sources = sourcesList.ToArray();
Operand[] dests = new Operand[BitOperations.PopCount((uint)componentMask)];
Operand GetDest()
int outputIndex = 0;
for (int i = 0; i < dests.Length; i++)
{
if (dest >= RegisterConsts.RegisterZeroIndex)
if (dest + i >= RegisterConsts.RegisterZeroIndex)
{
return null;
break;
}
return Register(dest++, RegisterType.Gpr);
dests[outputIndex++] = Register(dest + i, RegisterType.Gpr);
}
if (outputIndex != dests.Length)
{
Array.Resize(ref dests, outputIndex);
}
int handle = imm;
for (int compMask = componentMask, compIndex = 0; compMask != 0; compMask >>= 1, compIndex++)
{
if ((compMask & 1) != 0)
{
Operand destOperand = GetDest();
TextureOperation operation = context.CreateTextureOperation(
Instruction.TextureSample,
type,
flags,
handle,
componentMask,
dests,
sources);
if (destOperand == null)
{
break;
}
TextureOperation operation = context.CreateTextureOperation(
Instruction.TextureSample,
type,
flags,
handle,
compIndex,
destOperand,
sources);
context.Add(operation);
}
}
context.Add(operation);
}
private static void EmitTxq(
@@ -1188,7 +1175,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
flags,
imm,
compIndex,
destOperand,
new[] { destOperand },
sources);
context.Add(operation);

View File

@@ -134,6 +134,7 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
Truncate,
UnpackDouble2x32,
UnpackHalf2x16,
VectorExtract,
VoteAll,
VoteAllEqual,
VoteAny,

View File

@@ -62,18 +62,25 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
Inst = inst;
Index = index;
// The array may be modified externally, so we store a copy.
_dests = (Operand[])dests.Clone();
for (int dstIndex = 0; dstIndex < dests.Length; dstIndex++)
if (dests != null)
{
Operand dest = dests[dstIndex];
// The array may be modified externally, so we store a copy.
_dests = (Operand[])dests.Clone();
if (dest != null && dest.Type == OperandType.LocalVariable)
for (int dstIndex = 0; dstIndex < dests.Length; dstIndex++)
{
dest.AsgOp = this;
Operand dest = dests[dstIndex];
if (dest != null && dest.Type == OperandType.LocalVariable)
{
dest.AsgOp = this;
}
}
}
else
{
_dests = Array.Empty<Operand>();
}
}
public Operation(Instruction inst, Operand dest, params Operand[] sources) : this(sources)

View File

@@ -19,8 +19,8 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
int cbufSlot,
int handle,
int compIndex,
Operand dest,
Operand[] sources) : base(inst, compIndex, dest, sources)
Operand[] dests,
Operand[] sources) : base(inst, compIndex, dests, sources)
{
Type = type;
Format = format;
@@ -36,8 +36,8 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
TextureFlags flags,
int handle,
int compIndex,
Operand dest,
Operand[] sources) : this(inst, type, format, flags, DefaultCbufSlot, handle, compIndex, dest, sources)
Operand[] dests,
Operand[] sources) : this(inst, type, format, flags, DefaultCbufSlot, handle, compIndex, dests, sources)
{
}

View File

@@ -1,4 +1,4 @@
using Ryujinx.Graphics.Shader.StructuredIr;
using Ryujinx.Graphics.Shader.Translation;
using System;
namespace Ryujinx.Graphics.Shader
@@ -66,7 +66,7 @@ namespace Ryujinx.Graphics.Shader
return typeName;
}
public static string ToGlslImageType(this SamplerType type, VariableType componentType)
public static string ToGlslImageType(this SamplerType type, AggregateType componentType)
{
string typeName = (type & SamplerType.Mask) switch
{
@@ -90,8 +90,8 @@ namespace Ryujinx.Graphics.Shader
switch (componentType)
{
case VariableType.U32: typeName = 'u' + typeName; break;
case VariableType.S32: typeName = 'i' + typeName; break;
case AggregateType.U32: typeName = 'u' + typeName; break;
case AggregateType.S32: typeName = 'i' + typeName; break;
}
return typeName;

View File

@@ -1,4 +1,5 @@
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
using Ryujinx.Graphics.Shader.Translation;
namespace Ryujinx.Graphics.Shader.StructuredIr
{
@@ -46,7 +47,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
return new AstOperand(OperandType.Constant, value);
}
public static AstOperand Local(VariableType type)
public static AstOperand Local(AggregateType type)
{
AstOperand local = new AstOperand(OperandType.LocalVariable);

View File

@@ -1,4 +1,5 @@
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
using Ryujinx.Graphics.Shader.Translation;
using System.Collections.Generic;
namespace Ryujinx.Graphics.Shader.StructuredIr
@@ -10,7 +11,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
public OperandType Type { get; }
public VariableType VarType { get; set; }
public AggregateType VarType { get; set; }
public int Value { get; }
@@ -22,7 +23,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
Defs = new HashSet<IAstNode>();
Uses = new HashSet<IAstNode>();
VarType = VariableType.S32;
VarType = AggregateType.S32;
}
public AstOperand(Operand operand) : this()

View File

@@ -1,4 +1,6 @@
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
using Ryujinx.Graphics.Shader.Translation;
using System.Numerics;
using static Ryujinx.Graphics.Shader.StructuredIr.AstHelper;
@@ -56,5 +58,21 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
_sources[index] = source;
}
public AggregateType GetVectorType(AggregateType scalarType)
{
int componentsCount = BitOperations.PopCount((uint)Index);
AggregateType type = scalarType;
switch (componentsCount)
{
case 2: type |= AggregateType.Vector2; break;
case 3: type |= AggregateType.Vector3; break;
case 4: type |= AggregateType.Vector4; break;
}
return type;
}
}
}

View File

@@ -1,4 +1,5 @@
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
using Ryujinx.Graphics.Shader.Translation;
using System;
namespace Ryujinx.Graphics.Shader.StructuredIr
@@ -7,11 +8,11 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
{
private readonly struct InstInfo
{
public VariableType DestType { get; }
public AggregateType DestType { get; }
public VariableType[] SrcTypes { get; }
public AggregateType[] SrcTypes { get; }
public InstInfo(VariableType destType, params VariableType[] srcTypes)
public InstInfo(AggregateType destType, params AggregateType[] srcTypes)
{
DestType = destType;
SrcTypes = srcTypes;
@@ -24,176 +25,173 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
{
_infoTbl = new InstInfo[(int)Instruction.Count];
// Inst Destination type Source 1 type Source 2 type Source 3 type Source 4 type
Add(Instruction.AtomicAdd, VariableType.U32, VariableType.S32, VariableType.S32, VariableType.U32);
Add(Instruction.AtomicAnd, VariableType.U32, VariableType.S32, VariableType.S32, VariableType.U32);
Add(Instruction.AtomicCompareAndSwap, VariableType.U32, VariableType.S32, VariableType.S32, VariableType.U32, VariableType.U32);
Add(Instruction.AtomicMaxS32, VariableType.S32, VariableType.S32, VariableType.S32, VariableType.S32);
Add(Instruction.AtomicMaxU32, VariableType.U32, VariableType.S32, VariableType.S32, VariableType.U32);
Add(Instruction.AtomicMinS32, VariableType.S32, VariableType.S32, VariableType.S32, VariableType.S32);
Add(Instruction.AtomicMinU32, VariableType.U32, VariableType.S32, VariableType.S32, VariableType.U32);
Add(Instruction.AtomicOr, VariableType.U32, VariableType.S32, VariableType.S32, VariableType.U32);
Add(Instruction.AtomicSwap, VariableType.U32, VariableType.S32, VariableType.S32, VariableType.U32);
Add(Instruction.AtomicXor, VariableType.U32, VariableType.S32, VariableType.S32, VariableType.U32);
Add(Instruction.Absolute, VariableType.Scalar, VariableType.Scalar);
Add(Instruction.Add, VariableType.Scalar, VariableType.Scalar, VariableType.Scalar);
Add(Instruction.Ballot, VariableType.U32, VariableType.Bool);
Add(Instruction.BitCount, VariableType.Int, VariableType.Int);
Add(Instruction.BitfieldExtractS32, VariableType.S32, VariableType.S32, VariableType.S32, VariableType.S32);
Add(Instruction.BitfieldExtractU32, VariableType.U32, VariableType.U32, VariableType.S32, VariableType.S32);
Add(Instruction.BitfieldInsert, VariableType.Int, VariableType.Int, VariableType.Int, VariableType.S32, VariableType.S32);
Add(Instruction.BitfieldReverse, VariableType.Int, VariableType.Int);
Add(Instruction.BitwiseAnd, VariableType.Int, VariableType.Int, VariableType.Int);
Add(Instruction.BitwiseExclusiveOr, VariableType.Int, VariableType.Int, VariableType.Int);
Add(Instruction.BitwiseNot, VariableType.Int, VariableType.Int);
Add(Instruction.BitwiseOr, VariableType.Int, VariableType.Int, VariableType.Int);
Add(Instruction.BranchIfTrue, VariableType.None, VariableType.Bool);
Add(Instruction.BranchIfFalse, VariableType.None, VariableType.Bool);
Add(Instruction.Call, VariableType.Scalar);
Add(Instruction.Ceiling, VariableType.Scalar, VariableType.Scalar, VariableType.Scalar);
Add(Instruction.Clamp, VariableType.Scalar, VariableType.Scalar, VariableType.Scalar, VariableType.Scalar);
Add(Instruction.ClampU32, VariableType.U32, VariableType.U32, VariableType.U32, VariableType.U32);
Add(Instruction.CompareEqual, VariableType.Bool, VariableType.Scalar, VariableType.Scalar);
Add(Instruction.CompareGreater, VariableType.Bool, VariableType.Scalar, VariableType.Scalar);
Add(Instruction.CompareGreaterOrEqual, VariableType.Bool, VariableType.Scalar, VariableType.Scalar);
Add(Instruction.CompareGreaterOrEqualU32, VariableType.Bool, VariableType.U32, VariableType.U32);
Add(Instruction.CompareGreaterU32, VariableType.Bool, VariableType.U32, VariableType.U32);
Add(Instruction.CompareLess, VariableType.Bool, VariableType.Scalar, VariableType.Scalar);
Add(Instruction.CompareLessOrEqual, VariableType.Bool, VariableType.Scalar, VariableType.Scalar);
Add(Instruction.CompareLessOrEqualU32, VariableType.Bool, VariableType.U32, VariableType.U32);
Add(Instruction.CompareLessU32, VariableType.Bool, VariableType.U32, VariableType.U32);
Add(Instruction.CompareNotEqual, VariableType.Bool, VariableType.Scalar, VariableType.Scalar);
Add(Instruction.ConditionalSelect, VariableType.Scalar, VariableType.Bool, VariableType.Scalar, VariableType.Scalar);
Add(Instruction.ConvertFP32ToFP64, VariableType.F64, VariableType.F32);
Add(Instruction.ConvertFP64ToFP32, VariableType.F32, VariableType.F64);
Add(Instruction.ConvertFP32ToS32, VariableType.S32, VariableType.F32);
Add(Instruction.ConvertFP32ToU32, VariableType.U32, VariableType.F32);
Add(Instruction.ConvertFP64ToS32, VariableType.S32, VariableType.F64);
Add(Instruction.ConvertFP64ToU32, VariableType.U32, VariableType.F64);
Add(Instruction.ConvertS32ToFP32, VariableType.F32, VariableType.S32);
Add(Instruction.ConvertS32ToFP64, VariableType.F64, VariableType.S32);
Add(Instruction.ConvertU32ToFP32, VariableType.F32, VariableType.U32);
Add(Instruction.ConvertU32ToFP64, VariableType.F64, VariableType.U32);
Add(Instruction.Cosine, VariableType.Scalar, VariableType.Scalar);
Add(Instruction.Ddx, VariableType.F32, VariableType.F32);
Add(Instruction.Ddy, VariableType.F32, VariableType.F32);
Add(Instruction.Divide, VariableType.Scalar, VariableType.Scalar, VariableType.Scalar);
Add(Instruction.ExponentB2, VariableType.Scalar, VariableType.Scalar);
Add(Instruction.FindLSB, VariableType.Int, VariableType.Int);
Add(Instruction.FindMSBS32, VariableType.S32, VariableType.S32);
Add(Instruction.FindMSBU32, VariableType.S32, VariableType.U32);
Add(Instruction.Floor, VariableType.Scalar, VariableType.Scalar);
Add(Instruction.FusedMultiplyAdd, VariableType.Scalar, VariableType.Scalar, VariableType.Scalar, VariableType.Scalar);
Add(Instruction.ImageLoad, VariableType.F32);
Add(Instruction.ImageStore, VariableType.None);
Add(Instruction.ImageAtomic, VariableType.S32);
Add(Instruction.IsNan, VariableType.Bool, VariableType.Scalar);
Add(Instruction.LoadAttribute, VariableType.F32, VariableType.S32, VariableType.S32, VariableType.S32);
Add(Instruction.LoadConstant, VariableType.F32, VariableType.S32, VariableType.S32);
Add(Instruction.LoadGlobal, VariableType.U32, VariableType.S32, VariableType.S32);
Add(Instruction.LoadLocal, VariableType.U32, VariableType.S32);
Add(Instruction.LoadShared, VariableType.U32, VariableType.S32);
Add(Instruction.LoadStorage, VariableType.U32, VariableType.S32, VariableType.S32);
Add(Instruction.Lod, VariableType.F32);
Add(Instruction.LogarithmB2, VariableType.Scalar, VariableType.Scalar);
Add(Instruction.LogicalAnd, VariableType.Bool, VariableType.Bool, VariableType.Bool);
Add(Instruction.LogicalExclusiveOr, VariableType.Bool, VariableType.Bool, VariableType.Bool);
Add(Instruction.LogicalNot, VariableType.Bool, VariableType.Bool);
Add(Instruction.LogicalOr, VariableType.Bool, VariableType.Bool, VariableType.Bool);
Add(Instruction.Maximum, VariableType.Scalar, VariableType.Scalar, VariableType.Scalar);
Add(Instruction.MaximumU32, VariableType.U32, VariableType.U32, VariableType.U32);
Add(Instruction.Minimum, VariableType.Scalar, VariableType.Scalar, VariableType.Scalar);
Add(Instruction.MinimumU32, VariableType.U32, VariableType.U32, VariableType.U32);
Add(Instruction.Multiply, VariableType.Scalar, VariableType.Scalar, VariableType.Scalar);
Add(Instruction.MultiplyHighS32, VariableType.S32, VariableType.S32, VariableType.S32);
Add(Instruction.MultiplyHighU32, VariableType.U32, VariableType.U32, VariableType.U32);
Add(Instruction.Negate, VariableType.Scalar, VariableType.Scalar);
Add(Instruction.PackDouble2x32, VariableType.F64, VariableType.U32, VariableType.U32);
Add(Instruction.PackHalf2x16, VariableType.U32, VariableType.F32, VariableType.F32);
Add(Instruction.ReciprocalSquareRoot, VariableType.Scalar, VariableType.Scalar);
Add(Instruction.Round, VariableType.Scalar, VariableType.Scalar);
Add(Instruction.ShiftLeft, VariableType.Int, VariableType.Int, VariableType.Int);
Add(Instruction.ShiftRightS32, VariableType.S32, VariableType.S32, VariableType.Int);
Add(Instruction.ShiftRightU32, VariableType.U32, VariableType.U32, VariableType.Int);
Add(Instruction.Shuffle, VariableType.F32, VariableType.F32, VariableType.U32, VariableType.U32, VariableType.Bool);
Add(Instruction.ShuffleDown, VariableType.F32, VariableType.F32, VariableType.U32, VariableType.U32, VariableType.Bool);
Add(Instruction.ShuffleUp, VariableType.F32, VariableType.F32, VariableType.U32, VariableType.U32, VariableType.Bool);
Add(Instruction.ShuffleXor, VariableType.F32, VariableType.F32, VariableType.U32, VariableType.U32, VariableType.Bool);
Add(Instruction.Sine, VariableType.Scalar, VariableType.Scalar);
Add(Instruction.SquareRoot, VariableType.Scalar, VariableType.Scalar);
Add(Instruction.StoreAttribute, VariableType.None, VariableType.S32, VariableType.S32, VariableType.F32);
Add(Instruction.StoreGlobal, VariableType.None, VariableType.S32, VariableType.S32, VariableType.U32);
Add(Instruction.StoreLocal, VariableType.None, VariableType.S32, VariableType.U32);
Add(Instruction.StoreShared, VariableType.None, VariableType.S32, VariableType.U32);
Add(Instruction.StoreShared16, VariableType.None, VariableType.S32, VariableType.U32);
Add(Instruction.StoreShared8, VariableType.None, VariableType.S32, VariableType.U32);
Add(Instruction.StoreStorage, VariableType.None, VariableType.S32, VariableType.S32, VariableType.U32);
Add(Instruction.StoreStorage16, VariableType.None, VariableType.S32, VariableType.S32, VariableType.U32);
Add(Instruction.StoreStorage8, VariableType.None, VariableType.S32, VariableType.S32, VariableType.U32);
Add(Instruction.Subtract, VariableType.Scalar, VariableType.Scalar, VariableType.Scalar);
Add(Instruction.SwizzleAdd, VariableType.F32, VariableType.F32, VariableType.F32, VariableType.S32);
Add(Instruction.TextureSample, VariableType.F32);
Add(Instruction.TextureSize, VariableType.S32, VariableType.S32, VariableType.S32);
Add(Instruction.Truncate, VariableType.Scalar, VariableType.Scalar);
Add(Instruction.UnpackDouble2x32, VariableType.U32, VariableType.F64);
Add(Instruction.UnpackHalf2x16, VariableType.F32, VariableType.U32);
Add(Instruction.VoteAll, VariableType.Bool, VariableType.Bool);
Add(Instruction.VoteAllEqual, VariableType.Bool, VariableType.Bool);
Add(Instruction.VoteAny, VariableType.Bool, VariableType.Bool);
// Inst Destination type Source 1 type Source 2 type Source 3 type Source 4 type
Add(Instruction.AtomicAdd, AggregateType.U32, AggregateType.S32, AggregateType.S32, AggregateType.U32);
Add(Instruction.AtomicAnd, AggregateType.U32, AggregateType.S32, AggregateType.S32, AggregateType.U32);
Add(Instruction.AtomicCompareAndSwap, AggregateType.U32, AggregateType.S32, AggregateType.S32, AggregateType.U32, AggregateType.U32);
Add(Instruction.AtomicMaxS32, AggregateType.S32, AggregateType.S32, AggregateType.S32, AggregateType.S32);
Add(Instruction.AtomicMaxU32, AggregateType.U32, AggregateType.S32, AggregateType.S32, AggregateType.U32);
Add(Instruction.AtomicMinS32, AggregateType.S32, AggregateType.S32, AggregateType.S32, AggregateType.S32);
Add(Instruction.AtomicMinU32, AggregateType.U32, AggregateType.S32, AggregateType.S32, AggregateType.U32);
Add(Instruction.AtomicOr, AggregateType.U32, AggregateType.S32, AggregateType.S32, AggregateType.U32);
Add(Instruction.AtomicSwap, AggregateType.U32, AggregateType.S32, AggregateType.S32, AggregateType.U32);
Add(Instruction.AtomicXor, AggregateType.U32, AggregateType.S32, AggregateType.S32, AggregateType.U32);
Add(Instruction.Absolute, AggregateType.Scalar, AggregateType.Scalar);
Add(Instruction.Add, AggregateType.Scalar, AggregateType.Scalar, AggregateType.Scalar);
Add(Instruction.Ballot, AggregateType.U32, AggregateType.Bool);
Add(Instruction.BitCount, AggregateType.S32, AggregateType.S32);
Add(Instruction.BitfieldExtractS32, AggregateType.S32, AggregateType.S32, AggregateType.S32, AggregateType.S32);
Add(Instruction.BitfieldExtractU32, AggregateType.U32, AggregateType.U32, AggregateType.S32, AggregateType.S32);
Add(Instruction.BitfieldInsert, AggregateType.S32, AggregateType.S32, AggregateType.S32, AggregateType.S32, AggregateType.S32);
Add(Instruction.BitfieldReverse, AggregateType.S32, AggregateType.S32);
Add(Instruction.BitwiseAnd, AggregateType.S32, AggregateType.S32, AggregateType.S32);
Add(Instruction.BitwiseExclusiveOr, AggregateType.S32, AggregateType.S32, AggregateType.S32);
Add(Instruction.BitwiseNot, AggregateType.S32, AggregateType.S32);
Add(Instruction.BitwiseOr, AggregateType.S32, AggregateType.S32, AggregateType.S32);
Add(Instruction.BranchIfTrue, AggregateType.Void, AggregateType.Bool);
Add(Instruction.BranchIfFalse, AggregateType.Void, AggregateType.Bool);
Add(Instruction.Call, AggregateType.Scalar);
Add(Instruction.Ceiling, AggregateType.Scalar, AggregateType.Scalar, AggregateType.Scalar);
Add(Instruction.Clamp, AggregateType.Scalar, AggregateType.Scalar, AggregateType.Scalar, AggregateType.Scalar);
Add(Instruction.ClampU32, AggregateType.U32, AggregateType.U32, AggregateType.U32, AggregateType.U32);
Add(Instruction.CompareEqual, AggregateType.Bool, AggregateType.Scalar, AggregateType.Scalar);
Add(Instruction.CompareGreater, AggregateType.Bool, AggregateType.Scalar, AggregateType.Scalar);
Add(Instruction.CompareGreaterOrEqual, AggregateType.Bool, AggregateType.Scalar, AggregateType.Scalar);
Add(Instruction.CompareGreaterOrEqualU32, AggregateType.Bool, AggregateType.U32, AggregateType.U32);
Add(Instruction.CompareGreaterU32, AggregateType.Bool, AggregateType.U32, AggregateType.U32);
Add(Instruction.CompareLess, AggregateType.Bool, AggregateType.Scalar, AggregateType.Scalar);
Add(Instruction.CompareLessOrEqual, AggregateType.Bool, AggregateType.Scalar, AggregateType.Scalar);
Add(Instruction.CompareLessOrEqualU32, AggregateType.Bool, AggregateType.U32, AggregateType.U32);
Add(Instruction.CompareLessU32, AggregateType.Bool, AggregateType.U32, AggregateType.U32);
Add(Instruction.CompareNotEqual, AggregateType.Bool, AggregateType.Scalar, AggregateType.Scalar);
Add(Instruction.ConditionalSelect, AggregateType.Scalar, AggregateType.Bool, AggregateType.Scalar, AggregateType.Scalar);
Add(Instruction.ConvertFP32ToFP64, AggregateType.FP64, AggregateType.FP32);
Add(Instruction.ConvertFP64ToFP32, AggregateType.FP32, AggregateType.FP64);
Add(Instruction.ConvertFP32ToS32, AggregateType.S32, AggregateType.FP32);
Add(Instruction.ConvertFP32ToU32, AggregateType.U32, AggregateType.FP32);
Add(Instruction.ConvertFP64ToS32, AggregateType.S32, AggregateType.FP64);
Add(Instruction.ConvertFP64ToU32, AggregateType.U32, AggregateType.FP64);
Add(Instruction.ConvertS32ToFP32, AggregateType.FP32, AggregateType.S32);
Add(Instruction.ConvertS32ToFP64, AggregateType.FP64, AggregateType.S32);
Add(Instruction.ConvertU32ToFP32, AggregateType.FP32, AggregateType.U32);
Add(Instruction.ConvertU32ToFP64, AggregateType.FP64, AggregateType.U32);
Add(Instruction.Cosine, AggregateType.Scalar, AggregateType.Scalar);
Add(Instruction.Ddx, AggregateType.FP32, AggregateType.FP32);
Add(Instruction.Ddy, AggregateType.FP32, AggregateType.FP32);
Add(Instruction.Divide, AggregateType.Scalar, AggregateType.Scalar, AggregateType.Scalar);
Add(Instruction.ExponentB2, AggregateType.Scalar, AggregateType.Scalar);
Add(Instruction.FindLSB, AggregateType.S32, AggregateType.S32);
Add(Instruction.FindMSBS32, AggregateType.S32, AggregateType.S32);
Add(Instruction.FindMSBU32, AggregateType.S32, AggregateType.U32);
Add(Instruction.Floor, AggregateType.Scalar, AggregateType.Scalar);
Add(Instruction.FusedMultiplyAdd, AggregateType.Scalar, AggregateType.Scalar, AggregateType.Scalar, AggregateType.Scalar);
Add(Instruction.ImageLoad, AggregateType.FP32);
Add(Instruction.ImageStore, AggregateType.Void);
Add(Instruction.ImageAtomic, AggregateType.S32);
Add(Instruction.IsNan, AggregateType.Bool, AggregateType.Scalar);
Add(Instruction.LoadAttribute, AggregateType.FP32, AggregateType.S32, AggregateType.S32, AggregateType.S32);
Add(Instruction.LoadConstant, AggregateType.FP32, AggregateType.S32, AggregateType.S32);
Add(Instruction.LoadGlobal, AggregateType.U32, AggregateType.S32, AggregateType.S32);
Add(Instruction.LoadLocal, AggregateType.U32, AggregateType.S32);
Add(Instruction.LoadShared, AggregateType.U32, AggregateType.S32);
Add(Instruction.LoadStorage, AggregateType.U32, AggregateType.S32, AggregateType.S32);
Add(Instruction.Lod, AggregateType.FP32);
Add(Instruction.LogarithmB2, AggregateType.Scalar, AggregateType.Scalar);
Add(Instruction.LogicalAnd, AggregateType.Bool, AggregateType.Bool, AggregateType.Bool);
Add(Instruction.LogicalExclusiveOr, AggregateType.Bool, AggregateType.Bool, AggregateType.Bool);
Add(Instruction.LogicalNot, AggregateType.Bool, AggregateType.Bool);
Add(Instruction.LogicalOr, AggregateType.Bool, AggregateType.Bool, AggregateType.Bool);
Add(Instruction.Maximum, AggregateType.Scalar, AggregateType.Scalar, AggregateType.Scalar);
Add(Instruction.MaximumU32, AggregateType.U32, AggregateType.U32, AggregateType.U32);
Add(Instruction.Minimum, AggregateType.Scalar, AggregateType.Scalar, AggregateType.Scalar);
Add(Instruction.MinimumU32, AggregateType.U32, AggregateType.U32, AggregateType.U32);
Add(Instruction.Multiply, AggregateType.Scalar, AggregateType.Scalar, AggregateType.Scalar);
Add(Instruction.MultiplyHighS32, AggregateType.S32, AggregateType.S32, AggregateType.S32);
Add(Instruction.MultiplyHighU32, AggregateType.U32, AggregateType.U32, AggregateType.U32);
Add(Instruction.Negate, AggregateType.Scalar, AggregateType.Scalar);
Add(Instruction.PackDouble2x32, AggregateType.FP64, AggregateType.U32, AggregateType.U32);
Add(Instruction.PackHalf2x16, AggregateType.U32, AggregateType.FP32, AggregateType.FP32);
Add(Instruction.ReciprocalSquareRoot, AggregateType.Scalar, AggregateType.Scalar);
Add(Instruction.Round, AggregateType.Scalar, AggregateType.Scalar);
Add(Instruction.ShiftLeft, AggregateType.S32, AggregateType.S32, AggregateType.S32);
Add(Instruction.ShiftRightS32, AggregateType.S32, AggregateType.S32, AggregateType.S32);
Add(Instruction.ShiftRightU32, AggregateType.U32, AggregateType.U32, AggregateType.S32);
Add(Instruction.Shuffle, AggregateType.FP32, AggregateType.FP32, AggregateType.U32, AggregateType.U32, AggregateType.Bool);
Add(Instruction.ShuffleDown, AggregateType.FP32, AggregateType.FP32, AggregateType.U32, AggregateType.U32, AggregateType.Bool);
Add(Instruction.ShuffleUp, AggregateType.FP32, AggregateType.FP32, AggregateType.U32, AggregateType.U32, AggregateType.Bool);
Add(Instruction.ShuffleXor, AggregateType.FP32, AggregateType.FP32, AggregateType.U32, AggregateType.U32, AggregateType.Bool);
Add(Instruction.Sine, AggregateType.Scalar, AggregateType.Scalar);
Add(Instruction.SquareRoot, AggregateType.Scalar, AggregateType.Scalar);
Add(Instruction.StoreAttribute, AggregateType.Void, AggregateType.S32, AggregateType.S32, AggregateType.FP32);
Add(Instruction.StoreGlobal, AggregateType.Void, AggregateType.S32, AggregateType.S32, AggregateType.U32);
Add(Instruction.StoreLocal, AggregateType.Void, AggregateType.S32, AggregateType.U32);
Add(Instruction.StoreShared, AggregateType.Void, AggregateType.S32, AggregateType.U32);
Add(Instruction.StoreShared16, AggregateType.Void, AggregateType.S32, AggregateType.U32);
Add(Instruction.StoreShared8, AggregateType.Void, AggregateType.S32, AggregateType.U32);
Add(Instruction.StoreStorage, AggregateType.Void, AggregateType.S32, AggregateType.S32, AggregateType.U32);
Add(Instruction.StoreStorage16, AggregateType.Void, AggregateType.S32, AggregateType.S32, AggregateType.U32);
Add(Instruction.StoreStorage8, AggregateType.Void, AggregateType.S32, AggregateType.S32, AggregateType.U32);
Add(Instruction.Subtract, AggregateType.Scalar, AggregateType.Scalar, AggregateType.Scalar);
Add(Instruction.SwizzleAdd, AggregateType.FP32, AggregateType.FP32, AggregateType.FP32, AggregateType.S32);
Add(Instruction.TextureSample, AggregateType.FP32);
Add(Instruction.TextureSize, AggregateType.S32, AggregateType.S32, AggregateType.S32);
Add(Instruction.Truncate, AggregateType.Scalar, AggregateType.Scalar);
Add(Instruction.UnpackDouble2x32, AggregateType.U32, AggregateType.FP64);
Add(Instruction.UnpackHalf2x16, AggregateType.FP32, AggregateType.U32);
Add(Instruction.VectorExtract, AggregateType.Scalar, AggregateType.Vector4, AggregateType.S32);
Add(Instruction.VoteAll, AggregateType.Bool, AggregateType.Bool);
Add(Instruction.VoteAllEqual, AggregateType.Bool, AggregateType.Bool);
Add(Instruction.VoteAny, AggregateType.Bool, AggregateType.Bool);
}
private static void Add(Instruction inst, VariableType destType, params VariableType[] srcTypes)
private static void Add(Instruction inst, AggregateType destType, params AggregateType[] srcTypes)
{
_infoTbl[(int)inst] = new InstInfo(destType, srcTypes);
}
public static VariableType GetDestVarType(Instruction inst)
public static AggregateType GetDestVarType(Instruction inst)
{
return GetFinalVarType(_infoTbl[(int)(inst & Instruction.Mask)].DestType, inst);
}
public static VariableType GetSrcVarType(Instruction inst, int index)
public static AggregateType GetSrcVarType(Instruction inst, int index)
{
// TODO: Return correct type depending on source index,
// that can improve the decompiler output.
if (inst == Instruction.ImageLoad ||
inst == Instruction.ImageStore ||
if (inst == Instruction.ImageLoad ||
inst == Instruction.ImageStore ||
inst == Instruction.ImageAtomic ||
inst == Instruction.Lod ||
inst == Instruction.Lod ||
inst == Instruction.TextureSample)
{
return VariableType.F32;
return AggregateType.FP32;
}
else if (inst == Instruction.Call)
{
return VariableType.S32;
return AggregateType.S32;
}
return GetFinalVarType(_infoTbl[(int)(inst & Instruction.Mask)].SrcTypes[index], inst);
}
private static VariableType GetFinalVarType(VariableType type, Instruction inst)
private static AggregateType GetFinalVarType(AggregateType type, Instruction inst)
{
if (type == VariableType.Scalar)
if (type == AggregateType.Scalar)
{
if ((inst & Instruction.FP32) != 0)
{
return VariableType.F32;
return AggregateType.FP32;
}
else if ((inst & Instruction.FP64) != 0)
{
return VariableType.F64;
return AggregateType.FP64;
}
else
{
return VariableType.S32;
return AggregateType.S32;
}
}
else if (type == VariableType.Int)
{
return VariableType.S32;
}
else if (type == VariableType.None)
else if (type == AggregateType.Void)
{
throw new ArgumentException($"Invalid operand for instruction \"{inst}\".");
}

View File

@@ -1,11 +1,12 @@
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
using Ryujinx.Graphics.Shader.Translation;
using System;
namespace Ryujinx.Graphics.Shader.StructuredIr
{
static class OperandInfo
{
public static VariableType GetVarType(AstOperand operand)
public static AggregateType GetVarType(AstOperand operand)
{
if (operand.Type == OperandType.LocalVariable)
{
@@ -17,16 +18,16 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
}
}
public static VariableType GetVarType(OperandType type)
public static AggregateType GetVarType(OperandType type)
{
return type switch
{
OperandType.Argument => VariableType.S32,
OperandType.Attribute => VariableType.F32,
OperandType.AttributePerPatch => VariableType.F32,
OperandType.Constant => VariableType.S32,
OperandType.ConstantBuffer => VariableType.F32,
OperandType.Undefined => VariableType.S32,
OperandType.Argument => AggregateType.S32,
OperandType.Attribute => AggregateType.FP32,
OperandType.AttributePerPatch => AggregateType.FP32,
OperandType.Constant => AggregateType.S32,
OperandType.ConstantBuffer => AggregateType.FP32,
OperandType.Undefined => AggregateType.S32,
_ => throw new ArgumentException($"Invalid operand type \"{type}\".")
};
}

View File

@@ -1,3 +1,4 @@
using Ryujinx.Graphics.Shader.Translation;
using System.Collections.Generic;
namespace Ryujinx.Graphics.Shader.StructuredIr
@@ -8,19 +9,19 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
public string Name { get; }
public VariableType ReturnType { get; }
public AggregateType ReturnType { get; }
public VariableType[] InArguments { get; }
public VariableType[] OutArguments { get; }
public AggregateType[] InArguments { get; }
public AggregateType[] OutArguments { get; }
public HashSet<AstOperand> Locals { get; }
public StructuredFunction(
AstBlock mainBlock,
string name,
VariableType returnType,
VariableType[] inArguments,
VariableType[] outArguments)
AggregateType returnType,
AggregateType[] inArguments,
AggregateType[] outArguments)
{
MainBlock = mainBlock;
Name = name;
@@ -31,7 +32,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
Locals = new HashSet<AstOperand>();
}
public VariableType GetArgumentType(int index)
public AggregateType GetArgumentType(int index)
{
return index >= InArguments.Length
? OutArguments[index - InArguments.Length]

View File

@@ -2,6 +2,7 @@ using Ryujinx.Graphics.Shader.IntermediateRepresentation;
using Ryujinx.Graphics.Shader.Translation;
using System;
using System.Collections.Generic;
using System.Numerics;
namespace Ryujinx.Graphics.Shader.StructuredIr
{
@@ -17,19 +18,19 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
BasicBlock[] blocks = function.Blocks;
VariableType returnType = function.ReturnsValue ? VariableType.S32 : VariableType.None;
AggregateType returnType = function.ReturnsValue ? AggregateType.S32 : AggregateType.Void;
VariableType[] inArguments = new VariableType[function.InArgumentsCount];
VariableType[] outArguments = new VariableType[function.OutArgumentsCount];
AggregateType[] inArguments = new AggregateType[function.InArgumentsCount];
AggregateType[] outArguments = new AggregateType[function.OutArgumentsCount];
for (int i = 0; i < inArguments.Length; i++)
{
inArguments[i] = VariableType.S32;
inArguments[i] = AggregateType.S32;
}
for (int i = 0; i < outArguments.Length; i++)
{
outArguments[i] = VariableType.S32;
outArguments[i] = AggregateType.S32;
}
context.EnterFunction(blocks.Length, function.Name, returnType, inArguments, outArguments);
@@ -109,8 +110,10 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
}
}
bool vectorDest = IsVectorDestInst(inst);
int sourcesCount = operation.SourcesCount;
int outDestsCount = operation.DestsCount != 0 ? operation.DestsCount - 1 : 0;
int outDestsCount = operation.DestsCount != 0 && !vectorDest ? operation.DestsCount - 1 : 0;
IAstNode[] sources = new IAstNode[sourcesCount + outDestsCount];
@@ -141,7 +144,52 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
sources);
}
if (operation.Dest != null)
int componentsCount = BitOperations.PopCount((uint)operation.Index);
if (vectorDest && componentsCount > 1)
{
AggregateType destType = InstructionInfo.GetDestVarType(inst);
IAstNode source;
if (operation is TextureOperation texOp)
{
if (texOp.Inst == Instruction.ImageLoad)
{
destType = texOp.Format.GetComponentType();
}
source = GetAstTextureOperation(texOp);
}
else
{
source = new AstOperation(inst, operation.Index, sources, operation.SourcesCount);
}
AggregateType destElemType = destType;
switch (componentsCount)
{
case 2: destType |= AggregateType.Vector2; break;
case 3: destType |= AggregateType.Vector3; break;
case 4: destType |= AggregateType.Vector4; break;
}
AstOperand destVec = context.NewTemp(destType);
context.AddNode(new AstAssignment(destVec, source));
for (int i = 0; i < operation.DestsCount; i++)
{
AstOperand dest = context.GetOperandDef(operation.GetDest(i));
AstOperand index = new AstOperand(OperandType.Constant, i);
dest.VarType = destElemType;
context.AddNode(new AstAssignment(dest, new AstOperation(Instruction.VectorExtract, new[] { destVec, index }, 2)));
}
}
else if (operation.Dest != null)
{
AstOperand dest = context.GetOperandDef(operation.Dest);
@@ -149,7 +197,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
// logical operations, rather than forcing a cast to int and doing
// a bitwise operation with the value, as it is likely to be used as
// a bool in the end.
if (IsBitwiseInst(inst) && AreAllSourceTypesEqual(sources, VariableType.Bool))
if (IsBitwiseInst(inst) && AreAllSourceTypesEqual(sources, AggregateType.Bool))
{
inst = GetLogicalFromBitwiseInst(inst);
}
@@ -159,9 +207,9 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
if (isCondSel || isCopy)
{
VariableType type = GetVarTypeFromUses(operation.Dest);
AggregateType type = GetVarTypeFromUses(operation.Dest);
if (isCondSel && type == VariableType.F32)
if (isCondSel && type == AggregateType.FP32)
{
inst |= Instruction.FP32;
}
@@ -259,7 +307,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
}
}
private static VariableType GetVarTypeFromUses(Operand dest)
private static AggregateType GetVarTypeFromUses(Operand dest)
{
HashSet<Operand> visited = new HashSet<Operand>();
@@ -315,10 +363,10 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
}
}
return VariableType.S32;
return AggregateType.S32;
}
private static bool AreAllSourceTypesEqual(IAstNode[] sources, VariableType type)
private static bool AreAllSourceTypesEqual(IAstNode[] sources, AggregateType type)
{
foreach (IAstNode node in sources)
{
@@ -336,6 +384,16 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
return true;
}
private static bool IsVectorDestInst(Instruction inst)
{
return inst switch
{
Instruction.ImageLoad or
Instruction.TextureSample => true,
_ => false
};
}
private static bool IsBranchInst(Instruction inst)
{
return inst switch

View File

@@ -80,9 +80,9 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
public void EnterFunction(
int blocksCount,
string name,
VariableType returnType,
VariableType[] inArguments,
VariableType[] outArguments)
AggregateType returnType,
AggregateType[] inArguments,
AggregateType[] outArguments)
{
_loopTails = new HashSet<BasicBlock>();
@@ -218,7 +218,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
return gotoTempAsg;
}
AstOperand gotoTemp = NewTemp(VariableType.Bool);
AstOperand gotoTemp = NewTemp(AggregateType.Bool);
gotoTempAsg = Assign(gotoTemp, Const(IrConsts.False));
@@ -306,7 +306,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
return _gotos.ToArray();
}
private AstOperand NewTemp(VariableType type)
public AstOperand NewTemp(AggregateType type)
{
AstOperand newTemp = Local(type);

View File

@@ -1,14 +0,0 @@
namespace Ryujinx.Graphics.Shader.StructuredIr
{
enum VariableType
{
None,
Bool,
Scalar,
Int,
F32,
F64,
S32,
U32
}
}

View File

@@ -1,4 +1,4 @@
using Ryujinx.Graphics.Shader.StructuredIr;
using Ryujinx.Graphics.Shader.Translation;
namespace Ryujinx.Graphics.Shader
{
@@ -95,7 +95,7 @@ namespace Ryujinx.Graphics.Shader
};
}
public static VariableType GetComponentType(this TextureFormat format)
public static AggregateType GetComponentType(this TextureFormat format)
{
switch (format)
{
@@ -109,7 +109,7 @@ namespace Ryujinx.Graphics.Shader
case TextureFormat.R16G16B16A16Uint:
case TextureFormat.R32G32B32A32Uint:
case TextureFormat.R10G10B10A2Uint:
return VariableType.U32;
return AggregateType.U32;
case TextureFormat.R8Sint:
case TextureFormat.R16Sint:
case TextureFormat.R32Sint:
@@ -119,10 +119,10 @@ namespace Ryujinx.Graphics.Shader
case TextureFormat.R8G8B8A8Sint:
case TextureFormat.R16G16B16A16Sint:
case TextureFormat.R32G32B32A32Sint:
return VariableType.S32;
return AggregateType.S32;
}
return VariableType.F32;
return AggregateType.FP32;
}
}
}

View File

@@ -12,7 +12,14 @@
ElementTypeMask = 0xff,
Vector = 1 << 8,
Array = 1 << 9
ElementCountShift = 8,
ElementCountMask = 3 << ElementCountShift,
Scalar = 0 << ElementCountShift,
Vector2 = 1 << ElementCountShift,
Vector3 = 2 << ElementCountShift,
Vector4 = 3 << ElementCountShift,
Array = 1 << 10
}
}

View File

@@ -9,22 +9,22 @@ namespace Ryujinx.Graphics.Shader.Translation
{ AttributeConsts.Layer, new AttributeInfo(AttributeConsts.Layer, 0, 1, AggregateType.S32) },
{ AttributeConsts.ViewportIndex, new AttributeInfo(AttributeConsts.ViewportIndex, 0, 1, AggregateType.S32) },
{ AttributeConsts.PointSize, new AttributeInfo(AttributeConsts.PointSize, 0, 1, AggregateType.FP32) },
{ AttributeConsts.PositionX, new AttributeInfo(AttributeConsts.PositionX, 0, 4, AggregateType.Vector | AggregateType.FP32) },
{ AttributeConsts.PositionY, new AttributeInfo(AttributeConsts.PositionX, 1, 4, AggregateType.Vector | AggregateType.FP32) },
{ AttributeConsts.PositionZ, new AttributeInfo(AttributeConsts.PositionX, 2, 4, AggregateType.Vector | AggregateType.FP32) },
{ AttributeConsts.PositionW, new AttributeInfo(AttributeConsts.PositionX, 3, 4, AggregateType.Vector | AggregateType.FP32) },
{ AttributeConsts.ClipDistance0, new AttributeInfo(AttributeConsts.ClipDistance0, 0, 8, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.ClipDistance1, new AttributeInfo(AttributeConsts.ClipDistance0, 1, 8, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.ClipDistance2, new AttributeInfo(AttributeConsts.ClipDistance0, 2, 8, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.ClipDistance3, new AttributeInfo(AttributeConsts.ClipDistance0, 3, 8, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.ClipDistance4, new AttributeInfo(AttributeConsts.ClipDistance0, 4, 8, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.ClipDistance5, new AttributeInfo(AttributeConsts.ClipDistance0, 5, 8, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.ClipDistance6, new AttributeInfo(AttributeConsts.ClipDistance0, 6, 8, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.ClipDistance7, new AttributeInfo(AttributeConsts.ClipDistance0, 7, 8, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.PointCoordX, new AttributeInfo(AttributeConsts.PointCoordX, 0, 2, AggregateType.Vector | AggregateType.FP32) },
{ AttributeConsts.PointCoordY, new AttributeInfo(AttributeConsts.PointCoordX, 1, 2, AggregateType.Vector | AggregateType.FP32) },
{ AttributeConsts.TessCoordX, new AttributeInfo(AttributeConsts.TessCoordX, 0, 3, AggregateType.Vector | AggregateType.FP32) },
{ AttributeConsts.TessCoordY, new AttributeInfo(AttributeConsts.TessCoordX, 1, 3, AggregateType.Vector | AggregateType.FP32) },
{ AttributeConsts.PositionX, new AttributeInfo(AttributeConsts.PositionX, 0, 4, AggregateType.Vector4 | AggregateType.FP32) },
{ AttributeConsts.PositionY, new AttributeInfo(AttributeConsts.PositionX, 1, 4, AggregateType.Vector4 | AggregateType.FP32) },
{ AttributeConsts.PositionZ, new AttributeInfo(AttributeConsts.PositionX, 2, 4, AggregateType.Vector4 | AggregateType.FP32) },
{ AttributeConsts.PositionW, new AttributeInfo(AttributeConsts.PositionX, 3, 4, AggregateType.Vector4 | AggregateType.FP32) },
{ AttributeConsts.ClipDistance0, new AttributeInfo(AttributeConsts.ClipDistance0, 0, 8, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.ClipDistance1, new AttributeInfo(AttributeConsts.ClipDistance0, 1, 8, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.ClipDistance2, new AttributeInfo(AttributeConsts.ClipDistance0, 2, 8, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.ClipDistance3, new AttributeInfo(AttributeConsts.ClipDistance0, 3, 8, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.ClipDistance4, new AttributeInfo(AttributeConsts.ClipDistance0, 4, 8, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.ClipDistance5, new AttributeInfo(AttributeConsts.ClipDistance0, 5, 8, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.ClipDistance6, new AttributeInfo(AttributeConsts.ClipDistance0, 6, 8, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.ClipDistance7, new AttributeInfo(AttributeConsts.ClipDistance0, 7, 8, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.PointCoordX, new AttributeInfo(AttributeConsts.PointCoordX, 0, 2, AggregateType.Vector4 | AggregateType.FP32) },
{ AttributeConsts.PointCoordY, new AttributeInfo(AttributeConsts.PointCoordX, 1, 2, AggregateType.Vector4 | AggregateType.FP32) },
{ AttributeConsts.TessCoordX, new AttributeInfo(AttributeConsts.TessCoordX, 0, 3, AggregateType.Vector4 | AggregateType.FP32) },
{ AttributeConsts.TessCoordY, new AttributeInfo(AttributeConsts.TessCoordX, 1, 3, AggregateType.Vector4 | AggregateType.FP32) },
{ AttributeConsts.InstanceId, new AttributeInfo(AttributeConsts.InstanceId, 0, 1, AggregateType.S32) },
{ AttributeConsts.VertexId, new AttributeInfo(AttributeConsts.VertexId, 0, 1, AggregateType.S32) },
{ AttributeConsts.BaseInstance, new AttributeInfo(AttributeConsts.BaseInstance, 0, 1, AggregateType.S32) },
@@ -37,21 +37,21 @@ namespace Ryujinx.Graphics.Shader.Translation
// Special.
{ AttributeConsts.FragmentOutputDepth, new AttributeInfo(AttributeConsts.FragmentOutputDepth, 0, 1, AggregateType.FP32) },
{ AttributeConsts.ThreadKill, new AttributeInfo(AttributeConsts.ThreadKill, 0, 1, AggregateType.Bool) },
{ AttributeConsts.ThreadIdX, new AttributeInfo(AttributeConsts.ThreadIdX, 0, 3, AggregateType.Vector | AggregateType.U32) },
{ AttributeConsts.ThreadIdY, new AttributeInfo(AttributeConsts.ThreadIdX, 1, 3, AggregateType.Vector | AggregateType.U32) },
{ AttributeConsts.ThreadIdZ, new AttributeInfo(AttributeConsts.ThreadIdX, 2, 3, AggregateType.Vector | AggregateType.U32) },
{ AttributeConsts.CtaIdX, new AttributeInfo(AttributeConsts.CtaIdX, 0, 3, AggregateType.Vector | AggregateType.U32) },
{ AttributeConsts.CtaIdY, new AttributeInfo(AttributeConsts.CtaIdX, 1, 3, AggregateType.Vector | AggregateType.U32) },
{ AttributeConsts.CtaIdZ, new AttributeInfo(AttributeConsts.CtaIdX, 2, 3, AggregateType.Vector | AggregateType.U32) },
{ AttributeConsts.ThreadIdX, new AttributeInfo(AttributeConsts.ThreadIdX, 0, 3, AggregateType.Vector3 | AggregateType.U32) },
{ AttributeConsts.ThreadIdY, new AttributeInfo(AttributeConsts.ThreadIdX, 1, 3, AggregateType.Vector3 | AggregateType.U32) },
{ AttributeConsts.ThreadIdZ, new AttributeInfo(AttributeConsts.ThreadIdX, 2, 3, AggregateType.Vector3 | AggregateType.U32) },
{ AttributeConsts.CtaIdX, new AttributeInfo(AttributeConsts.CtaIdX, 0, 3, AggregateType.Vector3 | AggregateType.U32) },
{ AttributeConsts.CtaIdY, new AttributeInfo(AttributeConsts.CtaIdX, 1, 3, AggregateType.Vector3 | AggregateType.U32) },
{ AttributeConsts.CtaIdZ, new AttributeInfo(AttributeConsts.CtaIdX, 2, 3, AggregateType.Vector3 | AggregateType.U32) },
{ AttributeConsts.LaneId, new AttributeInfo(AttributeConsts.LaneId, 0, 1, AggregateType.U32) },
{ AttributeConsts.InvocationId, new AttributeInfo(AttributeConsts.InvocationId, 0, 1, AggregateType.S32) },
{ AttributeConsts.PrimitiveId, new AttributeInfo(AttributeConsts.PrimitiveId, 0, 1, AggregateType.S32) },
{ AttributeConsts.PatchVerticesIn, new AttributeInfo(AttributeConsts.PatchVerticesIn, 0, 1, AggregateType.S32) },
{ AttributeConsts.EqMask, new AttributeInfo(AttributeConsts.EqMask, 0, 4, AggregateType.Vector | AggregateType.U32) },
{ AttributeConsts.GeMask, new AttributeInfo(AttributeConsts.GeMask, 0, 4, AggregateType.Vector | AggregateType.U32) },
{ AttributeConsts.GtMask, new AttributeInfo(AttributeConsts.GtMask, 0, 4, AggregateType.Vector | AggregateType.U32) },
{ AttributeConsts.LeMask, new AttributeInfo(AttributeConsts.LeMask, 0, 4, AggregateType.Vector | AggregateType.U32) },
{ AttributeConsts.LtMask, new AttributeInfo(AttributeConsts.LtMask, 0, 4, AggregateType.Vector | AggregateType.U32) },
{ AttributeConsts.EqMask, new AttributeInfo(AttributeConsts.EqMask, 0, 4, AggregateType.Vector4 | AggregateType.U32) },
{ AttributeConsts.GeMask, new AttributeInfo(AttributeConsts.GeMask, 0, 4, AggregateType.Vector4 | AggregateType.U32) },
{ AttributeConsts.GtMask, new AttributeInfo(AttributeConsts.GtMask, 0, 4, AggregateType.Vector4 | AggregateType.U32) },
{ AttributeConsts.LeMask, new AttributeInfo(AttributeConsts.LeMask, 0, 4, AggregateType.Vector4 | AggregateType.U32) },
{ AttributeConsts.LtMask, new AttributeInfo(AttributeConsts.LtMask, 0, 4, AggregateType.Vector4 | AggregateType.U32) },
};
private static readonly Dictionary<int, AttributeInfo> _builtInAttributesPerPatch = new Dictionary<int, AttributeInfo>()
@@ -124,11 +124,11 @@ namespace Ryujinx.Graphics.Shader.Translation
elemType = AggregateType.FP32;
}
return new AttributeInfo(value & ~0xf, (value >> 2) & 3, 4, AggregateType.Vector | elemType, false);
return new AttributeInfo(value & ~0xf, (value >> 2) & 3, 4, AggregateType.Vector4 | elemType, false);
}
else if (value >= AttributeConsts.FragmentOutputColorBase && value < AttributeConsts.FragmentOutputColorEnd)
{
return new AttributeInfo(value & ~0xf, (value >> 2) & 3, 4, AggregateType.Vector | AggregateType.FP32, false);
return new AttributeInfo(value & ~0xf, (value >> 2) & 3, 4, AggregateType.Vector4 | AggregateType.FP32, false);
}
else if (value == AttributeConsts.SupportBlockViewInverseX || value == AttributeConsts.SupportBlockViewInverseY)
{
@@ -149,7 +149,7 @@ namespace Ryujinx.Graphics.Shader.Translation
if (value >= AttributeConsts.UserAttributePerPatchBase && value < AttributeConsts.UserAttributePerPatchEnd)
{
int offset = (value - AttributeConsts.UserAttributePerPatchBase) & 0xf;
return new AttributeInfo(value - offset, offset >> 2, 4, AggregateType.Vector | AggregateType.FP32, false);
return new AttributeInfo(value - offset, offset >> 2, 4, AggregateType.Vector4 | AggregateType.FP32, false);
}
else if (_builtInAttributesPerPatch.TryGetValue(value, out AttributeInfo info))
{

View File

@@ -109,10 +109,10 @@ namespace Ryujinx.Graphics.Shader.Translation
TextureFlags flags,
int handle,
int compIndex,
Operand dest,
Operand[] dests,
params Operand[] sources)
{
return CreateTextureOperation(inst, type, TextureFormat.Unknown, flags, handle, compIndex, dest, sources);
return CreateTextureOperation(inst, type, TextureFormat.Unknown, flags, handle, compIndex, dests, sources);
}
public TextureOperation CreateTextureOperation(
@@ -122,7 +122,7 @@ namespace Ryujinx.Graphics.Shader.Translation
TextureFlags flags,
int handle,
int compIndex,
Operand dest,
Operand[] dests,
params Operand[] sources)
{
if (!flags.HasFlag(TextureFlags.Bindless))
@@ -130,7 +130,7 @@ namespace Ryujinx.Graphics.Shader.Translation
Config.SetUsedTexture(inst, type, format, flags, TextureOperation.DefaultCbufSlot, handle);
}
return new TextureOperation(inst, type, format, flags, handle, compIndex, dest, sources);
return new TextureOperation(inst, type, format, flags, handle, compIndex, dests, sources);
}
public void FlagAttributeRead(int attribute)

View File

@@ -385,15 +385,6 @@ namespace Ryujinx.Graphics.Shader.Translation
int componentIndex = texOp.Index;
Operand Int(Operand value)
{
Operand res = Local();
node.List.AddBefore(node, new Operation(Instruction.ConvertFP32ToS32, res, value));
return res;
}
Operand Float(Operand value)
{
Operand res = Local();
@@ -436,7 +427,7 @@ namespace Ryujinx.Graphics.Shader.Translation
texOp.CbufSlot,
texOp.Handle,
index,
coordSize,
new[] { coordSize },
texSizeSources));
config.SetUsedTexture(Instruction.TextureSize, texOp.Type, texOp.Format, texOp.Flags, texOp.CbufSlot, texOp.Handle);
@@ -451,80 +442,53 @@ namespace Ryujinx.Graphics.Shader.Translation
}
}
Operand[] dests = new Operand[texOp.DestsCount];
for (int i = 0; i < texOp.DestsCount; i++)
{
dests[i] = texOp.GetDest(i);
}
Operand bindlessHandle = isBindless || isIndexed ? sources[0] : null;
LinkedListNode<INode> oldNode = node;
// Technically, non-constant texture offsets are not allowed (according to the spec),
// however some GPUs does support that.
// For GPUs where it is not supported, we can replace the instruction with the following:
// For texture*Offset, we replace it by texture*, and add the offset to the P coords.
// The offset can be calculated as offset / textureSize(lod), where lod = textureQueryLod(coords).
// For texelFetchOffset, we replace it by texelFetch and add the offset to the P coords directly.
// For textureGatherOffset, we take advantage of the fact that the operation is already broken down
// to read the 4 pixels separately, and just replace it with 4 textureGather with a different offset
// for each pixel.
if (hasInvalidOffset)
// For textureGatherOffset, we split the operation into up to 4 operations, one for each component
// that is accessed, where each textureGather operation has a different offset for each pixel.
if (hasInvalidOffset && isGather && !isShadow)
{
if (intCoords)
config.SetUsedFeature(FeatureFlags.IntegerSampling);
Operand[] newSources = new Operand[sources.Length];
sources.CopyTo(newSources, 0);
Operand[] texSizes = InsertTextureSize(node, texOp, lodSources, bindlessHandle, coordsCount);
int destIndex = 0;
for (int compIndex = 0; compIndex < 4; compIndex++)
{
for (int index = 0; index < coordsCount; index++)
if (((texOp.Index >> compIndex) & 1) == 0)
{
Operand source = sources[coordsIndex + index];
Operand coordPlusOffset = Local();
node.List.AddBefore(node, new Operation(Instruction.Add, coordPlusOffset, source, offsets[index]));
sources[coordsIndex + index] = coordPlusOffset;
continue;
}
}
else
{
config.SetUsedFeature(FeatureFlags.IntegerSampling);
Operand lod = Local();
node.List.AddBefore(node, new TextureOperation(
Instruction.Lod,
texOp.Type,
texOp.Format,
texOp.Flags,
texOp.CbufSlot,
texOp.Handle,
0,
lod,
lodSources));
for (int index = 0; index < coordsCount; index++)
{
Operand coordSize = Local();
Operand[] texSizeSources;
if (isBindless || isIndexed)
{
texSizeSources = new Operand[] { sources[0], Int(lod) };
}
else
{
texSizeSources = new Operand[] { Int(lod) };
}
node.List.AddBefore(node, new TextureOperation(
Instruction.TextureSize,
texOp.Type,
texOp.Format,
texOp.Flags,
texOp.CbufSlot,
texOp.Handle,
index,
coordSize,
texSizeSources));
config.SetUsedTexture(Instruction.TextureSize, texOp.Type, texOp.Format, texOp.Flags, texOp.CbufSlot, texOp.Handle);
Operand offset = Local();
Operand intOffset = offsets[index + (hasOffsets ? texOp.Index * coordsCount : 0)];
Operand intOffset = offsets[index + (hasOffsets ? compIndex * coordsCount : 0)];
node.List.AddBefore(node, new Operation(Instruction.FP32 | Instruction.Divide, offset, Float(intOffset), Float(coordSize)));
node.List.AddBefore(node, new Operation(Instruction.FP32 | Instruction.Divide, offset, Float(intOffset), Float(texSizes[index])));
Operand source = sources[coordsIndex + index];
@@ -532,45 +496,152 @@ namespace Ryujinx.Graphics.Shader.Translation
node.List.AddBefore(node, new Operation(Instruction.FP32 | Instruction.Add, coordPlusOffset, source, offset));
sources[coordsIndex + index] = coordPlusOffset;
newSources[coordsIndex + index] = coordPlusOffset;
}
TextureOperation newTexOp = new TextureOperation(
Instruction.TextureSample,
texOp.Type,
texOp.Format,
texOp.Flags & ~(TextureFlags.Offset | TextureFlags.Offsets),
texOp.CbufSlot,
texOp.Handle,
1,
new[] { dests[destIndex++] },
newSources);
node = node.List.AddBefore(node, newTexOp);
}
}
else
{
if (hasInvalidOffset)
{
if (intCoords)
{
for (int index = 0; index < coordsCount; index++)
{
Operand source = sources[coordsIndex + index];
Operand coordPlusOffset = Local();
node.List.AddBefore(node, new Operation(Instruction.Add, coordPlusOffset, source, offsets[index]));
sources[coordsIndex + index] = coordPlusOffset;
}
}
else
{
config.SetUsedFeature(FeatureFlags.IntegerSampling);
Operand[] texSizes = InsertTextureSize(node, texOp, lodSources, bindlessHandle, coordsCount);
for (int index = 0; index < coordsCount; index++)
{
config.SetUsedTexture(Instruction.TextureSize, texOp.Type, texOp.Format, texOp.Flags, texOp.CbufSlot, texOp.Handle);
Operand offset = Local();
Operand intOffset = offsets[index];
node.List.AddBefore(node, new Operation(Instruction.FP32 | Instruction.Divide, offset, Float(intOffset), Float(texSizes[index])));
Operand source = sources[coordsIndex + index];
Operand coordPlusOffset = Local();
node.List.AddBefore(node, new Operation(Instruction.FP32 | Instruction.Add, coordPlusOffset, source, offset));
sources[coordsIndex + index] = coordPlusOffset;
}
}
}
if (isGather && !isShadow)
{
Operand gatherComponent = sources[dstIndex - 1];
TextureOperation newTexOp = new TextureOperation(
Instruction.TextureSample,
texOp.Type,
texOp.Format,
texOp.Flags & ~(TextureFlags.Offset | TextureFlags.Offsets),
texOp.CbufSlot,
texOp.Handle,
componentIndex,
dests,
sources);
Debug.Assert(gatherComponent.Type == OperandType.Constant);
componentIndex = gatherComponent.Value;
}
node = node.List.AddBefore(node, newTexOp);
}
TextureOperation newTexOp = new TextureOperation(
Instruction.TextureSample,
texOp.Type,
texOp.Format,
texOp.Flags & ~(TextureFlags.Offset | TextureFlags.Offsets),
texOp.CbufSlot,
texOp.Handle,
componentIndex,
texOp.Dest,
sources);
node.List.Remove(oldNode);
for (int index = 0; index < texOp.SourcesCount; index++)
{
texOp.SetSource(index, null);
}
LinkedListNode<INode> oldNode = node;
node = node.List.AddBefore(node, newTexOp);
node.List.Remove(oldNode);
return node;
}
private static Operand[] InsertTextureSize(
LinkedListNode<INode> node,
TextureOperation texOp,
Operand[] lodSources,
Operand bindlessHandle,
int coordsCount)
{
Operand Int(Operand value)
{
Operand res = Local();
node.List.AddBefore(node, new Operation(Instruction.ConvertFP32ToS32, res, value));
return res;
}
Operand[] texSizes = new Operand[coordsCount];
Operand lod = Local();
node.List.AddBefore(node, new TextureOperation(
Instruction.Lod,
texOp.Type,
texOp.Format,
texOp.Flags,
texOp.CbufSlot,
texOp.Handle,
0,
new[] { lod },
lodSources));
for (int index = 0; index < coordsCount; index++)
{
texSizes[index] = Local();
Operand[] texSizeSources;
if (bindlessHandle != null)
{
texSizeSources = new Operand[] { bindlessHandle, Int(lod) };
}
else
{
texSizeSources = new Operand[] { Int(lod) };
}
node.List.AddBefore(node, new TextureOperation(
Instruction.TextureSize,
texOp.Type,
texOp.Format,
texOp.Flags,
texOp.CbufSlot,
texOp.Handle,
index,
new[] { texSizes[index] },
texSizeSources));
}
return texSizes;
}
private static LinkedListNode<INode> InsertSnormNormalization(LinkedListNode<INode> node, ShaderConfig config)
{
TextureOperation texOp = (TextureOperation)node.Value;
@@ -604,27 +675,32 @@ namespace Ryujinx.Graphics.Shader.Translation
// Do normalization. We assume SINT formats are being used
// as replacement for SNORM (which is not supported).
INode[] uses = texOp.Dest.UseOps.ToArray();
Operation convOp = new Operation(Instruction.ConvertS32ToFP32, Local(), texOp.Dest);
Operation normOp = new Operation(Instruction.FP32 | Instruction.Multiply, Local(), convOp.Dest, ConstF(1f / maxPositive));
node = node.List.AddAfter(node, convOp);
node = node.List.AddAfter(node, normOp);
foreach (INode useOp in uses)
for (int i = 0; i < texOp.DestsCount; i++)
{
if (useOp is not Operation op)
{
continue;
}
Operand dest = texOp.GetDest(i);
// Replace all uses of the texture pixel value with the normalized value.
for (int index = 0; index < op.SourcesCount; index++)
INode[] uses = dest.UseOps.ToArray();
Operation convOp = new Operation(Instruction.ConvertS32ToFP32, Local(), dest);
Operation normOp = new Operation(Instruction.FP32 | Instruction.Multiply, Local(), convOp.Dest, ConstF(1f / maxPositive));
node = node.List.AddAfter(node, convOp);
node = node.List.AddAfter(node, normOp);
foreach (INode useOp in uses)
{
if (op.GetSource(index) == texOp.Dest)
if (useOp is not Operation op)
{
op.SetSource(index, normOp.Dest);
continue;
}
// Replace all uses of the texture pixel value with the normalized value.
for (int index = 0; index < op.SourcesCount; index++)
{
if (op.GetSource(index) == dest)
{
op.SetSource(index, normOp.Dest);
}
}
}
}

View File

@@ -116,6 +116,22 @@ namespace Ryujinx.Graphics.Vulkan
}
}
public void AddInUseWaitable(MultiFenceHolder waitable)
{
lock (_commandBuffers)
{
for (int i = 0; i < _totalCommandBuffers; i++)
{
ref var entry = ref _commandBuffers[i];
if (entry.InUse)
{
AddWaitable(i, waitable);
}
}
}
}
public void AddDependency(int cbIndex, CommandBufferScoped dependencyCbs)
{
Debug.Assert(_commandBuffers[cbIndex].InUse);

View File

@@ -227,6 +227,7 @@ namespace Ryujinx.Graphics.Vulkan
}
CommandBuffer = (Cbs = Gd.CommandBufferPool.ReturnAndRent(Cbs)).CommandBuffer;
Gd.RegisterFlush();
// Restore per-command buffer state.

View File

@@ -11,7 +11,13 @@ namespace Ryujinx.Graphics.Vulkan
{
public ulong ID;
public MultiFenceHolder Waitable;
public ulong FlushId;
public bool Signalled;
public bool NeedsFlush(ulong currentFlushId)
{
return (long)(FlushId - currentFlushId) >= 0;
}
}
private ulong _firstHandle = 0;
@@ -19,6 +25,7 @@ namespace Ryujinx.Graphics.Vulkan
private readonly VulkanRenderer _gd;
private readonly Device _device;
private List<SyncHandle> _handles;
private ulong FlushId;
public SyncManager(VulkanRenderer gd, Device device)
{
@@ -27,17 +34,33 @@ namespace Ryujinx.Graphics.Vulkan
_handles = new List<SyncHandle>();
}
public void Create(ulong id)
public void RegisterFlush()
{
MultiFenceHolder waitable = new MultiFenceHolder();
FlushId++;
}
_gd.FlushAllCommands();
_gd.CommandBufferPool.AddWaitable(waitable);
public void Create(ulong id, bool strict)
{
ulong flushId = FlushId;
MultiFenceHolder waitable = new MultiFenceHolder();
if (strict || _gd.InterruptAction == null)
{
_gd.FlushAllCommands();
_gd.CommandBufferPool.AddWaitable(waitable);
}
else
{
// Don't flush commands, instead wait for the current command buffer to finish.
// If this sync is waited on before the command buffer is submitted, interrupt the gpu thread and flush it manually.
_gd.CommandBufferPool.AddInUseWaitable(waitable);
}
SyncHandle handle = new SyncHandle
{
ID = id,
Waitable = waitable
Waitable = waitable,
FlushId = flushId
};
lock (_handles)
@@ -107,6 +130,17 @@ namespace Ryujinx.Graphics.Vulkan
return;
}
if (result.NeedsFlush(FlushId))
{
_gd.InterruptAction(() =>
{
if (result.NeedsFlush(FlushId))
{
_gd.FlushAllCommands();
}
});
}
bool signaled = result.Signalled || result.Waitable.WaitForFences(_gd.Api, _device, 1000000000);
if (!signaled)
{
@@ -132,7 +166,7 @@ namespace Ryujinx.Graphics.Vulkan
first = _handles.FirstOrDefault();
}
if (first == null) break;
if (first == null || first.NeedsFlush(FlushId)) break;
bool signaled = first.Waitable.WaitForFences(_gd.Api, _device, 0);
if (signaled)

View File

@@ -48,6 +48,7 @@ namespace Ryujinx.Graphics.Vulkan
internal DescriptorSetManager DescriptorSetManager { get; private set; }
internal PipelineLayoutCache PipelineLayoutCache { get; private set; }
internal BackgroundResources BackgroundResources { get; private set; }
internal Action<Action> InterruptAction { get; private set; }
internal BufferManager BufferManager { get; private set; }
@@ -354,6 +355,11 @@ namespace Ryujinx.Graphics.Vulkan
_pipeline?.FlushCommandsImpl();
}
internal void RegisterFlush()
{
_syncManager.RegisterFlush();
}
public ReadOnlySpan<byte> GetBufferData(BufferHandle buffer, int offset, int size)
{
return BufferManager.GetData(buffer, offset, size);
@@ -593,9 +599,9 @@ namespace Ryujinx.Graphics.Vulkan
action();
}
public void CreateSync(ulong id)
public void CreateSync(ulong id, bool strict)
{
_syncManager.Create(id);
_syncManager.Create(id, strict);
}
public IProgram LoadProgramBinary(byte[] programBinary, bool isFragment, ShaderInfo info)
@@ -613,6 +619,11 @@ namespace Ryujinx.Graphics.Vulkan
return _syncManager.GetCurrent();
}
public void SetInterruptAction(Action<Action> interruptAction)
{
InterruptAction = interruptAction;
}
public void Screenshot()
{
_window.ScreenCaptureRequested = true;

View File

@@ -298,6 +298,18 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
return ResultCode.Success;
}
[CommandHipc(67)] //3.0.0+
// IsIlluminanceAvailable() -> bool
public ResultCode IsIlluminanceAvailable(ServiceCtx context)
{
// NOTE: This should call IsAmbientLightSensorAvailable through to Lbl, but there's no situation where we'd want false.
context.ResponseData.Write(true);
Logger.Stub?.PrintStub(LogClass.ServiceAm);
return ResultCode.Success;
}
[CommandHipc(68)]
// SetAutoSleepDisabled(u8)
public ResultCode SetAutoSleepDisabled(ServiceCtx context)
@@ -318,6 +330,19 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
return ResultCode.Success;
}
[CommandHipc(71)] //5.0.0+
// GetCurrentIlluminanceEx() -> (bool, f32)
public ResultCode GetCurrentIlluminanceEx(ServiceCtx context)
{
// TODO: The light value should be configurable - presumably users using software that takes advantage will want control.
context.ResponseData.Write(1); // OverLimit
context.ResponseData.Write(10000f); // Lux - 10K lux is ambient light.
Logger.Stub?.PrintStub(LogClass.ServiceAm);
return ResultCode.Success;
}
[CommandHipc(80)] // 4.0.0+
// SetWirelessPriorityMode(s32 wireless_priority_mode)
public ResultCode SetWirelessPriorityMode(ServiceCtx context)

View File

@@ -39,14 +39,10 @@ namespace Ryujinx.Memory.Tests
}
[Test]
// Memory aliasing tests fail on CI at the moment.
[Platform(Exclude = "MacOsX")]
public void Test_Alias()
{
if (OperatingSystem.IsMacOS())
{
// Memory aliasing tests fail on CI at the moment.
return;
}
using MemoryBlock backing = new MemoryBlock(0x10000, MemoryAllocationFlags.Mirrorable);
using MemoryBlock toAlias = new MemoryBlock(0x10000, MemoryAllocationFlags.Reserve | MemoryAllocationFlags.ViewCompatible);
@@ -58,14 +54,10 @@ namespace Ryujinx.Memory.Tests
}
[Test]
// Memory aliasing tests fail on CI at the moment.
[Platform(Exclude = "MacOsX")]
public void Test_AliasRandom()
{
if (OperatingSystem.IsMacOS())
{
// Memory aliasing tests fail on CI at the moment.
return;
}
using MemoryBlock backing = new MemoryBlock(0x80000, MemoryAllocationFlags.Mirrorable);
using MemoryBlock toAlias = new MemoryBlock(0x80000, MemoryAllocationFlags.Reserve | MemoryAllocationFlags.ViewCompatible);
@@ -94,14 +86,10 @@ namespace Ryujinx.Memory.Tests
}
[Test]
// Memory aliasing tests fail on CI at the moment.
[Platform(Exclude = "MacOsX")]
public void Test_AliasMapLeak()
{
if (OperatingSystem.IsMacOS())
{
// Memory aliasing tests fail on CI at the moment.
return;
}
ulong pageSize = 4096;
ulong size = 100000 * pageSize; // The mappings limit on Linux is usually around 65K, so let's make sure we are above that.

View File

@@ -379,7 +379,12 @@ namespace Ryujinx.Memory
/// <remarks>
/// It's an error to use the memory block after disposal.
/// </remarks>
public void Dispose() => FreeMemory();
public void Dispose()
{
FreeMemory();
GC.SuppressFinalize(this);
}
~MemoryBlock() => FreeMemory();

View File

@@ -9,6 +9,7 @@ using Ryujinx.Memory.Tests;
using Ryujinx.Memory.Tracking;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
@@ -57,14 +58,10 @@ namespace Ryujinx.Tests.Memory
}
[Test]
// Memory aliasing tests fail on CI at the moment.
[Platform(Exclude = "MacOsX")]
public void PartialUnmap([Values] bool readOnly)
{
if (OperatingSystem.IsMacOS())
{
// Memory aliasing tests fail on CI at the moment.
return;
}
// Set up an address space to test partial unmapping.
// Should register the signal handler to deal with this on Windows.
ulong vaSize = 0x100000;
@@ -78,11 +75,13 @@ namespace Ryujinx.Tests.Memory
ref var state = ref PartialUnmapState.GetRef();
Thread testThread = null;
bool shouldAccess = true;
try
{
// Globally reset the struct for handling partial unmap races.
PartialUnmapState.Reset();
bool shouldAccess = true;
bool error = false;
// Create a large mapping.
@@ -93,8 +92,6 @@ namespace Ryujinx.Tests.Memory
memory.Reprotect(0, vaSize, MemoryPermission.Read);
}
Thread testThread;
if (readOnly)
{
// Write a value to the physical memory, then try to read it repeately from virtual.
@@ -193,6 +190,10 @@ namespace Ryujinx.Tests.Memory
}
finally
{
// In case something failed, we want to ensure the test thread is dead before disposing of the memory.
shouldAccess = false;
testThread?.Join();
exceptionHandler.Dispose();
unusedMainMemory.Dispose();
memory.Dispose();
@@ -201,13 +202,10 @@ namespace Ryujinx.Tests.Memory
}
[Test]
// Memory aliasing tests fail on CI at the moment.
[Platform(Exclude = "MacOsX")]
public unsafe void PartialUnmapNative()
{
if (OperatingSystem.IsMacOS())
{
// Memory aliasing tests fail on CI at the moment.
return;
}
// Set up an address space to test partial unmapping.
// Should register the signal handler to deal with this on Windows.
@@ -284,26 +282,17 @@ namespace Ryujinx.Tests.Memory
}
[Test]
// Only test in Windows, as this is only used on Windows and uses Windows APIs for trimming.
[Platform("Win")]
[SuppressMessage("Interoperability", "CA1416:Validate platform compatibility")]
public void ThreadLocalMap()
{
if (!OperatingSystem.IsWindows())
{
// Only test in Windows, as this is only used on Windows and uses Windows APIs for trimming.
return;
}
PartialUnmapState.Reset();
ref var state = ref PartialUnmapState.GetRef();
bool running = true;
var testThread = new Thread(() =>
{
if (!OperatingSystem.IsWindows())
{
// Need this here to avoid a warning.
return;
}
PartialUnmapState.GetRef().RetryFromAccessViolation();
while (running)
{
@@ -330,14 +319,10 @@ namespace Ryujinx.Tests.Memory
}
[Test]
// Only test in Windows, as this is only used on Windows and uses Windows APIs for trimming.
[Platform("Win")]
public unsafe void ThreadLocalMapNative()
{
if (!OperatingSystem.IsWindows())
{
// Only test in Windows, as this is only used on Windows and uses Windows APIs for trimming.
return;
}
EnsureTranslator();
PartialUnmapState.Reset();
@@ -481,4 +466,4 @@ namespace Ryujinx.Tests.Memory
Assert.False(error);
}
}
}
}

View File

@@ -68,53 +68,6 @@ namespace Ryujinx.Ui.App.Common
_cancellationToken?.Cancel();
}
public IEnumerable<string> GetFilesInDirectory(string directory)
{
Stack<string> stack = new Stack<string>();
stack.Push(directory);
while (stack.Count > 0)
{
string dir = stack.Pop();
string[] content = Array.Empty<string>();
try
{
content = Directory.GetFiles(dir, "*");
}
catch (UnauthorizedAccessException)
{
Logger.Warning?.Print(LogClass.Application, $"Failed to get access to directory: \"{dir}\"");
}
if (content.Length > 0)
{
foreach (string file in content)
{
yield return file;
}
}
try
{
content = Directory.GetDirectories(dir);
}
catch (UnauthorizedAccessException)
{
Logger.Warning?.Print(LogClass.Application, $"Failed to get access to directory: \"{dir}\"");
}
if (content.Length > 0)
{
foreach (string subdir in content)
{
stack.Push(subdir);
}
}
}
}
public void ReadControlData(IFileSystem controlFs, Span<byte> outProperty)
{
using var controlFile = new UniqueRef<IFile>();
@@ -151,26 +104,28 @@ namespace Ryujinx.Ui.App.Common
continue;
}
foreach (string app in GetFilesInDirectory(appDir))
try
{
if (_cancellationToken.Token.IsCancellationRequested)
foreach (string app in Directory.EnumerateFiles(appDir, "*", SearchOption.AllDirectories))
{
return;
}
string extension = Path.GetExtension(app).ToLower();
if ((extension == ".nsp") ||
(extension == ".pfs0") ||
(extension == ".xci") ||
(extension == ".nca") ||
(extension == ".nro") ||
(extension == ".nso"))
{
applications.Add(app);
numApplicationsFound++;
if (_cancellationToken.Token.IsCancellationRequested)
{
return;
}
string extension = Path.GetExtension(app).ToLower();
if (!File.GetAttributes(app).HasFlag(FileAttributes.Hidden) && extension is ".nsp" or ".pfs0" or ".xci" or ".nca" or ".nro" or ".nso")
{
applications.Add(app);
numApplicationsFound++;
}
}
}
catch (UnauthorizedAccessException)
{
Logger.Warning?.Print(LogClass.Application, $"Failed to get access to directory: \"{appDir}\"");
}
}
// Loops through applications list, creating a struct and then firing an event containing the struct for each application

Some files were not shown because too many files have changed in this diff Show More