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="Microsoft.NET.Test.Sdk" Version="17.4.1" />
<PackageVersion Include="MsgPack.Cli" Version="1.0.1" /> <PackageVersion Include="MsgPack.Cli" Version="1.0.1" />
<PackageVersion Include="NUnit" Version="3.13.3" /> <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.Core" Version="4.7.5" />
<PackageVersion Include="OpenTK.Graphics" Version="4.7.5" /> <PackageVersion Include="OpenTK.Graphics" Version="4.7.5" />
<PackageVersion Include="OpenTK.OpenAL" 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.Net.NameResolution" Version="4.3.0" />
<PackageVersion Include="System.Threading.ThreadPool" Version="4.3.0" /> <PackageVersion Include="System.Threading.ThreadPool" Version="4.3.0" />
<PackageVersion Include="XamlNameReferenceGenerator" Version="1.5.1" /> <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> </ItemGroup>
</Project> </Project>

View File

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

View File

@@ -432,10 +432,10 @@ namespace Ryujinx.Ava
if (userError == UserError.NoFirmware) if (userError == UserError.NoFirmware)
{ {
UserResult result = await ContentDialogHelper.CreateConfirmationDialog( UserResult result = await ContentDialogHelper.CreateConfirmationDialog(
LocaleManager.Instance["DialogFirmwareNoFirmwareInstalledMessage"], LocaleManager.Instance[LocaleKeys.DialogFirmwareNoFirmwareInstalledMessage],
string.Format(LocaleManager.Instance["DialogFirmwareInstallEmbeddedMessage"], firmwareVersion.VersionString), string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallEmbeddedMessage], firmwareVersion.VersionString),
LocaleManager.Instance["InputDialogYes"], LocaleManager.Instance[LocaleKeys.InputDialogYes],
LocaleManager.Instance["InputDialogNo"], LocaleManager.Instance[LocaleKeys.InputDialogNo],
""); "");
if (result != UserResult.Yes) if (result != UserResult.Yes)
@@ -463,11 +463,11 @@ namespace Ryujinx.Ava
_parent.RefreshFirmwareStatus(); _parent.RefreshFirmwareStatus();
await ContentDialogHelper.CreateInfoDialog( await ContentDialogHelper.CreateInfoDialog(
string.Format(LocaleManager.Instance["DialogFirmwareInstalledMessage"], firmwareVersion.VersionString), string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstalledMessage], firmwareVersion.VersionString),
string.Format(LocaleManager.Instance["DialogFirmwareInstallEmbeddedSuccessMessage"], firmwareVersion.VersionString), string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallEmbeddedSuccessMessage], firmwareVersion.VersionString),
LocaleManager.Instance["InputDialogOk"], LocaleManager.Instance[LocaleKeys.InputDialogOk],
"", "",
LocaleManager.Instance["RyujinxInfo"]); LocaleManager.Instance[LocaleKeys.RyujinxInfo]);
} }
} }
else else
@@ -869,7 +869,7 @@ namespace Ryujinx.Ava
public void UpdateStatus() 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 // 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; float scale = GraphicsConfig.ResScale;
if (scale != 1) if (scale != 1)
@@ -879,11 +879,11 @@ namespace Ryujinx.Ava
StatusUpdatedEvent?.Invoke(this, new StatusUpdatedEventArgs( StatusUpdatedEvent?.Invoke(this, new StatusUpdatedEventArgs(
Device.EnableDeviceVsync, Device.EnableDeviceVsync,
LocaleManager.Instance["VolumeShort"] + $": {(int)(Device.GetVolume() * 100)}%", LocaleManager.Instance[LocaleKeys.VolumeShort] + $": {(int)(Device.GetVolume() * 100)}%",
Renderer.IsVulkan ? "Vulkan" : "OpenGL", Renderer.IsVulkan ? "Vulkan" : "OpenGL",
dockedMode, dockedMode,
ConfigurationState.Instance.Graphics.AspectRatio.Value.ToText(), 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} %", $"FIFO: {Device.Statistics.GetFifoPercent():00.00} %",
$"GPU: {_renderer.GetHardwareInfo().GpuVendor}")); $"GPU: {_renderer.GetHardwareInfo().GpuVendor}"));
} }

View File

@@ -81,7 +81,7 @@ namespace Ryujinx.Ava.Common
Dispatcher.UIThread.Post(async () => Dispatcher.UIThread.Post(async () =>
{ {
await ContentDialogHelper.CreateErrorDialog( await ContentDialogHelper.CreateErrorDialog(
string.Format(LocaleManager.Instance["DialogMessageCreateSaveErrorMessage"], result.ToStringWithName())); string.Format(LocaleManager.Instance[LocaleKeys.DialogMessageCreateSaveErrorMessage], result.ToStringWithName()));
}); });
return false; return false;
@@ -100,7 +100,7 @@ namespace Ryujinx.Ava.Common
Dispatcher.UIThread.Post(async () => 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; return false;
@@ -151,7 +151,7 @@ namespace Ryujinx.Ava.Common
public static async Task ExtractSection(NcaSectionType ncaSectionType, string titleFilePath, public static async Task ExtractSection(NcaSectionType ncaSectionType, string titleFilePath,
int programIndex = 0) int programIndex = 0)
{ {
OpenFolderDialog folderDialog = new() { Title = LocaleManager.Instance["FolderDialogExtractTitle"] }; OpenFolderDialog folderDialog = new() { Title = LocaleManager.Instance[LocaleKeys.FolderDialogExtractTitle] };
string destination = await folderDialog.ShowAsync(_owner); string destination = await folderDialog.ShowAsync(_owner);
@@ -164,11 +164,11 @@ namespace Ryujinx.Ava.Common
Dispatcher.UIThread.Post(async () => Dispatcher.UIThread.Post(async () =>
{ {
UserResult result = await ContentDialogHelper.CreateConfirmationDialog( 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[LocaleKeys.InputDialogCancel],
LocaleManager.Instance["DialogNcaExtractionTitle"]); LocaleManager.Instance[LocaleKeys.DialogNcaExtractionTitle]);
if (result == UserResult.Cancel) if (result == UserResult.Cancel)
{ {
@@ -234,7 +234,7 @@ namespace Ryujinx.Ava.Common
"Extraction failure. The main NCA was not present in the selected file"); "Extraction failure. The main NCA was not present in the selected file");
Dispatcher.UIThread.InvokeAsync(async () => Dispatcher.UIThread.InvokeAsync(async () =>
{ {
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance["DialogNcaExtractionMainNcaNotFoundErrorMessage"]); await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogNcaExtractionMainNcaNotFoundErrorMessage]);
}); });
return; return;
} }
@@ -275,7 +275,7 @@ namespace Ryujinx.Ava.Common
$"LibHac returned error code: {resultCode.Value.ErrorCode}"); $"LibHac returned error code: {resultCode.Value.ErrorCode}");
Dispatcher.UIThread.InvokeAsync(async () => Dispatcher.UIThread.InvokeAsync(async () =>
{ {
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance["DialogNcaExtractionCheckLogErrorMessage"]); await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogNcaExtractionCheckLogErrorMessage]);
}); });
} }
else if (resultCode.Value.IsSuccess()) else if (resultCode.Value.IsSuccess())
@@ -283,11 +283,11 @@ namespace Ryujinx.Ava.Common
Dispatcher.UIThread.InvokeAsync(async () => Dispatcher.UIThread.InvokeAsync(async () =>
{ {
await ContentDialogHelper.CreateInfoDialog( 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 internal class LocaleExtension : MarkupExtension
{ {
public LocaleExtension(string key) public LocaleExtension(LocaleKeys key)
{ {
Key = key; Key = key;
} }
public string Key { get; } public LocaleKeys Key { get; }
public override object ProvideValue(IServiceProvider serviceProvider) public override object ProvideValue(IServiceProvider serviceProvider)
{ {
string keyToUse = Key; LocaleKeys keyToUse = Key;
ReflectionBindingExtension binding = new($"[{keyToUse}]") ReflectionBindingExtension binding = new($"[{keyToUse}]")
{ {

View File

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

View File

@@ -56,7 +56,7 @@ namespace Ryujinx.Ava.Input
return null; 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) 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!"); Logger.Error?.Print(LogClass.Application, "Failed to convert the current Ryujinx version!");
Dispatcher.UIThread.Post(async () => 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; return;
@@ -119,7 +119,7 @@ namespace Ryujinx.Modules
{ {
Dispatcher.UIThread.Post(async () => 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 () => 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); Logger.Error?.Print(LogClass.Application, exception.Message);
Dispatcher.UIThread.Post(async () => Dispatcher.UIThread.Post(async () =>
{ {
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance["DialogUpdaterFailedToGetVersionMessage"]); await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterFailedToGetVersionMessage]);
}); });
return; return;
@@ -165,7 +165,7 @@ namespace Ryujinx.Modules
Logger.Error?.Print(LogClass.Application, "Failed to convert the received Ryujinx version from Github!"); Logger.Error?.Print(LogClass.Application, "Failed to convert the received Ryujinx version from Github!");
Dispatcher.UIThread.Post(async () => 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; return;
@@ -177,7 +177,7 @@ namespace Ryujinx.Modules
{ {
Dispatcher.UIThread.Post(async () => 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 () => Dispatcher.UIThread.Post(async () =>
{ {
// Show a message asking the user if they want to update // Show a message asking the user if they want to update
var shouldUpdate = await ContentDialogHelper.CreateChoiceDialog(LocaleManager.Instance["RyujinxUpdater"], var shouldUpdate = await ContentDialogHelper.CreateChoiceDialog(LocaleManager.Instance[LocaleKeys.RyujinxUpdater],
LocaleManager.Instance["RyujinxUpdaterMessage"], LocaleManager.Instance[LocaleKeys.RyujinxUpdaterMessage],
$"{Program.Version} -> {newVersion}"); $"{Program.Version} -> {newVersion}");
if (shouldUpdate) if (shouldUpdate)
@@ -247,8 +247,8 @@ namespace Ryujinx.Modules
var taskDialog = new TaskDialog() var taskDialog = new TaskDialog()
{ {
Header = LocaleManager.Instance["RyujinxUpdater"], Header = LocaleManager.Instance[LocaleKeys.RyujinxUpdater],
SubHeader = LocaleManager.Instance["UpdaterDownloading"], SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterDownloading],
IconSource = new SymbolIconSource { Symbol = Symbol.Download }, IconSource = new SymbolIconSource { Symbol = Symbol.Download },
Buttons = { }, Buttons = { },
ShowProgressBar = true ShowProgressBar = true
@@ -272,9 +272,9 @@ namespace Ryujinx.Modules
if (UpdateSuccessful) if (UpdateSuccessful)
{ {
var shouldRestart = await ContentDialogHelper.CreateChoiceDialog(LocaleManager.Instance["RyujinxUpdater"], var shouldRestart = await ContentDialogHelper.CreateChoiceDialog(LocaleManager.Instance[LocaleKeys.RyujinxUpdater],
LocaleManager.Instance["DialogUpdaterCompleteMessage"], LocaleManager.Instance[LocaleKeys.DialogUpdaterCompleteMessage],
LocaleManager.Instance["DialogUpdaterRestartMessage"]); LocaleManager.Instance[LocaleKeys.DialogUpdaterRestartMessage]);
if (shouldRestart) if (shouldRestart)
{ {
@@ -478,7 +478,7 @@ namespace Ryujinx.Modules
private static async void InstallUpdate(TaskDialog taskDialog, string updateFile) private static async void InstallUpdate(TaskDialog taskDialog, string updateFile)
{ {
// Extract Update // Extract Update
taskDialog.SubHeader = LocaleManager.Instance["UpdaterExtracting"]; taskDialog.SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterExtracting];
taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal); taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal);
if (OperatingSystem.IsLinux()) if (OperatingSystem.IsLinux())
@@ -556,7 +556,7 @@ namespace Ryujinx.Modules
List<string> allFiles = EnumerateFilesToDelete().ToList(); List<string> allFiles = EnumerateFilesToDelete().ToList();
taskDialog.SubHeader = LocaleManager.Instance["UpdaterRenaming"]; taskDialog.SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterRenaming];
taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal); taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal);
// Replace old files // Replace old files
@@ -577,13 +577,13 @@ namespace Ryujinx.Modules
} }
catch 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(() => Dispatcher.UIThread.Post(() =>
{ {
taskDialog.SubHeader = LocaleManager.Instance["UpdaterAddingFiles"]; taskDialog.SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterAddingFiles];
taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal); taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal);
}); });
@@ -607,8 +607,8 @@ namespace Ryujinx.Modules
{ {
if (showWarnings) if (showWarnings)
{ {
ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance["DialogUpdaterArchNotSupportedMessage"], ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterArchNotSupportedMessage],
LocaleManager.Instance["DialogUpdaterArchNotSupportedSubMessage"]); LocaleManager.Instance[LocaleKeys.DialogUpdaterArchNotSupportedSubMessage]);
} }
return false; return false;
@@ -618,8 +618,8 @@ namespace Ryujinx.Modules
{ {
if (showWarnings) if (showWarnings)
{ {
ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance["DialogUpdaterNoInternetMessage"], ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterNoInternetMessage],
LocaleManager.Instance["DialogUpdaterNoInternetSubMessage"]); LocaleManager.Instance[LocaleKeys.DialogUpdaterNoInternetSubMessage]);
} }
return false; return false;
@@ -629,8 +629,8 @@ namespace Ryujinx.Modules
{ {
if (showWarnings) if (showWarnings)
{ {
ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance["DialogUpdaterDirtyBuildMessage"], ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterDirtyBuildMessage],
LocaleManager.Instance["DialogUpdaterDirtyBuildSubMessage"]); LocaleManager.Instance[LocaleKeys.DialogUpdaterDirtyBuildSubMessage]);
} }
return false; return false;
@@ -642,11 +642,11 @@ namespace Ryujinx.Modules
{ {
if (ReleaseInformations.IsFlatHubBuild()) if (ReleaseInformations.IsFlatHubBuild())
{ {
ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance["UpdaterDisabledWarningTitle"], LocaleManager.Instance["DialogUpdaterFlatpakNotSupportedMessage"]); ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance[LocaleKeys.UpdaterDisabledWarningTitle], LocaleManager.Instance[LocaleKeys.DialogUpdaterFlatpakNotSupportedMessage]);
} }
else 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.OpenGL\Ryujinx.Graphics.OpenGL.csproj" />
<ProjectReference Include="..\Ryujinx.Graphics.Gpu\Ryujinx.Graphics.Gpu.csproj" /> <ProjectReference Include="..\Ryujinx.Graphics.Gpu\Ryujinx.Graphics.Gpu.csproj" />
<ProjectReference Include="..\Ryujinx.Ui.Common\Ryujinx.Ui.Common.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>
<ItemGroup> <ItemGroup>
@@ -158,4 +159,7 @@
<EmbeddedResource Include="Assets\Locales\zh_TW.json" /> <EmbeddedResource Include="Assets\Locales\zh_TW.json" />
<EmbeddedResource Include="Assets\Styles\Styles.xaml" /> <EmbeddedResource Include="Assets\Styles\Styles.xaml" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<AdditionalFiles Include="Assets\Locales\en_US.json" />
</ItemGroup>
</Project> </Project>

View File

@@ -33,15 +33,15 @@ namespace Ryujinx.Ava.UI.Applet
? args.PlayerCountMin.ToString() ? args.PlayerCountMin.ToString()
: $"{args.PlayerCountMin}-{args.PlayerCountMax}"; : $"{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], string message = string.Format(LocaleManager.Instance[key],
playerCount, playerCount,
args.SupportedStyles, args.SupportedStyles,
string.Join(", ", args.SupportedPlayers), 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) public bool DisplayMessageDialog(string title, string message)
@@ -62,9 +62,9 @@ namespace Ryujinx.Ava.UI.Applet
title, title,
message, message,
"", "",
LocaleManager.Instance["DialogOpenSettingsWindowLabel"], LocaleManager.Instance[LocaleKeys.DialogOpenSettingsWindowLabel],
"", "",
LocaleManager.Instance["SettingsButtonClose"], LocaleManager.Instance[LocaleKeys.SettingsButtonClose],
(int)Symbol.Important, (int)Symbol.Important,
deferEvent, deferEvent,
async (window) => async (window) =>
@@ -92,7 +92,7 @@ namespace Ryujinx.Ava.UI.Applet
} }
catch (Exception ex) catch (Exception ex)
{ {
await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance["DialogMessageDialogErrorExceptionMessage"], ex)); await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance[LocaleKeys.DialogMessageDialogErrorExceptionMessage], ex));
dialogCloseEvent.Set(); dialogCloseEvent.Set();
} }
@@ -115,7 +115,7 @@ namespace Ryujinx.Ava.UI.Applet
{ {
try 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) if (response.Result == UserResult.Ok)
{ {
@@ -126,7 +126,7 @@ namespace Ryujinx.Ava.UI.Applet
catch (Exception ex) catch (Exception ex)
{ {
error = true; error = true;
await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance["DialogSoftwareKeyboardErrorExceptionMessage"], ex)); await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance[LocaleKeys.DialogSoftwareKeyboardErrorExceptionMessage], ex));
} }
finally finally
{ {
@@ -181,7 +181,7 @@ namespace Ryujinx.Ava.UI.Applet
catch (Exception ex) catch (Exception ex)
{ {
dialogCloseEvent.Set(); 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 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.PrimaryButtonText = args.SubmitText;
contentDialog.IsPrimaryButtonEnabled = content._checkLength(content.Message.Length); contentDialog.IsPrimaryButtonEnabled = content._checkLength(content.Message.Length);
contentDialog.SecondaryButtonText = ""; contentDialog.SecondaryButtonText = "";
contentDialog.CloseButtonText = LocaleManager.Instance["InputDialogCancel"]; contentDialog.CloseButtonText = LocaleManager.Instance[LocaleKeys.InputDialogCancel];
contentDialog.Content = content; contentDialog.Content = content;
TypedEventHandler<ContentDialog, ContentDialogClosedEventArgs> handler = (sender, eventArgs) => TypedEventHandler<ContentDialog, ContentDialogClosedEventArgs> handler = (sender, eventArgs) =>
@@ -139,14 +139,14 @@ namespace Ryujinx.Ava.UI.Controls
else if (_inputMin > 0 && _inputMax == int.MaxValue) else if (_inputMin > 0 && _inputMax == int.MaxValue)
{ {
Error.IsVisible = true; Error.IsVisible = true;
Error.Text = string.Format(LocaleManager.Instance["SwkbdMinCharacters"], _inputMin); Error.Text = string.Format(LocaleManager.Instance[LocaleKeys.SwkbdMinCharacters], _inputMin);
_checkLength = length => _inputMin <= length; _checkLength = length => _inputMin <= length;
} }
else else
{ {
Error.IsVisible = true; 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; _checkLength = length => _inputMin <= length && length <= _inputMax;
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -344,10 +344,10 @@ namespace Ryujinx.Ava.UI.ViewModels
if (usageString.Length == 0) 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; return amiiboJsonString;
} }
await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance["DialogAmiiboApiTitle"], await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogAmiiboApiTitle],
LocaleManager.Instance["DialogAmiiboApiFailFetchMessage"], LocaleManager.Instance[LocaleKeys.DialogAmiiboApiFailFetchMessage],
LocaleManager.Instance["InputDialogOk"], LocaleManager.Instance[LocaleKeys.InputDialogOk],
"", "",
LocaleManager.Instance["RyujinxInfo"]); LocaleManager.Instance[LocaleKeys.RyujinxInfo]);
Close(); Close();
@@ -442,11 +442,11 @@ namespace Ryujinx.Ava.UI.ViewModels
private async void ShowInfoDialog() private async void ShowInfoDialog()
{ {
await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance["DialogAmiiboApiTitle"], await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogAmiiboApiTitle],
LocaleManager.Instance["DialogAmiiboApiConnectErrorMessage"], LocaleManager.Instance[LocaleKeys.DialogAmiiboApiConnectErrorMessage],
LocaleManager.Instance["InputDialogOk"], LocaleManager.Instance[LocaleKeys.InputDialogOk],
"", "",
LocaleManager.Instance["RyujinxInfo"]); LocaleManager.Instance[LocaleKeys.RyujinxInfo]);
} }
} }
} }

