Compare commits

...

3 Commits

Author SHA1 Message Date
Ac_K
cee667b491 Ava: Fixes Update count in heading (#4265) 2023-01-11 05:39:25 +01:00
gdkchan
94a64f2aea Remove textures from cache on unmap if not mapped and modified (#4211) 2023-01-11 01:53:56 +00:00
Ac_K
2355c2af62 ava: Generate Locale menu automatically (#4243)
Currently in `MenuMainBarView.axaml` we list all available languages and hardcode the language name with the language key.
It's a bit bad beause if we want to add a new language, we have to edit the `csproj` and the `axaml` with the translated language name and the language code.
I've put all translations in their respective locale files, add code into `MainMenuBarView` constructor to generate the menu automatically. Now we just have to edit the `csproj` if we want to add a new language.
2023-01-11 01:29:22 +01:00
25 changed files with 160 additions and 78 deletions

View File

@@ -1,4 +1,5 @@
{ {
"Language": "Deutsch",
"MenuBarFileOpenApplet": "Applet öffnen", "MenuBarFileOpenApplet": "Applet öffnen",
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "Öffnet das Mii Editor Applet im Standalone Modus", "MenuBarFileOpenAppletOpenMiiAppletToolTip": "Öffnet das Mii Editor Applet im Standalone Modus",
"SettingsTabInputDirectMouseAccess": "Direkter Mauszugriff", "SettingsTabInputDirectMouseAccess": "Direkter Mauszugriff",
@@ -610,4 +611,4 @@
"UserProfilesRecoverLostAccounts": "Konto wiederherstellen", "UserProfilesRecoverLostAccounts": "Konto wiederherstellen",
"Recover": "Wiederherstellen", "Recover": "Wiederherstellen",
"UserProfilesRecoverHeading": "Speicherstände wurden für die folgenden Konten gefunden" "UserProfilesRecoverHeading": "Speicherstände wurden für die folgenden Konten gefunden"
} }

View File

@@ -1,4 +1,5 @@
{ {
"Language": "Ελληνικά",
"MenuBarFileOpenApplet": "Άνοιγμα Applet", "MenuBarFileOpenApplet": "Άνοιγμα Applet",
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "Άνοιγμα του Mii Editor Applet σε Αυτόνομη λειτουργία", "MenuBarFileOpenAppletOpenMiiAppletToolTip": "Άνοιγμα του Mii Editor Applet σε Αυτόνομη λειτουργία",
"SettingsTabInputDirectMouseAccess": "Άμεση Πρόσβαση Ποντικιού", "SettingsTabInputDirectMouseAccess": "Άμεση Πρόσβαση Ποντικιού",
@@ -610,4 +611,4 @@
"UserProfilesRecoverLostAccounts": "Recover Lost Accounts", "UserProfilesRecoverLostAccounts": "Recover Lost Accounts",
"Recover": "Recover", "Recover": "Recover",
"UserProfilesRecoverHeading": "Saves were found for the following accounts" "UserProfilesRecoverHeading": "Saves were found for the following accounts"
} }

View File

@@ -1,4 +1,5 @@
{ {
"Language": "English (US)",
"MenuBarFileOpenApplet": "Open Applet", "MenuBarFileOpenApplet": "Open Applet",
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "Open Mii Editor Applet in Standalone mode", "MenuBarFileOpenAppletOpenMiiAppletToolTip": "Open Mii Editor Applet in Standalone mode",
"SettingsTabInputDirectMouseAccess": "Direct Mouse Access", "SettingsTabInputDirectMouseAccess": "Direct Mouse Access",
@@ -610,4 +611,5 @@
"UserProfilesRecoverLostAccounts": "Recover Lost Accounts", "UserProfilesRecoverLostAccounts": "Recover Lost Accounts",
"Recover": "Recover", "Recover": "Recover",
"UserProfilesRecoverHeading" : "Saves were found for the following accounts" "UserProfilesRecoverHeading" : "Saves were found for the following accounts"
} }

View File

@@ -1,4 +1,5 @@
{ {
"Language": "Español (ES)",
"MenuBarFileOpenApplet": "Abrir applet", "MenuBarFileOpenApplet": "Abrir applet",
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "Abre el editor de Mii en modo autónomo", "MenuBarFileOpenAppletOpenMiiAppletToolTip": "Abre el editor de Mii en modo autónomo",
"SettingsTabInputDirectMouseAccess": "Acceso directo al ratón", "SettingsTabInputDirectMouseAccess": "Acceso directo al ratón",
@@ -610,4 +611,4 @@
"UserProfilesRecoverLostAccounts": "Recover Lost Accounts", "UserProfilesRecoverLostAccounts": "Recover Lost Accounts",
"Recover": "Recover", "Recover": "Recover",
"UserProfilesRecoverHeading": "Saves were found for the following accounts" "UserProfilesRecoverHeading": "Saves were found for the following accounts"
} }

View File

@@ -1,4 +1,5 @@
{ {
"Language": "Français",
"MenuBarFileOpenApplet": "Ouvrir Applet", "MenuBarFileOpenApplet": "Ouvrir Applet",
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "Ouvrir l'Applet Mii Editor en mode Standalone", "MenuBarFileOpenAppletOpenMiiAppletToolTip": "Ouvrir l'Applet Mii Editor en mode Standalone",
"SettingsTabInputDirectMouseAccess": "Accès direct à la souris", "SettingsTabInputDirectMouseAccess": "Accès direct à la souris",
@@ -610,4 +611,4 @@
"UserProfilesRecoverLostAccounts": "Récupérer les comptes perdus", "UserProfilesRecoverLostAccounts": "Récupérer les comptes perdus",
"Recover": "Récupérer", "Recover": "Récupérer",
"UserProfilesRecoverHeading": "Des sauvegardes ont été trouvées pour les comptes suivants" "UserProfilesRecoverHeading": "Des sauvegardes ont été trouvées pour les comptes suivants"
} }

View File

@@ -1,4 +1,5 @@
{ {
"Language": "Italiano",
"MenuBarFileOpenApplet": "Apri Applet", "MenuBarFileOpenApplet": "Apri Applet",
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "Apri l'applet Mii Editor in modalità Standalone", "MenuBarFileOpenAppletOpenMiiAppletToolTip": "Apri l'applet Mii Editor in modalità Standalone",
"SettingsTabInputDirectMouseAccess": "Accesso diretto al mouse", "SettingsTabInputDirectMouseAccess": "Accesso diretto al mouse",
@@ -610,4 +611,4 @@
"UserProfilesRecoverLostAccounts": "Recupera il tuo account", "UserProfilesRecoverLostAccounts": "Recupera il tuo account",
"Recover": "Recupera", "Recover": "Recupera",
"UserProfilesRecoverHeading": "Sono stati trovati dei salvataggi per i seguenti account" "UserProfilesRecoverHeading": "Sono stati trovati dei salvataggi per i seguenti account"
} }

View File

@@ -1,4 +1,5 @@
{ {
"Language": "日本語",
"MenuBarFileOpenApplet": "アプレットを開く", "MenuBarFileOpenApplet": "アプレットを開く",
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "スタンドアロンモードで Mii エディタアプレットを開きます", "MenuBarFileOpenAppletOpenMiiAppletToolTip": "スタンドアロンモードで Mii エディタアプレットを開きます",
"SettingsTabInputDirectMouseAccess": "マウス直接アクセス", "SettingsTabInputDirectMouseAccess": "マウス直接アクセス",
@@ -610,4 +611,4 @@
"UserProfilesRecoverLostAccounts": "アカウントの復旧", "UserProfilesRecoverLostAccounts": "アカウントの復旧",
"Recover": "復旧", "Recover": "復旧",
"UserProfilesRecoverHeading": "以下のアカウントのセーブデータが見つかりました" "UserProfilesRecoverHeading": "以下のアカウントのセーブデータが見つかりました"
} }

View File

@@ -1,4 +1,5 @@
{ {
"Language": "한국어",
"MenuBarFileOpenApplet": "애플릿 열기", "MenuBarFileOpenApplet": "애플릿 열기",
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "독립 실행형 모드에서 Mii 편집기 애플릿 열기", "MenuBarFileOpenAppletOpenMiiAppletToolTip": "독립 실행형 모드에서 Mii 편집기 애플릿 열기",
"SettingsTabInputDirectMouseAccess": "직접 마우스 접속", "SettingsTabInputDirectMouseAccess": "직접 마우스 접속",
@@ -610,4 +611,4 @@
"UserProfilesRecoverLostAccounts": "잃어버린 계정 복구", "UserProfilesRecoverLostAccounts": "잃어버린 계정 복구",
"Recover": "복구", "Recover": "복구",
"UserProfilesRecoverHeading": "다음 계정에 대한 저장 발견" "UserProfilesRecoverHeading": "다음 계정에 대한 저장 발견"
} }

View File

@@ -1,4 +1,5 @@
{ {
"Language": "Polski",
"MenuBarFileOpenApplet": "Otwórz Aplet", "MenuBarFileOpenApplet": "Otwórz Aplet",
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "Otwórz aplet Mii Editor w trybie Indywidualnym", "MenuBarFileOpenAppletOpenMiiAppletToolTip": "Otwórz aplet Mii Editor w trybie Indywidualnym",
"SettingsTabInputDirectMouseAccess": "Bezpośredni Dostęp do Myszy", "SettingsTabInputDirectMouseAccess": "Bezpośredni Dostęp do Myszy",
@@ -610,4 +611,4 @@
"UserProfilesRecoverLostAccounts": "Odzyskaj Utracone Konta", "UserProfilesRecoverLostAccounts": "Odzyskaj Utracone Konta",
"Recover": "Odzyskaj", "Recover": "Odzyskaj",
"UserProfilesRecoverHeading": "Znaleziono zapisy dla następujących kont" "UserProfilesRecoverHeading": "Znaleziono zapisy dla następujących kont"
} }

View File

@@ -1,4 +1,5 @@
{ {
"Language": "Português (BR)",
"MenuBarFileOpenApplet": "Abrir Applet", "MenuBarFileOpenApplet": "Abrir Applet",
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "Abrir editor Mii em modo avulso", "MenuBarFileOpenAppletOpenMiiAppletToolTip": "Abrir editor Mii em modo avulso",
"SettingsTabInputDirectMouseAccess": "Acesso direto ao mouse", "SettingsTabInputDirectMouseAccess": "Acesso direto ao mouse",
@@ -610,4 +611,4 @@
"UserProfilesRecoverLostAccounts": "Recuperar contas perdidas", "UserProfilesRecoverLostAccounts": "Recuperar contas perdidas",
"Recover": "Recuperar", "Recover": "Recuperar",
"UserProfilesRecoverHeading": "Jogos salvos foram encontrados para as seguintes contas" "UserProfilesRecoverHeading": "Jogos salvos foram encontrados para as seguintes contas"
} }

View File

@@ -1,4 +1,5 @@
{ {
"Language": "Русский",
"MenuBarFileOpenApplet": "Открыть апплет", "MenuBarFileOpenApplet": "Открыть апплет",
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "Открыть апплет Mii Editor в автономном режиме.", "MenuBarFileOpenAppletOpenMiiAppletToolTip": "Открыть апплет Mii Editor в автономном режиме.",
"SettingsTabInputDirectMouseAccess": "Прямой доступ с помощью мыши", "SettingsTabInputDirectMouseAccess": "Прямой доступ с помощью мыши",
@@ -610,4 +611,4 @@
"UserProfilesRecoverLostAccounts": "Recover Lost Accounts", "UserProfilesRecoverLostAccounts": "Recover Lost Accounts",
"Recover": "Recover", "Recover": "Recover",
"UserProfilesRecoverHeading": "Saves were found for the following accounts" "UserProfilesRecoverHeading": "Saves were found for the following accounts"
} }

View File

@@ -1,4 +1,5 @@
{ {
"Language": "Türkçe",
"MenuBarFileOpenApplet": "Applet'i Aç", "MenuBarFileOpenApplet": "Applet'i Aç",
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "Mii Editör Applet'ini Bağımsız Mod'da Aç", "MenuBarFileOpenAppletOpenMiiAppletToolTip": "Mii Editör Applet'ini Bağımsız Mod'da Aç",
"SettingsTabInputDirectMouseAccess": "Doğrudan Mouse Erişimi", "SettingsTabInputDirectMouseAccess": "Doğrudan Mouse Erişimi",
@@ -610,4 +611,4 @@
"UserProfilesRecoverLostAccounts": "Kayıp Hesapları Kurtar", "UserProfilesRecoverLostAccounts": "Kayıp Hesapları Kurtar",
"Recover": "Kurtar", "Recover": "Kurtar",
"UserProfilesRecoverHeading": "Aşağıdaki hesaplar için kayıtlar bulundu" "UserProfilesRecoverHeading": "Aşağıdaki hesaplar için kayıtlar bulundu"
} }

View File

@@ -1,4 +1,5 @@
{ {
"Language": "Yкраїнська",
"MenuBarFileOpenApplet": "Відкрити аплет", "MenuBarFileOpenApplet": "Відкрити аплет",
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "Відкрийте аплет Mii Editor в автономному режимі", "MenuBarFileOpenAppletOpenMiiAppletToolTip": "Відкрийте аплет Mii Editor в автономному режимі",
"SettingsTabInputDirectMouseAccess": "Прямий доступ мишею", "SettingsTabInputDirectMouseAccess": "Прямий доступ мишею",
@@ -610,4 +611,4 @@
"UserProfilesRecoverLostAccounts": "Відновлення втрачених облікових записів", "UserProfilesRecoverLostAccounts": "Відновлення втрачених облікових записів",
"Recover": "Відновити", "Recover": "Відновити",
"UserProfilesRecoverHeading": "Знайдено збереження для наступних облікових записів" "UserProfilesRecoverHeading": "Знайдено збереження для наступних облікових записів"
} }

View File

@@ -1,4 +1,5 @@
{ {
"Language": "简体中文",
"MenuBarFileOpenApplet": "打开小程序", "MenuBarFileOpenApplet": "打开小程序",
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "打开独立的 Mii 小程序", "MenuBarFileOpenAppletOpenMiiAppletToolTip": "打开独立的 Mii 小程序",
"SettingsTabInputDirectMouseAccess": "直通鼠标操作", "SettingsTabInputDirectMouseAccess": "直通鼠标操作",
@@ -610,4 +611,4 @@
"UserProfilesRecoverLostAccounts": "恢复丢失的账户", "UserProfilesRecoverLostAccounts": "恢复丢失的账户",
"Recover": "恢复", "Recover": "恢复",
"UserProfilesRecoverHeading": "找到了这些用户的存档数据" "UserProfilesRecoverHeading": "找到了这些用户的存档数据"
} }

View File

@@ -1,4 +1,5 @@
{ {
"Language": "繁體中文",
"MenuBarFileOpenApplet": "打開小程式", "MenuBarFileOpenApplet": "打開小程式",
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "打開獨立的 Mii 小程式", "MenuBarFileOpenAppletOpenMiiAppletToolTip": "打開獨立的 Mii 小程式",
"SettingsTabInputDirectMouseAccess": "直通滑鼠操作", "SettingsTabInputDirectMouseAccess": "直通滑鼠操作",
@@ -610,4 +611,4 @@
"UserProfilesRecoverLostAccounts": "恢復遺失的帳號", "UserProfilesRecoverLostAccounts": "恢復遺失的帳號",
"Recover": "恢復", "Recover": "恢復",
"UserProfilesRecoverHeading": "在以下帳號找到了一些遊戲存檔" "UserProfilesRecoverHeading": "在以下帳號找到了一些遊戲存檔"
} }

View File

@@ -6,7 +6,6 @@ using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Text.Json;
namespace Ryujinx.Ava.Common.Locale namespace Ryujinx.Ava.Common.Locale
{ {

View File

@@ -145,6 +145,7 @@
<None Remove="Assets\Locales\pt_BR.json" /> <None Remove="Assets\Locales\pt_BR.json" />
<None Remove="Assets\Locales\ru_RU.json" /> <None Remove="Assets\Locales\ru_RU.json" />
<None Remove="Assets\Locales\tr_TR.json" /> <None Remove="Assets\Locales\tr_TR.json" />
<None Remove="Assets\Locales\uk_UA.json" />
<None Remove="Assets\Locales\zh_CN.json" /> <None Remove="Assets\Locales\zh_CN.json" />
<None Remove="Assets\Locales\zh_TW.json" /> <None Remove="Assets\Locales\zh_TW.json" />
<None Remove="Assets\Styles\Styles.xaml" /> <None Remove="Assets\Styles\Styles.xaml" />
@@ -165,6 +166,7 @@
<EmbeddedResource Include="Assets\Locales\pt_BR.json" /> <EmbeddedResource Include="Assets\Locales\pt_BR.json" />
<EmbeddedResource Include="Assets\Locales\ru_RU.json" /> <EmbeddedResource Include="Assets\Locales\ru_RU.json" />
<EmbeddedResource Include="Assets\Locales\tr_TR.json" /> <EmbeddedResource Include="Assets\Locales\tr_TR.json" />
<EmbeddedResource Include="Assets\Locales\uk_UA.json" />
<EmbeddedResource Include="Assets\Locales\zh_CN.json" /> <EmbeddedResource Include="Assets\Locales\zh_CN.json" />
<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" />

View File

@@ -77,63 +77,7 @@
</MenuItem.Icon> </MenuItem.Icon>
</MenuItem> </MenuItem>
<Separator /> <Separator />
<MenuItem Header="{locale:Locale MenuBarOptionsChangeLanguage}"> <MenuItem Name="ChangeLanguageMenuItem" Header="{locale:Locale MenuBarOptionsChangeLanguage}">
<MenuItem
Command="{ReflectionBinding ChangeLanguage}"
CommandParameter="de_DE"
Header="Deutsch" />
<MenuItem
Command="{ReflectionBinding ChangeLanguage}"
CommandParameter="en_US"
Header="English (US)" />
<MenuItem
Command="{ReflectionBinding ChangeLanguage}"
CommandParameter="es_ES"
Header="Español (ES)" />
<MenuItem
Command="{ReflectionBinding ChangeLanguage}"
CommandParameter="fr_FR"
Header="Français" />
<MenuItem
Command="{ReflectionBinding ChangeLanguage}"
CommandParameter="it_IT"
Header="Italiano" />
<MenuItem
Command="{ReflectionBinding ChangeLanguage}"
CommandParameter="pt_BR"
Header="Português (BR)" />
<MenuItem
Command="{ReflectionBinding ChangeLanguage}"
CommandParameter="tr_TR"
Header="Türkçe" />
<MenuItem
Command="{ReflectionBinding ChangeLanguage}"
CommandParameter="el_GR"
Header="Ελληνικά" />
<MenuItem
Command="{ReflectionBinding ChangeLanguage}"
CommandParameter="pl_PL"
Header="Polski" />
<MenuItem
Command="{ReflectionBinding ChangeLanguage}"
CommandParameter="ru_RU"
Header="Русский" />
<MenuItem
Command="{ReflectionBinding ChangeLanguage}"
CommandParameter="zh_CN"
Header="简体中文" />
<MenuItem
Command="{ReflectionBinding ChangeLanguage}"
CommandParameter="zh_TW"
Header="繁體中文" />
<MenuItem
Command="{ReflectionBinding ChangeLanguage}"
CommandParameter="ja_JP"
Header="日本語" />
<MenuItem
Command="{ReflectionBinding ChangeLanguage}"
CommandParameter="ko_KR"
Header="한국어" />
</MenuItem> </MenuItem>
<Separator /> <Separator />
<MenuItem <MenuItem

View File

@@ -1,13 +1,20 @@
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.UI.Windows;
using System.Threading.Tasks;
using LibHac.FsSystem; using LibHac.FsSystem;
using LibHac.Ncm; using LibHac.Ncm;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.UI.Windows;
using Ryujinx.Common;
using Ryujinx.Common.Utilities;
using Ryujinx.HLE.HOS; using Ryujinx.HLE.HOS;
using Ryujinx.Modules; using Ryujinx.Modules;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
namespace Ryujinx.Ava.UI.Views.Main namespace Ryujinx.Ava.UI.Views.Main
{ {
@@ -19,13 +26,47 @@ namespace Ryujinx.Ava.UI.Views.Main
public MainMenuBarView() public MainMenuBarView()
{ {
InitializeComponent(); InitializeComponent();
List<MenuItem> menuItems = new();
string localePath = "Ryujinx.Ava/Assets/Locales";
string localeExt = ".json";
string[] localesPath = EmbeddedResources.GetAllAvailableResources(localePath, localeExt);
Array.Sort(localesPath);
foreach (string locale in localesPath)
{
string languageCode = Path.GetFileNameWithoutExtension(locale).Split('.').Last();
string languageJson = EmbeddedResources.ReadAllText($"{localePath}/{languageCode}{localeExt}");
var strings = JsonHelper.Deserialize<Dictionary<string, string>>(languageJson);
if (!strings.TryGetValue("Language", out string languageName))
{
languageName = languageCode;
}
MenuItem menuItem = new()
{
Header = languageName,
Command = MiniCommand.Create(() =>
{
ViewModel.ChangeLanguage(languageCode);
})
};
menuItems.Add(menuItem);
}
ChangeLanguageMenuItem.Items = menuItems.ToArray();
} }
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{ {
base.OnAttachedToVisualTree(e); base.OnAttachedToVisualTree(e);
if (this.VisualRoot is MainWindow window) if (VisualRoot is MainWindow window)
{ {
Window = window; Window = window;
} }

View File

@@ -81,7 +81,7 @@ namespace Ryujinx.Ava.UI.Windows
private void PrintHeading() private void PrintHeading()
{ {
Heading.Text = string.Format(LocaleManager.Instance[LocaleKeys.GameUpdateWindowHeading], _titleUpdates.Count, _titleName, _titleId.ToString("X16")); Heading.Text = string.Format(LocaleManager.Instance[LocaleKeys.GameUpdateWindowHeading], _titleUpdates.Count - 1, _titleName, _titleId.ToString("X16"));
} }
private void LoadUpdates() private void LoadUpdates()

View File

@@ -1,5 +1,6 @@
using System; using System;
using System.IO; using System.IO;
using System.Linq;
using System.Reflection; using System.Reflection;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -127,6 +128,13 @@ namespace Ryujinx.Common
return stream; return stream;
} }
public static string[] GetAllAvailableResources(string path, string ext = "")
{
return ResolveManifestPath(path).Item1.GetManifestResourceNames()
.Where(r => r.EndsWith(ext))
.ToArray();
}
private static (Assembly, string) ResolveManifestPath(string filename) private static (Assembly, string) ResolveManifestPath(string filename)
{ {
var segments = filename.Split('/', 2, StringSplitOptions.RemoveEmptyEntries); var segments = filename.Split('/', 2, StringSplitOptions.RemoveEmptyEntries);

View File

@@ -1,4 +1,5 @@
using System.Collections; using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
namespace Ryujinx.Graphics.Gpu.Image namespace Ryujinx.Graphics.Gpu.Image
@@ -13,6 +14,7 @@ namespace Ryujinx.Graphics.Gpu.Image
private const int MaxCapacity = 2048; private const int MaxCapacity = 2048;
private readonly LinkedList<Texture> _textures; private readonly LinkedList<Texture> _textures;
private readonly ConcurrentQueue<Texture> _deferredRemovals;
/// <summary> /// <summary>
/// Creates a new instance of the automatic deletion cache. /// Creates a new instance of the automatic deletion cache.
@@ -20,6 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Image
public AutoDeleteCache() public AutoDeleteCache()
{ {
_textures = new LinkedList<Texture>(); _textures = new LinkedList<Texture>();
_deferredRemovals = new ConcurrentQueue<Texture>();
} }
/// <summary> /// <summary>
@@ -56,6 +59,14 @@ namespace Ryujinx.Graphics.Gpu.Image
oldestTexture.CacheNode = null; oldestTexture.CacheNode = null;
} }
if (_deferredRemovals.Count > 0)
{
while (_deferredRemovals.TryDequeue(out Texture textureToRemove))
{
Remove(textureToRemove, false);
}
}
} }
/// <summary> /// <summary>
@@ -84,6 +95,12 @@ namespace Ryujinx.Graphics.Gpu.Image
} }
} }
/// <summary>
/// Removes a texture from the cache.
/// </summary>
/// <param name="texture">The texture to be removed from the cache</param>
/// <param name="flush">True to remove the texture if it was on the cache</param>
/// <returns>True if the texture was found and removed, false otherwise</returns>
public bool Remove(Texture texture, bool flush) public bool Remove(Texture texture, bool flush)
{ {
if (texture.CacheNode == null) if (texture.CacheNode == null)
@@ -104,6 +121,15 @@ namespace Ryujinx.Graphics.Gpu.Image
return texture.DecrementReferenceCount(); return texture.DecrementReferenceCount();
} }
/// <summary>
/// Queues removal of a texture from the cache in a thread safe way.
/// </summary>
/// <param name="texture">The texture to be removed from the cache</param>
public void RemoveDeferred(Texture texture)
{
_deferredRemovals.Enqueue(texture);
}
public IEnumerator<Texture> GetEnumerator() public IEnumerator<Texture> GetEnumerator()
{ {
return _textures.GetEnumerator(); return _textures.GetEnumerator();

View File

@@ -1676,6 +1676,13 @@ namespace Ryujinx.Graphics.Gpu.Image
} }
RemoveFromPools(true); RemoveFromPools(true);
// We only want to remove if there's no mapped region of the texture that was modified by the GPU,
// otherwise we could lose data.
if (!Group.AnyModified(this))
{
_physicalMemory.TextureCache.QueueAutoDeleteCacheRemoval(this);
}
} }
/// <summary> /// <summary>

