mirror of
https://github.com/par274/sharpemu.git
synced 2026-07-30 06:29:46 +08:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 26cf72189c |
@@ -45,9 +45,6 @@ public sealed class GuiSettings
|
|||||||
/// <summary>UI language, matching a file code under Languages/ (e.g. "en", "tr").</summary>
|
/// <summary>UI language, matching a file code under Languages/ (e.g. "en", "tr").</summary>
|
||||||
public string Language { get; set; } = "en";
|
public string Language { get; set; } = "en";
|
||||||
|
|
||||||
/// <summary>Default text-entry profile exposed to games.</summary>
|
|
||||||
public string DefaultProfile { get; set; } = "Sharp";
|
|
||||||
|
|
||||||
/// <summary>Publish launcher/game status to Discord Rich Presence.</summary>
|
/// <summary>Publish launcher/game status to Discord Rich Presence.</summary>
|
||||||
public bool DiscordRichPresence { get; set; } = true;
|
public bool DiscordRichPresence { get; set; } = true;
|
||||||
|
|
||||||
@@ -115,18 +112,6 @@ public sealed class GuiSettings
|
|||||||
settings.EnvironmentToggles = FilterNullOrEmpty(settings.EnvironmentToggles);
|
settings.EnvironmentToggles = FilterNullOrEmpty(settings.EnvironmentToggles);
|
||||||
settings.LogLevel ??= "Info";
|
settings.LogLevel ??= "Info";
|
||||||
settings.Language ??= "en";
|
settings.Language ??= "en";
|
||||||
var legacyProfile = settings.EnvironmentToggles
|
|
||||||
.Select(entry => entry.Split('=', 2, StringSplitOptions.TrimEntries))
|
|
||||||
.FirstOrDefault(parts =>
|
|
||||||
parts.Length == 2 &&
|
|
||||||
string.Equals(parts[0], "SHARPEMU_DEFAULT_PROFILE", StringComparison.OrdinalIgnoreCase));
|
|
||||||
settings.EnvironmentToggles.RemoveAll(entry =>
|
|
||||||
string.Equals(
|
|
||||||
entry.Split('=', 2, StringSplitOptions.TrimEntries)[0],
|
|
||||||
"SHARPEMU_DEFAULT_PROFILE",
|
|
||||||
StringComparison.OrdinalIgnoreCase));
|
|
||||||
settings.DefaultProfile = NormalizeDefaultProfile(
|
|
||||||
legacyProfile is { Length: 2 } ? legacyProfile[1] : settings.DefaultProfile);
|
|
||||||
settings.DiscordClientId ??= "1525606762248540221";
|
settings.DiscordClientId ??= "1525606762248540221";
|
||||||
if (settings.RenderResolutionScale <= 0 || settings.RenderResolutionScale > 2.0)
|
if (settings.RenderResolutionScale <= 0 || settings.RenderResolutionScale > 2.0)
|
||||||
{
|
{
|
||||||
@@ -167,12 +152,6 @@ public sealed class GuiSettings
|
|||||||
return $"{width}x{height}";
|
return $"{width}x{height}";
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static string NormalizeDefaultProfile(string? value)
|
|
||||||
{
|
|
||||||
var trimmed = value?.Trim();
|
|
||||||
return string.IsNullOrEmpty(trimmed) ? "Sharp" : trimmed;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Save()
|
public void Save()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|||||||
@@ -66,8 +66,6 @@
|
|||||||
|
|
||||||
"Options.Language.Label": "لغة المحاكي",
|
"Options.Language.Label": "لغة المحاكي",
|
||||||
"Options.Language.Desc": "اللغة المستخدمة في جميع أنحاء المشغل. تُطبق فوراً.",
|
"Options.Language.Desc": "اللغة المستخدمة في جميع أنحاء المشغل. تُطبق فوراً.",
|
||||||
"Options.DefaultProfile.Label": "اسم الملف الشخصي الافتراضي",
|
|
||||||
"Options.DefaultProfile.Desc": "الاسم المستخدم عندما تطلب لعبة إدخال نص. القيمة الافتراضية هي Sharp.",
|
|
||||||
|
|
||||||
"Common.On": "تشغيل",
|
"Common.On": "تشغيل",
|
||||||
"Common.Off": "إيقاف",
|
"Common.Off": "إيقاف",
|
||||||
|
|||||||
@@ -76,8 +76,6 @@
|
|||||||
|
|
||||||
"Options.Language.Label": "Idioma do emulador",
|
"Options.Language.Label": "Idioma do emulador",
|
||||||
"Options.Language.Desc": "Idioma usado em toda a interface do emulador. A alteração é aplicada imediatamente.",
|
"Options.Language.Desc": "Idioma usado em toda a interface do emulador. A alteração é aplicada imediatamente.",
|
||||||
"Options.DefaultProfile.Label": "Nome de perfil padrão",
|
|
||||||
"Options.DefaultProfile.Desc": "Nome usado quando um jogo solicita entrada de texto. O padrão é Sharp.",
|
|
||||||
|
|
||||||
"Common.On": "Ativado",
|
"Common.On": "Ativado",
|
||||||
"Common.Off": "Desativado",
|
"Common.Off": "Desativado",
|
||||||
|
|||||||
@@ -66,8 +66,6 @@
|
|||||||
|
|
||||||
"Options.Language.Label": "Emulator-Sprache",
|
"Options.Language.Label": "Emulator-Sprache",
|
||||||
"Options.Language.Desc": "Sprache der Benutzeroberfläche. Wird sofort angewendet.",
|
"Options.Language.Desc": "Sprache der Benutzeroberfläche. Wird sofort angewendet.",
|
||||||
"Options.DefaultProfile.Label": "Standardprofilname",
|
|
||||||
"Options.DefaultProfile.Desc": "Name für Texteingaben in Spielen. Der Standardwert ist Sharp.",
|
|
||||||
|
|
||||||
"Common.On": "An",
|
"Common.On": "An",
|
||||||
"Common.Off": "Aus",
|
"Common.Off": "Aus",
|
||||||
|
|||||||
@@ -66,8 +66,6 @@
|
|||||||
|
|
||||||
"Options.Language.Label": "Emulatorsprog",
|
"Options.Language.Label": "Emulatorsprog",
|
||||||
"Options.Language.Desc": "Sprog der bruges i hele launcheren. Anvendes med det samme.",
|
"Options.Language.Desc": "Sprog der bruges i hele launcheren. Anvendes med det samme.",
|
||||||
"Options.DefaultProfile.Label": "Standardprofilnavn",
|
|
||||||
"Options.DefaultProfile.Desc": "Navn der bruges, når et spil beder om tekstinput. Standardværdien er Sharp.",
|
|
||||||
|
|
||||||
"Common.On": "Til",
|
"Common.On": "Til",
|
||||||
"Common.Off": "Fra",
|
"Common.Off": "Fra",
|
||||||
|
|||||||
@@ -38,8 +38,6 @@
|
|||||||
"Options.Env.LogIo.Desc": "Log file open, read, and path-resolve activity to the console.\nUse when a game cannot find its data files during boot.",
|
"Options.Env.LogIo.Desc": "Log file open, read, and path-resolve activity to the console.\nUse when a game cannot find its data files during boot.",
|
||||||
"Options.Env.LogNp.Desc": "Log NP (PlayStation Network) library calls to the console.",
|
"Options.Env.LogNp.Desc": "Log NP (PlayStation Network) library calls to the console.",
|
||||||
"Options.Env.GuestImageCpuSync.Desc": "Re-upload guest surfaces the game's own CPU code rewrites.\nLeave off normally. Turn on for titles whose CPU-drawn surfaces never reach the screen.\nCosts performance and regresses some titles, such as GTA V.",
|
"Options.Env.GuestImageCpuSync.Desc": "Re-upload guest surfaces the game's own CPU code rewrites.\nLeave off normally. Turn on for titles whose CPU-drawn surfaces never reach the screen.\nCosts performance and regresses some titles, such as GTA V.",
|
||||||
"Options.DefaultProfile.Label": "Default profile name",
|
|
||||||
"Options.DefaultProfile.Desc": "Name used when a game asks for text input. Defaults to Sharp.",
|
|
||||||
"Options.Section.Emulation": "EMULATION",
|
"Options.Section.Emulation": "EMULATION",
|
||||||
"Options.Section.Logging": "LOGGING",
|
"Options.Section.Logging": "LOGGING",
|
||||||
"Options.Section.Launcher": "LAUNCHER",
|
"Options.Section.Launcher": "LAUNCHER",
|
||||||
|
|||||||
@@ -66,8 +66,6 @@
|
|||||||
|
|
||||||
"Options.Language.Label": "Idioma del emulador",
|
"Options.Language.Label": "Idioma del emulador",
|
||||||
"Options.Language.Desc": "Idioma utilizado en todo el launcher. Se aplica inmediatamente.",
|
"Options.Language.Desc": "Idioma utilizado en todo el launcher. Se aplica inmediatamente.",
|
||||||
"Options.DefaultProfile.Label": "Nombre de perfil predeterminado",
|
|
||||||
"Options.DefaultProfile.Desc": "Nombre utilizado cuando un juego solicita introducir texto. El valor predeterminado es Sharp.",
|
|
||||||
|
|
||||||
"Common.On": "Encendido",
|
"Common.On": "Encendido",
|
||||||
"Common.Off": "Apagado",
|
"Common.Off": "Apagado",
|
||||||
|
|||||||
@@ -76,8 +76,6 @@
|
|||||||
|
|
||||||
"Options.Language.Label": "Langue de l’émulateur",
|
"Options.Language.Label": "Langue de l’émulateur",
|
||||||
"Options.Language.Desc": "Langue utilisée dans l’ensemble du lanceur. Le changement est immédiat.",
|
"Options.Language.Desc": "Langue utilisée dans l’ensemble du lanceur. Le changement est immédiat.",
|
||||||
"Options.DefaultProfile.Label": "Nom de profil par défaut",
|
|
||||||
"Options.DefaultProfile.Desc": "Nom utilisé lorsqu’un jeu demande une saisie de texte. La valeur par défaut est Sharp.",
|
|
||||||
|
|
||||||
"Common.On": "Activé",
|
"Common.On": "Activé",
|
||||||
"Common.Off": "Désactivé",
|
"Common.Off": "Désactivé",
|
||||||
|
|||||||
@@ -76,8 +76,6 @@
|
|||||||
|
|
||||||
"Options.Language.Label": "Emulátor nyelve",
|
"Options.Language.Label": "Emulátor nyelve",
|
||||||
"Options.Language.Desc": "Az indítóban használt nyelv. Azonnal érvénybe lép.",
|
"Options.Language.Desc": "Az indítóban használt nyelv. Azonnal érvénybe lép.",
|
||||||
"Options.DefaultProfile.Label": "Alapértelmezett profilnév",
|
|
||||||
"Options.DefaultProfile.Desc": "A játékok szövegbeviteli kéréseinél használt név. Az alapértelmezett érték Sharp.",
|
|
||||||
|
|
||||||
"Common.On": "Be",
|
"Common.On": "Be",
|
||||||
"Common.Off": "Ki",
|
"Common.Off": "Ki",
|
||||||
|
|||||||
@@ -69,8 +69,6 @@
|
|||||||
|
|
||||||
"Options.Language.Label": "Lingua dell'emulatore",
|
"Options.Language.Label": "Lingua dell'emulatore",
|
||||||
"Options.Language.Desc": "Lingua utilizzata in tutto il launcher. Viene applicata immediatamente.",
|
"Options.Language.Desc": "Lingua utilizzata in tutto il launcher. Viene applicata immediatamente.",
|
||||||
"Options.DefaultProfile.Label": "Nome profilo predefinito",
|
|
||||||
"Options.DefaultProfile.Desc": "Nome usato quando un gioco richiede l'inserimento di testo. Il valore predefinito è Sharp.",
|
|
||||||
|
|
||||||
"Common.On": "On",
|
"Common.On": "On",
|
||||||
"Common.Off": "Off",
|
"Common.Off": "Off",
|
||||||
|
|||||||
@@ -66,8 +66,6 @@
|
|||||||
|
|
||||||
"Options.Language.Label": "エミュレータの言語",
|
"Options.Language.Label": "エミュレータの言語",
|
||||||
"Options.Language.Desc": "ランチャー全体で使用される言語。変更はすぐに適用されます。",
|
"Options.Language.Desc": "ランチャー全体で使用される言語。変更はすぐに適用されます。",
|
||||||
"Options.DefaultProfile.Label": "既定のプロフィール名",
|
|
||||||
"Options.DefaultProfile.Desc": "ゲームがテキスト入力を要求したときに使用する名前です。既定値は Sharp です。",
|
|
||||||
|
|
||||||
"Common.On": "オン",
|
"Common.On": "オン",
|
||||||
"Common.Off": "オフ",
|
"Common.Off": "オフ",
|
||||||
|
|||||||
@@ -66,8 +66,6 @@
|
|||||||
|
|
||||||
"Options.Language.Label": "에뮬레이터 언어",
|
"Options.Language.Label": "에뮬레이터 언어",
|
||||||
"Options.Language.Desc": "런처 전체에 사용되는 언어입니다. 변경 사항은 즉시 적용됩니다.",
|
"Options.Language.Desc": "런처 전체에 사용되는 언어입니다. 변경 사항은 즉시 적용됩니다.",
|
||||||
"Options.DefaultProfile.Label": "기본 프로필 이름",
|
|
||||||
"Options.DefaultProfile.Desc": "게임에서 텍스트 입력을 요청할 때 사용할 이름입니다. 기본값은 Sharp입니다.",
|
|
||||||
|
|
||||||
"Common.On": "켬",
|
"Common.On": "켬",
|
||||||
"Common.Off": "끔",
|
"Common.Off": "끔",
|
||||||
|
|||||||
@@ -66,8 +66,6 @@
|
|||||||
|
|
||||||
"Options.Language.Label": "Taal van de emulator",
|
"Options.Language.Label": "Taal van de emulator",
|
||||||
"Options.Language.Desc": "Taal die in de hele launcher wordt gebruikt. Wordt direct toegepast.",
|
"Options.Language.Desc": "Taal die in de hele launcher wordt gebruikt. Wordt direct toegepast.",
|
||||||
"Options.DefaultProfile.Label": "Standaardprofielnaam",
|
|
||||||
"Options.DefaultProfile.Desc": "Naam die wordt gebruikt wanneer een game om tekstinvoer vraagt. De standaardwaarde is Sharp.",
|
|
||||||
|
|
||||||
"Common.On": "Aan",
|
"Common.On": "Aan",
|
||||||
"Common.Off": "Uit",
|
"Common.Off": "Uit",
|
||||||
|
|||||||
@@ -76,8 +76,6 @@
|
|||||||
|
|
||||||
"Options.Language.Label": "Idioma do emulador",
|
"Options.Language.Label": "Idioma do emulador",
|
||||||
"Options.Language.Desc": "Idioma utilizado em todo o lançador. Aplica-se de imediato.",
|
"Options.Language.Desc": "Idioma utilizado em todo o lançador. Aplica-se de imediato.",
|
||||||
"Options.DefaultProfile.Label": "Nome de perfil predefinido",
|
|
||||||
"Options.DefaultProfile.Desc": "Nome utilizado quando um jogo solicita a introdução de texto. O valor predefinido é Sharp.",
|
|
||||||
|
|
||||||
"Common.On": "Ativado",
|
"Common.On": "Ativado",
|
||||||
"Common.Off": "Desativado",
|
"Common.Off": "Desativado",
|
||||||
|
|||||||
@@ -109,8 +109,6 @@
|
|||||||
|
|
||||||
"Options.Language.Label": "Язык эмулятора",
|
"Options.Language.Label": "Язык эмулятора",
|
||||||
"Options.Language.Desc": "Язык интерфейса лаунчера. Изменение применяется сразу.",
|
"Options.Language.Desc": "Язык интерфейса лаунчера. Изменение применяется сразу.",
|
||||||
"Options.DefaultProfile.Label": "Имя профиля по умолчанию",
|
|
||||||
"Options.DefaultProfile.Desc": "Имя, используемое, когда игра запрашивает ввод текста. Значение по умолчанию — Sharp.",
|
|
||||||
|
|
||||||
"Common.On": "Включено",
|
"Common.On": "Включено",
|
||||||
"Common.Off": "Выключено",
|
"Common.Off": "Выключено",
|
||||||
|
|||||||
@@ -173,8 +173,6 @@
|
|||||||
"Options.Env.LogIo.Desc": "Dosya açma, okuma ve yol çözümleme etkinliğini konsola günlükle.\nBir oyun açılışta veri dosyalarını bulamıyorsa kullanın.",
|
"Options.Env.LogIo.Desc": "Dosya açma, okuma ve yol çözümleme etkinliğini konsola günlükle.\nBir oyun açılışta veri dosyalarını bulamıyorsa kullanın.",
|
||||||
"Options.Env.LogNp.Desc": "NP (PlayStation Network) kütüphane çağrılarını konsola günlükle.",
|
"Options.Env.LogNp.Desc": "NP (PlayStation Network) kütüphane çağrılarını konsola günlükle.",
|
||||||
"Options.Env.GuestImageCpuSync.Desc": "Oyunun kendi CPU kodunun yeniden yazdığı misafir yüzeyleri tekrar yükler.\nNormalde kapalı bırakın. CPU ile çizilen yüzeyleri ekrana ulaşmayan oyunlarda açın.\nPerformansa mal olur ve GTA V gibi bazı oyunlarda soruna yol açar.",
|
"Options.Env.GuestImageCpuSync.Desc": "Oyunun kendi CPU kodunun yeniden yazdığı misafir yüzeyleri tekrar yükler.\nNormalde kapalı bırakın. CPU ile çizilen yüzeyleri ekrana ulaşmayan oyunlarda açın.\nPerformansa mal olur ve GTA V gibi bazı oyunlarda soruna yol açar.",
|
||||||
"Options.DefaultProfile.Label": "Varsayilan profil adi",
|
|
||||||
"Options.DefaultProfile.Desc": "Oyun metin girisi istediginde kullanilacak ad. Varsayilan deger Sharp'tir.",
|
|
||||||
"Common.Save": "Kaydet",
|
"Common.Save": "Kaydet",
|
||||||
"Common.Cancel": "İptal",
|
"Common.Cancel": "İptal",
|
||||||
"PerGame.Title": "Oyuna özel ayarlar — {0} ({1})",
|
"PerGame.Title": "Oyuna özel ayarlar — {0} ({1})",
|
||||||
|
|||||||
@@ -346,15 +346,6 @@ SPDX-License-Identifier: GPL-2.0-or-later
|
|||||||
</ComboBox>
|
</ComboBox>
|
||||||
</local:SettingRow>
|
</local:SettingRow>
|
||||||
|
|
||||||
<local:SettingRow x:Name="DefaultProfileRow"
|
|
||||||
Label="{Binding [Options.DefaultProfile.Label], Source={x:Static local:Localization.Instance}}"
|
|
||||||
Description="{Binding [Options.DefaultProfile.Desc], Source={x:Static local:Localization.Instance}}">
|
|
||||||
<TextBox x:Name="DefaultProfileBox"
|
|
||||||
Width="180"
|
|
||||||
MaxLength="16"
|
|
||||||
VerticalAlignment="Center" />
|
|
||||||
</local:SettingRow>
|
|
||||||
|
|
||||||
<local:SettingRow x:Name="TitleMusicRow"
|
<local:SettingRow x:Name="TitleMusicRow"
|
||||||
Label="{Binding [Options.TitleMusic.Label], Source={x:Static local:Localization.Instance}}"
|
Label="{Binding [Options.TitleMusic.Label], Source={x:Static local:Localization.Instance}}"
|
||||||
Description="{Binding [Options.TitleMusic.Desc], Source={x:Static local:Localization.Instance}}">
|
Description="{Binding [Options.TitleMusic.Desc], Source={x:Static local:Localization.Instance}}">
|
||||||
@@ -655,7 +646,6 @@ SPDX-License-Identifier: GPL-2.0-or-later
|
|||||||
OffContent="{Binding [Common.Off], Source={x:Static local:Localization.Instance}}"
|
OffContent="{Binding [Common.Off], Source={x:Static local:Localization.Instance}}"
|
||||||
VerticalAlignment="Center" />
|
VerticalAlignment="Center" />
|
||||||
</local:SettingRow>
|
</local:SettingRow>
|
||||||
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
|
|||||||
@@ -298,8 +298,6 @@ public partial class MainWindow : Window
|
|||||||
SetEnvironmentToggle(
|
SetEnvironmentToggle(
|
||||||
"SHARPEMU_GUEST_IMAGE_CPU_SYNC",
|
"SHARPEMU_GUEST_IMAGE_CPU_SYNC",
|
||||||
EnvGuestImageCpuSyncToggle.IsChecked == true);
|
EnvGuestImageCpuSyncToggle.IsChecked == true);
|
||||||
DefaultProfileBox.TextChanged += (_, _) =>
|
|
||||||
_settings.DefaultProfile = GuiSettings.NormalizeDefaultProfile(DefaultProfileBox.Text);
|
|
||||||
LanguageBox.SelectionChanged += (_, _) => OnLanguageChanged();
|
LanguageBox.SelectionChanged += (_, _) => OnLanguageChanged();
|
||||||
|
|
||||||
GameList.AddHandler(ContextRequestedEvent, OnGameContextRequested, RoutingStrategies.Tunnel);
|
GameList.AddHandler(ContextRequestedEvent, OnGameContextRequested, RoutingStrategies.Tunnel);
|
||||||
@@ -885,7 +883,6 @@ public partial class MainWindow : Window
|
|||||||
EnvLogNpToggle.IsChecked = _settings.EnvironmentToggles.Contains("SHARPEMU_LOG_NP");
|
EnvLogNpToggle.IsChecked = _settings.EnvironmentToggles.Contains("SHARPEMU_LOG_NP");
|
||||||
EnvGuestImageCpuSyncToggle.IsChecked =
|
EnvGuestImageCpuSyncToggle.IsChecked =
|
||||||
_settings.EnvironmentToggles.Contains("SHARPEMU_GUEST_IMAGE_CPU_SYNC");
|
_settings.EnvironmentToggles.Contains("SHARPEMU_GUEST_IMAGE_CPU_SYNC");
|
||||||
DefaultProfileBox.Text = _settings.DefaultProfile;
|
|
||||||
WindowModeBox.SelectedIndex = ChoiceIndex(_settings.WindowMode, "Windowed", "Borderless", "Exclusive");
|
WindowModeBox.SelectedIndex = ChoiceIndex(_settings.WindowMode, "Windowed", "Borderless", "Exclusive");
|
||||||
LoadHostDisplayOptions();
|
LoadHostDisplayOptions();
|
||||||
ScalingModeBox.SelectedIndex = ChoiceIndex(_settings.ScalingMode, "Fit", "Cover", "Stretch", "Integer");
|
ScalingModeBox.SelectedIndex = ChoiceIndex(_settings.ScalingMode, "Fit", "Cover", "Stretch", "Integer");
|
||||||
@@ -1112,8 +1109,6 @@ public partial class MainWindow : Window
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private const string DefaultProfileEnvironmentName = "SHARPEMU_DEFAULT_PROFILE";
|
|
||||||
|
|
||||||
private string SelectedLogLevel()
|
private string SelectedLogLevel()
|
||||||
{
|
{
|
||||||
return LogLevelBox.SelectedIndex switch
|
return LogLevelBox.SelectedIndex switch
|
||||||
@@ -2011,20 +2006,10 @@ public partial class MainWindow : Window
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (string.Equals(name, DefaultProfileEnvironmentName, StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Environment.SetEnvironmentVariable(name, value);
|
Environment.SetEnvironmentVariable(name, value);
|
||||||
_appliedEnvironmentVariables.Add(name);
|
_appliedEnvironmentVariables.Add(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
Environment.SetEnvironmentVariable(
|
|
||||||
DefaultProfileEnvironmentName,
|
|
||||||
GuiSettings.NormalizeDefaultProfile(_settings.DefaultProfile));
|
|
||||||
_appliedEnvironmentVariables.Add(DefaultProfileEnvironmentName);
|
|
||||||
|
|
||||||
Environment.SetEnvironmentVariable(
|
Environment.SetEnvironmentVariable(
|
||||||
"SHARPEMU_RENDER_SCALE",
|
"SHARPEMU_RENDER_SCALE",
|
||||||
_settings.RenderResolutionScale.ToString(
|
_settings.RenderResolutionScale.ToString(
|
||||||
|
|||||||
@@ -46,9 +46,6 @@ public static partial class AgcExports
|
|||||||
private const uint ItNumInstances = 0x2F;
|
private const uint ItNumInstances = 0x2F;
|
||||||
private const uint ItDrawIndexMultiAuto = 0x30;
|
private const uint ItDrawIndexMultiAuto = 0x30;
|
||||||
private const uint ItDrawIndexOffset2 = 0x35;
|
private const uint ItDrawIndexOffset2 = 0x35;
|
||||||
private const uint ItDrawIndexIndirectMulti = 0x38;
|
|
||||||
private const uint DrawIndexedIndirectArgsSize = 20;
|
|
||||||
private const uint DrawIndexedIndirectMaxScan = 1024;
|
|
||||||
private const uint ItWriteData = 0x37;
|
private const uint ItWriteData = 0x37;
|
||||||
private const uint ItDispatchDirect = 0x15;
|
private const uint ItDispatchDirect = 0x15;
|
||||||
private const uint ItDispatchIndirect = 0x16;
|
private const uint ItDispatchIndirect = 0x16;
|
||||||
@@ -322,7 +319,6 @@ public static partial class AgcExports
|
|||||||
private static int _tracedVertexRangeCount;
|
private static int _tracedVertexRangeCount;
|
||||||
private static long _dcbWaitRegMemTraceCount;
|
private static long _dcbWaitRegMemTraceCount;
|
||||||
private static long _createShaderTraceCount;
|
private static long _createShaderTraceCount;
|
||||||
private static long _duplicateTargetTraceCount;
|
|
||||||
private static long _cbMetadataSkipTraceCount;
|
private static long _cbMetadataSkipTraceCount;
|
||||||
private static long _packetPayloadTraceCount;
|
private static long _packetPayloadTraceCount;
|
||||||
private static bool _tracedMissingPixelShaderBindings;
|
private static bool _tracedMissingPixelShaderBindings;
|
||||||
@@ -1998,65 +1994,6 @@ public static partial class AgcExports
|
|||||||
return ReturnPointer(ctx, drawCommand);
|
return ReturnPointer(ctx, drawCommand);
|
||||||
}
|
}
|
||||||
|
|
||||||
[SysAbiExport(
|
|
||||||
Nid = "1q1titRBL6o",
|
|
||||||
ExportName = "sceAgcDcbDrawIndirect",
|
|
||||||
Target = Generation.Gen5,
|
|
||||||
LibraryName = "libSceAgc")]
|
|
||||||
public static int DcbDrawIndirect(CpuContext ctx)
|
|
||||||
{
|
|
||||||
var commandBufferAddress = ctx[CpuRegister.Rdi];
|
|
||||||
var dataOffset = (uint)ctx[CpuRegister.Rsi];
|
|
||||||
var emit = Interlocked.Increment(ref _indirectDrawEmitCount);
|
|
||||||
|
|
||||||
if (emit <= 12 || emit % 250 == 0)
|
|
||||||
{
|
|
||||||
var rcx = ctx[CpuRegister.Rcx];
|
|
||||||
var dump = string.Empty;
|
|
||||||
for (var word = 0; word < 8; word++)
|
|
||||||
{
|
|
||||||
dump += TryReadUInt32(ctx, rcx + dataOffset + ((ulong)word * 4), out var raw)
|
|
||||||
? $" {raw}"
|
|
||||||
: " ?";
|
|
||||||
}
|
|
||||||
|
|
||||||
Console.Error.WriteLine(
|
|
||||||
$"[LOADER][WARN] agc.emit_indirect#{emit} buf=0x{commandBufferAddress:X16} " +
|
|
||||||
$"off=0x{dataOffset:X} rdx=0x{ctx[CpuRegister.Rdx]:X} rcx=0x{rcx:X} " +
|
|
||||||
$"r8=0x{ctx[CpuRegister.R8]:X} rcx_words:{dump}");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (commandBufferAddress == 0)
|
|
||||||
{
|
|
||||||
Interlocked.Increment(ref _indirectDrawEmitRejectCount);
|
|
||||||
return ReturnPointer(ctx, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!TryAllocateCommandDwords(ctx, commandBufferAddress, 5, out var drawCommand) ||
|
|
||||||
!TryWriteUInt32(ctx, drawCommand, Pm4(5, ItDrawIndirect, 0)) ||
|
|
||||||
!TryWriteUInt32(ctx, drawCommand + 4, dataOffset) ||
|
|
||||||
!TryWriteUInt32(ctx, drawCommand + 8, 0) ||
|
|
||||||
!TryWriteUInt32(ctx, drawCommand + 12, 0) ||
|
|
||||||
!TryWriteUInt32(ctx, drawCommand + 16, 0))
|
|
||||||
{
|
|
||||||
var rejects = Interlocked.Increment(ref _indirectDrawEmitRejectCount);
|
|
||||||
if (rejects <= 8 || rejects % 250 == 0)
|
|
||||||
{
|
|
||||||
Console.Error.WriteLine(
|
|
||||||
$"[LOADER][WARN] agc.emit_indirect_reject#{rejects} " +
|
|
||||||
$"buf=0x{commandBufferAddress:X16} reason=alloc_or_write");
|
|
||||||
}
|
|
||||||
|
|
||||||
return ReturnPointer(ctx, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
TraceAgc(
|
|
||||||
$"agc.dcb_draw_indirect buf=0x{commandBufferAddress:X16} " +
|
|
||||||
$"draw=0x{drawCommand:X16} offset=0x{dataOffset:X}");
|
|
||||||
|
|
||||||
return ReturnPointer(ctx, drawCommand);
|
|
||||||
}
|
|
||||||
|
|
||||||
[SysAbiExport(
|
[SysAbiExport(
|
||||||
Nid = "Yw0jKSqop+E",
|
Nid = "Yw0jKSqop+E",
|
||||||
ExportName = "sceAgcDcbDrawIndexAuto",
|
ExportName = "sceAgcDcbDrawIndexAuto",
|
||||||
@@ -2115,39 +2052,6 @@ public static partial class AgcExports
|
|||||||
return ReturnPointer(ctx, commandAddress);
|
return ReturnPointer(ctx, commandAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
[SysAbiExport(
|
|
||||||
Nid = "ypVBz4uPKcQ",
|
|
||||||
ExportName = "sceAgcDcbDrawIndexIndirectMulti",
|
|
||||||
Target = Generation.Gen5,
|
|
||||||
LibraryName = "libSceAgc")]
|
|
||||||
public static int DcbDrawIndexIndirectMulti(CpuContext ctx)
|
|
||||||
{
|
|
||||||
var commandBufferAddress = ctx[CpuRegister.Rdi];
|
|
||||||
var dataOffset = (uint)ctx[CpuRegister.Rsi];
|
|
||||||
var drawCount = (uint)ctx[CpuRegister.Rdx];
|
|
||||||
var stride = DrawIndexedIndirectArgsSize;
|
|
||||||
var modifier = (uint)ctx[CpuRegister.R8];
|
|
||||||
if (commandBufferAddress == 0 ||
|
|
||||||
!TryAllocateCommandDwords(ctx, commandBufferAddress, 8, out var commandAddress) ||
|
|
||||||
!TryWriteUInt32(ctx, commandAddress, Pm4(8, ItDrawIndexIndirectMulti, 0)) ||
|
|
||||||
!TryWriteUInt32(ctx, commandAddress + 4, dataOffset) ||
|
|
||||||
!TryWriteUInt32(ctx, commandAddress + 8, 0) ||
|
|
||||||
!TryWriteUInt32(ctx, commandAddress + 12, 0) ||
|
|
||||||
!TryWriteUInt32(ctx, commandAddress + 16, 0) ||
|
|
||||||
!TryWriteUInt32(ctx, commandAddress + 20, drawCount) ||
|
|
||||||
!TryWriteUInt32(ctx, commandAddress + 24, stride) ||
|
|
||||||
!TryWriteUInt32(ctx, commandAddress + 28, modifier))
|
|
||||||
{
|
|
||||||
return ReturnPointer(ctx, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
TraceAgc(
|
|
||||||
$"agc.dcb_draw_index_indirect_multi buf=0x{commandBufferAddress:X16} " +
|
|
||||||
$"cmd=0x{commandAddress:X16} offset=0x{dataOffset:X8} draws={drawCount} " +
|
|
||||||
$"stride={stride} modifier=0x{modifier:X8}");
|
|
||||||
return ReturnPointer(ctx, commandAddress);
|
|
||||||
}
|
|
||||||
|
|
||||||
[SysAbiExport(
|
[SysAbiExport(
|
||||||
Nid = "mStuvI0zOtc",
|
Nid = "mStuvI0zOtc",
|
||||||
ExportName = "sceAgcDcbDrawIndexIndirectGetSize",
|
ExportName = "sceAgcDcbDrawIndexIndirectGetSize",
|
||||||
@@ -3908,7 +3812,6 @@ public static partial class AgcExports
|
|||||||
|
|
||||||
if (TryReadSubmittedDrawCount(
|
if (TryReadSubmittedDrawCount(
|
||||||
ctx,
|
ctx,
|
||||||
gpuState,
|
|
||||||
state,
|
state,
|
||||||
currentAddress,
|
currentAddress,
|
||||||
length,
|
length,
|
||||||
@@ -3932,8 +3835,7 @@ public static partial class AgcExports
|
|||||||
var indexed = op is
|
var indexed = op is
|
||||||
ItDrawIndex2 or
|
ItDrawIndex2 or
|
||||||
ItDrawIndexOffset2 or
|
ItDrawIndexOffset2 or
|
||||||
ItDrawIndexIndirect or
|
ItDrawIndexIndirect;
|
||||||
ItDrawIndexIndirectMulti;
|
|
||||||
state.SawIndexedDraw |= indexed;
|
state.SawIndexedDraw |= indexed;
|
||||||
TryTranslateGuestDraw(ctx, gpuState, state, indexCount, indexed);
|
TryTranslateGuestDraw(ctx, gpuState, state, indexCount, indexed);
|
||||||
}
|
}
|
||||||
@@ -4293,7 +4195,6 @@ public static partial class AgcExports
|
|||||||
op is ItDispatchDirect or ItDispatchIndirect ||
|
op is ItDispatchDirect or ItDispatchIndirect ||
|
||||||
op is ItDrawIndirect or
|
op is ItDrawIndirect or
|
||||||
ItDrawIndexIndirect or
|
ItDrawIndexIndirect or
|
||||||
ItDrawIndexIndirectMulti or
|
|
||||||
ItDrawIndex2 or
|
ItDrawIndex2 or
|
||||||
ItDrawIndexAuto or
|
ItDrawIndexAuto or
|
||||||
ItDrawIndexMultiAuto or
|
ItDrawIndexMultiAuto or
|
||||||
@@ -6263,24 +6164,8 @@ public static partial class AgcExports
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private const int IndirectArgsFlushTimeoutMilliseconds = 250;
|
|
||||||
|
|
||||||
private static void FlushGpuWorkForIndirectArgs(SubmittedGpuState gpuState)
|
|
||||||
{
|
|
||||||
var pending = gpuState.WorkSequence;
|
|
||||||
if (pending == 0 || pending > long.MaxValue)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
GuestGpu.Current.WaitForGuestWork(
|
|
||||||
(long)pending,
|
|
||||||
IndirectArgsFlushTimeoutMilliseconds);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool TryReadSubmittedDrawCount(
|
private static bool TryReadSubmittedDrawCount(
|
||||||
CpuContext ctx,
|
CpuContext ctx,
|
||||||
SubmittedGpuState gpuState,
|
|
||||||
SubmittedDcbState state,
|
SubmittedDcbState state,
|
||||||
ulong packetAddress,
|
ulong packetAddress,
|
||||||
uint packetLength,
|
uint packetLength,
|
||||||
@@ -6311,89 +6196,17 @@ public static partial class AgcExports
|
|||||||
|
|
||||||
drawCount = (control >> 21) & 0x7FFu;
|
drawCount = (control >> 21) & 0x7FFu;
|
||||||
return true;
|
return true;
|
||||||
case ItDrawIndexIndirectMulti when packetLength >= 8 &&
|
case ItDrawIndirect or ItDrawIndexIndirect
|
||||||
state.IndirectArgsAddress != 0:
|
when packetLength >= 5 && state.IndirectArgsAddress != 0:
|
||||||
if (!TryReadUInt32(ctx, packetAddress + 4, out var multiOffset) ||
|
if (!TryReadUInt32(ctx, packetAddress + 4, out var dataOffset))
|
||||||
!TryReadUInt32(ctx, packetAddress + 20, out var multiDraws) ||
|
|
||||||
!TryReadUInt32(ctx, packetAddress + 24, out var multiStride))
|
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return TryReadUInt32(
|
||||||
if (multiStride < DrawIndexedIndirectArgsSize)
|
ctx,
|
||||||
{
|
state.IndirectArgsAddress + dataOffset,
|
||||||
multiStride = DrawIndexedIndirectArgsSize;
|
out drawCount);
|
||||||
}
|
|
||||||
|
|
||||||
var multiTotal = 0UL;
|
|
||||||
var multiCapped = multiDraws == 0
|
|
||||||
? DrawIndexedIndirectMaxScan
|
|
||||||
: Math.Min(multiDraws, 4096u);
|
|
||||||
for (var draw = 0u; draw < multiCapped; draw++)
|
|
||||||
{
|
|
||||||
if (!TryReadUInt32(
|
|
||||||
ctx,
|
|
||||||
state.IndirectArgsAddress + multiOffset + ((ulong)draw * multiStride),
|
|
||||||
out var subCount))
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (subCount == 0 && multiDraws == 0)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
multiTotal += subCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
var multiProbe = Interlocked.Increment(ref _indirectMultiProbeCount);
|
|
||||||
if (multiProbe <= 12 || multiProbe % 250 == 0)
|
|
||||||
{
|
|
||||||
Console.Error.WriteLine(
|
|
||||||
$"[LOADER][WARN] agc.draw_multi#{multiProbe} args=0x{state.IndirectArgsAddress:X} " +
|
|
||||||
$"off=0x{multiOffset:X} draws={multiDraws} stride={multiStride} " +
|
|
||||||
$"total={multiTotal}");
|
|
||||||
}
|
|
||||||
|
|
||||||
drawCount = (uint)Math.Min(multiTotal, uint.MaxValue);
|
|
||||||
return drawCount != 0;
|
|
||||||
case ItDrawIndirect or ItDrawIndexIndirect:
|
|
||||||
var probe = Interlocked.Increment(ref _indirectDrawProbeCount);
|
|
||||||
if (!TryReadUInt32(ctx, packetAddress + 4, out var dataOffset))
|
|
||||||
{
|
|
||||||
dataOffset = 0xFFFFFFFFu;
|
|
||||||
}
|
|
||||||
|
|
||||||
var readable = packetLength >= 5 &&
|
|
||||||
state.IndirectArgsAddress != 0 &&
|
|
||||||
dataOffset != 0xFFFFFFFFu;
|
|
||||||
var resolved = readable &&
|
|
||||||
TryReadUInt32(
|
|
||||||
ctx,
|
|
||||||
state.IndirectArgsAddress + dataOffset,
|
|
||||||
out drawCount);
|
|
||||||
if (probe <= 12 || probe % 100 == 0)
|
|
||||||
{
|
|
||||||
var dump = string.Empty;
|
|
||||||
for (var word = 0; word < 8; word++)
|
|
||||||
{
|
|
||||||
dump += TryReadUInt32(
|
|
||||||
ctx,
|
|
||||||
state.IndirectArgsAddress + dataOffset + ((ulong)word * 4),
|
|
||||||
out var raw)
|
|
||||||
? $" {raw}"
|
|
||||||
: " ?";
|
|
||||||
}
|
|
||||||
|
|
||||||
Console.Error.WriteLine(
|
|
||||||
$"[LOADER][WARN] agc.draw_indirect#{probe} op=0x{op:X} len={packetLength} " +
|
|
||||||
$"args=0x{state.IndirectArgsAddress:X} off=0x{dataOffset:X} " +
|
|
||||||
$"resolved={resolved} count={drawCount} words:{dump}");
|
|
||||||
}
|
|
||||||
|
|
||||||
return resolved;
|
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -8243,61 +8056,6 @@ public static partial class AgcExports
|
|||||||
source.Format == destination.Format;
|
source.Format == destination.Format;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static readonly HashSet<ulong> _renderTargetAddresses = new();
|
|
||||||
private static readonly HashSet<ulong> _sampledRenderTargets = new();
|
|
||||||
private static readonly object _renderTargetProbeGate = new();
|
|
||||||
private static long _renderTargetSampleTraceCount;
|
|
||||||
private static long _indirectDrawProbeCount;
|
|
||||||
private static long _indirectDrawEmitCount;
|
|
||||||
private static long _indirectDrawEmitRejectCount;
|
|
||||||
private static long _indirectMultiProbeCount;
|
|
||||||
|
|
||||||
private static void NoteRenderTargetAddress(ulong address)
|
|
||||||
{
|
|
||||||
if (address == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
lock (_renderTargetProbeGate)
|
|
||||||
{
|
|
||||||
if (_renderTargetAddresses.Count < 512)
|
|
||||||
{
|
|
||||||
_renderTargetAddresses.Add(address);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void NoteSampledAddress(ulong address, uint format = 0, uint numberType = 0)
|
|
||||||
{
|
|
||||||
if (address == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool firstTime;
|
|
||||||
int distinctTargets;
|
|
||||||
lock (_renderTargetProbeGate)
|
|
||||||
{
|
|
||||||
if (!_renderTargetAddresses.Contains(address))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
firstTime = _sampledRenderTargets.Add(address);
|
|
||||||
distinctTargets = _renderTargetAddresses.Count;
|
|
||||||
}
|
|
||||||
|
|
||||||
var count = Interlocked.Increment(ref _renderTargetSampleTraceCount);
|
|
||||||
if (firstTime || count % 2000 == 0)
|
|
||||||
{
|
|
||||||
var gpuResident = GuestGpu.Current.IsGpuGuestImageAvailable(address, format, numberType);
|
|
||||||
Console.Error.WriteLine(
|
|
||||||
$"[LOADER][WARN] agc.rt_sampled#{count} addr=0x{address:X} first={firstTime} " +
|
|
||||||
$"gpu_resident={gpuResident} fmt={format}/{numberType} known_targets={distinctTargets}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static IReadOnlyList<RenderTargetDescriptor> GetRenderTargets(
|
private static IReadOnlyList<RenderTargetDescriptor> GetRenderTargets(
|
||||||
IReadOnlyDictionary<uint, uint> registers,
|
IReadOnlyDictionary<uint, uint> registers,
|
||||||
bool includeMaskedTargets = false)
|
bool includeMaskedTargets = false)
|
||||||
@@ -8324,13 +8082,6 @@ public static partial class AgcExports
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (targets.Exists(existing => existing.Address == address))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
NoteRenderTargetAddress(address);
|
|
||||||
|
|
||||||
targets.Add(new RenderTargetDescriptor(
|
targets.Add(new RenderTargetDescriptor(
|
||||||
slot,
|
slot,
|
||||||
address,
|
address,
|
||||||
@@ -8341,21 +8092,6 @@ public static partial class AgcExports
|
|||||||
(attrib3 >> 14) & 0x1Fu));
|
(attrib3 >> 14) & 0x1Fu));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (targets.Count > 1 &&
|
|
||||||
targets.Select(t => t.Address).Distinct().Count() != targets.Count)
|
|
||||||
{
|
|
||||||
var dupCount = Interlocked.Increment(ref _duplicateTargetTraceCount);
|
|
||||||
if (dupCount <= 12 || dupCount % 500 == 0)
|
|
||||||
{
|
|
||||||
Console.Error.WriteLine(
|
|
||||||
$"[LOADER][WARN] agc.rt_duplicate#{dupCount} has_mask={hasTargetMask} " +
|
|
||||||
$"mask=0x{targetMask:X8} slots=[" +
|
|
||||||
string.Join(",", targets.Select(t =>
|
|
||||||
$"{t.Slot}:0x{t.Address:X}:m{(targetMask >> ((int)t.Slot * 4)) & 0xFu}")) +
|
|
||||||
"]");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return targets;
|
return targets;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -9595,7 +9331,6 @@ public static partial class AgcExports
|
|||||||
descriptor.Format,
|
descriptor.Format,
|
||||||
descriptor.NumberType))
|
descriptor.NumberType))
|
||||||
{
|
{
|
||||||
NoteSampledAddress(descriptor.Address, descriptor.Format, descriptor.NumberType);
|
|
||||||
texture = new GuestDrawTexture(
|
texture = new GuestDrawTexture(
|
||||||
descriptor.Address,
|
descriptor.Address,
|
||||||
descriptor.Width,
|
descriptor.Width,
|
||||||
@@ -9675,7 +9410,6 @@ public static partial class AgcExports
|
|||||||
$"tile={descriptor.TileMode} mip={mipLevel}");
|
$"tile={descriptor.TileMode} mip={mipLevel}");
|
||||||
}
|
}
|
||||||
|
|
||||||
NoteSampledAddress(descriptor.Address, descriptor.Format, descriptor.NumberType);
|
|
||||||
texture = new GuestDrawTexture(
|
texture = new GuestDrawTexture(
|
||||||
descriptor.Address,
|
descriptor.Address,
|
||||||
descriptor.Width,
|
descriptor.Width,
|
||||||
@@ -9736,7 +9470,6 @@ public static partial class AgcExports
|
|||||||
descriptor.Type,
|
descriptor.Type,
|
||||||
textureDepth)))
|
textureDepth)))
|
||||||
{
|
{
|
||||||
NoteSampledAddress(descriptor.Address, descriptor.Format, descriptor.NumberType);
|
|
||||||
texture = new GuestDrawTexture(
|
texture = new GuestDrawTexture(
|
||||||
descriptor.Address,
|
descriptor.Address,
|
||||||
descriptor.Width,
|
descriptor.Width,
|
||||||
@@ -9799,8 +9532,7 @@ public static partial class AgcExports
|
|||||||
|
|
||||||
if (readAllLayers)
|
if (readAllLayers)
|
||||||
{
|
{
|
||||||
NoteSampledAddress(descriptor.Address, descriptor.Format, descriptor.NumberType);
|
texture = new GuestDrawTexture(
|
||||||
texture = new GuestDrawTexture(
|
|
||||||
descriptor.Address,
|
descriptor.Address,
|
||||||
descriptor.Width,
|
descriptor.Width,
|
||||||
descriptor.Height,
|
descriptor.Height,
|
||||||
@@ -9862,8 +9594,7 @@ public static partial class AgcExports
|
|||||||
|
|
||||||
if (uploadedLayers == arrayLayers)
|
if (uploadedLayers == arrayLayers)
|
||||||
{
|
{
|
||||||
NoteSampledAddress(descriptor.Address, descriptor.Format, descriptor.NumberType);
|
texture = new GuestDrawTexture(
|
||||||
texture = new GuestDrawTexture(
|
|
||||||
descriptor.Address,
|
descriptor.Address,
|
||||||
descriptor.Width,
|
descriptor.Width,
|
||||||
descriptor.Height,
|
descriptor.Height,
|
||||||
@@ -9966,8 +9697,7 @@ public static partial class AgcExports
|
|||||||
if (IsGpuDetileEquation(gpuDetileParams.Equation) &&
|
if (IsGpuDetileEquation(gpuDetileParams.Equation) &&
|
||||||
(long)elementsWide * elementsHigh * bytesPerElement <= source.Length)
|
(long)elementsWide * elementsHigh * bytesPerElement <= source.Length)
|
||||||
{
|
{
|
||||||
NoteSampledAddress(descriptor.Address, descriptor.Format, descriptor.NumberType);
|
texture = new GuestDrawTexture(
|
||||||
texture = new GuestDrawTexture(
|
|
||||||
descriptor.Address,
|
descriptor.Address,
|
||||||
descriptor.Width,
|
descriptor.Width,
|
||||||
descriptor.Height,
|
descriptor.Height,
|
||||||
|
|||||||
@@ -1,175 +0,0 @@
|
|||||||
// Copyright (C) 2026 SharpEmu Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Buffers.Binary;
|
|
||||||
using System.Text;
|
|
||||||
using SharpEmu.HLE;
|
|
||||||
|
|
||||||
namespace SharpEmu.Libs.Ime;
|
|
||||||
|
|
||||||
public static class ImeDialogExports
|
|
||||||
{
|
|
||||||
private const int StatusNone = 0;
|
|
||||||
private const int StatusRunning = 1;
|
|
||||||
private const int StatusFinished = 2;
|
|
||||||
|
|
||||||
private const int EndStatusOk = 0;
|
|
||||||
|
|
||||||
private const ulong ParamMaxTextLengthOffset = 0x24;
|
|
||||||
private const ulong ParamInputTextBufferOffset = 0x28;
|
|
||||||
|
|
||||||
private const int ImeDialogErrorInvalidAddress = unchecked((int)0x80BC0001);
|
|
||||||
|
|
||||||
private const string DefaultInputText = "Sharp";
|
|
||||||
|
|
||||||
private static readonly object _gate = new();
|
|
||||||
private static int _status = StatusNone;
|
|
||||||
|
|
||||||
[SysAbiExport(
|
|
||||||
Nid = "NUeBrN7hzf0",
|
|
||||||
ExportName = "sceImeDialogInit",
|
|
||||||
Target = Generation.Gen4 | Generation.Gen5,
|
|
||||||
LibraryName = "libSceImeDialog")]
|
|
||||||
public static int ImeDialogInit(CpuContext ctx)
|
|
||||||
{
|
|
||||||
var parameterAddress = ctx[CpuRegister.Rdi];
|
|
||||||
if (parameterAddress == 0)
|
|
||||||
{
|
|
||||||
return SetReturn(ctx, ImeDialogErrorInvalidAddress);
|
|
||||||
}
|
|
||||||
|
|
||||||
TryWriteInputText(ctx, parameterAddress);
|
|
||||||
|
|
||||||
lock (_gate)
|
|
||||||
{
|
|
||||||
_status = StatusFinished;
|
|
||||||
}
|
|
||||||
|
|
||||||
return SetReturn(ctx, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
[SysAbiExport(
|
|
||||||
Nid = "IADmD4tScBY",
|
|
||||||
ExportName = "sceImeDialogGetStatus",
|
|
||||||
Target = Generation.Gen4 | Generation.Gen5,
|
|
||||||
LibraryName = "libSceImeDialog")]
|
|
||||||
public static int ImeDialogGetStatus(CpuContext ctx)
|
|
||||||
{
|
|
||||||
int status;
|
|
||||||
lock (_gate)
|
|
||||||
{
|
|
||||||
status = _status;
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx[CpuRegister.Rax] = unchecked((ulong)(long)status);
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
[SysAbiExport(
|
|
||||||
Nid = "x01jxu+vxlc",
|
|
||||||
ExportName = "sceImeDialogGetResult",
|
|
||||||
Target = Generation.Gen4 | Generation.Gen5,
|
|
||||||
LibraryName = "libSceImeDialog")]
|
|
||||||
public static int ImeDialogGetResult(CpuContext ctx)
|
|
||||||
{
|
|
||||||
var resultAddress = ctx[CpuRegister.Rdi];
|
|
||||||
if (resultAddress == 0)
|
|
||||||
{
|
|
||||||
return SetReturn(ctx, ImeDialogErrorInvalidAddress);
|
|
||||||
}
|
|
||||||
|
|
||||||
Span<byte> result = stackalloc byte[8];
|
|
||||||
result.Clear();
|
|
||||||
BinaryPrimitives.WriteInt32LittleEndian(result, EndStatusOk);
|
|
||||||
ctx.Memory.TryWrite(resultAddress, result);
|
|
||||||
|
|
||||||
return SetReturn(ctx, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
[SysAbiExport(
|
|
||||||
Nid = "oBmw4xrmfKs",
|
|
||||||
ExportName = "sceImeDialogAbort",
|
|
||||||
Target = Generation.Gen4 | Generation.Gen5,
|
|
||||||
LibraryName = "libSceImeDialog")]
|
|
||||||
public static int ImeDialogAbort(CpuContext ctx)
|
|
||||||
{
|
|
||||||
lock (_gate)
|
|
||||||
{
|
|
||||||
_status = StatusFinished;
|
|
||||||
}
|
|
||||||
|
|
||||||
return SetReturn(ctx, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
[SysAbiExport(
|
|
||||||
Nid = "gyTyVn+bXMw",
|
|
||||||
ExportName = "sceImeDialogTerm",
|
|
||||||
Target = Generation.Gen4 | Generation.Gen5,
|
|
||||||
LibraryName = "libSceImeDialog")]
|
|
||||||
public static int ImeDialogTerm(CpuContext ctx)
|
|
||||||
{
|
|
||||||
lock (_gate)
|
|
||||||
{
|
|
||||||
_status = StatusNone;
|
|
||||||
}
|
|
||||||
|
|
||||||
return SetReturn(ctx, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void TryWriteInputText(CpuContext ctx, ulong parameterAddress)
|
|
||||||
{
|
|
||||||
Span<byte> field = stackalloc byte[8];
|
|
||||||
if (!ctx.Memory.TryRead(parameterAddress + ParamInputTextBufferOffset, field))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var bufferAddress = BinaryPrimitives.ReadUInt64LittleEndian(field);
|
|
||||||
if (bufferAddress == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var maxTextLength = 16u;
|
|
||||||
Span<byte> lengthField = stackalloc byte[4];
|
|
||||||
if (ctx.Memory.TryRead(parameterAddress + ParamMaxTextLengthOffset, lengthField))
|
|
||||||
{
|
|
||||||
var declared = BinaryPrimitives.ReadUInt32LittleEndian(lengthField);
|
|
||||||
if (declared is > 0 and <= 256)
|
|
||||||
{
|
|
||||||
maxTextLength = declared;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var text = Environment.GetEnvironmentVariable("SHARPEMU_DEFAULT_PROFILE");
|
|
||||||
if (string.IsNullOrEmpty(text))
|
|
||||||
{
|
|
||||||
text = DefaultInputText;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((uint)text.Length > maxTextLength)
|
|
||||||
{
|
|
||||||
text = text[..(int)maxTextLength];
|
|
||||||
}
|
|
||||||
|
|
||||||
var encoded = Encoding.Unicode.GetBytes(text);
|
|
||||||
Span<byte> payload = stackalloc byte[encoded.Length + 2];
|
|
||||||
encoded.CopyTo(payload);
|
|
||||||
payload[^2] = 0;
|
|
||||||
payload[^1] = 0;
|
|
||||||
|
|
||||||
if (ctx.Memory.TryWrite(bufferAddress, payload))
|
|
||||||
{
|
|
||||||
Console.Error.WriteLine(
|
|
||||||
$"[LOADER][WARN] ime.dialog_autofill buf=0x{bufferAddress:X16} " +
|
|
||||||
$"max={maxTextLength} text=\"{text}\"");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int SetReturn(CpuContext ctx, int result)
|
|
||||||
{
|
|
||||||
ctx[CpuRegister.Rax] = unchecked((ulong)(long)result);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -33,17 +33,12 @@ public static class SaveDataDialogExports
|
|||||||
LibraryName = "libSceSaveDataDialog")]
|
LibraryName = "libSceSaveDataDialog")]
|
||||||
public static int SaveDataDialogInitialize(CpuContext ctx)
|
public static int SaveDataDialogInitialize(CpuContext ctx)
|
||||||
{
|
{
|
||||||
var previous = Interlocked.CompareExchange(
|
if (Interlocked.CompareExchange(ref _status, StatusInitialized, StatusNone) != StatusNone)
|
||||||
ref _status,
|
|
||||||
StatusInitialized,
|
|
||||||
StatusNone);
|
|
||||||
if (previous == StatusNone || previous == StatusFinished)
|
|
||||||
{
|
{
|
||||||
Interlocked.Exchange(ref _status, StatusInitialized);
|
return ctx.SetReturn(ErrorAlreadyInitialized);
|
||||||
return ctx.SetReturn(ErrorOk);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ctx.SetReturn(ErrorAlreadyInitialized);
|
return ctx.SetReturn(ErrorOk);
|
||||||
}
|
}
|
||||||
|
|
||||||
[SysAbiExport(
|
[SysAbiExport(
|
||||||
|
|||||||
@@ -1046,50 +1046,14 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
targets.Count > 8 ||
|
targets.Count > 8 ||
|
||||||
AnyRenderTargetInvalid(targets))
|
AnyRenderTargetInvalid(targets))
|
||||||
{
|
{
|
||||||
var dropCount = Interlocked.Increment(ref _offscreenDropTraceCount);
|
|
||||||
if (dropCount <= 16 || dropCount % 500 == 0)
|
|
||||||
{
|
|
||||||
var detail = targets.Count == 0
|
|
||||||
? "<no targets>"
|
|
||||||
: string.Join(
|
|
||||||
" ",
|
|
||||||
targets.Select(t => $"0x{t.Address:X}:{t.Width}x{t.Height}"));
|
|
||||||
Console.Error.WriteLine(
|
|
||||||
$"[LOADER][WARN] vk.offscreen_drop#{dropCount} spirv={pixelSpirv.Length} " +
|
|
||||||
$"mrt={targets.Count} vs=0x{shaderAddress:X16} {detail}");
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var firstTarget = targets[0];
|
var firstTarget = targets[0];
|
||||||
if (RenderTargetsMismatchedOrAliased(targets, firstTarget))
|
if (RenderTargetsMismatchedOrAliased(targets, firstTarget))
|
||||||
{
|
{
|
||||||
var skipCount = Interlocked.Increment(ref _mrtSkipTraceCount);
|
Console.Error.WriteLine(
|
||||||
if (skipCount <= 16 || skipCount % 200 == 0)
|
"[LOADER][WARN] Vulkan skipped MRT draw with mismatched dimensions or aliased targets.");
|
||||||
{
|
|
||||||
var aliased = false;
|
|
||||||
for (var i = 0; i < targets.Count && !aliased; i++)
|
|
||||||
{
|
|
||||||
for (var j = i + 1; j < targets.Count; j++)
|
|
||||||
{
|
|
||||||
if (targets[i].Address == targets[j].Address)
|
|
||||||
{
|
|
||||||
aliased = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var detail = string.Join(
|
|
||||||
" ",
|
|
||||||
targets.Select(t =>
|
|
||||||
$"0x{t.Address:X}:{t.Width}x{t.Height}:f{t.Format}/{t.NumberType}"));
|
|
||||||
Console.Error.WriteLine(
|
|
||||||
$"[LOADER][WARN] vk.mrt_skip#{skipCount} mrt={targets.Count} " +
|
|
||||||
$"aliased={aliased} vs=0x{shaderAddress:X16} {detail}");
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1334,8 +1298,6 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static long _mrtSkipTraceCount;
|
|
||||||
private static long _offscreenDropTraceCount;
|
|
||||||
private static long _perfDrawCount;
|
private static long _perfDrawCount;
|
||||||
private static long _perfDrawTicks;
|
private static long _perfDrawTicks;
|
||||||
private static long _perfPipelineCreations;
|
private static long _perfPipelineCreations;
|
||||||
@@ -1872,22 +1834,8 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
|
|
||||||
lock (_gate)
|
lock (_gate)
|
||||||
{
|
{
|
||||||
if (!_availableGuestImages.TryGetValue(address, out var availableFormat))
|
return _availableGuestImages.TryGetValue(address, out var availableFormat) &&
|
||||||
{
|
availableFormat == guestFormat;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (availableFormat == guestFormat)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return TryDecodeRenderTargetFormat(
|
|
||||||
(availableFormat >> 8) & 0x1FFu,
|
|
||||||
availableFormat & 0xFFu,
|
|
||||||
out var resident) &&
|
|
||||||
TryDecodeRenderTargetFormat(format, numberType, out var requested) &&
|
|
||||||
Presenter.IsCompatibleViewFormat(resident.Format, requested.Format);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -12127,7 +12075,6 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
{
|
{
|
||||||
Console.Error.WriteLine(
|
Console.Error.WriteLine(
|
||||||
$"[LOADER][WARN] Vulkan skipped storage render-target feedback loop " +
|
$"[LOADER][WARN] Vulkan skipped storage render-target feedback loop " +
|
||||||
$"vs=0x{work.ShaderAddress:X16} " +
|
|
||||||
$"targets={string.Join(',', work.Targets.Where(target => target.Address != 0).Select(target => $"0x{target.Address:X16}"))}; " +
|
$"targets={string.Join(',', work.Targets.Where(target => target.Address != 0).Select(target => $"0x{target.Address:X16}"))}; " +
|
||||||
"sampled aliases use ordered snapshots");
|
"sampled aliases use ordered snapshots");
|
||||||
ReturnPooledGuestData(work.Draw);
|
ReturnPooledGuestData(work.Draw);
|
||||||
|
|||||||
Reference in New Issue
Block a user