View File

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

View File

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

View File

@@ -73,11 +73,11 @@ namespace Ryujinx.Ava.UI.ViewModels
{ {
Dispatcher.UIThread.Post(async () => Dispatcher.UIThread.Post(async () =>
{ {
await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance["DialogSettingsBackendThreadingWarningMessage"], await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogSettingsBackendThreadingWarningMessage],
"", "",
"", "",
LocaleManager.Instance["InputDialogOk"], LocaleManager.Instance[LocaleKeys.InputDialogOk],
LocaleManager.Instance["DialogSettingsBackendThreadingWarningTitle"]); LocaleManager.Instance[LocaleKeys.DialogSettingsBackendThreadingWarningTitle]);
}); });
} }
} }
@@ -306,11 +306,11 @@ namespace Ryujinx.Ava.UI.ViewModels
{ {
var dialog = new OpenFileDialog() var dialog = new OpenFileDialog()
{ {
Title = LocaleManager.Instance["SettingsSelectThemeFileDialogTitle"], Title = LocaleManager.Instance[LocaleKeys.SettingsSelectThemeFileDialogTitle],
AllowMultiple = false 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); var file = await dialog.ShowAsync(_owner);

View File

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

View File

@@ -17,7 +17,7 @@ namespace Ryujinx.Ava.UI.Windows
{ {
if (Program.PreviewerDetached) if (Program.PreviewerDetached)
{ {
Title = $"Ryujinx {Program.Version} - " + LocaleManager.Instance["MenuBarHelpAbout"]; Title = $"Ryujinx {Program.Version} - " + LocaleManager.Instance[LocaleKeys.MenuBarHelpAbout];
} }
Version = Program.Version; Version = Program.Version;
@@ -32,7 +32,7 @@ namespace Ryujinx.Ava.UI.Windows
public string Supporters { get; set; } public string Supporters { get; set; }
public string Version { 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) private void Button_OnClick(object sender, RoutedEventArgs e)
{ {
@@ -46,7 +46,7 @@ namespace Ryujinx.Ava.UI.Windows
{ {
if (!NetworkInterface.GetIsNetworkAvailable()) if (!NetworkInterface.GetIsNetworkAvailable())
{ {
Supporters = LocaleManager.Instance["ConnectionError"]; Supporters = LocaleManager.Instance[LocaleKeys.ConnectionError];
return; return;
} }
@@ -61,7 +61,7 @@ namespace Ryujinx.Ava.UI.Windows
} }
catch catch
{ {
Supporters = LocaleManager.Instance["ApiError"]; Supporters = LocaleManager.Instance[LocaleKeys.ApiError];
} }
await Dispatcher.UIThread.InvokeAsync(() => SupportersTextBlock.Text = Supporters); await Dispatcher.UIThread.InvokeAsync(() => SupportersTextBlock.Text = Supporters);

View File

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

View File

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

View File

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

View File

@@ -43,7 +43,7 @@ namespace Ryujinx.Ava.UI.Windows
InitializeComponent(); 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) public DownloadableContentManagerWindow(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName)
@@ -73,7 +73,7 @@ namespace Ryujinx.Ava.UI.Windows
DlcDataGrid.SelectionChanged += DlcDataGrid_SelectionChanged; 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(); LoadDownloadableContents();
PrintHeading(); PrintHeading();
@@ -86,7 +86,7 @@ namespace Ryujinx.Ava.UI.Windows
private void PrintHeading() 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() private void LoadDownloadableContents()
@@ -133,7 +133,7 @@ namespace Ryujinx.Ava.UI.Windows
{ {
Dispatcher.UIThread.InvokeAsync(async () => 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) 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() OpenFileDialog dialog = new OpenFileDialog()
{ {
Title = LocaleManager.Instance["SelectDlcDialogTitle"], Title = LocaleManager.Instance[LocaleKeys.SelectDlcDialogTitle],
AllowMultiple = true AllowMultiple = true
}; };

View File

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

View File

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

View File

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

View File

@@ -29,7 +29,7 @@ namespace Ryujinx.Ava.UI.Windows
public SettingsWindow(VirtualFileSystem virtualFileSystem, ContentManager contentManager) 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); ViewModel = new SettingsViewModel(virtualFileSystem, contentManager, this);
DataContext = ViewModel; DataContext = ViewModel;

View File

@@ -43,7 +43,7 @@ namespace Ryujinx.Ava.UI.Windows
InitializeComponent(); 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) public TitleUpdateWindow(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName)
@@ -73,7 +73,7 @@ namespace Ryujinx.Ava.UI.Windows
InitializeComponent(); InitializeComponent();
Title = $"Ryujinx {Program.Version} - {LocaleManager.Instance["UpdateWindowTitle"]} - {_titleName} ({_titleId:X16})"; Title = $"Ryujinx {Program.Version} - {LocaleManager.Instance[LocaleKeys.UpdateWindowTitle]} - {_titleName} ({_titleId:X16})";
LoadUpdates(); LoadUpdates();
PrintHeading(); PrintHeading();
@@ -81,7 +81,7 @@ namespace Ryujinx.Ava.UI.Windows
private void PrintHeading() 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() private void LoadUpdates()
@@ -148,7 +148,7 @@ namespace Ryujinx.Ava.UI.Windows
{ {
Dispatcher.UIThread.Post(async () => 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 () => 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() OpenFileDialog dialog = new()
{ {
Title = LocaleManager.Instance["SelectUpdateDialogTitle"], Title = LocaleManager.Instance[LocaleKeys.SelectUpdateDialogTitle],
AllowMultiple = true AllowMultiple = true
}; };

View File

@@ -27,7 +27,7 @@ namespace Ryujinx.Common.Memory.PartialUnmaps
[SupportedOSPlatform("windows")] [SupportedOSPlatform("windows")]
[LibraryImport("kernel32.dll")] [LibraryImport("kernel32.dll")]
public static partial int GetCurrentThreadId(); private static partial int GetCurrentThreadId();
[SupportedOSPlatform("windows")] [SupportedOSPlatform("windows")]
[LibraryImport("kernel32.dll", SetLastError = true)] [LibraryImport("kernel32.dll", SetLastError = true)]
@@ -36,7 +36,7 @@ namespace Ryujinx.Common.Memory.PartialUnmaps
[SupportedOSPlatform("windows")] [SupportedOSPlatform("windows")]
[LibraryImport("kernel32.dll", SetLastError = true)] [LibraryImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs (UnmanagedType.Bool)] [return: MarshalAs (UnmanagedType.Bool)]
public static partial bool CloseHandle(IntPtr hObject); private static partial bool CloseHandle(IntPtr hObject);
[SupportedOSPlatform("windows")] [SupportedOSPlatform("windows")]
[LibraryImport("kernel32.dll", SetLastError = true)] [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); ISampler CreateSampler(SamplerCreateInfo info);
ITexture CreateTexture(TextureCreateInfo info, float scale); ITexture CreateTexture(TextureCreateInfo info, float scale);
void CreateSync(ulong id); void CreateSync(ulong id, bool strict);
void DeleteBuffer(BufferHandle buffer); void DeleteBuffer(BufferHandle buffer);
@@ -53,6 +53,8 @@ namespace Ryujinx.Graphics.GAL
void Initialize(GraphicsDebugLevel logLevel); void Initialize(GraphicsDebugLevel logLevel);
void SetInterruptAction(Action<Action> interruptAction);
void Screenshot(); void Screenshot();
} }
} }

View File

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

View File

@@ -29,6 +29,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
private int _elementSize; private int _elementSize;
private IRenderer _baseRenderer; private IRenderer _baseRenderer;
private Thread _gpuThread; private Thread _gpuThread;
private Thread _backendThread;
private bool _disposed; private bool _disposed;
private bool _running; private bool _running;
@@ -38,6 +39,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
private CircularSpanPool _spanPool; private CircularSpanPool _spanPool;
private ManualResetEventSlim _invokeRun; private ManualResetEventSlim _invokeRun;
private AutoResetEvent _interruptRun;
private bool _lastSampleCounterClear = true; private bool _lastSampleCounterClear = true;
@@ -54,6 +56,8 @@ namespace Ryujinx.Graphics.GAL.Multithreading
private int _refProducerPtr; private int _refProducerPtr;
private int _refConsumerPtr; private int _refConsumerPtr;
private Action _interruptAction;
public event EventHandler<ScreenCaptureImageInfo> ScreenCaptured; public event EventHandler<ScreenCaptureImageInfo> ScreenCaptured;
internal BufferMap Buffers { get; } internal BufferMap Buffers { get; }
@@ -73,6 +77,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
_baseRenderer = renderer; _baseRenderer = renderer;
renderer.ScreenCaptured += (sender, info) => ScreenCaptured?.Invoke(this, info); renderer.ScreenCaptured += (sender, info) => ScreenCaptured?.Invoke(this, info);
renderer.SetInterruptAction(Interrupt);
Pipeline = new ThreadedPipeline(this, renderer.Pipeline); Pipeline = new ThreadedPipeline(this, renderer.Pipeline);
Window = new ThreadedWindow(this, renderer); Window = new ThreadedWindow(this, renderer);
@@ -82,6 +87,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
_galWorkAvailable = new ManualResetEventSlim(false); _galWorkAvailable = new ManualResetEventSlim(false);
_invokeRun = new ManualResetEventSlim(); _invokeRun = new ManualResetEventSlim();
_interruptRun = new AutoResetEvent(false);
_spanPool = new CircularSpanPool(this, SpanPoolBytes); _spanPool = new CircularSpanPool(this, SpanPoolBytes);
SpanPool = _spanPool; SpanPool = _spanPool;
@@ -95,6 +101,8 @@ namespace Ryujinx.Graphics.GAL.Multithreading
{ {
_running = true; _running = true;
_backendThread = Thread.CurrentThread;
_gpuThread = new Thread(() => { _gpuThread = new Thread(() => {
gpuLoop(); gpuLoop();
_running = false; _running = false;
@@ -116,10 +124,18 @@ namespace Ryujinx.Graphics.GAL.Multithreading
_galWorkAvailable.Wait(); _galWorkAvailable.Wait();
_galWorkAvailable.Reset(); _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. // 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. // 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; int commandPtr = _consumerPtr;
@@ -281,10 +297,10 @@ namespace Ryujinx.Graphics.GAL.Multithreading
return sampler; return sampler;
} }
public void CreateSync(ulong id) public void CreateSync(ulong id, bool strict)
{ {
Sync.CreateSyncHandle(id); Sync.CreateSyncHandle(id);
New<CreateSyncCommand>().Set(id); New<CreateSyncCommand>().Set(id, strict);
QueueCommand(); QueueCommand();
} }
@@ -421,6 +437,30 @@ namespace Ryujinx.Graphics.GAL.Multithreading
_baseRenderer.WaitSync(id); _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() public void Dispose()
{ {
// Dispose must happen from the render thread, after all commands have completed. // Dispose must happen from the render thread, after all commands have completed.
@@ -440,6 +480,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
_frameComplete.Dispose(); _frameComplete.Dispose();
_galWorkAvailable.Dispose(); _galWorkAvailable.Dispose();
_invokeRun.Dispose(); _invokeRun.Dispose();
_interruptRun.Dispose();
Sync.Dispose(); Sync.Dispose();
} }

View File

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

View File

@@ -250,7 +250,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
uint syncpointId = (uint)argument & 0xFFFF; uint syncpointId = (uint)argument & 0xFFFF;
_context.AdvanceSequence(); _context.AdvanceSequence();
_context.CreateHostSyncIfNeeded(true); _context.CreateHostSyncIfNeeded(true, true);
_context.Renderer.UpdateCounters(); // Poll the query counters, the game may want an updated result. _context.Renderer.UpdateCounters(); // Poll the query counters, the game may want an updated result.
_context.Synchronization.IncrementSyncpoint(syncpointId); _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. /// If no actions are present, a host sync object is not created.
/// </summary> /// </summary>
/// <param name="syncpoint">True if host sync is being created by a syncpoint</param> /// <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) if (BufferMigrations.Count > 0)
{ {
@@ -337,7 +338,7 @@ namespace Ryujinx.Graphics.Gpu
if (_pendingSync || (syncpoint && SyncpointActions.Count > 0)) if (_pendingSync || (syncpoint && SyncpointActions.Count > 0))
{ {
Renderer.CreateSync(SyncNumber); Renderer.CreateSync(SyncNumber, strict);
SyncNumber++; SyncNumber++;

View File

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

View File

@@ -106,7 +106,7 @@ namespace Ryujinx.Graphics.Host1x
private void Method1(int data) 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) private void Process(CommandAction cmdAction)

View File

@@ -1,6 +1,6 @@
namespace Ryujinx.Graphics.Nvdec namespace Ryujinx.Graphics.Nvdec
{ {
public enum CodecId public enum ApplicationId
{ {
Mpeg = 1, Mpeg = 1,
Vc1 = 2, Vc1 = 2,
@@ -8,6 +8,7 @@
Mpeg4 = 4, Mpeg4 = 4,
Vp8 = 5, Vp8 = 5,
Hevc = 7, 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) 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(); 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 width = (int)pictureInfo.PicWidthInMbs * MbSizeInPixels;
int height = (int)pictureInfo.PicHeightInMbs * MbSizeInPixels; int height = (int)pictureInfo.PicHeightInMbs * MbSizeInPixels;
int surfaceIndex = (int)pictureInfo.OutputSurfaceIndex; int surfaceIndex = (int)pictureInfo.OutputSurfaceIndex;
uint lumaOffset = state.SetSurfaceLumaOffset[surfaceIndex]; uint lumaOffset = state.SetPictureLumaOffset[surfaceIndex];
uint chromaOffset = state.SetSurfaceChromaOffset[surfaceIndex]; uint chromaOffset = state.SetPictureChromaOffset[surfaceIndex];
Decoder decoder = context.GetH264Decoder(); Decoder decoder = context.GetH264Decoder();

View File

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

View File

@@ -2,43 +2,62 @@
namespace Ryujinx.Graphics.Nvdec namespace Ryujinx.Graphics.Nvdec
{ {
// Note: Most of those names are not official.
struct NvdecRegisters struct NvdecRegisters
{ {
#pragma warning disable CS0649 #pragma warning disable CS0649
public Array64<uint> Reserved0; public Array64<uint> Reserved0;
public Array64<uint> Reserved100; public uint Nop;
public uint SetCodecID; public Array63<uint> Reserved104;
public Array63<uint> Reserved204; 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 uint Execute;
public Array63<uint> Reserved304; public uint SemaphoreD;
public uint SetPlatformID; public Array62<uint> Reserved308;
public uint SetPictureInfoOffset; public uint SetControlParams;
public uint SetBitstreamOffset; public uint SetDrvPicSetupOffset;
public uint SetFrameNumber; public uint SetInBufBaseOffset;
public uint SetH264SliceDataOffsetsOffset; // Also used by VC1 public uint SetPictureIndex;
public uint SetH264MvDumpOffset; // Also used by VC1 public uint SetSliceOffsetsBufOffset; // Also used by VC1
public uint Unknown418; // Used by VC1 public uint SetColocDataOffset; // Also used by VC1
public uint Unknown41C; public uint SetHistoryOffset; // Used by VC1
public uint Unknown420; // Used by VC1 public uint SetDisplayBufSize;
public uint SetFrameStatsOffset; public uint SetHistogramOffset; // Used by VC1
public uint SetH264LastSurfaceLumaOffset; public uint SetNvDecStatusOffset;
public uint SetH264LastSurfaceChromaOffset; public uint SetDisplayBufLumaOffset;
public Array17<uint> SetSurfaceLumaOffset; public uint SetDisplayBufChromaOffset;
public Array17<uint> SetSurfaceChromaOffset; public Array17<uint> SetPictureLumaOffset;
public uint Unknown4B8; public Array17<uint> SetPictureChromaOffset;
public uint Unknown4BC; public uint SetPicScratchBufOffset;
public uint SetExternalMvBufferOffset;
public uint SetCryptoData0Offset; public uint SetCryptoData0Offset;
public uint SetCryptoData1Offset; public uint SetCryptoData1Offset;
public Array62<uint> Unknown4C8; public Array14<uint> Unknown4C8;
public uint SetVp9EntropyProbsOffset; public uint H264SetMbHistBufOffset;
public uint SetVp9BackwardUpdatesOffset; public Array15<uint> Unknown504;
public uint SetVp9LastFrameSegMapOffset; public uint Vp8SetProbDataOffset;
public uint SetVp9CurrFrameSegMapOffset; public uint Vp8SetHeaderPartitionBufBaseOffset;
public uint Unknown5D0; public Array14<uint> Unknown548;
public uint SetVp9LastFrameMvsOffset; public uint HevcSetScalingListOffset;
public uint SetVp9CurrFrameMvsOffset; public uint HevcSetTileSizesOffset;
public uint Unknown5DC; 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 #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) 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);
ReadOnlySpan<byte> bitstream = rm.Gmm.DeviceGetSpan(state.SetBitstreamOffset, (int)pictureInfo.VLDBufferSize); ReadOnlySpan<byte> bitstream = rm.Gmm.DeviceGetSpan(state.SetInBufBaseOffset, (int)pictureInfo.VLDBufferSize);
Decoder decoder = context.GetVp8Decoder(); Decoder decoder = context.GetVp8Decoder();
@@ -19,8 +19,8 @@ namespace Ryujinx.Graphics.Nvdec
Vp8PictureInfo info = pictureInfo.Convert(); Vp8PictureInfo info = pictureInfo.Convert();
uint lumaOffset = state.SetSurfaceLumaOffset[3]; uint lumaOffset = state.SetPictureLumaOffset[3];
uint chromaOffset = state.SetSurfaceChromaOffset[3]; uint chromaOffset = state.SetPictureChromaOffset[3];
if (decoder.Decode(ref info, outputSurface, bitstream)) 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) public unsafe static void Decode(ResourceManager rm, ref NvdecRegisters state)
{ {
PictureInfo pictureInfo = rm.Gmm.DeviceRead<PictureInfo>(state.SetPictureInfoOffset); PictureInfo pictureInfo = rm.Gmm.DeviceRead<PictureInfo>(state.SetDrvPicSetupOffset);
EntropyProbs entropy = rm.Gmm.DeviceRead<EntropyProbs>(state.SetVp9EntropyProbsOffset); EntropyProbs entropy = rm.Gmm.DeviceRead<EntropyProbs>(state.Vp9SetProbTabBufOffset);
ISurface Rent(uint lumaOffset, uint chromaOffset, FrameSize size) ISurface Rent(uint lumaOffset, uint chromaOffset, FrameSize size)
{ {
return rm.Cache.Get(_decoder, lumaOffset, chromaOffset, size.Width, size.Height); return rm.Cache.Get(_decoder, lumaOffset, chromaOffset, size.Width, size.Height);
} }
ISurface lastSurface = Rent(state.SetSurfaceLumaOffset[0], state.SetSurfaceChromaOffset[0], pictureInfo.LastFrameSize); ISurface lastSurface = Rent(state.SetPictureLumaOffset[0], state.SetPictureChromaOffset[0], pictureInfo.LastFrameSize);
ISurface goldenSurface = Rent(state.SetSurfaceLumaOffset[1], state.SetSurfaceChromaOffset[1], pictureInfo.GoldenFrameSize); ISurface goldenSurface = Rent(state.SetPictureLumaOffset[1], state.SetPictureChromaOffset[1], pictureInfo.GoldenFrameSize);
ISurface altSurface = Rent(state.SetSurfaceLumaOffset[2], state.SetSurfaceChromaOffset[2], pictureInfo.AltFrameSize); ISurface altSurface = Rent(state.SetPictureLumaOffset[2], state.SetPictureChromaOffset[2], pictureInfo.AltFrameSize);
ISurface currentSurface = Rent(state.SetSurfaceLumaOffset[3], state.SetSurfaceChromaOffset[3], pictureInfo.CurrentFrameSize); ISurface currentSurface = Rent(state.SetPictureLumaOffset[3], state.SetPictureChromaOffset[3], pictureInfo.CurrentFrameSize);
Vp9PictureInfo info = pictureInfo.Convert(); Vp9PictureInfo info = pictureInfo.Convert();
@@ -38,31 +38,31 @@ namespace Ryujinx.Graphics.Nvdec
entropy.Convert(ref info.Entropy); 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; ReadOnlySpan<Vp9MvRef> mvsIn = ReadOnlySpan<Vp9MvRef>.Empty;
if (info.UsePrevInFindMvRefs) 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 miCols = BitUtils.DivRoundUp(pictureInfo.CurrentFrameSize.Width, 8);
int miRows = BitUtils.DivRoundUp(pictureInfo.CurrentFrameSize.Height, 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); Span<Vp9MvRef> mvsOut = MemoryMarshal.Cast<byte, Vp9MvRef>(mvsRegion.Memory.Span);
uint lumaOffset = state.SetSurfaceLumaOffset[3]; uint lumaOffset = state.SetPictureLumaOffset[3];
uint chromaOffset = state.SetSurfaceChromaOffset[3]; uint chromaOffset = state.SetPictureChromaOffset[3];
if (_decoder.Decode(ref info, currentSurface, bitstream, mvsIn, mvsOut)) if (_decoder.Decode(ref info, currentSurface, bitstream, mvsIn, mvsOut))
{ {
SurfaceWriter.Write(rm.Gmm, currentSurface, lumaOffset, chromaOffset); 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(lastSurface);
rm.Cache.Put(goldenSurface); rm.Cache.Put(goldenSurface);

View File

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

View File

@@ -1,4 +1,3 @@
using Ryujinx.Graphics.Shader.StructuredIr;
using Ryujinx.Graphics.Shader.Translation; using Ryujinx.Graphics.Shader.Translation;
using System; 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) public static AggregateType ToAggregateType(this AttributeType type)
{ {
return type switch 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"; AggregateType.Void => "void",
case VariableType.F32: return "precise float"; AggregateType.Bool => "bool",
case VariableType.F64: return "double"; AggregateType.FP32 => precise ? "precise float" : "float",
case VariableType.None: return "void"; AggregateType.FP64 => "double",
case VariableType.S32: return "int"; AggregateType.S32 => "int",
case VariableType.U32: return "uint"; AggregateType.U32 => "uint",
} AggregateType.Vector2 | AggregateType.Bool => "bvec2",
AggregateType.Vector2 | AggregateType.FP32 => precise ? "precise vec2" : "vec2",
throw new ArgumentException($"Invalid variable type \"{type}\"."); 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) 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) else if (node is AstAssignment assignment)
{ {
VariableType srcType = OperandManager.GetNodeDestType(context, assignment.Source); AggregateType srcType = OperandManager.GetNodeDestType(context, assignment.Source);
VariableType dstType = OperandManager.GetNodeDestType(context, assignment.Destination, isAsgDest: true); AggregateType dstType = OperandManager.GetNodeDestType(context, assignment.Destination, isAsgDest: true);
string dest; string dest;
@@ -158,9 +158,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
private static string GetCondExpr(CodeGenContext context, IAstNode cond) 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.IntermediateRepresentation;
using Ryujinx.Graphics.Shader.StructuredIr; using Ryujinx.Graphics.Shader.StructuredIr;
using Ryujinx.Graphics.Shader.Translation;
using System; using System;
using static Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions.InstGenBallot; 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.InstGenHelper;
using static Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions.InstGenMemory; 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.InstGenPacking;
using static Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions.InstGenVector;
using static Ryujinx.Graphics.Shader.StructuredIr.InstructionInfo; using static Ryujinx.Graphics.Shader.StructuredIr.InstructionInfo;
namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
@@ -32,12 +34,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
{ {
IAstNode src = operation.GetSource(0); IAstNode src = operation.GetSource(0);
VariableType type = GetSrcVarType(operation.Inst, 0); AggregateType type = GetSrcVarType(operation.Inst, 0);
string srcExpr = GetSoureExpr(context, src, type); string srcExpr = GetSoureExpr(context, src, type);
string zero; string zero;
if (type == VariableType.F64) if (type == AggregateType.FP64)
{ {
zero = "0.0"; zero = "0.0";
} }
@@ -95,7 +97,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
} }
else else
{ {
VariableType dstType = GetSrcVarType(inst, argIndex); AggregateType dstType = GetSrcVarType(inst, argIndex);
args += GetSoureExpr(context, operation.GetSource(argIndex), dstType); args += GetSoureExpr(context, operation.GetSource(argIndex), dstType);
} }
@@ -226,6 +228,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
case Instruction.UnpackHalf2x16: case Instruction.UnpackHalf2x16:
return UnpackHalf2x16(context, operation); 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.StructuredIr;
using Ryujinx.Graphics.Shader.Translation;
using static Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions.InstGenHelper; using static Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions.InstGenHelper;
using static Ryujinx.Graphics.Shader.StructuredIr.InstructionInfo; 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) 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); string arg = GetSoureExpr(context, operation.GetSource(0), dstType);

View File

@@ -1,5 +1,6 @@
using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.IntermediateRepresentation;
using Ryujinx.Graphics.Shader.StructuredIr; using Ryujinx.Graphics.Shader.StructuredIr;
using Ryujinx.Graphics.Shader.Translation;
using static Ryujinx.Graphics.Shader.CodeGen.Glsl.TypeConversion; using static Ryujinx.Graphics.Shader.CodeGen.Glsl.TypeConversion;
@@ -7,11 +8,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
{ {
static class InstGenHelper static class InstGenHelper
{ {
private static readonly InstInfo[] InfoTable; private static readonly InstInfo[] _infoTable;
static InstGenHelper() static InstGenHelper()
{ {
InfoTable = new InstInfo[(int)Instruction.Count]; _infoTable = new InstInfo[(int)Instruction.Count];
Add(Instruction.AtomicAdd, InstType.AtomicBinary, "atomicAdd"); Add(Instruction.AtomicAdd, InstType.AtomicBinary, "atomicAdd");
Add(Instruction.AtomicAnd, InstType.AtomicBinary, "atomicAnd"); 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.Truncate, InstType.CallUnary, "trunc");
Add(Instruction.UnpackDouble2x32, InstType.Special); Add(Instruction.UnpackDouble2x32, InstType.Special);
Add(Instruction.UnpackHalf2x16, InstType.Special); Add(Instruction.UnpackHalf2x16, InstType.Special);
Add(Instruction.VectorExtract, InstType.Special);
Add(Instruction.VoteAll, InstType.CallUnary, "allInvocationsARB"); Add(Instruction.VoteAll, InstType.CallUnary, "allInvocationsARB");
Add(Instruction.VoteAllEqual, InstType.CallUnary, "allInvocationsEqualARB"); Add(Instruction.VoteAllEqual, InstType.CallUnary, "allInvocationsEqualARB");
Add(Instruction.VoteAny, InstType.CallUnary, "anyInvocationARB"); 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) 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) 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); return ReinterpretCast(context, node, OperandManager.GetNodeDestType(context, node), dstType);
} }
@@ -191,7 +193,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
return false; 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) if ((info.Type & (InstType.Call | InstType.Special)) != 0)
{ {

View File

@@ -1,5 +1,6 @@
using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.IntermediateRepresentation;
using Ryujinx.Graphics.Shader.StructuredIr; using Ryujinx.Graphics.Shader.StructuredIr;
using Ryujinx.Graphics.Shader.Translation;
using System; using System;
using static Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions.InstGenHelper; using static Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions.InstGenHelper;
@@ -23,7 +24,17 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
case Instruction.ImageStore: case Instruction.ImageStore:
return "// imageStore(bindless)"; return "// imageStore(bindless)";
case Instruction.ImageLoad: 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; return imageConst;
default: default:
return NumberFormatter.FormatInt(0); return NumberFormatter.FormatInt(0);
@@ -58,7 +69,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
int srcIndex = isBindless ? 1 : 0; int srcIndex = isBindless ? 1 : 0;
string Src(VariableType type) string Src(AggregateType type)
{ {
return GetSoureExpr(context, texOp.GetSource(srcIndex++), type); return GetSoureExpr(context, texOp.GetSource(srcIndex++), type);
} }
@@ -67,7 +78,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
if (isIndexed) if (isIndexed)
{ {
indexExpr = Src(VariableType.S32); indexExpr = Src(AggregateType.S32);
} }
string imageName = OperandManager.GetImageName(context.Config.Stage, texOp, indexExpr); 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++) for (int index = 0; index < pCount; index++)
{ {
elems[index] = Src(VariableType.S32); elems[index] = Src(AggregateType.S32);
} }
Append(ApplyScaling("ivec" + pCount + "(" + string.Join(", ", elems) + ")")); Append(ApplyScaling("ivec" + pCount + "(" + string.Join(", ", elems) + ")"));
} }
else else
{ {
Append(Src(VariableType.S32)); Append(Src(AggregateType.S32));
} }
if (texOp.Inst == Instruction.ImageStore) if (texOp.Inst == Instruction.ImageStore)
{ {
VariableType type = texOp.Format.GetComponentType(); AggregateType type = texOp.Format.GetComponentType();
string[] cElems = new string[4]; string[] cElems = new string[4];
@@ -139,8 +150,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
{ {
cElems[index] = type switch cElems[index] = type switch
{ {
VariableType.S32 => NumberFormatter.FormatInt(0), AggregateType.S32 => NumberFormatter.FormatInt(0),
VariableType.U32 => NumberFormatter.FormatUint(0), AggregateType.U32 => NumberFormatter.FormatUint(0),
_ => NumberFormatter.FormatFloat(0) _ => NumberFormatter.FormatFloat(0)
}; };
} }
@@ -148,8 +159,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
string prefix = type switch string prefix = type switch
{ {
VariableType.S32 => "i", AggregateType.S32 => "i",
VariableType.U32 => "u", AggregateType.U32 => "u",
_ => string.Empty _ => string.Empty
}; };
@@ -158,7 +169,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
if (texOp.Inst == Instruction.ImageAtomic) if (texOp.Inst == Instruction.ImageAtomic)
{ {
VariableType type = texOp.Format.GetComponentType(); AggregateType type = texOp.Format.GetComponentType();
if ((texOp.Flags & TextureFlags.AtomicMask) == TextureFlags.CAS) if ((texOp.Flags & TextureFlags.AtomicMask) == TextureFlags.CAS)
{ {
@@ -176,14 +187,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
texCall += ")"; texCall += ")";
if (type != VariableType.S32) if (type != AggregateType.S32)
{ {
texCall = "int(" + texCall + ")"; texCall = "int(" + texCall + ")";
} }
} }
else else
{ {
texCall += ")" + (texOp.Inst == Instruction.ImageLoad ? GetMask(texOp.Index) : ""); texCall += ")" + (texOp.Inst == Instruction.ImageLoad ? GetMaskMultiDest(texOp.Index) : "");
} }
return texCall; return texCall;
@@ -288,7 +299,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
if (isIndexed) 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); 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++) 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) + ")"; coordsExpr = "vec" + coordsCount + "(" + string.Join(", ", elems) + ")";
} }
else else
{ {
coordsExpr = GetSoureExpr(context, texOp.GetSource(coordsIndex), VariableType.F32); coordsExpr = GetSoureExpr(context, texOp.GetSource(coordsIndex), AggregateType.FP32);
} }
return $"textureQueryLod({samplerName}, {coordsExpr}){GetMask(texOp.Index)}"; 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)); 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}"; return $"{arrayName}[{offsetExpr}] = {src}";
} }
@@ -376,9 +387,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
string offsetExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0)); 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})"; 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)); 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})"; 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 indexExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0));
string offsetExpr = GetSoureExpr(context, src2, GetSrcVarType(operation.Inst, 1)); 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); 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 indexExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0));
string offsetExpr = GetSoureExpr(context, src2, GetSrcVarType(operation.Inst, 1)); 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); 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 indexExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0));
string offsetExpr = GetSoureExpr(context, src2, GetSrcVarType(operation.Inst, 1)); 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); 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 isMultisample = (texOp.Type & SamplerType.Multisample) != 0;
bool isShadow = (texOp.Type & SamplerType.Shadow) != 0; bool isShadow = (texOp.Type & SamplerType.Shadow) != 0;
bool colorIsVector = isGather || !isShadow;
SamplerType type = texOp.Type & SamplerType.Mask; SamplerType type = texOp.Type & SamplerType.Mask;
bool is2D = type == SamplerType.Texture2D; 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. // TODO: Bindless texture support. For now we just return 0.
if (isBindless) 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"; string texCall = intCoords ? "texelFetch" : "texture";
@@ -521,7 +546,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
int srcIndex = isBindless ? 1 : 0; int srcIndex = isBindless ? 1 : 0;
string Src(VariableType type) string Src(AggregateType type)
{ {
return GetSoureExpr(context, texOp.GetSource(srcIndex++), type); return GetSoureExpr(context, texOp.GetSource(srcIndex++), type);
} }
@@ -530,7 +555,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
if (isIndexed) if (isIndexed)
{ {
indexExpr = Src(VariableType.S32); indexExpr = Src(AggregateType.S32);
} }
string samplerName = OperandManager.GetSamplerName(context.Config.Stage, texOp, indexExpr); string samplerName = OperandManager.GetSamplerName(context.Config.Stage, texOp, indexExpr);
@@ -578,7 +603,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
texCall += ", " + str; texCall += ", " + str;
} }
VariableType coordType = intCoords ? VariableType.S32 : VariableType.F32; AggregateType coordType = intCoords ? AggregateType.S32 : AggregateType.FP32;
string AssemblePVector(int count) string AssemblePVector(int count)
{ {
@@ -590,7 +615,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
{ {
if (arrayIndexElem == index) if (arrayIndexElem == index)
{ {
elems[index] = Src(VariableType.S32); elems[index] = Src(AggregateType.S32);
if (!intCoords) if (!intCoords)
{ {
@@ -652,20 +677,20 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
for (int index = 0; index < count; index++) for (int index = 0; index < count; index++)
{ {
elems[index] = Src(VariableType.F32); elems[index] = Src(AggregateType.FP32);
} }
return "vec" + count + "(" + string.Join(", ", elems) + ")"; return "vec" + count + "(" + string.Join(", ", elems) + ")";
} }
else else
{ {
return Src(VariableType.F32); return Src(AggregateType.FP32);
} }
} }
if (hasExtraCompareArg) if (hasExtraCompareArg)
{ {
Append(Src(VariableType.F32)); Append(Src(AggregateType.FP32));
} }
if (hasDerivatives) if (hasDerivatives)
@@ -676,7 +701,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
if (isMultisample) if (isMultisample)
{ {
Append(Src(VariableType.S32)); Append(Src(AggregateType.S32));
} }
else if (hasLodLevel) else if (hasLodLevel)
{ {
@@ -691,14 +716,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
for (int index = 0; index < count; index++) for (int index = 0; index < count; index++)
{ {
elems[index] = Src(VariableType.S32); elems[index] = Src(AggregateType.S32);
} }
return "ivec" + count + "(" + string.Join(", ", elems) + ")"; return "ivec" + count + "(" + string.Join(", ", elems) + ")";
} }
else else
{ {
return Src(VariableType.S32); return Src(AggregateType.S32);
} }
} }
@@ -718,17 +743,17 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
if (hasLodBias) if (hasLodBias)
{ {
Append(Src(VariableType.F32)); Append(Src(AggregateType.FP32));
} }
// textureGather* optional extra component index, // textureGather* optional extra component index,
// not needed for shadow samplers. // not needed for shadow samplers.
if (isGather && !isShadow) if (isGather && !isShadow)
{ {
Append(Src(VariableType.S32)); Append(Src(AggregateType.S32));
} }
texCall += ")" + (isGather || !isShadow ? GetMask(texOp.Index) : ""); texCall += ")" + (colorIsVector ? GetMaskMultiDest(texOp.Index) : "");
return texCall; return texCall;
} }
@@ -751,7 +776,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
if (isIndexed) 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); 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); 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;
using System.Globalization; using System.Globalization;
@@ -8,21 +8,21 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
{ {
private const int MaxDecimal = 256; 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); return TryFormatFloat(BitConverter.Int32BitsToSingle(value), out formatted);
} }
else if (dstType == VariableType.S32) else if (dstType == AggregateType.S32)
{ {
formatted = FormatInt(value); formatted = FormatInt(value);
} }
else if (dstType == VariableType.U32) else if (dstType == AggregateType.U32)
{ {
formatted = FormatUint((uint)value); formatted = FormatUint((uint)value);
} }
else if (dstType == VariableType.Bool) else if (dstType == AggregateType.Bool)
{ {
formatted = value != 0 ? "true" : "false"; formatted = value != 0 ? "true" : "false";
} }
@@ -65,13 +65,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
return true; 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); return FormatInt(value);
} }
else if (dstType == VariableType.U32) else if (dstType == AggregateType.U32)
{ {
return FormatUint((uint)value); return FormatUint((uint)value);
} }