View File

@@ -1165,6 +1165,19 @@ namespace Ryujinx.Graphics.Gpu.Image
} }
} }
/// <summary>
/// Queues the removal of a texture from the auto delete cache.
/// </summary>
/// <remarks>
/// This function is thread safe and can be called from any thread.
/// The texture will be deleted on the next time the cache is used.
/// </remarks>
/// <param name="texture">The texture to be removed</param>
public void QueueAutoDeleteCacheRemoval(Texture texture)
{
_cache.RemoveDeferred(texture);
}
/// <summary> /// <summary>
/// Disposes all textures and samplers in the cache. /// Disposes all textures and samplers in the cache.
/// It's an error to use the texture cache after disposal. /// It's an error to use the texture cache after disposal.

View File

@@ -434,6 +434,32 @@ namespace Ryujinx.Graphics.Gpu.Image
} }
} }
/// <summary>
/// Checks if a texture was modified by the GPU.
/// </summary>
/// <param name="texture">The texture to be checked</param>
/// <returns>True if any region of the texture was modified by the GPU, false otherwise</returns>
public bool AnyModified(Texture texture)
{
bool anyModified = false;
EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) =>
{
for (int i = 0; i < regionCount; i++)
{
TextureGroupHandle group = _handles[baseHandle + i];
if (group.Modified)
{
anyModified = true;
break;
}
}
});
return anyModified;
}
/// <summary> /// <summary>
/// Flush modified ranges for a given texture. /// Flush modified ranges for a given texture.
/// </summary> /// </summary>