View File

@@ -4,6 +4,7 @@ using Ryujinx.Graphics.Shader.Translation;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Numerics;
using static Ryujinx.Graphics.Shader.StructuredIr.InstructionInfo; using static Ryujinx.Graphics.Shader.StructuredIr.InstructionInfo;
@@ -17,9 +18,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
{ {
public string Name { get; } 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; Name = name;
Type = type; Type = type;
@@ -28,64 +29,64 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
private static Dictionary<int, BuiltInAttribute> _builtInAttributes = new Dictionary<int, BuiltInAttribute>() private static Dictionary<int, BuiltInAttribute> _builtInAttributes = new Dictionary<int, BuiltInAttribute>()
{ {
{ AttributeConsts.Layer, new BuiltInAttribute("gl_Layer", VariableType.S32) }, { AttributeConsts.Layer, new BuiltInAttribute("gl_Layer", AggregateType.S32) },
{ AttributeConsts.PointSize, new BuiltInAttribute("gl_PointSize", VariableType.F32) }, { AttributeConsts.PointSize, new BuiltInAttribute("gl_PointSize", AggregateType.FP32) },
{ AttributeConsts.PositionX, new BuiltInAttribute("gl_Position.x", VariableType.F32) }, { AttributeConsts.PositionX, new BuiltInAttribute("gl_Position.x", AggregateType.FP32) },
{ AttributeConsts.PositionY, new BuiltInAttribute("gl_Position.y", VariableType.F32) }, { AttributeConsts.PositionY, new BuiltInAttribute("gl_Position.y", AggregateType.FP32) },
{ AttributeConsts.PositionZ, new BuiltInAttribute("gl_Position.z", VariableType.F32) }, { AttributeConsts.PositionZ, new BuiltInAttribute("gl_Position.z", AggregateType.FP32) },
{ AttributeConsts.PositionW, new BuiltInAttribute("gl_Position.w", VariableType.F32) }, { AttributeConsts.PositionW, new BuiltInAttribute("gl_Position.w", AggregateType.FP32) },
{ AttributeConsts.ClipDistance0, new BuiltInAttribute("gl_ClipDistance[0]", VariableType.F32) }, { AttributeConsts.ClipDistance0, new BuiltInAttribute("gl_ClipDistance[0]", AggregateType.FP32) },
{ AttributeConsts.ClipDistance1, new BuiltInAttribute("gl_ClipDistance[1]", VariableType.F32) }, { AttributeConsts.ClipDistance1, new BuiltInAttribute("gl_ClipDistance[1]", AggregateType.FP32) },
{ AttributeConsts.ClipDistance2, new BuiltInAttribute("gl_ClipDistance[2]", VariableType.F32) }, { AttributeConsts.ClipDistance2, new BuiltInAttribute("gl_ClipDistance[2]", AggregateType.FP32) },
{ AttributeConsts.ClipDistance3, new BuiltInAttribute("gl_ClipDistance[3]", VariableType.F32) }, { AttributeConsts.ClipDistance3, new BuiltInAttribute("gl_ClipDistance[3]", AggregateType.FP32) },
{ AttributeConsts.ClipDistance4, new BuiltInAttribute("gl_ClipDistance[4]", VariableType.F32) }, { AttributeConsts.ClipDistance4, new BuiltInAttribute("gl_ClipDistance[4]", AggregateType.FP32) },
{ AttributeConsts.ClipDistance5, new BuiltInAttribute("gl_ClipDistance[5]", VariableType.F32) }, { AttributeConsts.ClipDistance5, new BuiltInAttribute("gl_ClipDistance[5]", AggregateType.FP32) },
{ AttributeConsts.ClipDistance6, new BuiltInAttribute("gl_ClipDistance[6]", VariableType.F32) }, { AttributeConsts.ClipDistance6, new BuiltInAttribute("gl_ClipDistance[6]", AggregateType.FP32) },
{ AttributeConsts.ClipDistance7, new BuiltInAttribute("gl_ClipDistance[7]", VariableType.F32) }, { AttributeConsts.ClipDistance7, new BuiltInAttribute("gl_ClipDistance[7]", AggregateType.FP32) },
{ AttributeConsts.PointCoordX, new BuiltInAttribute("gl_PointCoord.x", VariableType.F32) }, { AttributeConsts.PointCoordX, new BuiltInAttribute("gl_PointCoord.x", AggregateType.FP32) },
{ AttributeConsts.PointCoordY, new BuiltInAttribute("gl_PointCoord.y", VariableType.F32) }, { AttributeConsts.PointCoordY, new BuiltInAttribute("gl_PointCoord.y", AggregateType.FP32) },
{ AttributeConsts.TessCoordX, new BuiltInAttribute("gl_TessCoord.x", VariableType.F32) }, { AttributeConsts.TessCoordX, new BuiltInAttribute("gl_TessCoord.x", AggregateType.FP32) },
{ AttributeConsts.TessCoordY, new BuiltInAttribute("gl_TessCoord.y", VariableType.F32) }, { AttributeConsts.TessCoordY, new BuiltInAttribute("gl_TessCoord.y", AggregateType.FP32) },
{ AttributeConsts.InstanceId, new BuiltInAttribute("gl_InstanceID", VariableType.S32) }, { AttributeConsts.InstanceId, new BuiltInAttribute("gl_InstanceID", AggregateType.S32) },
{ AttributeConsts.VertexId, new BuiltInAttribute("gl_VertexID", VariableType.S32) }, { AttributeConsts.VertexId, new BuiltInAttribute("gl_VertexID", AggregateType.S32) },
{ AttributeConsts.BaseInstance, new BuiltInAttribute("gl_BaseInstanceARB", VariableType.S32) }, { AttributeConsts.BaseInstance, new BuiltInAttribute("gl_BaseInstanceARB", AggregateType.S32) },
{ AttributeConsts.BaseVertex, new BuiltInAttribute("gl_BaseVertexARB", VariableType.S32) }, { AttributeConsts.BaseVertex, new BuiltInAttribute("gl_BaseVertexARB", AggregateType.S32) },
{ AttributeConsts.InstanceIndex, new BuiltInAttribute("gl_InstanceIndex", VariableType.S32) }, { AttributeConsts.InstanceIndex, new BuiltInAttribute("gl_InstanceIndex", AggregateType.S32) },
{ AttributeConsts.VertexIndex, new BuiltInAttribute("gl_VertexIndex", VariableType.S32) }, { AttributeConsts.VertexIndex, new BuiltInAttribute("gl_VertexIndex", AggregateType.S32) },
{ AttributeConsts.DrawIndex, new BuiltInAttribute("gl_DrawIDARB", VariableType.S32) }, { AttributeConsts.DrawIndex, new BuiltInAttribute("gl_DrawIDARB", AggregateType.S32) },
{ AttributeConsts.FrontFacing, new BuiltInAttribute("gl_FrontFacing", VariableType.Bool) }, { AttributeConsts.FrontFacing, new BuiltInAttribute("gl_FrontFacing", AggregateType.Bool) },
// Special. // Special.
{ AttributeConsts.FragmentOutputDepth, new BuiltInAttribute("gl_FragDepth", VariableType.F32) }, { AttributeConsts.FragmentOutputDepth, new BuiltInAttribute("gl_FragDepth", AggregateType.FP32) },
{ AttributeConsts.ThreadKill, new BuiltInAttribute("gl_HelperInvocation", VariableType.Bool) }, { AttributeConsts.ThreadKill, new BuiltInAttribute("gl_HelperInvocation", AggregateType.Bool) },
{ AttributeConsts.ThreadIdX, new BuiltInAttribute("gl_LocalInvocationID.x", VariableType.U32) }, { AttributeConsts.ThreadIdX, new BuiltInAttribute("gl_LocalInvocationID.x", AggregateType.U32) },
{ AttributeConsts.ThreadIdY, new BuiltInAttribute("gl_LocalInvocationID.y", VariableType.U32) }, { AttributeConsts.ThreadIdY, new BuiltInAttribute("gl_LocalInvocationID.y", AggregateType.U32) },
{ AttributeConsts.ThreadIdZ, new BuiltInAttribute("gl_LocalInvocationID.z", VariableType.U32) }, { AttributeConsts.ThreadIdZ, new BuiltInAttribute("gl_LocalInvocationID.z", AggregateType.U32) },
{ AttributeConsts.CtaIdX, new BuiltInAttribute("gl_WorkGroupID.x", VariableType.U32) }, { AttributeConsts.CtaIdX, new BuiltInAttribute("gl_WorkGroupID.x", AggregateType.U32) },
{ AttributeConsts.CtaIdY, new BuiltInAttribute("gl_WorkGroupID.y", VariableType.U32) }, { AttributeConsts.CtaIdY, new BuiltInAttribute("gl_WorkGroupID.y", AggregateType.U32) },
{ AttributeConsts.CtaIdZ, new BuiltInAttribute("gl_WorkGroupID.z", VariableType.U32) }, { AttributeConsts.CtaIdZ, new BuiltInAttribute("gl_WorkGroupID.z", AggregateType.U32) },
{ AttributeConsts.LaneId, new BuiltInAttribute(null, VariableType.U32) }, { AttributeConsts.LaneId, new BuiltInAttribute(null, AggregateType.U32) },
{ AttributeConsts.InvocationId, new BuiltInAttribute("gl_InvocationID", VariableType.S32) }, { AttributeConsts.InvocationId, new BuiltInAttribute("gl_InvocationID", AggregateType.S32) },
{ AttributeConsts.PrimitiveId, new BuiltInAttribute("gl_PrimitiveID", VariableType.S32) }, { AttributeConsts.PrimitiveId, new BuiltInAttribute("gl_PrimitiveID", AggregateType.S32) },
{ AttributeConsts.PatchVerticesIn, new BuiltInAttribute("gl_PatchVerticesIn", VariableType.S32) }, { AttributeConsts.PatchVerticesIn, new BuiltInAttribute("gl_PatchVerticesIn", AggregateType.S32) },
{ AttributeConsts.EqMask, new BuiltInAttribute(null, VariableType.U32) }, { AttributeConsts.EqMask, new BuiltInAttribute(null, AggregateType.U32) },
{ AttributeConsts.GeMask, new BuiltInAttribute(null, VariableType.U32) }, { AttributeConsts.GeMask, new BuiltInAttribute(null, AggregateType.U32) },
{ AttributeConsts.GtMask, new BuiltInAttribute(null, VariableType.U32) }, { AttributeConsts.GtMask, new BuiltInAttribute(null, AggregateType.U32) },
{ AttributeConsts.LeMask, new BuiltInAttribute(null, VariableType.U32) }, { AttributeConsts.LeMask, new BuiltInAttribute(null, AggregateType.U32) },
{ AttributeConsts.LtMask, new BuiltInAttribute(null, VariableType.U32) }, { AttributeConsts.LtMask, new BuiltInAttribute(null, AggregateType.U32) },
// Support uniforms. // Support uniforms.
{ AttributeConsts.FragmentOutputIsBgraBase + 0, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[0]", VariableType.Bool) }, { AttributeConsts.FragmentOutputIsBgraBase + 0, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[0]", AggregateType.Bool) },
{ AttributeConsts.FragmentOutputIsBgraBase + 4, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[1]", VariableType.Bool) }, { AttributeConsts.FragmentOutputIsBgraBase + 4, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[1]", AggregateType.Bool) },
{ AttributeConsts.FragmentOutputIsBgraBase + 8, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[2]", VariableType.Bool) }, { AttributeConsts.FragmentOutputIsBgraBase + 8, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[2]", AggregateType.Bool) },
{ AttributeConsts.FragmentOutputIsBgraBase + 12, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[3]", VariableType.Bool) }, { AttributeConsts.FragmentOutputIsBgraBase + 12, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[3]", AggregateType.Bool) },
{ AttributeConsts.FragmentOutputIsBgraBase + 16, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[4]", VariableType.Bool) }, { AttributeConsts.FragmentOutputIsBgraBase + 16, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[4]", AggregateType.Bool) },
{ AttributeConsts.FragmentOutputIsBgraBase + 20, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[5]", VariableType.Bool) }, { AttributeConsts.FragmentOutputIsBgraBase + 20, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[5]", AggregateType.Bool) },
{ AttributeConsts.FragmentOutputIsBgraBase + 24, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[6]", VariableType.Bool) }, { AttributeConsts.FragmentOutputIsBgraBase + 24, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[6]", AggregateType.Bool) },
{ AttributeConsts.FragmentOutputIsBgraBase + 28, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[7]", VariableType.Bool) }, { AttributeConsts.FragmentOutputIsBgraBase + 28, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[7]", AggregateType.Bool) },
{ AttributeConsts.SupportBlockViewInverseX, new BuiltInAttribute($"{DefaultNames.SupportBlockViewportInverse}.x", VariableType.F32) }, { AttributeConsts.SupportBlockViewInverseX, new BuiltInAttribute($"{DefaultNames.SupportBlockViewportInverse}.x", AggregateType.FP32) },
{ AttributeConsts.SupportBlockViewInverseY, new BuiltInAttribute($"{DefaultNames.SupportBlockViewportInverse}.y", VariableType.F32) } { AttributeConsts.SupportBlockViewInverseY, new BuiltInAttribute($"{DefaultNames.SupportBlockViewportInverse}.y", AggregateType.FP32) }
}; };
private Dictionary<AstOperand, string> _locals; private Dictionary<AstOperand, string> _locals;
@@ -329,7 +330,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
{ {
if (cbIndexable) 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}"; return $"{GetShaderStagePrefix(stage)}_{DefaultNames.UniformNamePrefix}{slot}_{DefaultNames.UniformNameSuffix}";
@@ -404,7 +405,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
return $"{DefaultNames.ArgumentNamePrefix}{argIndex}"; 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) if (node is AstOperation operation)
{ {
@@ -431,12 +432,22 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
return context.GetFunction(funcId.Value).ReturnType; return context.GetFunction(funcId.Value).ReturnType;
} }
else if (operation is AstTextureOperation texOp && else if (operation.Inst == Instruction.VectorExtract)
(texOp.Inst == Instruction.ImageLoad ||
texOp.Inst == Instruction.ImageStore ||
texOp.Inst == Instruction.ImageAtomic))
{ {
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); 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) if (operand.Type == OperandType.Attribute)
{ {
@@ -474,7 +485,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
AttributeType type = context.Config.GpuAccessor.QueryAttributeType(location); 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.CodeGen.Glsl.Instructions;
using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.IntermediateRepresentation;
using Ryujinx.Graphics.Shader.StructuredIr; using Ryujinx.Graphics.Shader.StructuredIr;
using Ryujinx.Graphics.Shader.Translation;
using System; using System;
namespace Ryujinx.Graphics.Shader.CodeGen.Glsl namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
@@ -10,8 +11,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
public static string ReinterpretCast( public static string ReinterpretCast(
CodeGenContext context, CodeGenContext context,
IAstNode node, IAstNode node,
VariableType srcType, AggregateType srcType,
VariableType dstType) AggregateType dstType)
{ {
if (node is AstOperand operand && operand.Type == OperandType.Constant) 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); 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) if (srcType == dstType)
{ {
return expr; return expr;
} }
if (srcType == VariableType.F32) if (srcType == AggregateType.FP32)
{ {
switch (dstType) switch (dstType)
{ {
case VariableType.Bool: return $"(floatBitsToInt({expr}) != 0)"; case AggregateType.Bool: return $"(floatBitsToInt({expr}) != 0)";
case VariableType.S32: return $"floatBitsToInt({expr})"; case AggregateType.S32: return $"floatBitsToInt({expr})";
case VariableType.U32: return $"floatBitsToUint({expr})"; case AggregateType.U32: return $"floatBitsToUint({expr})";
} }
} }
else if (dstType == VariableType.F32) else if (dstType == AggregateType.FP32)
{ {
switch (srcType) switch (srcType)
{ {
case VariableType.Bool: return $"intBitsToFloat({ReinterpretBoolToInt(expr, node, VariableType.S32)})"; case AggregateType.Bool: return $"intBitsToFloat({ReinterpretBoolToInt(expr, node, AggregateType.S32)})";
case VariableType.S32: return $"intBitsToFloat({expr})"; case AggregateType.S32: return $"intBitsToFloat({expr})";
case VariableType.U32: return $"uintBitsToFloat({expr})"; case AggregateType.U32: return $"uintBitsToFloat({expr})";
} }
} }
else if (srcType == VariableType.Bool) else if (srcType == AggregateType.Bool)
{ {
return ReinterpretBoolToInt(expr, node, dstType); return ReinterpretBoolToInt(expr, node, dstType);
} }
else if (dstType == VariableType.Bool) else if (dstType == AggregateType.Bool)
{ {
expr = InstGenHelper.Enclose(expr, node, Instruction.CompareNotEqual, isLhs: true); expr = InstGenHelper.Enclose(expr, node, Instruction.CompareNotEqual, isLhs: true);
return $"({expr} != 0)"; return $"({expr} != 0)";
} }
else if (dstType == VariableType.S32) else if (dstType == AggregateType.S32)
{ {
return $"int({expr})"; return $"int({expr})";
} }
else if (dstType == VariableType.U32) else if (dstType == AggregateType.U32)
{ {
return $"uint({expr})"; return $"uint({expr})";
} }
@@ -73,7 +74,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
throw new ArgumentException($"Invalid reinterpret cast from \"{srcType}\" to \"{dstType}\"."); 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 trueExpr = NumberFormatter.FormatInt(IrConsts.True, dstType);
string falseExpr = NumberFormatter.FormatInt(IrConsts.False, 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); 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) private Instruction GetUndefined(AggregateType type)
{ {
return type switch return type switch
@@ -325,7 +348,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
if (components > 1) if (components > 1)
{ {
attrOffset &= ~0xf; 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); 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)); 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) if (invocationId != null)
{ {
@@ -452,7 +482,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
elemType = attrInfo.Type & AggregateType.ElementTypeMask; elemType = attrInfo.Type & AggregateType.ElementTypeMask;
if ((attrInfo.Type & (AggregateType.Array | AggregateType.Vector)) == 0) if ((attrInfo.Type & (AggregateType.Array | AggregateType.ElementCountMask)) == 0)
{ {
return ioVariable; return ioVariable;
} }
@@ -533,13 +563,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
public Instruction GetLocal(AggregateType dstType, AstOperand local) 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))); return BitcastIfNeeded(dstType, srcType, Load(GetType(srcType), GetLocalPointer(local)));
} }
public Instruction GetArgument(AggregateType dstType, AstOperand funcArg) 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))); 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) 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)); 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 return type switch

View File

@@ -23,11 +23,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
DeclareParameters(context, function.OutArguments, function.InArguments.Length); 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) 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); var spvArg = context.FunctionParameter(argPointerType);
context.DeclareArgument(argIndex++, spvArg); context.DeclareArgument(argIndex++, spvArg);
@@ -38,7 +38,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
{ {
foreach (AstOperand local in function.Locals) 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); var spvLocal = context.Variable(localPointerType, StorageClass.Function);
context.AddLocalVariable(spvLocal); context.AddLocalVariable(spvLocal);
@@ -62,7 +62,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
for (int i = 0; i < function.InArguments.Length; i++) 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 localPointerType = context.TypePointer(StorageClass.Function, context.GetType(type));
var spvLocal = context.Variable(localPointerType, StorageClass.Function); var spvLocal = context.Variable(localPointerType, StorageClass.Function);
@@ -303,7 +303,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
var dim = GetDim(descriptor.Type); var dim = GetDim(descriptor.Type);
var imageType = context.TypeImage( var imageType = context.TypeImage(
context.GetType(meta.Format.GetComponentType().Convert()), context.GetType(meta.Format.GetComponentType()),
dim, dim,
descriptor.Type.HasFlag(SamplerType.Shadow), descriptor.Type.HasFlag(SamplerType.Shadow),
descriptor.Type.HasFlag(SamplerType.Array), descriptor.Type.HasFlag(SamplerType.Array),
@@ -652,7 +652,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
if (components > 1) if (components > 1)
{ {
attr &= ~0xf; 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; 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 System;
using static Spv.Specification; using static Spv.Specification;
@@ -7,20 +6,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
{ {
static class EnumConversion 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) public static ExecutionModel Convert(this ShaderStage stage)
{ {
return stage switch return stage switch

View File

@@ -4,6 +4,7 @@ using Ryujinx.Graphics.Shader.Translation;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Numerics;
using static Spv.Specification; using static Spv.Specification;
namespace Ryujinx.Graphics.Shader.CodeGen.Spirv namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
@@ -146,6 +147,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
Add(Instruction.Truncate, GenerateTruncate); Add(Instruction.Truncate, GenerateTruncate);
Add(Instruction.UnpackDouble2x32, GenerateUnpackDouble2x32); Add(Instruction.UnpackDouble2x32, GenerateUnpackDouble2x32);
Add(Instruction.UnpackHalf2x16, GenerateUnpackHalf2x16); Add(Instruction.UnpackHalf2x16, GenerateUnpackHalf2x16);
Add(Instruction.VectorExtract, GenerateVectorExtract);
Add(Instruction.VoteAll, GenerateVoteAll); Add(Instruction.VoteAll, GenerateVoteAll);
Add(Instruction.VoteAllEqual, GenerateVoteAllEqual); Add(Instruction.VoteAllEqual, GenerateVoteAllEqual);
Add(Instruction.VoteAny, GenerateVoteAny); Add(Instruction.VoteAny, GenerateVoteAny);
@@ -317,7 +319,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
} }
else else
{ {
var type = function.GetArgumentType(i).Convert(); var type = function.GetArgumentType(i);
var value = context.Get(type, operand); var value = context.Get(type, operand);
var spvLocal = spvLocals[i]; 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); var result = context.FunctionCall(context.GetType(retType), spvFunc, args);
return new OperationResult(retType, result); 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. // TODO: Bindless texture support. For now we just return 0/do nothing.
if (isBindless) if (isBindless)
{ {
return new OperationResult(componentType.Convert(), componentType switch return new OperationResult(componentType, componentType switch
{ {
VariableType.S32 => context.Constant(context.TypeS32(), 0), AggregateType.S32 => context.Constant(context.TypeS32(), 0),
VariableType.U32 => context.Constant(context.TypeU32(), 0u), AggregateType.U32 => context.Constant(context.TypeU32(), 0u),
_ => context.Constant(context.TypeFP32(), 0f), _ => context.Constant(context.TypeFP32(), 0f),
}); });
} }
@@ -652,13 +654,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
pCoords = Src(AggregateType.S32); 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 imageType, var imageVariable) = context.Images[new TextureMeta(texOp.CbufSlot, texOp.Handle, texOp.Format)];
var image = context.Load(imageType, imageVariable); var image = context.Load(imageType, imageVariable);
SpvInstruction resultType = context.GetType(componentType.Convert()); SpvInstruction resultType = context.GetType(componentType);
SpvInstruction imagePointerType = context.TypePointer(StorageClass.Image, resultType); SpvInstruction imagePointerType = context.TypePointer(StorageClass.Image, resultType);
var pointer = context.ImageTexelPointer(imagePointerType, imageVariable, pCoords, context.Constant(context.TypeU32(), 0)); 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 var result = (texOp.Flags & TextureFlags.AtomicMask) switch
{ {
TextureFlags.Add => context.AtomicIAdd(resultType, pointer, one, zero, value), 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.AtomicSMin(resultType, pointer, one, zero, value)
: context.AtomicUMin(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.AtomicSMax(resultType, pointer, one, zero, value)
: context.AtomicUMax(resultType, pointer, one, zero, value), : context.AtomicUMax(resultType, pointer, one, zero, value),
TextureFlags.Increment => context.AtomicIIncrement(resultType, pointer, one, zero), 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.BitwiseOr => context.AtomicOr(resultType, pointer, one, zero, value),
TextureFlags.BitwiseXor => context.AtomicXor(resultType, pointer, one, zero, value), TextureFlags.BitwiseXor => context.AtomicXor(resultType, pointer, one, zero, value),
TextureFlags.Swap => context.AtomicExchange(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), _ => 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) 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. // TODO: Bindless texture support. For now we just return 0/do nothing.
if (isBindless) if (isBindless)
{ {
var zero = componentType switch return GetZeroOperationResult(context, texOp, componentType, isVector: true);
{
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);
} }
bool isArray = (texOp.Type & SamplerType.Array) != 0; 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 imageType, var imageVariable) = context.Images[new TextureMeta(texOp.CbufSlot, texOp.Handle, texOp.Format)];
var image = context.Load(imageType, imageVariable); 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 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) private static OperationResult GenerateImageStore(CodeGenContext context, AstOperation operation)
@@ -823,20 +819,20 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
{ {
if (srcIndex < texOp.SourcesCount) if (srcIndex < texOp.SourcesCount)
{ {
cElems[i] = Src(componentType.Convert()); cElems[i] = Src(componentType);
} }
else else
{ {
cElems[i] = componentType switch cElems[i] = componentType switch
{ {
VariableType.S32 => context.Constant(context.TypeS32(), 0), AggregateType.S32 => context.Constant(context.TypeS32(), 0),
VariableType.U32 => context.Constant(context.TypeU32(), 0u), AggregateType.U32 => context.Constant(context.TypeU32(), 0u),
_ => context.Constant(context.TypeFP32(), 0f), _ => 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)]; (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); 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); return new OperationResult(AggregateType.FP32, result);
} }
@@ -1268,7 +1264,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
var validLocal = (AstOperand)operation.GetSource(3); 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); return new OperationResult(AggregateType.FP32, result);
} }
@@ -1294,7 +1290,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
var validLocal = (AstOperand)operation.GetSource(3); 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); return new OperationResult(AggregateType.FP32, result);
} }
@@ -1324,7 +1320,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
var validLocal = (AstOperand)operation.GetSource(3); 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); return new OperationResult(AggregateType.FP32, result);
} }
@@ -1485,10 +1481,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
bool isMultisample = (texOp.Type & SamplerType.Multisample) != 0; bool isMultisample = (texOp.Type & SamplerType.Multisample) != 0;
bool isShadow = (texOp.Type & SamplerType.Shadow) != 0; bool isShadow = (texOp.Type & SamplerType.Shadow) != 0;
bool colorIsVector = isGather || !isShadow;
// TODO: Bindless texture support. For now we just return 0. // TODO: Bindless texture support. For now we just return 0.
if (isBindless) 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. // This combination is valid, but not available on GLSL.
@@ -1705,7 +1703,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
operandsList.Add(sample); operandsList.Add(sample);
} }
bool colorIsVector = isGather || !isShadow;
var resultType = colorIsVector ? context.TypeVector(context.TypeFP32(), 4) : context.TypeFP32(); var resultType = colorIsVector ? context.TypeVector(context.TypeFP32(), 4) : context.TypeFP32();
var meta = new TextureMeta(texOp.CbufSlot, texOp.Handle, texOp.Format); 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); result = context.ImageSampleImplicitLod(resultType, image, pCoords, operandsMask, operands);
} }
var swizzledResultType = AggregateType.FP32;
if (colorIsVector) 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) private static OperationResult GenerateTextureSize(CodeGenContext context, AstOperation operation)
@@ -1862,6 +1863,26 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
return new OperationResult(AggregateType.FP32, result); 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) private static OperationResult GenerateVoteAll(CodeGenContext context, AstOperation operation)
{ {
var execution = context.Constant(context.TypeU32(), Scope.Subgroup); var execution = context.Constant(context.TypeU32(), Scope.Subgroup);
@@ -2044,6 +2065,64 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
context.AddLabel(loopEnd); 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) private static SpvInstruction GetStorageElemPointer(CodeGenContext context, AstOperation operation)
{ {
var sbVariable = context.StorageBuffersArray; 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++) for (int funcIndex = 0; funcIndex < info.Functions.Count; funcIndex++)
{ {
var function = info.Functions[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]; var funcArgs = new SpvInstruction[function.InArguments.Length + function.OutArguments.Length];
for (int argIndex = 0; argIndex < funcArgs.Length; argIndex++) 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); var argPointerType = context.TypePointer(StorageClass.Function, argType);
funcArgs[argIndex] = argPointerType; funcArgs[argIndex] = argPointerType;
} }
@@ -387,7 +387,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
if (dest.Type == OperandType.LocalVariable) 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); context.Store(context.GetLocalPointer(dest), source);
} }
else if (dest.Type == OperandType.Attribute || dest.Type == OperandType.AttributePerPatch) 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) 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); context.Store(context.GetArgumentPointer(dest), source);
} }
else else

View File

@@ -1,7 +1,9 @@
using Ryujinx.Graphics.Shader.Decoders; using Ryujinx.Graphics.Shader.Decoders;
using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.IntermediateRepresentation;
using Ryujinx.Graphics.Shader.Translation; using Ryujinx.Graphics.Shader.Translation;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Numerics;
using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper; using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper;
using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
@@ -217,15 +219,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
return context.Copy(Register(srcB++, RegisterType.Gpr)); return context.Copy(Register(srcB++, RegisterType.Gpr));
} }
Operand GetDest() Operand destOperand = dest != RegisterConsts.RegisterZeroIndex ? Register(dest, RegisterType.Gpr) : null;
{
if (dest >= RegisterConsts.RegisterZeroIndex)
{
return null;
}
return Register(dest++, RegisterType.Gpr);
}
List<Operand> sourcesList = new List<Operand>(); List<Operand> sourcesList = new List<Operand>();
@@ -291,7 +285,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
flags, flags,
imm, imm,
0, 0,
GetDest(), new[] { destOperand },
sources); sources);
context.Add(operation); context.Add(operation);
@@ -371,36 +365,40 @@ namespace Ryujinx.Graphics.Shader.Instructions
if (useComponents) if (useComponents)
{ {
for (int compMask = (int)componentMask, compIndex = 0; compMask != 0; compMask >>= 1, compIndex++) Operand[] dests = new Operand[BitOperations.PopCount((uint)componentMask)];
{
if ((compMask & 1) == 0)
{
continue;
}
if (srcB == RegisterConsts.RegisterZeroIndex) int outputIndex = 0;
for (int i = 0; i < dests.Length; i++)
{
if (srcB + i >= RegisterConsts.RegisterZeroIndex)
{ {
break; break;
} }
Operand rd = Register(srcB++, RegisterType.Gpr); dests[outputIndex++] = Register(srcB + i, 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);
} }
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 else
{ {
@@ -412,35 +410,45 @@ namespace Ryujinx.Graphics.Shader.Instructions
} }
int components = GetComponents(size); 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; break;
} }
Operand rd = Register(srcB++, RegisterType.Gpr); dests[outputIndex++] = Register(srcB + i, RegisterType.Gpr);
}
TextureOperation operation = context.CreateTextureOperation( if (outputIndex != dests.Length)
Instruction.ImageLoad, {
type, Array.Resize(ref dests, outputIndex);
GetTextureFormat(size), }
flags,
handle,
compIndex,
rd,
sources);
context.Add(operation); TextureOperation operation = context.CreateTextureOperation(
Instruction.ImageLoad,
type,
GetTextureFormat(size),
flags,
handle,
compMask,
dests,
sources);
switch (size) context.Add(operation);
{
case SuSize.U8: context.Copy(rd, ZeroExtendTo32(context, rd, 8)); break; switch (size)
case SuSize.U16: context.Copy(rd, ZeroExtendTo32(context, rd, 16)); break; {
case SuSize.S8: context.Copy(rd, SignExtendTo32(context, rd, 8)); break; case SuSize.U8: context.Copy(dests[0], ZeroExtendTo32(context, dests[0], 8)); break;
case SuSize.S16: context.Copy(rd, SignExtendTo32(context, rd, 16)); 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 Ryujinx.Graphics.Shader.Translation;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Numerics;
using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
@@ -303,42 +304,37 @@ namespace Ryujinx.Graphics.Shader.Instructions
} }
Operand[] sources = sourcesList.ToArray(); 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; int handle = !isBindless ? imm : 0;
for (int compMask = componentMask, compIndex = 0; compMask != 0; compMask >>= 1, compIndex++) TextureOperation operation = context.CreateTextureOperation(
{ Instruction.TextureSample,
if ((compMask & 1) != 0) type,
{ flags,
Operand dest = GetDest(); handle,
componentMask,
dests,
sources);
if (dest == null) context.Add(operation);
{
break;
}
TextureOperation operation = context.CreateTextureOperation(
Instruction.TextureSample,
type,
flags,
handle,
compIndex,
dest,
sources);
context.Add(operation);
}
}
} }
private static void EmitTexs( private static void EmitTexs(
@@ -624,18 +620,23 @@ namespace Ryujinx.Graphics.Shader.Instructions
Operand[] rd0 = new Operand[2] { ConstF(0), ConstF(0) }; Operand[] rd0 = new Operand[2] { ConstF(0), ConstF(0) };
Operand[] rd1 = 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 high = i >> 1;
int low = destIncrement & 1; int low = i & 1;
destIncrement++;
if (isF16) if (isF16)
{ {
return high != 0 dests[outputIndex++] = high != 0
? (rd1[low] = Local()) ? (rd1[low] = Local())
: (rd0[low] = Local()); : (rd0[low] = Local());
} }
@@ -648,30 +649,26 @@ namespace Ryujinx.Graphics.Shader.Instructions
rdIndex += low; rdIndex += low;
} }
return Register(rdIndex, RegisterType.Gpr); dests[outputIndex++] = Register(rdIndex, RegisterType.Gpr);
} }
} }
int handle = imm; if (outputIndex != dests.Length)
int componentMask = _maskLut[dest2 == RegisterConsts.RegisterZeroIndex ? 0 : 1, writeMask];
for (int compMask = componentMask, compIndex = 0; compMask != 0; compMask >>= 1, compIndex++)
{ {
if ((compMask & 1) != 0) Array.Resize(ref dests, outputIndex);
{
TextureOperation operation = context.CreateTextureOperation(
Instruction.TextureSample,
type,
flags,
handle,
compIndex,
GetDest(),
sources);
context.Add(operation);
}
} }
TextureOperation operation = context.CreateTextureOperation(
Instruction.TextureSample,
type,
flags,
handle,
componentMask,
dests,
sources);
context.Add(operation);
if (isF16) if (isF16)
{ {
context.Copy(Register(dest, RegisterType.Gpr), context.PackHalf2x16(rd0[0], rd0[1])); 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)); sourcesList.Add(Const((int)component));
Operand[] sources = sourcesList.ToArray(); 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; int handle = imm;
for (int compMask = componentMask, compIndex = 0; compMask != 0; compMask >>= 1, compIndex++) TextureOperation operation = context.CreateTextureOperation(
{ Instruction.TextureSample,
if ((compMask & 1) != 0) type,
{ flags,
Operand destOperand = GetDest(); handle,
componentMask,
dests,
sources);
if (destOperand == null) context.Add(operation);
{
break;
}
TextureOperation operation = context.CreateTextureOperation(
Instruction.TextureSample,
type,
flags,
handle,
compIndex,
destOperand,
sources);
context.Add(operation);
}
}
} }
private static void EmitTmml( private static void EmitTmml(
@@ -951,7 +943,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
flags, flags,
handle, handle,
compIndex ^ 1, // The instruction component order is the inverse of GLSL's. compIndex ^ 1, // The instruction component order is the inverse of GLSL's.
tempDest, new[] { tempDest },
sources); sources);
context.Add(operation); context.Add(operation);
@@ -1071,42 +1063,37 @@ namespace Ryujinx.Graphics.Shader.Instructions
} }
Operand[] sources = sourcesList.ToArray(); 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; int handle = imm;
for (int compMask = componentMask, compIndex = 0; compMask != 0; compMask >>= 1, compIndex++) TextureOperation operation = context.CreateTextureOperation(
{ Instruction.TextureSample,
if ((compMask & 1) != 0) type,
{ flags,
Operand destOperand = GetDest(); handle,
componentMask,
dests,
sources);
if (destOperand == null) context.Add(operation);
{
break;
}
TextureOperation operation = context.CreateTextureOperation(
Instruction.TextureSample,
type,
flags,
handle,
compIndex,
destOperand,
sources);
context.Add(operation);
}
}
} }
private static void EmitTxq( private static void EmitTxq(
@@ -1188,7 +1175,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
flags, flags,
imm, imm,
compIndex, compIndex,
destOperand, new[] { destOperand },
sources); sources);
context.Add(operation); context.Add(operation);

View File

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

View File

@@ -62,18 +62,25 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
Inst = inst; Inst = inst;
Index = index; Index = index;
// The array may be modified externally, so we store a copy. if (dests != null)
_dests = (Operand[])dests.Clone();
for (int dstIndex = 0; dstIndex < dests.Length; dstIndex++)
{ {
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) 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 cbufSlot,
int handle, int handle,
int compIndex, int compIndex,
Operand dest, Operand[] dests,
Operand[] sources) : base(inst, compIndex, dest, sources) Operand[] sources) : base(inst, compIndex, dests, sources)
{ {
Type = type; Type = type;
Format = format; Format = format;
@@ -36,8 +36,8 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
TextureFlags flags, TextureFlags flags,
int handle, int handle,
int compIndex, int compIndex,
Operand dest, Operand[] dests,
Operand[] sources) : this(inst, type, format, flags, DefaultCbufSlot, handle, compIndex, dest, sources) 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; using System;
namespace Ryujinx.Graphics.Shader namespace Ryujinx.Graphics.Shader
@@ -66,7 +66,7 @@ namespace Ryujinx.Graphics.Shader
return typeName; 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 string typeName = (type & SamplerType.Mask) switch
{ {
@@ -90,8 +90,8 @@ namespace Ryujinx.Graphics.Shader
switch (componentType) switch (componentType)
{ {
case VariableType.U32: typeName = 'u' + typeName; break; case AggregateType.U32: typeName = 'u' + typeName; break;
case VariableType.S32: typeName = 'i' + typeName; break; case AggregateType.S32: typeName = 'i' + typeName; break;
} }
return typeName; return typeName;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -2,6 +2,7 @@ using Ryujinx.Graphics.Shader.IntermediateRepresentation;
using Ryujinx.Graphics.Shader.Translation; using Ryujinx.Graphics.Shader.Translation;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Numerics;
namespace Ryujinx.Graphics.Shader.StructuredIr namespace Ryujinx.Graphics.Shader.StructuredIr
{ {
@@ -17,19 +18,19 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
BasicBlock[] blocks = function.Blocks; 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]; AggregateType[] inArguments = new AggregateType[function.InArgumentsCount];
VariableType[] outArguments = new VariableType[function.OutArgumentsCount]; AggregateType[] outArguments = new AggregateType[function.OutArgumentsCount];
for (int i = 0; i < inArguments.Length; i++) for (int i = 0; i < inArguments.Length; i++)
{ {
inArguments[i] = VariableType.S32; inArguments[i] = AggregateType.S32;
} }
for (int i = 0; i < outArguments.Length; i++) 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); 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 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]; IAstNode[] sources = new IAstNode[sourcesCount + outDestsCount];
@@ -141,7 +144,52 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
sources); 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); 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 // 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 bitwise operation with the value, as it is likely to be used as
// a bool in the end. // a bool in the end.
if (IsBitwiseInst(inst) && AreAllSourceTypesEqual(sources, VariableType.Bool)) if (IsBitwiseInst(inst) && AreAllSourceTypesEqual(sources, AggregateType.Bool))
{ {
inst = GetLogicalFromBitwiseInst(inst); inst = GetLogicalFromBitwiseInst(inst);
} }
@@ -159,9 +207,9 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
if (isCondSel || isCopy) 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; 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>(); 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) foreach (IAstNode node in sources)
{ {
@@ -336,6 +384,16 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
return true; return true;
} }
private static bool IsVectorDestInst(Instruction inst)
{
return inst switch
{
Instruction.ImageLoad or
Instruction.TextureSample => true,
_ => false
};
}
private static bool IsBranchInst(Instruction inst) private static bool IsBranchInst(Instruction inst)
{ {
return inst switch return inst switch

View File

@@ -80,9 +80,9 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
public void EnterFunction( public void EnterFunction(
int blocksCount, int blocksCount,
string name, string name,
VariableType returnType, AggregateType returnType,
VariableType[] inArguments, AggregateType[] inArguments,
VariableType[] outArguments) AggregateType[] outArguments)
{ {
_loopTails = new HashSet<BasicBlock>(); _loopTails = new HashSet<BasicBlock>();
@@ -218,7 +218,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
return gotoTempAsg; return gotoTempAsg;
} }
AstOperand gotoTemp = NewTemp(VariableType.Bool); AstOperand gotoTemp = NewTemp(AggregateType.Bool);
gotoTempAsg = Assign(gotoTemp, Const(IrConsts.False)); gotoTempAsg = Assign(gotoTemp, Const(IrConsts.False));
@@ -306,7 +306,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
return _gotos.ToArray(); return _gotos.ToArray();
} }
private AstOperand NewTemp(VariableType type) public AstOperand NewTemp(AggregateType type)
{ {
AstOperand newTemp = Local(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 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) switch (format)
{ {
@@ -109,7 +109,7 @@ namespace Ryujinx.Graphics.Shader
case TextureFormat.R16G16B16A16Uint: case TextureFormat.R16G16B16A16Uint:
case TextureFormat.R32G32B32A32Uint: case TextureFormat.R32G32B32A32Uint:
case TextureFormat.R10G10B10A2Uint: case TextureFormat.R10G10B10A2Uint:
return VariableType.U32; return AggregateType.U32;
case TextureFormat.R8Sint: case TextureFormat.R8Sint:
case TextureFormat.R16Sint: case TextureFormat.R16Sint:
case TextureFormat.R32Sint: case TextureFormat.R32Sint:
@@ -119,10 +119,10 @@ namespace Ryujinx.Graphics.Shader
case TextureFormat.R8G8B8A8Sint: case TextureFormat.R8G8B8A8Sint:
case TextureFormat.R16G16B16A16Sint: case TextureFormat.R16G16B16A16Sint:
case TextureFormat.R32G32B32A32Sint: case TextureFormat.R32G32B32A32Sint:
return VariableType.S32; return AggregateType.S32;
} }
return VariableType.F32; return AggregateType.FP32;
} }
} }
} }

View File

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

View File

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

View File

@@ -385,15 +385,6 @@ namespace Ryujinx.Graphics.Shader.Translation
int componentIndex = texOp.Index; 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 Float(Operand value)
{ {
Operand res = Local(); Operand res = Local();
@@ -436,7 +427,7 @@ namespace Ryujinx.Graphics.Shader.Translation
texOp.CbufSlot, texOp.CbufSlot,
texOp.Handle, texOp.Handle,
index, index,
coordSize, new[] { coordSize },
texSizeSources)); texSizeSources));
config.SetUsedTexture(Instruction.TextureSize, texOp.Type, texOp.Format, texOp.Flags, texOp.CbufSlot, texOp.Handle); 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), // Technically, non-constant texture offsets are not allowed (according to the spec),
// however some GPUs does support that. // however some GPUs does support that.
// For GPUs where it is not supported, we can replace the instruction with the following: // 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. // 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). // 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 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 // For textureGatherOffset, we split the operation into up to 4 operations, one for each component
// to read the 4 pixels separately, and just replace it with 4 textureGather with a different offset // that is accessed, where each textureGather operation has a different offset for each pixel.
// for each pixel. if (hasInvalidOffset && isGather && !isShadow)
if (hasInvalidOffset)
{ {
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]; continue;
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 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++) 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); config.SetUsedTexture(Instruction.TextureSize, texOp.Type, texOp.Format, texOp.Flags, texOp.CbufSlot, texOp.Handle);
Operand offset = Local(); 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]; 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)); 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) TextureOperation newTexOp = new TextureOperation(
{ Instruction.TextureSample,
Operand gatherComponent = sources[dstIndex - 1]; texOp.Type,
texOp.Format,
texOp.Flags & ~(TextureFlags.Offset | TextureFlags.Offsets),
texOp.CbufSlot,
texOp.Handle,
componentIndex,
dests,
sources);
Debug.Assert(gatherComponent.Type == OperandType.Constant); node = node.List.AddBefore(node, newTexOp);
componentIndex = gatherComponent.Value;
}
} }
TextureOperation newTexOp = new TextureOperation( node.List.Remove(oldNode);
Instruction.TextureSample,
texOp.Type,
texOp.Format,
texOp.Flags & ~(TextureFlags.Offset | TextureFlags.Offsets),
texOp.CbufSlot,
texOp.Handle,
componentIndex,
texOp.Dest,
sources);
for (int index = 0; index < texOp.SourcesCount; index++) for (int index = 0; index < texOp.SourcesCount; index++)
{ {
texOp.SetSource(index, null); texOp.SetSource(index, null);
} }
LinkedListNode<INode> oldNode = node;
node = node.List.AddBefore(node, newTexOp);
node.List.Remove(oldNode);
return node; 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) private static LinkedListNode<INode> InsertSnormNormalization(LinkedListNode<INode> node, ShaderConfig config)
{ {
TextureOperation texOp = (TextureOperation)node.Value; TextureOperation texOp = (TextureOperation)node.Value;
@@ -604,27 +675,32 @@ namespace Ryujinx.Graphics.Shader.Translation
// Do normalization. We assume SINT formats are being used // Do normalization. We assume SINT formats are being used
// as replacement for SNORM (which is not supported). // as replacement for SNORM (which is not supported).
INode[] uses = texOp.Dest.UseOps.ToArray(); for (int i = 0; i < texOp.DestsCount; i++)
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)
{ {
if (useOp is not Operation op) Operand dest = texOp.GetDest(i);
{
continue;
}
// Replace all uses of the texture pixel value with the normalized value. INode[] uses = dest.UseOps.ToArray();
for (int index = 0; index < op.SourcesCount; index++)
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) public void AddDependency(int cbIndex, CommandBufferScoped dependencyCbs)
{ {
Debug.Assert(_commandBuffers[cbIndex].InUse); Debug.Assert(_commandBuffers[cbIndex].InUse);

View File

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

View File

@@ -11,7 +11,13 @@ namespace Ryujinx.Graphics.Vulkan
{ {
public ulong ID; public ulong ID;
public MultiFenceHolder Waitable; public MultiFenceHolder Waitable;
public ulong FlushId;
public bool Signalled; public bool Signalled;
public bool NeedsFlush(ulong currentFlushId)
{
return (long)(FlushId - currentFlushId) >= 0;
}
} }
private ulong _firstHandle = 0; private ulong _firstHandle = 0;
@@ -19,6 +25,7 @@ namespace Ryujinx.Graphics.Vulkan
private readonly VulkanRenderer _gd; private readonly VulkanRenderer _gd;
private readonly Device _device; private readonly Device _device;
private List<SyncHandle> _handles; private List<SyncHandle> _handles;
private ulong FlushId;
public SyncManager(VulkanRenderer gd, Device device) public SyncManager(VulkanRenderer gd, Device device)
{ {
@@ -27,17 +34,33 @@ namespace Ryujinx.Graphics.Vulkan
_handles = new List<SyncHandle>(); _handles = new List<SyncHandle>();
} }
public void Create(ulong id) public void RegisterFlush()
{ {
MultiFenceHolder waitable = new MultiFenceHolder(); FlushId++;
}
_gd.FlushAllCommands(); public void Create(ulong id, bool strict)
_gd.CommandBufferPool.AddWaitable(waitable); {
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 SyncHandle handle = new SyncHandle
{ {
ID = id, ID = id,
Waitable = waitable Waitable = waitable,
FlushId = flushId
}; };
lock (_handles) lock (_handles)
@@ -107,6 +130,17 @@ namespace Ryujinx.Graphics.Vulkan
return; return;
} }
if (result.NeedsFlush(FlushId))
{
_gd.InterruptAction(() =>
{
if (result.NeedsFlush(FlushId))
{
_gd.FlushAllCommands();
}
});
}
bool signaled = result.Signalled || result.Waitable.WaitForFences(_gd.Api, _device, 1000000000); bool signaled = result.Signalled || result.Waitable.WaitForFences(_gd.Api, _device, 1000000000);
if (!signaled) if (!signaled)
{ {
@@ -132,7 +166,7 @@ namespace Ryujinx.Graphics.Vulkan
first = _handles.FirstOrDefault(); first = _handles.FirstOrDefault();
} }
if (first == null) break; if (first == null || first.NeedsFlush(FlushId)) break;
bool signaled = first.Waitable.WaitForFences(_gd.Api, _device, 0); bool signaled = first.Waitable.WaitForFences(_gd.Api, _device, 0);
if (signaled) if (signaled)

View File

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

View File

@@ -298,6 +298,18 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
return ResultCode.Success; 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)] [CommandHipc(68)]
// SetAutoSleepDisabled(u8) // SetAutoSleepDisabled(u8)
public ResultCode SetAutoSleepDisabled(ServiceCtx context) public ResultCode SetAutoSleepDisabled(ServiceCtx context)
@@ -318,6 +330,19 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
return ResultCode.Success; 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+ [CommandHipc(80)] // 4.0.0+
// SetWirelessPriorityMode(s32 wireless_priority_mode) // SetWirelessPriorityMode(s32 wireless_priority_mode)
public ResultCode SetWirelessPriorityMode(ServiceCtx context) public ResultCode SetWirelessPriorityMode(ServiceCtx context)

View File

@@ -39,14 +39,10 @@ namespace Ryujinx.Memory.Tests
} }
[Test] [Test]
// Memory aliasing tests fail on CI at the moment.
[Platform(Exclude = "MacOsX")]
public void Test_Alias() 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 backing = new MemoryBlock(0x10000, MemoryAllocationFlags.Mirrorable);
using MemoryBlock toAlias = new MemoryBlock(0x10000, MemoryAllocationFlags.Reserve | MemoryAllocationFlags.ViewCompatible); using MemoryBlock toAlias = new MemoryBlock(0x10000, MemoryAllocationFlags.Reserve | MemoryAllocationFlags.ViewCompatible);
@@ -58,14 +54,10 @@ namespace Ryujinx.Memory.Tests
} }
[Test] [Test]
// Memory aliasing tests fail on CI at the moment.
[Platform(Exclude = "MacOsX")]
public void Test_AliasRandom() 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 backing = new MemoryBlock(0x80000, MemoryAllocationFlags.Mirrorable);
using MemoryBlock toAlias = new MemoryBlock(0x80000, MemoryAllocationFlags.Reserve | MemoryAllocationFlags.ViewCompatible); using MemoryBlock toAlias = new MemoryBlock(0x80000, MemoryAllocationFlags.Reserve | MemoryAllocationFlags.ViewCompatible);
@@ -94,14 +86,10 @@ namespace Ryujinx.Memory.Tests
} }
[Test] [Test]
// Memory aliasing tests fail on CI at the moment.
[Platform(Exclude = "MacOsX")]
public void Test_AliasMapLeak() public void Test_AliasMapLeak()
{ {
if (OperatingSystem.IsMacOS())
{
// Memory aliasing tests fail on CI at the moment.
return;
}
ulong pageSize = 4096; 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. 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> /// <remarks>
/// It's an error to use the memory block after disposal. /// It's an error to use the memory block after disposal.
/// </remarks> /// </remarks>
public void Dispose() => FreeMemory(); public void Dispose()
{
FreeMemory();
GC.SuppressFinalize(this);
}
~MemoryBlock() => FreeMemory(); ~MemoryBlock() => FreeMemory();

View File

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

View File

@@ -68,53 +68,6 @@ namespace Ryujinx.Ui.App.Common
_cancellationToken?.Cancel(); _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) public void ReadControlData(IFileSystem controlFs, Span<byte> outProperty)
{ {
using var controlFile = new UniqueRef<IFile>(); using var controlFile = new UniqueRef<IFile>();
@@ -151,26 +104,28 @@ namespace Ryujinx.Ui.App.Common
continue; continue;
} }
foreach (string app in GetFilesInDirectory(appDir)) try
{ {
if (_cancellationToken.Token.IsCancellationRequested) foreach (string app in Directory.EnumerateFiles(appDir, "*", SearchOption.AllDirectories))
{ {
return; if (_cancellationToken.Token.IsCancellationRequested)
} {
return;
string extension = Path.GetExtension(app).ToLower(); }
if ((extension == ".nsp") || string extension = Path.GetExtension(app).ToLower();
(extension == ".pfs0") ||
(extension == ".xci") || if (!File.GetAttributes(app).HasFlag(FileAttributes.Hidden) && extension is ".nsp" or ".pfs0" or ".xci" or ".nca" or ".nro" or ".nso")
(extension == ".nca") || {
(extension == ".nro") || applications.Add(app);
(extension == ".nso")) numApplicationsFound++;
{ }
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 // 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