Compare commits
25 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
4965681e06 | ||
|
3868a00206 | ||
|
933e5144a9 | ||
|
73a42c85c4 | ||
|
39ba11054b | ||
|
c250e3392c | ||
|
e56b069081 | ||
|
204c031fef | ||
|
d9053bbe37 | ||
|
c25e8427aa | ||
|
21a081b185 | ||
|
b540ea80d1 | ||
|
d692a9b83e | ||
|
9677ddaa5d | ||
|
ce92e8cd04 | ||
|
456fc04007 | ||
|
458452279c | ||
|
817b89767a | ||
|
3fb583c98c | ||
|
d2686e0a5b | ||
|
4905101df1 | ||
|
8750b90a7f | ||
|
af01100050 | ||
|
c0821fee1f | ||
|
a5c2aead67 |
10
README.md
10
README.md
@@ -21,6 +21,10 @@
|
|||||||
<img src="https://github.com/Ryujinx/Ryujinx/actions/workflows/release.yml/badge.svg"
|
<img src="https://github.com/Ryujinx/Ryujinx/actions/workflows/release.yml/badge.svg"
|
||||||
alt="">
|
alt="">
|
||||||
</a>
|
</a>
|
||||||
|
<a href="https://crwd.in/ryujinx">
|
||||||
|
<img src="https://badges.crowdin.net/ryujinx/localized.svg"
|
||||||
|
alt="">
|
||||||
|
</a>
|
||||||
<a href="https://discord.com/invite/VkQYXAZ">
|
<a href="https://discord.com/invite/VkQYXAZ">
|
||||||
<img src="https://img.shields.io/discord/410208534861447168?color=5865F2&label=Ryujinx&logo=discord&logoColor=white"
|
<img src="https://img.shields.io/discord/410208534861447168?color=5865F2&label=Ryujinx&logo=discord&logoColor=white"
|
||||||
alt="Discord">
|
alt="Discord">
|
||||||
@@ -36,7 +40,7 @@
|
|||||||
|
|
||||||
## Compatibility
|
## Compatibility
|
||||||
|
|
||||||
As of October 2022, Ryujinx has been tested on approximately 3,700 titles; over 3,500 boot past menus and into gameplay, with roughly 3,000 of those being considered playable.
|
As of November 2022, Ryujinx has been tested on approximately 3,800 titles; over 3,600 boot past menus and into gameplay, with roughly 3,200 of those being considered playable.
|
||||||
You can check out the compatibility list [here](https://github.com/Ryujinx/Ryujinx-Games-List/issues). Anyone is free to submit a new game test or update an existing game test entry; simply follow the new issue template and testing guidelines, or post as a reply to the applicable game issue. Use the search function to see if a game has been tested already!
|
You can check out the compatibility list [here](https://github.com/Ryujinx/Ryujinx-Games-List/issues). Anyone is free to submit a new game test or update an existing game test entry; simply follow the new issue template and testing guidelines, or post as a reply to the applicable game issue. Use the search function to see if a game has been tested already!
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
@@ -48,6 +52,8 @@ See our [Setup & Configuration Guide](https://github.com/Ryujinx/Ryujinx/wiki/Ry
|
|||||||
For our Local Wireless and LAN builds, see our [Multiplayer: Local Play/Local Wireless Guide
|
For our Local Wireless and LAN builds, see our [Multiplayer: Local Play/Local Wireless Guide
|
||||||
](https://github.com/Ryujinx/Ryujinx/wiki/Multiplayer-(LDN-Local-Wireless)-Guide).
|
](https://github.com/Ryujinx/Ryujinx/wiki/Multiplayer-(LDN-Local-Wireless)-Guide).
|
||||||
|
|
||||||
|
Avalonia UI comes with translations for various languages. See [Crowdin](https://crwd.in/ryujinx) for more information.
|
||||||
|
|
||||||
## Latest build
|
## Latest build
|
||||||
|
|
||||||
These builds are compiled automatically for each commit on the master branch. While we strive to ensure optimal stability and performance prior to pushing an update, our automated builds **may be unstable or completely broken.**
|
These builds are compiled automatically for each commit on the master branch. While we strive to ensure optimal stability and performance prior to pushing an update, our automated builds **may be unstable or completely broken.**
|
||||||
@@ -90,7 +96,7 @@ Ryujinx system files are stored in the `Ryujinx` folder. This folder is located
|
|||||||
|
|
||||||
- **GPU**
|
- **GPU**
|
||||||
|
|
||||||
The GPU emulator emulates the Switch's Maxwell GPU using either the OpenGL (version 4.5 minimum) or Vulkan APIs through a custom build of OpenTK or Silk.NET respectively. There are currently four graphics enhancements available to the end user in Ryujinx: Disk Shader Caching, Resolution Scaling, Aspect Ratio Adjustment, and Anisotropic Filtering. These enhancements can be adjusted or toggled as desired in the GUI.
|
The GPU emulator emulates the Switch's Maxwell GPU using either the OpenGL (version 4.5 minimum), Vulkan, or Metal (via MoltenVK) APIs through a custom build of OpenTK or Silk.NET respectively. There are currently four graphics enhancements available to the end user in Ryujinx: Disk Shader Caching, Resolution Scaling, Aspect Ratio Adjustment, and Anisotropic Filtering. These enhancements can be adjusted or toggled as desired in the GUI.
|
||||||
|
|
||||||
- **Input**
|
- **Input**
|
||||||
|
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
using Ryujinx.Audio.Common;
|
using Ryujinx.Audio.Common;
|
||||||
using Ryujinx.Audio.Integration;
|
using Ryujinx.Audio.Integration;
|
||||||
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
using Ryujinx.SDL2.Common;
|
using Ryujinx.SDL2.Common;
|
||||||
using System;
|
using System;
|
||||||
@@ -112,6 +113,9 @@ namespace Ryujinx.Audio.Backends.SDL2
|
|||||||
|
|
||||||
if (device == 0)
|
if (device == 0)
|
||||||
{
|
{
|
||||||
|
Logger.Error?.Print(LogClass.Application,
|
||||||
|
$"SDL2 open audio device initialization failed with error \"{SDL_GetError()}\"");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,6 +123,7 @@ namespace Ryujinx.Audio.Backends.SDL2
|
|||||||
|
|
||||||
if (!isValid)
|
if (!isValid)
|
||||||
{
|
{
|
||||||
|
Logger.Error?.Print(LogClass.Application, "SDL2 open audio device is not valid");
|
||||||
SDL_CloseAudioDevice(device);
|
SDL_CloseAudioDevice(device);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@@ -116,6 +116,11 @@ namespace Ryujinx.Audio.Renderer.Dsp
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool HasRemainingCommands(int sessionId)
|
||||||
|
{
|
||||||
|
return _sessionCommandList[sessionId] != null;
|
||||||
|
}
|
||||||
|
|
||||||
public void Signal()
|
public void Signal()
|
||||||
{
|
{
|
||||||
_mailbox.SendMessage(MailboxMessage.RenderStart);
|
_mailbox.SendMessage(MailboxMessage.RenderStart);
|
||||||
|
@@ -670,14 +670,21 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
{
|
{
|
||||||
_terminationEvent.Reset();
|
_terminationEvent.Reset();
|
||||||
|
|
||||||
GenerateCommandList(out CommandList commands);
|
if (!_manager.Processor.HasRemainingCommands(_sessionId))
|
||||||
|
{
|
||||||
|
GenerateCommandList(out CommandList commands);
|
||||||
|
|
||||||
_manager.Processor.Send(_sessionId,
|
_manager.Processor.Send(_sessionId,
|
||||||
commands,
|
commands,
|
||||||
GetMaxAllocatedTimeForDsp(),
|
GetMaxAllocatedTimeForDsp(),
|
||||||
_appletResourceId);
|
_appletResourceId);
|
||||||
|
|
||||||
_systemEvent.Signal();
|
_systemEvent.Signal();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_isDspRunningBehind = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@@ -168,6 +168,8 @@
|
|||||||
"SettingsTabInputDirectKeyboardAccess": "Direkter Tastaturzugriff",
|
"SettingsTabInputDirectKeyboardAccess": "Direkter Tastaturzugriff",
|
||||||
"SettingsButtonSave": "Speichern",
|
"SettingsButtonSave": "Speichern",
|
||||||
"SettingsButtonClose": "Schließen",
|
"SettingsButtonClose": "Schließen",
|
||||||
|
"SettingsButtonOk": "OK",
|
||||||
|
"SettingsButtonCancel": "Abbrechen",
|
||||||
"SettingsButtonApply": "Übernehmen",
|
"SettingsButtonApply": "Übernehmen",
|
||||||
"ControllerSettingsPlayer": "Spieler",
|
"ControllerSettingsPlayer": "Spieler",
|
||||||
"ControllerSettingsPlayer1": "Spieler 1",
|
"ControllerSettingsPlayer1": "Spieler 1",
|
||||||
|
@@ -168,6 +168,8 @@
|
|||||||
"SettingsTabInputDirectKeyboardAccess": "Άμεση Πρόσβαση στο Πληκτρολόγιο",
|
"SettingsTabInputDirectKeyboardAccess": "Άμεση Πρόσβαση στο Πληκτρολόγιο",
|
||||||
"SettingsButtonSave": "Αποθήκευση",
|
"SettingsButtonSave": "Αποθήκευση",
|
||||||
"SettingsButtonClose": "Κλείσιμο",
|
"SettingsButtonClose": "Κλείσιμο",
|
||||||
|
"SettingsButtonOk": "OK",
|
||||||
|
"SettingsButtonCancel": "Ακύρωση",
|
||||||
"SettingsButtonApply": "Εφαρμογή",
|
"SettingsButtonApply": "Εφαρμογή",
|
||||||
"ControllerSettingsPlayer": "Παίχτης",
|
"ControllerSettingsPlayer": "Παίχτης",
|
||||||
"ControllerSettingsPlayer1": "Παίχτης 1",
|
"ControllerSettingsPlayer1": "Παίχτης 1",
|
||||||
|
@@ -168,6 +168,8 @@
|
|||||||
"SettingsTabInputDirectKeyboardAccess": "Direct Keyboard Access",
|
"SettingsTabInputDirectKeyboardAccess": "Direct Keyboard Access",
|
||||||
"SettingsButtonSave": "Save",
|
"SettingsButtonSave": "Save",
|
||||||
"SettingsButtonClose": "Close",
|
"SettingsButtonClose": "Close",
|
||||||
|
"SettingsButtonOk": "OK",
|
||||||
|
"SettingsButtonCancel": "Cancel",
|
||||||
"SettingsButtonApply": "Apply",
|
"SettingsButtonApply": "Apply",
|
||||||
"ControllerSettingsPlayer": "Player",
|
"ControllerSettingsPlayer": "Player",
|
||||||
"ControllerSettingsPlayer1": "Player 1",
|
"ControllerSettingsPlayer1": "Player 1",
|
||||||
@@ -594,7 +596,18 @@
|
|||||||
"RyujinxUpdaterMessage": "Do you want to update Ryujinx to the latest version?",
|
"RyujinxUpdaterMessage": "Do you want to update Ryujinx to the latest version?",
|
||||||
"SettingsTabHotkeysVolumeUpHotkey": "Increase Volume:",
|
"SettingsTabHotkeysVolumeUpHotkey": "Increase Volume:",
|
||||||
"SettingsTabHotkeysVolumeDownHotkey": "Decrease Volume:",
|
"SettingsTabHotkeysVolumeDownHotkey": "Decrease Volume:",
|
||||||
"VolumeShort": "Vol",
|
|
||||||
"SettingsEnableMacroHLE": "Enable Macro HLE",
|
"SettingsEnableMacroHLE": "Enable Macro HLE",
|
||||||
"SettingsEnableMacroHLETooltip": "High-level emulation of GPU Macro code.\n\nImproves performance, but may cause graphical glitches in some games.\n\nLeave ON if unsure."
|
"SettingsEnableMacroHLETooltip": "High-level emulation of GPU Macro code.\n\nImproves performance, but may cause graphical glitches in some games.\n\nLeave ON if unsure.",
|
||||||
|
"VolumeShort": "Vol",
|
||||||
|
"UserProfilesManageSaves": "Manage Saves",
|
||||||
|
"DeleteUserSave": "Do you want to delete user save for this game?",
|
||||||
|
"IrreversibleActionNote": "This action is not reversible.",
|
||||||
|
"SaveManagerHeading": "Manage Saves for {0}",
|
||||||
|
"SaveManagerTitle": "Save Manager",
|
||||||
|
"Name": "Name",
|
||||||
|
"Size": "Size",
|
||||||
|
"Search": "Search",
|
||||||
|
"UserProfilesRecoverLostAccounts": "Recover Lost Accounts",
|
||||||
|
"Recover": "Recover",
|
||||||
|
"UserProfilesRecoverHeading" : "Saves were found for the following accounts"
|
||||||
}
|
}
|
||||||
|
@@ -168,6 +168,8 @@
|
|||||||
"SettingsTabInputDirectKeyboardAccess": "Acceso directo al teclado",
|
"SettingsTabInputDirectKeyboardAccess": "Acceso directo al teclado",
|
||||||
"SettingsButtonSave": "Guardar",
|
"SettingsButtonSave": "Guardar",
|
||||||
"SettingsButtonClose": "Cerrar",
|
"SettingsButtonClose": "Cerrar",
|
||||||
|
"SettingsButtonOk": "OK",
|
||||||
|
"SettingsButtonCancel": "Cancelar",
|
||||||
"SettingsButtonApply": "Aplicar",
|
"SettingsButtonApply": "Aplicar",
|
||||||
"ControllerSettingsPlayer": "Jugador",
|
"ControllerSettingsPlayer": "Jugador",
|
||||||
"ControllerSettingsPlayer1": "Jugador 1",
|
"ControllerSettingsPlayer1": "Jugador 1",
|
||||||
|
@@ -161,6 +161,8 @@
|
|||||||
"SettingsTabInputDirectKeyboardAccess": "Accès direct au clavier",
|
"SettingsTabInputDirectKeyboardAccess": "Accès direct au clavier",
|
||||||
"SettingsButtonSave": "Enregistrer",
|
"SettingsButtonSave": "Enregistrer",
|
||||||
"SettingsButtonClose": "Fermer",
|
"SettingsButtonClose": "Fermer",
|
||||||
|
"SettingsButtonOk": "OK",
|
||||||
|
"SettingsButtonCancel": "Annuler",
|
||||||
"SettingsButtonApply": "Appliquer",
|
"SettingsButtonApply": "Appliquer",
|
||||||
"ControllerSettingsPlayer": "Joueur",
|
"ControllerSettingsPlayer": "Joueur",
|
||||||
"ControllerSettingsPlayer1": "Joueur 1",
|
"ControllerSettingsPlayer1": "Joueur 1",
|
||||||
|
@@ -168,6 +168,8 @@
|
|||||||
"SettingsTabInputDirectKeyboardAccess": "Accesso diretto alla tastiera",
|
"SettingsTabInputDirectKeyboardAccess": "Accesso diretto alla tastiera",
|
||||||
"SettingsButtonSave": "Salva",
|
"SettingsButtonSave": "Salva",
|
||||||
"SettingsButtonClose": "Chiudi",
|
"SettingsButtonClose": "Chiudi",
|
||||||
|
"SettingsButtonOk": "OK",
|
||||||
|
"SettingsButtonCancel": "Cancella",
|
||||||
"SettingsButtonApply": "Applica",
|
"SettingsButtonApply": "Applica",
|
||||||
"ControllerSettingsPlayer": "Giocatore",
|
"ControllerSettingsPlayer": "Giocatore",
|
||||||
"ControllerSettingsPlayer1": "Giocatore 1",
|
"ControllerSettingsPlayer1": "Giocatore 1",
|
||||||
|
@@ -168,6 +168,8 @@
|
|||||||
"SettingsTabInputDirectKeyboardAccess": "キーボード直接アクセス",
|
"SettingsTabInputDirectKeyboardAccess": "キーボード直接アクセス",
|
||||||
"SettingsButtonSave": "セーブ",
|
"SettingsButtonSave": "セーブ",
|
||||||
"SettingsButtonClose": "閉じる",
|
"SettingsButtonClose": "閉じる",
|
||||||
|
"SettingsButtonOk": "オーケー",
|
||||||
|
"SettingsButtonCancel": "キャンセル",
|
||||||
"SettingsButtonApply": "適用",
|
"SettingsButtonApply": "適用",
|
||||||
"ControllerSettingsPlayer": "プレイヤー",
|
"ControllerSettingsPlayer": "プレイヤー",
|
||||||
"ControllerSettingsPlayer1": "プレイヤー 1",
|
"ControllerSettingsPlayer1": "プレイヤー 1",
|
||||||
|
@@ -167,6 +167,8 @@
|
|||||||
"SettingsTabInputDirectKeyboardAccess": "직접 키보드 액세스",
|
"SettingsTabInputDirectKeyboardAccess": "직접 키보드 액세스",
|
||||||
"SettingsButtonSave": "구하다",
|
"SettingsButtonSave": "구하다",
|
||||||
"SettingsButtonClose": "출구",
|
"SettingsButtonClose": "출구",
|
||||||
|
"SettingsButtonOk": "좋아",
|
||||||
|
"SettingsButtonCancel": "취소",
|
||||||
"SettingsButtonApply": "적용하다",
|
"SettingsButtonApply": "적용하다",
|
||||||
"ControllerSettingsPlayer": "플레이어",
|
"ControllerSettingsPlayer": "플레이어",
|
||||||
"ControllerSettingsPlayer1": "플레이어 1",
|
"ControllerSettingsPlayer1": "플레이어 1",
|
||||||
|
@@ -168,6 +168,8 @@
|
|||||||
"SettingsTabInputDirectKeyboardAccess": "Bezpośredni Dostęp do Klawiatury",
|
"SettingsTabInputDirectKeyboardAccess": "Bezpośredni Dostęp do Klawiatury",
|
||||||
"SettingsButtonSave": "Zapisz",
|
"SettingsButtonSave": "Zapisz",
|
||||||
"SettingsButtonClose": "Zamknij",
|
"SettingsButtonClose": "Zamknij",
|
||||||
|
"SettingsButtonOk": "OK",
|
||||||
|
"SettingsButtonCancel": "Anuluj",
|
||||||
"SettingsButtonApply": "Zastosuj",
|
"SettingsButtonApply": "Zastosuj",
|
||||||
"ControllerSettingsPlayer": "Gracz",
|
"ControllerSettingsPlayer": "Gracz",
|
||||||
"ControllerSettingsPlayer1": "Gracz 1",
|
"ControllerSettingsPlayer1": "Gracz 1",
|
||||||
|
@@ -168,6 +168,8 @@
|
|||||||
"SettingsTabInputDirectKeyboardAccess": "Acesso direto ao teclado",
|
"SettingsTabInputDirectKeyboardAccess": "Acesso direto ao teclado",
|
||||||
"SettingsButtonSave": "Salvar",
|
"SettingsButtonSave": "Salvar",
|
||||||
"SettingsButtonClose": "Fechar",
|
"SettingsButtonClose": "Fechar",
|
||||||
|
"SettingsButtonOk": "OK",
|
||||||
|
"SettingsButtonCancel": "Cancelar",
|
||||||
"SettingsButtonApply": "Aplicar",
|
"SettingsButtonApply": "Aplicar",
|
||||||
"ControllerSettingsPlayer": "Jogador",
|
"ControllerSettingsPlayer": "Jogador",
|
||||||
"ControllerSettingsPlayer1": "Jogador 1",
|
"ControllerSettingsPlayer1": "Jogador 1",
|
||||||
|
@@ -167,6 +167,8 @@
|
|||||||
"SettingsTabInputDirectKeyboardAccess": "Прямой доступ с клавиатуры",
|
"SettingsTabInputDirectKeyboardAccess": "Прямой доступ с клавиатуры",
|
||||||
"SettingsButtonSave": "Сохранить",
|
"SettingsButtonSave": "Сохранить",
|
||||||
"SettingsButtonClose": "Закрыть",
|
"SettingsButtonClose": "Закрыть",
|
||||||
|
"SettingsButtonOk": "OK",
|
||||||
|
"SettingsButtonCancel": "Отмена",
|
||||||
"SettingsButtonApply": "Применить",
|
"SettingsButtonApply": "Применить",
|
||||||
"ControllerSettingsPlayer": "Игрок",
|
"ControllerSettingsPlayer": "Игрок",
|
||||||
"ControllerSettingsPlayer1": "Игрок 1",
|
"ControllerSettingsPlayer1": "Игрок 1",
|
||||||
|
@@ -168,6 +168,8 @@
|
|||||||
"SettingsTabInputDirectKeyboardAccess": "Doğrudan Klavye Erişimi",
|
"SettingsTabInputDirectKeyboardAccess": "Doğrudan Klavye Erişimi",
|
||||||
"SettingsButtonSave": "Kaydet",
|
"SettingsButtonSave": "Kaydet",
|
||||||
"SettingsButtonClose": "Kapat",
|
"SettingsButtonClose": "Kapat",
|
||||||
|
"SettingsButtonOk": "Tamam",
|
||||||
|
"SettingsButtonCancel": "İptal",
|
||||||
"SettingsButtonApply": "Uygula",
|
"SettingsButtonApply": "Uygula",
|
||||||
"ControllerSettingsPlayer": "Oyuncu",
|
"ControllerSettingsPlayer": "Oyuncu",
|
||||||
"ControllerSettingsPlayer1": "Oyuncu 1",
|
"ControllerSettingsPlayer1": "Oyuncu 1",
|
||||||
|
@@ -168,6 +168,8 @@
|
|||||||
"SettingsTabInputDirectKeyboardAccess": "直通键盘控制",
|
"SettingsTabInputDirectKeyboardAccess": "直通键盘控制",
|
||||||
"SettingsButtonSave": "保存",
|
"SettingsButtonSave": "保存",
|
||||||
"SettingsButtonClose": "关闭",
|
"SettingsButtonClose": "关闭",
|
||||||
|
"SettingsButtonOk": "批准",
|
||||||
|
"SettingsButtonCancel": "取消",
|
||||||
"SettingsButtonApply": "应用",
|
"SettingsButtonApply": "应用",
|
||||||
"ControllerSettingsPlayer": "玩家",
|
"ControllerSettingsPlayer": "玩家",
|
||||||
"ControllerSettingsPlayer1": "玩家 1",
|
"ControllerSettingsPlayer1": "玩家 1",
|
||||||
|
@@ -168,6 +168,8 @@
|
|||||||
"SettingsTabInputDirectKeyboardAccess": "直通鍵盤控制",
|
"SettingsTabInputDirectKeyboardAccess": "直通鍵盤控制",
|
||||||
"SettingsButtonSave": "儲存",
|
"SettingsButtonSave": "儲存",
|
||||||
"SettingsButtonClose": "關閉",
|
"SettingsButtonClose": "關閉",
|
||||||
|
"SettingsButtonOk": "嘛好",
|
||||||
|
"SettingsButtonCancel": "取消",
|
||||||
"SettingsButtonApply": "套用",
|
"SettingsButtonApply": "套用",
|
||||||
"ControllerSettingsPlayer": "玩家",
|
"ControllerSettingsPlayer": "玩家",
|
||||||
"ControllerSettingsPlayer1": "玩家 1",
|
"ControllerSettingsPlayer1": "玩家 1",
|
||||||
|
@@ -41,6 +41,9 @@
|
|||||||
<SolidColorBrush x:Key="DataGridSelectionBackgroundBrush" Color="{DynamicResource DataGridSelectionColor}" />
|
<SolidColorBrush x:Key="DataGridSelectionBackgroundBrush" Color="{DynamicResource DataGridSelectionColor}" />
|
||||||
<SolidColorBrush x:Key="ThemeAccentColorBrush" Color="{DynamicResource SystemAccentColor}" />
|
<SolidColorBrush x:Key="ThemeAccentColorBrush" Color="{DynamicResource SystemAccentColor}" />
|
||||||
<SolidColorBrush x:Key="ThemeAccentBrush4" Color="{DynamicResource ThemeAccentColor4}" />
|
<SolidColorBrush x:Key="ThemeAccentBrush4" Color="{DynamicResource ThemeAccentColor4}" />
|
||||||
|
<Color x:Key="ControlFillColorSecondary">#008AA8</Color>
|
||||||
|
<SolidColorBrush x:Key="ControlFillColorSecondaryBrush" Color="{StaticResource ControlFillColorSecondary}" />
|
||||||
|
<StaticResource x:Key="ButtonBackgroundPointerOver" ResourceKey="ControlFillColorSecondaryBrush" />
|
||||||
<Color x:Key="SystemAccentColor">#FF00C3E3</Color>
|
<Color x:Key="SystemAccentColor">#FF00C3E3</Color>
|
||||||
<Color x:Key="SystemAccentColorDark1">#FF99b000</Color>
|
<Color x:Key="SystemAccentColorDark1">#FF99b000</Color>
|
||||||
<Color x:Key="SystemAccentColorDark2">#FF006d7d</Color>
|
<Color x:Key="SystemAccentColorDark2">#FF006d7d</Color>
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
<Styles
|
<Styles
|
||||||
xmlns="https://github.com/avaloniaui"
|
xmlns="https://github.com/avaloniaui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:sys="clr-namespace:System;assembly=netstandard"
|
|
||||||
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia">
|
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia">
|
||||||
<Design.PreviewWith>
|
<Design.PreviewWith>
|
||||||
<Border Height="2000" Padding="20">
|
<Border Height="2000" Padding="20">
|
||||||
@@ -269,13 +268,15 @@
|
|||||||
<Color x:Key="DataGridSelectionColor">#FF00FABB</Color>
|
<Color x:Key="DataGridSelectionColor">#FF00FABB</Color>
|
||||||
<Color x:Key="ThemeContentBackgroundColor">#FF2D2D2D</Color>
|
<Color x:Key="ThemeContentBackgroundColor">#FF2D2D2D</Color>
|
||||||
<Color x:Key="ThemeControlBorderColor">#FF505050</Color>
|
<Color x:Key="ThemeControlBorderColor">#FF505050</Color>
|
||||||
<sys:Double x:Key="ScrollBarThickness">15</sys:Double>
|
<x:Double x:Key="ScrollBarThickness">15</x:Double>
|
||||||
<sys:Double x:Key="FontSizeSmall">8</sys:Double>
|
<x:Double x:Key="FontSizeSmall">8</x:Double>
|
||||||
<sys:Double x:Key="FontSizeNormal">10</sys:Double>
|
<x:Double x:Key="FontSizeNormal">10</x:Double>
|
||||||
<sys:Double x:Key="FontSize">12</sys:Double>
|
<x:Double x:Key="FontSize">12</x:Double>
|
||||||
<sys:Double x:Key="FontSizeLarge">15</sys:Double>
|
<x:Double x:Key="FontSizeLarge">15</x:Double>
|
||||||
<sys:Double x:Key="ControlContentThemeFontSize">13</sys:Double>
|
<x:Double x:Key="ControlContentThemeFontSize">13</x:Double>
|
||||||
<x:Double x:Key="MenuItemHeight">26</x:Double>
|
<x:Double x:Key="MenuItemHeight">26</x:Double>
|
||||||
<x:Double x:Key="TabItemMinHeight">28</x:Double>
|
<x:Double x:Key="TabItemMinHeight">28</x:Double>
|
||||||
|
<x:Double x:Key="ContentDialogMaxWidth">600</x:Double>
|
||||||
|
<x:Double x:Key="ContentDialogMaxHeight">756</x:Double>
|
||||||
</Styles.Resources>
|
</Styles.Resources>
|
||||||
</Styles>
|
</Styles>
|
@@ -113,6 +113,11 @@ namespace Ryujinx.Ava.Common
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OpenSaveDir(saveDataId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void OpenSaveDir(ulong saveDataId)
|
||||||
|
{
|
||||||
string saveRootPath = Path.Combine(_virtualFileSystem.GetNandPath(), $"user/save/{saveDataId:x16}");
|
string saveRootPath = Path.Combine(_virtualFileSystem.GetNandPath(), $"user/save/{saveDataId:x16}");
|
||||||
|
|
||||||
if (!Directory.Exists(saveRootPath))
|
if (!Directory.Exists(saveRootPath))
|
||||||
|
@@ -1,8 +1,6 @@
|
|||||||
using ARMeilleure.Translation.PTC;
|
using ARMeilleure.Translation.PTC;
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Rendering;
|
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
using Ryujinx.Ava.Ui.Controls;
|
|
||||||
using Ryujinx.Ava.Ui.Windows;
|
using Ryujinx.Ava.Ui.Windows;
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Configuration;
|
using Ryujinx.Common.Configuration;
|
||||||
@@ -11,6 +9,7 @@ using Ryujinx.Common.Logging;
|
|||||||
using Ryujinx.Common.System;
|
using Ryujinx.Common.System;
|
||||||
using Ryujinx.Common.SystemInfo;
|
using Ryujinx.Common.SystemInfo;
|
||||||
using Ryujinx.Modules;
|
using Ryujinx.Modules;
|
||||||
|
using Ryujinx.SDL2.Common;
|
||||||
using Ryujinx.Ui.Common;
|
using Ryujinx.Ui.Common;
|
||||||
using Ryujinx.Ui.Common.Configuration;
|
using Ryujinx.Ui.Common.Configuration;
|
||||||
using Ryujinx.Ui.Common.Helper;
|
using Ryujinx.Ui.Common.Helper;
|
||||||
@@ -23,18 +22,15 @@ namespace Ryujinx.Ava
|
|||||||
{
|
{
|
||||||
internal class Program
|
internal class Program
|
||||||
{
|
{
|
||||||
public static double WindowScaleFactor { get; set; }
|
public static double WindowScaleFactor { get; set; }
|
||||||
public static double ActualScaleFactor { get; set; }
|
public static string Version { get; private set; }
|
||||||
public static string Version { get; private set; }
|
public static string ConfigurationPath { get; private set; }
|
||||||
public static string ConfigurationPath { get; private set; }
|
public static bool PreviewerDetached { get; private set; }
|
||||||
public static bool PreviewerDetached { get; private set; }
|
|
||||||
public static RenderTimer RenderTimer { get; private set; }
|
|
||||||
|
|
||||||
[DllImport("user32.dll", SetLastError = true)]
|
[DllImport("user32.dll", SetLastError = true)]
|
||||||
public static extern int MessageBoxA(IntPtr hWnd, string text, string caption, uint type);
|
public static extern int MessageBoxA(IntPtr hWnd, string text, string caption, uint type);
|
||||||
|
|
||||||
private const uint MB_ICONWARNING = 0x30;
|
private const uint MB_ICONWARNING = 0x30;
|
||||||
private const int BaseDpi = 96;
|
|
||||||
|
|
||||||
public static void Main(string[] args)
|
public static void Main(string[] args)
|
||||||
{
|
{
|
||||||
@@ -49,11 +45,7 @@ namespace Ryujinx.Ava
|
|||||||
|
|
||||||
Initialize(args);
|
Initialize(args);
|
||||||
|
|
||||||
RenderTimer = new RenderTimer();
|
|
||||||
|
|
||||||
BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
|
BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
|
||||||
|
|
||||||
RenderTimer.Dispose();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static AppBuilder BuildAvaloniaApp()
|
public static AppBuilder BuildAvaloniaApp()
|
||||||
@@ -65,7 +57,7 @@ namespace Ryujinx.Ava
|
|||||||
EnableMultiTouch = true,
|
EnableMultiTouch = true,
|
||||||
EnableIme = true,
|
EnableIme = true,
|
||||||
UseEGL = false,
|
UseEGL = false,
|
||||||
UseGpu = false
|
UseGpu = true
|
||||||
})
|
})
|
||||||
.With(new Win32PlatformOptions
|
.With(new Win32PlatformOptions
|
||||||
{
|
{
|
||||||
@@ -75,12 +67,6 @@ namespace Ryujinx.Ava
|
|||||||
CompositionBackdropCornerRadius = 8.0f,
|
CompositionBackdropCornerRadius = 8.0f,
|
||||||
})
|
})
|
||||||
.UseSkia()
|
.UseSkia()
|
||||||
.AfterSetup(_ =>
|
|
||||||
{
|
|
||||||
AvaloniaLocator.CurrentMutable
|
|
||||||
.Bind<IRenderTimer>().ToConstant(RenderTimer)
|
|
||||||
.Bind<IRenderLoop>().ToConstant(new RenderLoop(RenderTimer, Dispatcher.UIThread));
|
|
||||||
})
|
|
||||||
.LogToTrace();
|
.LogToTrace();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,12 +96,14 @@ namespace Ryujinx.Ava
|
|||||||
// Initialize Discord integration.
|
// Initialize Discord integration.
|
||||||
DiscordIntegrationModule.Initialize();
|
DiscordIntegrationModule.Initialize();
|
||||||
|
|
||||||
|
// Initialize SDL2 driver
|
||||||
|
SDL2Driver.MainThreadDispatcher = action => Dispatcher.UIThread.InvokeAsync(action, DispatcherPriority.Input);
|
||||||
|
|
||||||
ReloadConfig();
|
ReloadConfig();
|
||||||
|
|
||||||
ForceDpiAware.Windows();
|
ForceDpiAware.Windows();
|
||||||
|
|
||||||
WindowScaleFactor = ForceDpiAware.GetWindowScaleFactor();
|
WindowScaleFactor = ForceDpiAware.GetWindowScaleFactor();
|
||||||
ActualScaleFactor = ForceDpiAware.GetActualScaleFactor() / BaseDpi;
|
|
||||||
|
|
||||||
// Logging system information.
|
// Logging system information.
|
||||||
PrintSystemInfo();
|
PrintSystemInfo();
|
||||||
@@ -236,4 +224,4 @@ namespace Ryujinx.Ava
|
|||||||
Logger.Shutdown();
|
Logger.Shutdown();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -33,9 +33,9 @@
|
|||||||
<PackageReference Include="OpenTK.Core" Version="4.7.5" />
|
<PackageReference Include="OpenTK.Core" Version="4.7.5" />
|
||||||
<PackageReference Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'" />
|
<PackageReference Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'" />
|
||||||
<PackageReference Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.1-build10" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'" />
|
<PackageReference Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.1-build10" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'" />
|
||||||
<PackageReference Include="Silk.NET.Vulkan" Version="2.10.1" />
|
<PackageReference Include="Silk.NET.Vulkan" Version="2.16.0" />
|
||||||
<PackageReference Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.10.1" />
|
<PackageReference Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.16.0" />
|
||||||
<PackageReference Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.10.1" />
|
<PackageReference Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.16.0" />
|
||||||
<PackageReference Include="SPB" Version="0.0.4-build28" />
|
<PackageReference Include="SPB" Version="0.0.4-build28" />
|
||||||
<PackageReference Include="SharpZipLib" Version="1.4.1" />
|
<PackageReference Include="SharpZipLib" Version="1.4.1" />
|
||||||
<PackageReference Include="SixLabors.ImageSharp" Version="1.0.4" />
|
<PackageReference Include="SixLabors.ImageSharp" Version="1.0.4" />
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using FluentAvalonia.UI.Controls;
|
using FluentAvalonia.UI.Controls;
|
||||||
|
using LibHac;
|
||||||
using Ryujinx.Ava.Common.Locale;
|
using Ryujinx.Ava.Common.Locale;
|
||||||
using Ryujinx.Ava.Ui.ViewModels;
|
using Ryujinx.Ava.Ui.ViewModels;
|
||||||
using Ryujinx.HLE.FileSystem;
|
using Ryujinx.HLE.FileSystem;
|
||||||
@@ -14,6 +15,8 @@ namespace Ryujinx.Ava.Ui.Controls
|
|||||||
{
|
{
|
||||||
public AccountManager AccountManager { get; }
|
public AccountManager AccountManager { get; }
|
||||||
public ContentManager ContentManager { get; }
|
public ContentManager ContentManager { get; }
|
||||||
|
public VirtualFileSystem VirtualFileSystem { get; }
|
||||||
|
public HorizonClient HorizonClient { get; }
|
||||||
public UserProfileViewModel ViewModel { get; set; }
|
public UserProfileViewModel ViewModel { get; set; }
|
||||||
|
|
||||||
public NavigationDialogHost()
|
public NavigationDialogHost()
|
||||||
@@ -22,10 +25,12 @@ namespace Ryujinx.Ava.Ui.Controls
|
|||||||
}
|
}
|
||||||
|
|
||||||
public NavigationDialogHost(AccountManager accountManager, ContentManager contentManager,
|
public NavigationDialogHost(AccountManager accountManager, ContentManager contentManager,
|
||||||
VirtualFileSystem virtualFileSystem)
|
VirtualFileSystem virtualFileSystem, HorizonClient horizonClient)
|
||||||
{
|
{
|
||||||
AccountManager = accountManager;
|
AccountManager = accountManager;
|
||||||
ContentManager = contentManager;
|
ContentManager = contentManager;
|
||||||
|
VirtualFileSystem = virtualFileSystem;
|
||||||
|
HorizonClient = horizonClient;
|
||||||
ViewModel = new UserProfileViewModel(this);
|
ViewModel = new UserProfileViewModel(this);
|
||||||
|
|
||||||
|
|
||||||
@@ -54,9 +59,10 @@ namespace Ryujinx.Ava.Ui.Controls
|
|||||||
ContentFrame.Navigate(sourcePageType, parameter);
|
ContentFrame.Navigate(sourcePageType, parameter);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task Show(AccountManager ownerAccountManager, ContentManager ownerContentManager, VirtualFileSystem ownerVirtualFileSystem)
|
public static async Task Show(AccountManager ownerAccountManager, ContentManager ownerContentManager,
|
||||||
|
VirtualFileSystem ownerVirtualFileSystem, HorizonClient ownerHorizonClient)
|
||||||
{
|
{
|
||||||
var content = new NavigationDialogHost(ownerAccountManager, ownerContentManager, ownerVirtualFileSystem);
|
var content = new NavigationDialogHost(ownerAccountManager, ownerContentManager, ownerVirtualFileSystem, ownerHorizonClient);
|
||||||
ContentDialog contentDialog = new ContentDialog
|
ContentDialog contentDialog = new ContentDialog
|
||||||
{
|
{
|
||||||
Title = LocaleManager.Instance["UserProfileWindowTitle"],
|
Title = LocaleManager.Instance["UserProfileWindowTitle"],
|
||||||
|
@@ -1,100 +0,0 @@
|
|||||||
using Avalonia.Rendering;
|
|
||||||
using System;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Timers;
|
|
||||||
|
|
||||||
namespace Ryujinx.Ava.Ui.Controls
|
|
||||||
{
|
|
||||||
internal class RenderTimer : IRenderTimer, IDisposable
|
|
||||||
{
|
|
||||||
public event Action<TimeSpan> Tick
|
|
||||||
{
|
|
||||||
add
|
|
||||||
{
|
|
||||||
_tick += value;
|
|
||||||
|
|
||||||
if (_subscriberCount++ == 0)
|
|
||||||
{
|
|
||||||
Start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
remove
|
|
||||||
{
|
|
||||||
if (--_subscriberCount == 0)
|
|
||||||
{
|
|
||||||
Stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
_tick -= value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Thread _tickThread;
|
|
||||||
private readonly System.Timers.Timer _timer;
|
|
||||||
|
|
||||||
private Action<TimeSpan> _tick;
|
|
||||||
private int _subscriberCount;
|
|
||||||
|
|
||||||
private bool _isRunning;
|
|
||||||
|
|
||||||
private AutoResetEvent _resetEvent;
|
|
||||||
|
|
||||||
public RenderTimer()
|
|
||||||
{
|
|
||||||
_timer = new System.Timers.Timer(15);
|
|
||||||
_resetEvent = new AutoResetEvent(true);
|
|
||||||
_timer.Elapsed += Timer_Elapsed;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Timer_Elapsed(object sender, ElapsedEventArgs e)
|
|
||||||
{
|
|
||||||
TickNow();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Start()
|
|
||||||
{
|
|
||||||
_timer.Start();
|
|
||||||
if (_tickThread == null)
|
|
||||||
{
|
|
||||||
_tickThread = new Thread(RunTick);
|
|
||||||
_tickThread.Name = "RenderTimerTickThread";
|
|
||||||
_tickThread.IsBackground = true;
|
|
||||||
_isRunning = true;
|
|
||||||
_tickThread.Start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RunTick()
|
|
||||||
{
|
|
||||||
while (_isRunning)
|
|
||||||
{
|
|
||||||
_resetEvent.WaitOne();
|
|
||||||
_tick?.Invoke(TimeSpan.FromMilliseconds(Environment.TickCount));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void TickNow()
|
|
||||||
{
|
|
||||||
lock (_timer)
|
|
||||||
{
|
|
||||||
_resetEvent.Set();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Stop()
|
|
||||||
{
|
|
||||||
_timer.Stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
_timer.Elapsed -= Timer_Elapsed;
|
|
||||||
_timer.Stop();
|
|
||||||
_isRunning = false;
|
|
||||||
_resetEvent.Set();
|
|
||||||
_tickThread.Join();
|
|
||||||
_resetEvent.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -4,11 +4,4 @@
|
|||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
x:Class="Ryujinx.Ava.Ui.Controls.RendererHost">
|
x:Class="Ryujinx.Ava.Ui.Controls.RendererHost">
|
||||||
<ContentControl
|
|
||||||
HorizontalAlignment="Stretch"
|
|
||||||
HorizontalContentAlignment="Stretch"
|
|
||||||
VerticalAlignment="Stretch"
|
|
||||||
VerticalContentAlignment="Stretch"
|
|
||||||
Name="View"
|
|
||||||
/>
|
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
@@ -41,7 +41,7 @@ namespace Ryujinx.Ava.Ui.Controls
|
|||||||
{
|
{
|
||||||
_currentWindow.WindowCreated += CurrentWindow_WindowCreated;
|
_currentWindow.WindowCreated += CurrentWindow_WindowCreated;
|
||||||
_currentWindow.SizeChanged += CurrentWindow_SizeChanged;
|
_currentWindow.SizeChanged += CurrentWindow_SizeChanged;
|
||||||
View.Content = _currentWindow;
|
Content = _currentWindow;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CreateVulkan()
|
public void CreateVulkan()
|
||||||
|
102
Ryujinx.Ava/Ui/Controls/SaveManager.axaml
Normal file
102
Ryujinx.Ava/Ui/Controls/SaveManager.axaml
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
<UserControl xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:controls="clr-namespace:Ryujinx.Ava.Ui.Controls"
|
||||||
|
xmlns:models="clr-namespace:Ryujinx.Ava.Ui.Models"
|
||||||
|
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||||
|
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
Height="400"
|
||||||
|
Width="550"
|
||||||
|
x:Class="Ryujinx.Ava.Ui.Controls.SaveManager">
|
||||||
|
<UserControl.Resources>
|
||||||
|
<controls:BitmapArrayValueConverter x:Key="ByteImage" />
|
||||||
|
</UserControl.Resources>
|
||||||
|
<Grid>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<Grid Grid.Row="0" HorizontalAlignment="Stretch">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<StackPanel Spacing="10" Orientation="Horizontal" HorizontalAlignment="Left" VerticalAlignment="Center">
|
||||||
|
<Label Content="{locale:Locale CommonSort}" VerticalAlignment="Center" />
|
||||||
|
<ComboBox SelectedIndex="{Binding SortIndex}" Width="100">
|
||||||
|
<ComboBoxItem>
|
||||||
|
<Label VerticalAlignment="Center" HorizontalContentAlignment="Left"
|
||||||
|
Content="{locale:Locale Name}" />
|
||||||
|
</ComboBoxItem>
|
||||||
|
<ComboBoxItem>
|
||||||
|
<Label VerticalAlignment="Center" HorizontalContentAlignment="Left"
|
||||||
|
Content="{locale:Locale Size}" />
|
||||||
|
</ComboBoxItem>
|
||||||
|
</ComboBox>
|
||||||
|
<ComboBox SelectedIndex="{Binding OrderIndex}" Width="150">
|
||||||
|
<ComboBoxItem>
|
||||||
|
<Label VerticalAlignment="Center" HorizontalContentAlignment="Left"
|
||||||
|
Content="{locale:Locale OrderAscending}" />
|
||||||
|
</ComboBoxItem>
|
||||||
|
<ComboBoxItem>
|
||||||
|
<Label VerticalAlignment="Center" HorizontalContentAlignment="Left"
|
||||||
|
Content="{locale:Locale Descending}" />
|
||||||
|
</ComboBoxItem>
|
||||||
|
</ComboBox>
|
||||||
|
</StackPanel>
|
||||||
|
<Grid Grid.Column="1" HorizontalAlignment="Stretch" Margin="10,0, 0, 0">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto"/>
|
||||||
|
<ColumnDefinition/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Label Content="{locale:Locale Search}" VerticalAlignment="Center"/>
|
||||||
|
<TextBox Margin="5,0,0,0" Grid.Column="1" HorizontalAlignment="Stretch" Text="{Binding Search}"/>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
<Border Grid.Row="1" Margin="0,5" BorderThickness="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
|
||||||
|
<ListBox Name="SaveList" Items="{Binding View}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
|
||||||
|
<ListBox.ItemTemplate>
|
||||||
|
<DataTemplate x:DataType="models:SaveModel">
|
||||||
|
<Grid HorizontalAlignment="Stretch" Margin="0,5">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<StackPanel Grid.Column="0" Orientation="Horizontal">
|
||||||
|
<Border Height="42" Margin="2" Width="42" Padding="10"
|
||||||
|
IsVisible="{Binding !InGameList}">
|
||||||
|
<ui:SymbolIcon Symbol="Help" FontSize="30" HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
</Border>
|
||||||
|
<Image IsVisible="{Binding InGameList}"
|
||||||
|
Margin="2"
|
||||||
|
Width="42"
|
||||||
|
Height="42"
|
||||||
|
Source="{Binding Icon, Converter={StaticResource ByteImage}}" />
|
||||||
|
<TextBlock MaxLines="3" Width="320" Margin="5" TextWrapping="Wrap"
|
||||||
|
Text="{Binding Title}" VerticalAlignment="Center" />
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel Grid.Column="1" Spacing="10" HorizontalAlignment="Right"
|
||||||
|
Orientation="Horizontal">
|
||||||
|
<Label Content="{Binding SizeString}" IsVisible="{Binding SizeAvailable}"
|
||||||
|
VerticalAlignment="Center" HorizontalAlignment="Right" />
|
||||||
|
<Button VerticalAlignment="Center" HorizontalAlignment="Right" Padding="10"
|
||||||
|
MinWidth="0" MinHeight="0" Name="OpenLocation" Command="{Binding OpenLocation}">
|
||||||
|
<ui:SymbolIcon Symbol="OpenFolder" HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
</Button>
|
||||||
|
<Button VerticalAlignment="Center" HorizontalAlignment="Right" Padding="10"
|
||||||
|
MinWidth="0" MinHeight="0" Name="Delete" Command="{Binding Delete}">
|
||||||
|
<ui:SymbolIcon Symbol="Delete" HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
</Button>
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</DataTemplate>
|
||||||
|
</ListBox.ItemTemplate>
|
||||||
|
</ListBox>
|
||||||
|
</Border>
|
||||||
|
</Grid>
|
||||||
|
</UserControl>
|
160
Ryujinx.Ava/Ui/Controls/SaveManager.axaml.cs
Normal file
160
Ryujinx.Ava/Ui/Controls/SaveManager.axaml.cs
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
using Avalonia.Controls;
|
||||||
|
using DynamicData;
|
||||||
|
using DynamicData.Binding;
|
||||||
|
using LibHac;
|
||||||
|
using LibHac.Common;
|
||||||
|
using LibHac.Fs;
|
||||||
|
using LibHac.Fs.Shim;
|
||||||
|
using Ryujinx.Ava.Common;
|
||||||
|
using Ryujinx.Ava.Common.Locale;
|
||||||
|
using Ryujinx.Ava.Ui.Models;
|
||||||
|
using Ryujinx.HLE.FileSystem;
|
||||||
|
using Ryujinx.Ui.App.Common;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using UserProfile = Ryujinx.Ava.Ui.Models.UserProfile;
|
||||||
|
|
||||||
|
namespace Ryujinx.Ava.Ui.Controls
|
||||||
|
{
|
||||||
|
public partial class SaveManager : UserControl
|
||||||
|
{
|
||||||
|
private readonly UserProfile _userProfile;
|
||||||
|
private readonly HorizonClient _horizonClient;
|
||||||
|
private readonly VirtualFileSystem _virtualFileSystem;
|
||||||
|
private int _sortIndex;
|
||||||
|
private int _orderIndex;
|
||||||
|
private ObservableCollection<SaveModel> _view = new ObservableCollection<SaveModel>();
|
||||||
|
private string _search;
|
||||||
|
|
||||||
|
public ObservableCollection<SaveModel> Saves { get; set; } = new ObservableCollection<SaveModel>();
|
||||||
|
|
||||||
|
public ObservableCollection<SaveModel> View
|
||||||
|
{
|
||||||
|
get => _view;
|
||||||
|
set => _view = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int SortIndex
|
||||||
|
{
|
||||||
|
get => _sortIndex;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_sortIndex = value;
|
||||||
|
Sort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int OrderIndex
|
||||||
|
{
|
||||||
|
get => _orderIndex;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_orderIndex = value;
|
||||||
|
Sort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Search
|
||||||
|
{
|
||||||
|
get => _search;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_search = value;
|
||||||
|
Sort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public SaveManager()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
public SaveManager(UserProfile userProfile, HorizonClient horizonClient, VirtualFileSystem virtualFileSystem)
|
||||||
|
{
|
||||||
|
_userProfile = userProfile;
|
||||||
|
_horizonClient = horizonClient;
|
||||||
|
_virtualFileSystem = virtualFileSystem;
|
||||||
|
InitializeComponent();
|
||||||
|
|
||||||
|
DataContext = this;
|
||||||
|
|
||||||
|
Task.Run(LoadSaves);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LoadSaves()
|
||||||
|
{
|
||||||
|
Saves.Clear();
|
||||||
|
var saveDataFilter = SaveDataFilter.Make(programId: default, saveType: SaveDataType.Account,
|
||||||
|
new UserId((ulong)_userProfile.UserId.High, (ulong)_userProfile.UserId.Low), saveDataId: default, index: default);
|
||||||
|
|
||||||
|
using var saveDataIterator = new UniqueRef<SaveDataIterator>();
|
||||||
|
|
||||||
|
_horizonClient.Fs.OpenSaveDataIterator(ref saveDataIterator.Ref(), SaveDataSpaceId.User, in saveDataFilter).ThrowIfFailure();
|
||||||
|
|
||||||
|
Span<SaveDataInfo> saveDataInfo = stackalloc SaveDataInfo[10];
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
saveDataIterator.Get.ReadSaveDataInfo(out long readCount, saveDataInfo).ThrowIfFailure();
|
||||||
|
|
||||||
|
if (readCount == 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < readCount; i++)
|
||||||
|
{
|
||||||
|
var save = saveDataInfo[i];
|
||||||
|
if (save.ProgramId.Value != 0)
|
||||||
|
{
|
||||||
|
var saveModel = new SaveModel(save, _horizonClient, _virtualFileSystem);
|
||||||
|
Saves.Add(saveModel);
|
||||||
|
saveModel.DeleteAction = () => { Saves.Remove(saveModel); };
|
||||||
|
}
|
||||||
|
|
||||||
|
Sort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Sort()
|
||||||
|
{
|
||||||
|
Saves.AsObservableChangeSet()
|
||||||
|
.Filter(Filter)
|
||||||
|
.Sort(GetComparer())
|
||||||
|
.Bind(out var view).AsObservableList();
|
||||||
|
|
||||||
|
_view.Clear();
|
||||||
|
_view.AddRange(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IComparer<SaveModel> GetComparer()
|
||||||
|
{
|
||||||
|
switch (SortIndex)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
return OrderIndex == 0
|
||||||
|
? SortExpressionComparer<SaveModel>.Ascending(save => save.Title)
|
||||||
|
: SortExpressionComparer<SaveModel>.Descending(save => save.Title);
|
||||||
|
case 1:
|
||||||
|
return OrderIndex == 0
|
||||||
|
? SortExpressionComparer<SaveModel>.Ascending(save => save.Size)
|
||||||
|
: SortExpressionComparer<SaveModel>.Descending(save => save.Size);
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool Filter(object arg)
|
||||||
|
{
|
||||||
|
if (arg is SaveModel save)
|
||||||
|
{
|
||||||
|
return string.IsNullOrWhiteSpace(_search) || save.Title.ToLower().Contains(_search.ToLower());
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -10,6 +10,7 @@
|
|||||||
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||||
xmlns:viewModels="clr-namespace:Ryujinx.Ava.Ui.ViewModels"
|
xmlns:viewModels="clr-namespace:Ryujinx.Ava.Ui.ViewModels"
|
||||||
Margin="0"
|
Margin="0"
|
||||||
|
MinWidth="500"
|
||||||
Padding="0"
|
Padding="0"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
<UserControl.Resources>
|
<UserControl.Resources>
|
||||||
@@ -63,7 +64,7 @@
|
|||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
MaxLength="{Binding MaxProfileNameLength}"
|
MaxLength="{Binding MaxProfileNameLength}"
|
||||||
Text="{Binding Name}" />
|
Text="{Binding Name}" />
|
||||||
<TextBlock Text="{Locale:Locale UserProfilesUserId}" />
|
<TextBlock Name="IdText" Text="{Locale:Locale UserProfilesUserId}" />
|
||||||
<TextBlock Name="IdLabel" Text="{Binding UserId}" />
|
<TextBlock Name="IdLabel" Text="{Binding UserId}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel
|
<StackPanel
|
||||||
|
@@ -36,15 +36,8 @@ namespace Ryujinx.Ava.Ui.Controls
|
|||||||
case NavigationMode.New:
|
case NavigationMode.New:
|
||||||
var args = ((NavigationDialogHost parent, UserProfile profile, bool isNewUser))arg.Parameter;
|
var args = ((NavigationDialogHost parent, UserProfile profile, bool isNewUser))arg.Parameter;
|
||||||
_isNewUser = args.isNewUser;
|
_isNewUser = args.isNewUser;
|
||||||
if (!_isNewUser)
|
_profile = args.profile;
|
||||||
{
|
TempProfile = new TempProfile(_profile);
|
||||||
_profile = args.profile;
|
|
||||||
TempProfile = new TempProfile(_profile);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
TempProfile = new TempProfile();
|
|
||||||
}
|
|
||||||
|
|
||||||
_parent = args.parent;
|
_parent = args.parent;
|
||||||
break;
|
break;
|
||||||
@@ -53,7 +46,8 @@ namespace Ryujinx.Ava.Ui.Controls
|
|||||||
DataContext = TempProfile;
|
DataContext = TempProfile;
|
||||||
|
|
||||||
AddPictureButton.IsVisible = _isNewUser;
|
AddPictureButton.IsVisible = _isNewUser;
|
||||||
IdLabel.IsVisible = !_isNewUser;
|
IdLabel.IsVisible = _profile != null;
|
||||||
|
IdText.IsVisible = _profile != null;
|
||||||
ChangePictureButton.IsVisible = !_isNewUser;
|
ChangePictureButton.IsVisible = !_isNewUser;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -87,7 +81,7 @@ namespace Ryujinx.Ava.Ui.Controls
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_profile != null)
|
if (_profile != null && !_isNewUser)
|
||||||
{
|
{
|
||||||
_profile.Name = TempProfile.Name;
|
_profile.Name = TempProfile.Name;
|
||||||
_profile.Image = TempProfile.Image;
|
_profile.Image = TempProfile.Image;
|
||||||
@@ -97,7 +91,7 @@ namespace Ryujinx.Ava.Ui.Controls
|
|||||||
}
|
}
|
||||||
else if (_isNewUser)
|
else if (_isNewUser)
|
||||||
{
|
{
|
||||||
_parent.AccountManager.AddUser(TempProfile.Name, TempProfile.Image);
|
_parent.AccountManager.AddUser(TempProfile.Name, TempProfile.Image, TempProfile.UserId);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
70
Ryujinx.Ava/Ui/Controls/UserRecoverer.axaml
Normal file
70
Ryujinx.Ava/Ui/Controls/UserRecoverer.axaml
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
<UserControl xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
mc:Ignorable="d"
|
||||||
|
d:DesignWidth="800"
|
||||||
|
d:DesignHeight="450"
|
||||||
|
MinWidth="500"
|
||||||
|
MinHeight="400"
|
||||||
|
xmlns:Locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
||||||
|
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||||
|
xmlns:viewModels="clr-namespace:Ryujinx.Ava.Ui.ViewModels"
|
||||||
|
x:Class="Ryujinx.Ava.Ui.Controls.UserRecoverer">
|
||||||
|
<Design.DataContext>
|
||||||
|
<viewModels:UserProfileViewModel />
|
||||||
|
</Design.DataContext>
|
||||||
|
<Grid HorizontalAlignment="Stretch"
|
||||||
|
VerticalAlignment="Stretch">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<Button Grid.Row="0"
|
||||||
|
Margin="5"
|
||||||
|
Height="30"
|
||||||
|
Width="50"
|
||||||
|
MinWidth="50"
|
||||||
|
HorizontalAlignment="Left"
|
||||||
|
Command="{Binding GoBack}">
|
||||||
|
<ui:SymbolIcon Symbol="Back"/>
|
||||||
|
</Button>
|
||||||
|
<TextBlock Grid.Row="1"
|
||||||
|
Text="{Locale:Locale UserProfilesRecoverHeading}"/>
|
||||||
|
<ListBox
|
||||||
|
Margin="5"
|
||||||
|
Grid.Row="2"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
VerticalAlignment="Stretch"
|
||||||
|
Items="{Binding LostProfiles}">
|
||||||
|
<ListBox.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<Border
|
||||||
|
Margin="2"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
VerticalAlignment="Stretch"
|
||||||
|
ClipToBounds="True"
|
||||||
|
CornerRadius="5">
|
||||||
|
<Grid Margin="0">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition/>
|
||||||
|
<ColumnDefinition Width="Auto"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<TextBlock
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
Text="{Binding UserId}"
|
||||||
|
TextAlignment="Left"
|
||||||
|
TextWrapping="Wrap" />
|
||||||
|
<Button Grid.Column="1"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
Command="{Binding Recover}"
|
||||||
|
CommandParameter="{Binding}"
|
||||||
|
Content="{Locale:Locale Recover}"/>
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
</DataTemplate>
|
||||||
|
</ListBox.ItemTemplate>
|
||||||
|
</ListBox>
|
||||||
|
</Grid>
|
||||||
|
</UserControl>
|
44
Ryujinx.Ava/Ui/Controls/UserRecoverer.axaml.cs
Normal file
44
Ryujinx.Ava/Ui/Controls/UserRecoverer.axaml.cs
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Interactivity;
|
||||||
|
using Avalonia.Markup.Xaml;
|
||||||
|
using FluentAvalonia.UI.Controls;
|
||||||
|
using FluentAvalonia.UI.Navigation;
|
||||||
|
using Ryujinx.Ava.Ui.Models;
|
||||||
|
using Ryujinx.Ava.Ui.ViewModels;
|
||||||
|
|
||||||
|
namespace Ryujinx.Ava.Ui.Controls
|
||||||
|
{
|
||||||
|
public partial class UserRecoverer : UserControl
|
||||||
|
{
|
||||||
|
private UserProfileViewModel _viewModel;
|
||||||
|
private NavigationDialogHost _parent;
|
||||||
|
|
||||||
|
public UserRecoverer()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
AddHandler(Frame.NavigatedToEvent, (s, e) =>
|
||||||
|
{
|
||||||
|
NavigatedTo(e);
|
||||||
|
}, RoutingStrategies.Direct);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void NavigatedTo(NavigationEventArgs arg)
|
||||||
|
{
|
||||||
|
if (Program.PreviewerDetached)
|
||||||
|
{
|
||||||
|
switch (arg.NavigationMode)
|
||||||
|
{
|
||||||
|
case NavigationMode.New:
|
||||||
|
var args = ((NavigationDialogHost parent, UserProfileViewModel viewModel))arg.Parameter;
|
||||||
|
|
||||||
|
_viewModel = args.viewModel;
|
||||||
|
_parent = args.parent;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
DataContext = _viewModel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -10,6 +10,7 @@
|
|||||||
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||||
xmlns:viewModels="clr-namespace:Ryujinx.Ava.Ui.ViewModels"
|
xmlns:viewModels="clr-namespace:Ryujinx.Ava.Ui.ViewModels"
|
||||||
d:DesignHeight="450"
|
d:DesignHeight="450"
|
||||||
|
MinWidth="500"
|
||||||
d:DesignWidth="800"
|
d:DesignWidth="800"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
<UserControl.Resources>
|
<UserControl.Resources>
|
||||||
@@ -25,6 +26,7 @@
|
|||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<ListBox
|
<ListBox
|
||||||
Margin="5"
|
Margin="5"
|
||||||
|
MaxHeight="300"
|
||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
DoubleTapped="ProfilesList_DoubleTapped"
|
DoubleTapped="ProfilesList_DoubleTapped"
|
||||||
@@ -88,21 +90,56 @@
|
|||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ListBox.ItemTemplate>
|
</ListBox.ItemTemplate>
|
||||||
</ListBox>
|
</ListBox>
|
||||||
<StackPanel
|
<Grid
|
||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
Margin="10,0"
|
HorizontalAlignment="Center">
|
||||||
HorizontalAlignment="Center"
|
<Grid.RowDefinitions>
|
||||||
Orientation="Horizontal"
|
<RowDefinition Height="Auto"/>
|
||||||
Spacing="10">
|
<RowDefinition Height="Auto"/>
|
||||||
<Button Command="{Binding AddUser}" Content="{Locale:Locale UserProfilesAddNewProfile}" />
|
<RowDefinition Height="Auto"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto"/>
|
||||||
|
<ColumnDefinition Width="Auto"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Button
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
Grid.Row="0"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="2"
|
||||||
|
Command="{Binding AddUser}"
|
||||||
|
Content="{Locale:Locale UserProfilesAddNewProfile}" />
|
||||||
<Button
|
<Button
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
Grid.Row="0"
|
||||||
|
Margin="2"
|
||||||
|
Grid.Column="1"
|
||||||
Command="{Binding EditUser}"
|
Command="{Binding EditUser}"
|
||||||
Content="{Locale:Locale UserProfilesEditProfile}"
|
Content="{Locale:Locale UserProfilesEditProfile}"
|
||||||
IsEnabled="{Binding IsSelectedProfiledEditable}" />
|
IsEnabled="{Binding IsSelectedProfiledEditable}" />
|
||||||
|
<Button
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
Grid.Row="1"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="2"
|
||||||
|
Content="{Locale:Locale UserProfilesManageSaves}"
|
||||||
|
Command="{Binding ManageSaves}" />
|
||||||
<Button
|
<Button
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
Grid.Row="1"
|
||||||
|
Grid.Column="1"
|
||||||
|
Margin="2"
|
||||||
Command="{Binding DeleteUser}"
|
Command="{Binding DeleteUser}"
|
||||||
Content="{Locale:Locale UserProfilesDeleteSelectedProfile}"
|
Content="{Locale:Locale UserProfilesDeleteSelectedProfile}"
|
||||||
IsEnabled="{Binding IsSelectedProfileDeletable}" />
|
IsEnabled="{Binding IsSelectedProfileDeletable}" />
|
||||||
</StackPanel>
|
<Button
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
Grid.Row="2"
|
||||||
|
Grid.ColumnSpan="2"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="2"
|
||||||
|
Command="{Binding RecoverLostAccounts}"
|
||||||
|
Content="{Locale:Locale UserProfilesRecoverLostAccounts}" />
|
||||||
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
122
Ryujinx.Ava/Ui/Models/SaveModel.cs
Normal file
122
Ryujinx.Ava/Ui/Models/SaveModel.cs
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
using LibHac;
|
||||||
|
using LibHac.Fs;
|
||||||
|
using LibHac.Fs.Shim;
|
||||||
|
using LibHac.Ncm;
|
||||||
|
using Ryujinx.Ava.Common;
|
||||||
|
using Ryujinx.Ava.Common.Locale;
|
||||||
|
using Ryujinx.Ava.Ui.Controls;
|
||||||
|
using Ryujinx.Ava.Ui.ViewModels;
|
||||||
|
using Ryujinx.Ava.Ui.Windows;
|
||||||
|
using Ryujinx.HLE.FileSystem;
|
||||||
|
using Ryujinx.Ui.App.Common;
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Ryujinx.Ava.Ui.Models
|
||||||
|
{
|
||||||
|
public class SaveModel : BaseModel
|
||||||
|
{
|
||||||
|
private readonly HorizonClient _horizonClient;
|
||||||
|
private long _size;
|
||||||
|
|
||||||
|
public Action DeleteAction { get; set; }
|
||||||
|
public ulong SaveId { get; }
|
||||||
|
public ProgramId TitleId { get; }
|
||||||
|
public string TitleIdString => $"{TitleId.Value:X16}";
|
||||||
|
public UserId UserId { get; }
|
||||||
|
public bool InGameList { get; }
|
||||||
|
public string Title { get; }
|
||||||
|
public byte[] Icon { get; }
|
||||||
|
|
||||||
|
public long Size
|
||||||
|
{
|
||||||
|
get => _size; set
|
||||||
|
{
|
||||||
|
_size = value;
|
||||||
|
SizeAvailable = true;
|
||||||
|
OnPropertyChanged();
|
||||||
|
OnPropertyChanged(nameof(SizeString));
|
||||||
|
OnPropertyChanged(nameof(SizeAvailable));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool SizeAvailable { get; set; }
|
||||||
|
|
||||||
|
public string SizeString => $"{((float)_size * 0.000000954):0.###}MB";
|
||||||
|
|
||||||
|
public SaveModel(SaveDataInfo info, HorizonClient horizonClient, VirtualFileSystem virtualFileSystem)
|
||||||
|
{
|
||||||
|
_horizonClient = horizonClient;
|
||||||
|
SaveId = info.SaveDataId;
|
||||||
|
TitleId = info.ProgramId;
|
||||||
|
UserId = info.UserId;
|
||||||
|
|
||||||
|
var appData = MainWindow.MainWindowViewModel.Applications.FirstOrDefault(x => x.TitleId.ToUpper() == TitleIdString);
|
||||||
|
|
||||||
|
InGameList = appData != null;
|
||||||
|
|
||||||
|
if (InGameList)
|
||||||
|
{
|
||||||
|
Icon = appData.Icon;
|
||||||
|
Title = appData.TitleName;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var appMetadata = MainWindow.MainWindowViewModel.ApplicationLibrary.LoadAndSaveMetaData(TitleIdString);
|
||||||
|
Title = appMetadata.Title ?? TitleIdString;
|
||||||
|
}
|
||||||
|
|
||||||
|
Task.Run(() =>
|
||||||
|
{
|
||||||
|
var saveRoot = System.IO.Path.Combine(virtualFileSystem.GetNandPath(), $"user/save/{info.SaveDataId:x16}");
|
||||||
|
|
||||||
|
long total_size = GetDirectorySize(saveRoot);
|
||||||
|
long GetDirectorySize(string path)
|
||||||
|
{
|
||||||
|
long size = 0;
|
||||||
|
if (Directory.Exists(path))
|
||||||
|
{
|
||||||
|
var directories = Directory.GetDirectories(path);
|
||||||
|
foreach (var directory in directories)
|
||||||
|
{
|
||||||
|
size += GetDirectorySize(directory);
|
||||||
|
}
|
||||||
|
|
||||||
|
var files = Directory.GetFiles(path);
|
||||||
|
foreach (var file in files)
|
||||||
|
{
|
||||||
|
size += new FileInfo(file).Length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
Size = total_size;
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OpenLocation()
|
||||||
|
{
|
||||||
|
ApplicationHelper.OpenSaveDir(SaveId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async void Delete()
|
||||||
|
{
|
||||||
|
var result = await ContentDialogHelper.CreateConfirmationDialog(LocaleManager.Instance["DeleteUserSave"],
|
||||||
|
LocaleManager.Instance["IrreversibleActionNote"],
|
||||||
|
LocaleManager.Instance["InputDialogYes"],
|
||||||
|
LocaleManager.Instance["InputDialogNo"], "");
|
||||||
|
|
||||||
|
if (result == UserResult.Yes)
|
||||||
|
{
|
||||||
|
_horizonClient.Fs.DeleteSaveData(SaveDataSpaceId.User, SaveId);
|
||||||
|
|
||||||
|
DeleteAction?.Invoke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -45,9 +45,12 @@ namespace Ryujinx.Ava.Ui.Models
|
|||||||
{
|
{
|
||||||
_profile = profile;
|
_profile = profile;
|
||||||
|
|
||||||
Image = profile.Image;
|
if (_profile != null)
|
||||||
Name = profile.Name;
|
{
|
||||||
UserId = profile.UserId;
|
Image = profile.Image;
|
||||||
|
Name = profile.Name;
|
||||||
|
UserId = profile.UserId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public TempProfile(){}
|
public TempProfile(){}
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
using Ryujinx.Ava.Ui.Controls;
|
||||||
using Ryujinx.Ava.Ui.ViewModels;
|
using Ryujinx.Ava.Ui.ViewModels;
|
||||||
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
||||||
using Profile = Ryujinx.HLE.HOS.Services.Account.Acc.UserProfile;
|
using Profile = Ryujinx.HLE.HOS.Services.Account.Acc.UserProfile;
|
||||||
@@ -7,6 +8,7 @@ namespace Ryujinx.Ava.Ui.Models
|
|||||||
public class UserProfile : BaseModel
|
public class UserProfile : BaseModel
|
||||||
{
|
{
|
||||||
private readonly Profile _profile;
|
private readonly Profile _profile;
|
||||||
|
private readonly NavigationDialogHost _owner;
|
||||||
private byte[] _image;
|
private byte[] _image;
|
||||||
private string _name;
|
private string _name;
|
||||||
private UserId _userId;
|
private UserId _userId;
|
||||||
@@ -41,9 +43,10 @@ namespace Ryujinx.Ava.Ui.Models
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public UserProfile(Profile profile)
|
public UserProfile(Profile profile, NavigationDialogHost owner)
|
||||||
{
|
{
|
||||||
_profile = profile;
|
_profile = profile;
|
||||||
|
_owner = owner;
|
||||||
|
|
||||||
Image = profile.Image;
|
Image = profile.Image;
|
||||||
Name = profile.Name;
|
Name = profile.Name;
|
||||||
@@ -57,5 +60,10 @@ namespace Ryujinx.Ava.Ui.Models
|
|||||||
OnPropertyChanged(nameof(IsOpened));
|
OnPropertyChanged(nameof(IsOpened));
|
||||||
OnPropertyChanged(nameof(Name));
|
OnPropertyChanged(nameof(Name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Recover(UserProfile userProfile)
|
||||||
|
{
|
||||||
|
_owner.Navigate(typeof(UserEditor), (_owner, userProfile, true));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -76,6 +76,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
|||||||
private bool _showAll;
|
private bool _showAll;
|
||||||
private string _lastScannedAmiiboId;
|
private string _lastScannedAmiiboId;
|
||||||
private ReadOnlyObservableCollection<ApplicationData> _appsObservableList;
|
private ReadOnlyObservableCollection<ApplicationData> _appsObservableList;
|
||||||
|
public ApplicationLibrary ApplicationLibrary => _owner.ApplicationLibrary;
|
||||||
|
|
||||||
public string TitleName { get; internal set; }
|
public string TitleName { get; internal set; }
|
||||||
|
|
||||||
@@ -103,8 +104,8 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
|||||||
|
|
||||||
public void Initialize()
|
public void Initialize()
|
||||||
{
|
{
|
||||||
_owner.ApplicationLibrary.ApplicationCountUpdated += ApplicationLibrary_ApplicationCountUpdated;
|
ApplicationLibrary.ApplicationCountUpdated += ApplicationLibrary_ApplicationCountUpdated;
|
||||||
_owner.ApplicationLibrary.ApplicationAdded += ApplicationLibrary_ApplicationAdded;
|
ApplicationLibrary.ApplicationAdded += ApplicationLibrary_ApplicationAdded;
|
||||||
|
|
||||||
Ptc.PtcStateChanged -= ProgressHandler;
|
Ptc.PtcStateChanged -= ProgressHandler;
|
||||||
Ptc.PtcStateChanged += ProgressHandler;
|
Ptc.PtcStateChanged += ProgressHandler;
|
||||||
@@ -817,7 +818,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
|||||||
|
|
||||||
Thread thread = new(() =>
|
Thread thread = new(() =>
|
||||||
{
|
{
|
||||||
_owner.ApplicationLibrary.LoadApplications(ConfigurationState.Instance.Ui.GameDirs.Value, ConfigurationState.Instance.System.Language);
|
ApplicationLibrary.LoadApplications(ConfigurationState.Instance.Ui.GameDirs.Value, ConfigurationState.Instance.System.Language);
|
||||||
|
|
||||||
_isLoading = false;
|
_isLoading = false;
|
||||||
})
|
})
|
||||||
@@ -1005,7 +1006,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
|||||||
|
|
||||||
public async void ManageProfiles()
|
public async void ManageProfiles()
|
||||||
{
|
{
|
||||||
await NavigationDialogHost.Show(_owner.AccountManager, _owner.ContentManager, _owner.VirtualFileSystem);
|
await NavigationDialogHost.Show(_owner.AccountManager, _owner.ContentManager, _owner.VirtualFileSystem, _owner.LibHacHorizonManager.RyujinxClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void OpenAboutWindow()
|
public async void OpenAboutWindow()
|
||||||
@@ -1098,7 +1099,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
|||||||
{
|
{
|
||||||
selection.Favorite = !selection.Favorite;
|
selection.Favorite = !selection.Favorite;
|
||||||
|
|
||||||
_owner.ApplicationLibrary.LoadAndSaveMetaData(selection.TitleId, appMetadata =>
|
ApplicationLibrary.LoadAndSaveMetaData(selection.TitleId, appMetadata =>
|
||||||
{
|
{
|
||||||
appMetadata.Favorite = selection.Favorite;
|
appMetadata.Favorite = selection.Favorite;
|
||||||
});
|
});
|
||||||
|
@@ -118,7 +118,12 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
|||||||
OnPropertyChanged();
|
OnPropertyChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool IsMacOS
|
||||||
|
{
|
||||||
|
get => OperatingSystem.IsMacOS();
|
||||||
|
}
|
||||||
|
|
||||||
public bool EnableDiscordIntegration { get; set; }
|
public bool EnableDiscordIntegration { get; set; }
|
||||||
public bool CheckUpdatesOnStart { get; set; }
|
public bool CheckUpdatesOnStart { get; set; }
|
||||||
public bool ShowConfirmExit { get; set; }
|
public bool ShowConfirmExit { get; set; }
|
||||||
@@ -474,11 +479,40 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
|||||||
MainWindow.UpdateGraphicsConfig();
|
MainWindow.UpdateGraphicsConfig();
|
||||||
|
|
||||||
_previousVolumeLevel = Volume;
|
_previousVolumeLevel = Volume;
|
||||||
|
|
||||||
|
if (_owner is SettingsWindow owner)
|
||||||
|
{
|
||||||
|
owner.ControllerSettings?.SaveCurrentProfile();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_owner.Owner is MainWindow window && _directoryChanged)
|
||||||
|
{
|
||||||
|
window.ViewModel.LoadApplications();
|
||||||
|
}
|
||||||
|
|
||||||
|
_directoryChanged = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RevertIfNotSaved()
|
public void RevertIfNotSaved()
|
||||||
{
|
{
|
||||||
Program.ReloadConfig();
|
Program.ReloadConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void ApplyButton()
|
||||||
|
{
|
||||||
|
SaveSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OkButton()
|
||||||
|
{
|
||||||
|
SaveSettings();
|
||||||
|
_owner.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CancelButton()
|
||||||
|
{
|
||||||
|
RevertIfNotSaved();
|
||||||
|
_owner.Close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,8 +1,14 @@
|
|||||||
|
using Avalonia;
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
|
using FluentAvalonia.UI.Controls;
|
||||||
|
using LibHac.Common;
|
||||||
|
using LibHac.Fs;
|
||||||
|
using LibHac.Fs.Shim;
|
||||||
using Ryujinx.Ava.Common.Locale;
|
using Ryujinx.Ava.Common.Locale;
|
||||||
using Ryujinx.Ava.Ui.Controls;
|
using Ryujinx.Ava.Ui.Controls;
|
||||||
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using UserProfile = Ryujinx.Ava.Ui.Models.UserProfile;
|
using UserProfile = Ryujinx.Ava.Ui.Models.UserProfile;
|
||||||
@@ -19,6 +25,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
|||||||
public UserProfileViewModel()
|
public UserProfileViewModel()
|
||||||
{
|
{
|
||||||
Profiles = new ObservableCollection<UserProfile>();
|
Profiles = new ObservableCollection<UserProfile>();
|
||||||
|
LostProfiles = new ObservableCollection<UserProfile>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public UserProfileViewModel(NavigationDialogHost owner) : this()
|
public UserProfileViewModel(NavigationDialogHost owner) : this()
|
||||||
@@ -30,6 +37,8 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
|||||||
|
|
||||||
public ObservableCollection<UserProfile> Profiles { get; set; }
|
public ObservableCollection<UserProfile> Profiles { get; set; }
|
||||||
|
|
||||||
|
public ObservableCollection<UserProfile> LostProfiles { get; set; }
|
||||||
|
|
||||||
public UserProfile SelectedProfile
|
public UserProfile SelectedProfile
|
||||||
{
|
{
|
||||||
get => _selectedProfile;
|
get => _selectedProfile;
|
||||||
@@ -65,12 +74,13 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
|||||||
public void LoadProfiles()
|
public void LoadProfiles()
|
||||||
{
|
{
|
||||||
Profiles.Clear();
|
Profiles.Clear();
|
||||||
|
LostProfiles.Clear();
|
||||||
|
|
||||||
var profiles = _owner.AccountManager.GetAllUsers().OrderByDescending(x => x.AccountState == AccountState.Open);
|
var profiles = _owner.AccountManager.GetAllUsers().OrderByDescending(x => x.AccountState == AccountState.Open);
|
||||||
|
|
||||||
foreach (var profile in profiles)
|
foreach (var profile in profiles)
|
||||||
{
|
{
|
||||||
Profiles.Add(new UserProfile(profile));
|
Profiles.Add(new UserProfile(profile, _owner));
|
||||||
}
|
}
|
||||||
|
|
||||||
SelectedProfile = Profiles.FirstOrDefault(x => x.UserId == _owner.AccountManager.LastOpenedUser.UserId);
|
SelectedProfile = Profiles.FirstOrDefault(x => x.UserId == _owner.AccountManager.LastOpenedUser.UserId);
|
||||||
@@ -84,6 +94,42 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
|||||||
_owner.AccountManager.OpenUser(_selectedProfile.UserId);
|
_owner.AccountManager.OpenUser(_selectedProfile.UserId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var saveDataFilter = SaveDataFilter.Make(programId: default, saveType: SaveDataType.Account,
|
||||||
|
default, saveDataId: default, index: default);
|
||||||
|
|
||||||
|
using var saveDataIterator = new UniqueRef<SaveDataIterator>();
|
||||||
|
|
||||||
|
_owner.HorizonClient.Fs.OpenSaveDataIterator(ref saveDataIterator.Ref(), SaveDataSpaceId.User, in saveDataFilter).ThrowIfFailure();
|
||||||
|
|
||||||
|
Span<SaveDataInfo> saveDataInfo = stackalloc SaveDataInfo[10];
|
||||||
|
|
||||||
|
HashSet<HLE.HOS.Services.Account.Acc.UserId> lostAccounts = new HashSet<HLE.HOS.Services.Account.Acc.UserId>();
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
saveDataIterator.Get.ReadSaveDataInfo(out long readCount, saveDataInfo).ThrowIfFailure();
|
||||||
|
|
||||||
|
if (readCount == 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < readCount; i++)
|
||||||
|
{
|
||||||
|
var save = saveDataInfo[i];
|
||||||
|
var id = new HLE.HOS.Services.Account.Acc.UserId((long)save.UserId.Id.Low, (long)save.UserId.Id.High);
|
||||||
|
if (Profiles.FirstOrDefault( x=> x.UserId == id) == null)
|
||||||
|
{
|
||||||
|
lostAccounts.Add(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach(var account in lostAccounts)
|
||||||
|
{
|
||||||
|
LostProfiles.Add(new UserProfile(new HLE.HOS.Services.Account.Acc.UserProfile(account, "", null), _owner));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddUser()
|
public void AddUser()
|
||||||
@@ -93,6 +139,25 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
|||||||
_owner.Navigate(typeof(UserEditor), (this._owner, userProfile, true));
|
_owner.Navigate(typeof(UserEditor), (this._owner, userProfile, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async void ManageSaves()
|
||||||
|
{
|
||||||
|
UserProfile userProfile = _highlightedProfile ?? SelectedProfile;
|
||||||
|
|
||||||
|
SaveManager manager = new SaveManager(userProfile, _owner.HorizonClient, _owner.VirtualFileSystem);
|
||||||
|
|
||||||
|
ContentDialog contentDialog = new ContentDialog
|
||||||
|
{
|
||||||
|
Title = string.Format(LocaleManager.Instance["SaveManagerHeading"], userProfile.Name),
|
||||||
|
PrimaryButtonText = "",
|
||||||
|
SecondaryButtonText = "",
|
||||||
|
CloseButtonText = LocaleManager.Instance["UserProfilesClose"],
|
||||||
|
Content = manager,
|
||||||
|
Padding = new Thickness(0)
|
||||||
|
};
|
||||||
|
|
||||||
|
await contentDialog.ShowAsync();
|
||||||
|
}
|
||||||
|
|
||||||
public void EditUser()
|
public void EditUser()
|
||||||
{
|
{
|
||||||
_owner.Navigate(typeof(UserEditor), (this._owner, _highlightedProfile ?? SelectedProfile, false));
|
_owner.Navigate(typeof(UserEditor), (this._owner, _highlightedProfile ?? SelectedProfile, false));
|
||||||
@@ -134,5 +199,15 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
|||||||
|
|
||||||
LoadProfiles();
|
LoadProfiles();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void GoBack()
|
||||||
|
{
|
||||||
|
_owner.GoBack();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RecoverLostAccounts()
|
||||||
|
{
|
||||||
|
_owner.Navigate(typeof(UserRecoverer), (this._owner, this));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -13,7 +13,7 @@
|
|||||||
Title="Ryujinx"
|
Title="Ryujinx"
|
||||||
Width="1280"
|
Width="1280"
|
||||||
Height="785"
|
Height="785"
|
||||||
MinWidth="1024"
|
MinWidth="1092"
|
||||||
MinHeight="680"
|
MinHeight="680"
|
||||||
d:DesignHeight="720"
|
d:DesignHeight="720"
|
||||||
d:DesignWidth="1280"
|
d:DesignWidth="1280"
|
||||||
@@ -57,6 +57,8 @@
|
|||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<StackPanel
|
<StackPanel
|
||||||
|
Name="MenuBar"
|
||||||
|
MinHeight="35"
|
||||||
Grid.Row="0"
|
Grid.Row="0"
|
||||||
Margin="0"
|
Margin="0"
|
||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
@@ -549,6 +551,7 @@
|
|||||||
<Grid
|
<Grid
|
||||||
Name="StatusBar"
|
Name="StatusBar"
|
||||||
Grid.Row="2"
|
Grid.Row="2"
|
||||||
|
MinHeight="30"
|
||||||
Height="30"
|
Height="30"
|
||||||
Margin="0,0"
|
Margin="0,0"
|
||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
|
@@ -36,6 +36,7 @@ namespace Ryujinx.Ava.Ui.Windows
|
|||||||
{
|
{
|
||||||
public partial class MainWindow : StyleableWindow
|
public partial class MainWindow : StyleableWindow
|
||||||
{
|
{
|
||||||
|
internal static MainWindowViewModel MainWindowViewModel { get; private set; }
|
||||||
private bool _canUpdate;
|
private bool _canUpdate;
|
||||||
private bool _isClosing;
|
private bool _isClosing;
|
||||||
private bool _isLoading;
|
private bool _isLoading;
|
||||||
@@ -81,6 +82,8 @@ namespace Ryujinx.Ava.Ui.Windows
|
|||||||
{
|
{
|
||||||
ViewModel = new MainWindowViewModel(this);
|
ViewModel = new MainWindowViewModel(this);
|
||||||
|
|
||||||
|
MainWindowViewModel = ViewModel;
|
||||||
|
|
||||||
DataContext = ViewModel;
|
DataContext = ViewModel;
|
||||||
|
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
@@ -90,7 +93,9 @@ namespace Ryujinx.Ava.Ui.Windows
|
|||||||
|
|
||||||
Title = $"Ryujinx {Program.Version}";
|
Title = $"Ryujinx {Program.Version}";
|
||||||
|
|
||||||
Height /= Program.WindowScaleFactor;
|
// NOTE: Height of MenuBar and StatusBar is not usable here, since it would still be 0 at this point.
|
||||||
|
double barHeight = MenuBar.MinHeight + StatusBar.MinHeight;
|
||||||
|
Height = ((Height - barHeight) / Program.WindowScaleFactor) + barHeight;
|
||||||
Width /= Program.WindowScaleFactor;
|
Width /= Program.WindowScaleFactor;
|
||||||
|
|
||||||
if (Program.PreviewerDetached)
|
if (Program.PreviewerDetached)
|
||||||
|
@@ -955,16 +955,25 @@
|
|||||||
Icon="Document" />
|
Icon="Document" />
|
||||||
</ui:NavigationView.MenuItems>
|
</ui:NavigationView.MenuItems>
|
||||||
</ui:NavigationView>
|
</ui:NavigationView>
|
||||||
<StackPanel
|
<ReversibleStackPanel
|
||||||
Grid.Row="2"
|
Grid.Row="2"
|
||||||
Margin="10"
|
Margin="10"
|
||||||
Spacing="10"
|
Spacing="10"
|
||||||
Orientation="Horizontal"
|
Orientation="Horizontal"
|
||||||
HorizontalAlignment="Right">
|
HorizontalAlignment="Right"
|
||||||
<Button Content="{locale:Locale SettingsButtonSave}" Click="SaveButton_Clicked" />
|
ReverseOrder="{ReflectionBinding IsMacOS}">
|
||||||
<Button Content="{locale:Locale SettingsButtonClose}" Click="CloseButton_Clicked" />
|
<Button
|
||||||
<Button Content="{locale:Locale SettingsButtonApply}"
|
HotKey="Enter"
|
||||||
Click="ApplyButton_Clicked" />
|
Classes="accent"
|
||||||
</StackPanel>
|
Content="{locale:Locale SettingsButtonOk}"
|
||||||
|
Command="{ReflectionBinding OkButton}" />
|
||||||
|
<Button
|
||||||
|
HotKey="Escape"
|
||||||
|
Content="{locale:Locale SettingsButtonCancel}"
|
||||||
|
Command="{ReflectionBinding CancelButton}" />
|
||||||
|
<Button
|
||||||
|
Content="{locale:Locale SettingsButtonApply}"
|
||||||
|
Command="{ReflectionBinding ApplyButton}" />
|
||||||
|
</ReversibleStackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
</window:StyleableWindow>
|
</window:StyleableWindow>
|
@@ -199,36 +199,6 @@ namespace Ryujinx.Ava.Ui.Windows
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SaveButton_Clicked(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
SaveSettings();
|
|
||||||
Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CloseButton_Clicked(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
ViewModel.RevertIfNotSaved();
|
|
||||||
Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ApplyButton_Clicked(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
SaveSettings();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SaveSettings()
|
|
||||||
{
|
|
||||||
ViewModel.SaveSettings();
|
|
||||||
ControllerSettings?.SaveCurrentProfile();
|
|
||||||
|
|
||||||
if (Owner is MainWindow window && ViewModel.DirectoryChanged)
|
|
||||||
{
|
|
||||||
window.ViewModel.LoadApplications();
|
|
||||||
}
|
|
||||||
|
|
||||||
ViewModel.DirectoryChanged = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnClosed(EventArgs e)
|
protected override void OnClosed(EventArgs e)
|
||||||
{
|
{
|
||||||
ControllerSettings.Dispose();
|
ControllerSettings.Dispose();
|
||||||
|
@@ -8,7 +8,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="MsgPack.Cli" Version="1.0.1" />
|
<PackageReference Include="MsgPack.Cli" Version="1.0.1" />
|
||||||
<PackageReference Include="System.Drawing.Common" Version="6.0.0" />
|
<PackageReference Include="System.Drawing.Common" Version="6.0.0" />
|
||||||
<PackageReference Include="System.Management" Version="6.0.0" />
|
<PackageReference Include="System.Management" Version="7.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@@ -28,9 +28,39 @@ namespace Ryujinx.Common.SystemInfo
|
|||||||
|
|
||||||
CpuName = $"{cpuName} ; {LogicalCoreCount} logical";
|
CpuName = $"{cpuName} ; {LogicalCoreCount} logical";
|
||||||
RamTotal = totalRAM;
|
RamTotal = totalRAM;
|
||||||
|
RamAvailable = GetVMInfoAvailableMemory();
|
||||||
}
|
}
|
||||||
|
|
||||||
[DllImport("libSystem.dylib", CharSet = CharSet.Ansi, SetLastError = true)]
|
static ulong GetVMInfoAvailableMemory()
|
||||||
|
{
|
||||||
|
var port = mach_host_self();
|
||||||
|
|
||||||
|
uint pageSize = 0;
|
||||||
|
var result = host_page_size(port, ref pageSize);
|
||||||
|
|
||||||
|
if (result != 0)
|
||||||
|
{
|
||||||
|
Logger.Error?.Print(LogClass.Application, $"Failed to query Available RAM. host_page_size() error = {result}");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int flavor = 4; // HOST_VM_INFO64
|
||||||
|
uint count = (uint)(Marshal.SizeOf<VMStatistics64>() / sizeof(int)); // HOST_VM_INFO64_COUNT
|
||||||
|
VMStatistics64 stats = new();
|
||||||
|
result = host_statistics64(port, flavor, ref stats, ref count);
|
||||||
|
|
||||||
|
if (result != 0)
|
||||||
|
{
|
||||||
|
Logger.Error?.Print(LogClass.Application, $"Failed to query Available RAM. host_statistics64() error = {result}");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (ulong)(stats.FreeCount + stats.InactiveCount) * pageSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
private const string SystemLibraryName = "libSystem.dylib";
|
||||||
|
|
||||||
|
[DllImport(SystemLibraryName, CharSet = CharSet.Ansi, SetLastError = true)]
|
||||||
private static extern int sysctlbyname(string name, IntPtr oldValue, ref ulong oldSize, IntPtr newValue, ulong newValueSize);
|
private static extern int sysctlbyname(string name, IntPtr oldValue, ref ulong oldSize, IntPtr newValue, ulong newValueSize);
|
||||||
|
|
||||||
private static int sysctlbyname(string name, IntPtr oldValue, ref ulong oldSize)
|
private static int sysctlbyname(string name, IntPtr oldValue, ref ulong oldSize)
|
||||||
@@ -85,5 +115,43 @@ namespace Ryujinx.Common.SystemInfo
|
|||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[DllImport(SystemLibraryName, CharSet = CharSet.Ansi, SetLastError = true)]
|
||||||
|
private static extern uint mach_host_self();
|
||||||
|
|
||||||
|
[DllImport(SystemLibraryName, CharSet = CharSet.Ansi, SetLastError = true)]
|
||||||
|
private static extern int host_page_size(uint host, ref uint out_page_size);
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 8)]
|
||||||
|
struct VMStatistics64
|
||||||
|
{
|
||||||
|
public uint FreeCount;
|
||||||
|
public uint ActiveCount;
|
||||||
|
public uint InactiveCount;
|
||||||
|
public uint WireCount;
|
||||||
|
public ulong ZeroFillCount;
|
||||||
|
public ulong Reactivations;
|
||||||
|
public ulong Pageins;
|
||||||
|
public ulong Pageouts;
|
||||||
|
public ulong Faults;
|
||||||
|
public ulong CowFaults;
|
||||||
|
public ulong Lookups;
|
||||||
|
public ulong Hits;
|
||||||
|
public ulong Purges;
|
||||||
|
public uint PurgeableCount;
|
||||||
|
public uint SpeculativeCount;
|
||||||
|
public ulong Decompressions;
|
||||||
|
public ulong Compressions;
|
||||||
|
public ulong Swapins;
|
||||||
|
public ulong Swapouts;
|
||||||
|
public uint CompressorPageCount;
|
||||||
|
public uint ThrottledCount;
|
||||||
|
public uint ExternalPageCount;
|
||||||
|
public uint InternalPageCount;
|
||||||
|
public ulong TotalUncompressedPagesInCompressor;
|
||||||
|
}
|
||||||
|
|
||||||
|
[DllImport(SystemLibraryName, CharSet = CharSet.Ansi, SetLastError = true)]
|
||||||
|
private static extern int host_statistics64(uint host_priv, int host_flavor, ref VMStatistics64 host_info64_out, ref uint host_info64_outCnt);
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -29,6 +29,7 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
ReadOnlySpan<byte> GetBufferData(BufferHandle buffer, int offset, int size);
|
ReadOnlySpan<byte> GetBufferData(BufferHandle buffer, int offset, int size);
|
||||||
|
|
||||||
Capabilities GetCapabilities();
|
Capabilities GetCapabilities();
|
||||||
|
ulong GetCurrentSync();
|
||||||
HardwareInfo GetHardwareInfo();
|
HardwareInfo GetHardwareInfo();
|
||||||
|
|
||||||
IProgram LoadProgramBinary(byte[] programBinary, bool hasFragmentShader, ShaderInfo info);
|
IProgram LoadProgramBinary(byte[] programBinary, bool hasFragmentShader, ShaderInfo info);
|
||||||
|
@@ -338,6 +338,11 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
|||||||
return box.Result;
|
return box.Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ulong GetCurrentSync()
|
||||||
|
{
|
||||||
|
return _baseRenderer.GetCurrentSync();
|
||||||
|
}
|
||||||
|
|
||||||
public HardwareInfo GetHardwareInfo()
|
public HardwareInfo GetHardwareInfo()
|
||||||
{
|
{
|
||||||
return _baseRenderer.GetHardwareInfo();
|
return _baseRenderer.GetHardwareInfo();
|
||||||
|
@@ -202,57 +202,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
|
|||||||
_channel.BufferManager.SetComputeUniformBuffer(cb.Slot, cbDescriptor.PackAddress(), (uint)cbDescriptor.Size);
|
_channel.BufferManager.SetComputeUniformBuffer(cb.Slot, cbDescriptor.PackAddress(), (uint)cbDescriptor.Size);
|
||||||
}
|
}
|
||||||
|
|
||||||
_channel.BufferManager.SetComputeStorageBufferBindings(info.SBuffers);
|
_channel.BufferManager.SetComputeBufferBindings(cs.Bindings);
|
||||||
_channel.BufferManager.SetComputeUniformBufferBindings(info.CBuffers);
|
|
||||||
|
|
||||||
int maxTextureBinding = -1;
|
_channel.TextureManager.SetComputeBindings(cs.Bindings);
|
||||||
int maxImageBinding = -1;
|
|
||||||
|
|
||||||
TextureBindingInfo[] textureBindings = _channel.TextureManager.RentComputeTextureBindings(info.Textures.Count);
|
|
||||||
|
|
||||||
for (int index = 0; index < info.Textures.Count; index++)
|
|
||||||
{
|
|
||||||
var descriptor = info.Textures[index];
|
|
||||||
|
|
||||||
Target target = ShaderTexture.GetTarget(descriptor.Type);
|
|
||||||
|
|
||||||
textureBindings[index] = new TextureBindingInfo(
|
|
||||||
target,
|
|
||||||
descriptor.Binding,
|
|
||||||
descriptor.CbufSlot,
|
|
||||||
descriptor.HandleIndex,
|
|
||||||
descriptor.Flags);
|
|
||||||
|
|
||||||
if (descriptor.Binding > maxTextureBinding)
|
|
||||||
{
|
|
||||||
maxTextureBinding = descriptor.Binding;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TextureBindingInfo[] imageBindings = _channel.TextureManager.RentComputeImageBindings(info.Images.Count);
|
|
||||||
|
|
||||||
for (int index = 0; index < info.Images.Count; index++)
|
|
||||||
{
|
|
||||||
var descriptor = info.Images[index];
|
|
||||||
|
|
||||||
Target target = ShaderTexture.GetTarget(descriptor.Type);
|
|
||||||
Format format = ShaderTexture.GetFormat(descriptor.Format);
|
|
||||||
|
|
||||||
imageBindings[index] = new TextureBindingInfo(
|
|
||||||
target,
|
|
||||||
format,
|
|
||||||
descriptor.Binding,
|
|
||||||
descriptor.CbufSlot,
|
|
||||||
descriptor.HandleIndex,
|
|
||||||
descriptor.Flags);
|
|
||||||
|
|
||||||
if (descriptor.Binding > maxImageBinding)
|
|
||||||
{
|
|
||||||
maxImageBinding = descriptor.Binding;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_channel.TextureManager.SetComputeMaxBindings(maxTextureBinding, maxImageBinding);
|
|
||||||
|
|
||||||
// Should never return false for mismatching spec state, since the shader was fetched above.
|
// Should never return false for mismatching spec state, since the shader was fetched above.
|
||||||
_channel.TextureManager.CommitComputeBindings(cs.SpecializationState);
|
_channel.TextureManager.CommitComputeBindings(cs.SpecializationState);
|
||||||
|
@@ -37,6 +37,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
private bool _vsUsesDrawParameters;
|
private bool _vsUsesDrawParameters;
|
||||||
private bool _vtgWritesRtLayer;
|
private bool _vtgWritesRtLayer;
|
||||||
private byte _vsClipDistancesWritten;
|
private byte _vsClipDistancesWritten;
|
||||||
|
private uint _vbEnableMask;
|
||||||
|
|
||||||
private bool _prevDrawIndexed;
|
private bool _prevDrawIndexed;
|
||||||
private bool _prevDrawIndirect;
|
private bool _prevDrawIndirect;
|
||||||
@@ -76,6 +77,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
nameof(ThreedClassState.VertexBufferState),
|
nameof(ThreedClassState.VertexBufferState),
|
||||||
nameof(ThreedClassState.VertexBufferEndAddress)),
|
nameof(ThreedClassState.VertexBufferEndAddress)),
|
||||||
|
|
||||||
|
// Must be done after vertex buffer updates.
|
||||||
new StateUpdateCallbackEntry(UpdateVertexAttribState, nameof(ThreedClassState.VertexAttribState)),
|
new StateUpdateCallbackEntry(UpdateVertexAttribState, nameof(ThreedClassState.VertexAttribState)),
|
||||||
|
|
||||||
new StateUpdateCallbackEntry(UpdateBlendState,
|
new StateUpdateCallbackEntry(UpdateBlendState,
|
||||||
@@ -852,12 +854,23 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void UpdateVertexAttribState()
|
private void UpdateVertexAttribState()
|
||||||
{
|
{
|
||||||
|
uint vbEnableMask = _vbEnableMask;
|
||||||
|
|
||||||
Span<VertexAttribDescriptor> vertexAttribs = stackalloc VertexAttribDescriptor[Constants.TotalVertexAttribs];
|
Span<VertexAttribDescriptor> vertexAttribs = stackalloc VertexAttribDescriptor[Constants.TotalVertexAttribs];
|
||||||
|
|
||||||
for (int index = 0; index < Constants.TotalVertexAttribs; index++)
|
for (int index = 0; index < Constants.TotalVertexAttribs; index++)
|
||||||
{
|
{
|
||||||
var vertexAttrib = _state.State.VertexAttribState[index];
|
var vertexAttrib = _state.State.VertexAttribState[index];
|
||||||
|
|
||||||
|
int bufferIndex = vertexAttrib.UnpackBufferIndex();
|
||||||
|
|
||||||
|
if ((vbEnableMask & (1u << bufferIndex)) == 0)
|
||||||
|
{
|
||||||
|
// Using a vertex buffer that doesn't exist is invalid, so let's use a dummy attribute for those cases.
|
||||||
|
vertexAttribs[index] = new VertexAttribDescriptor(0, 0, true, Format.R32G32B32A32Float);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (!FormatTable.TryGetAttribFormat(vertexAttrib.UnpackFormat(), out Format format))
|
if (!FormatTable.TryGetAttribFormat(vertexAttrib.UnpackFormat(), out Format format))
|
||||||
{
|
{
|
||||||
Logger.Debug?.Print(LogClass.Gpu, $"Invalid attribute format 0x{vertexAttrib.UnpackFormat():X}.");
|
Logger.Debug?.Print(LogClass.Gpu, $"Invalid attribute format 0x{vertexAttrib.UnpackFormat():X}.");
|
||||||
@@ -866,7 +879,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
}
|
}
|
||||||
|
|
||||||
vertexAttribs[index] = new VertexAttribDescriptor(
|
vertexAttribs[index] = new VertexAttribDescriptor(
|
||||||
vertexAttrib.UnpackBufferIndex(),
|
bufferIndex,
|
||||||
vertexAttrib.UnpackOffset(),
|
vertexAttrib.UnpackOffset(),
|
||||||
vertexAttrib.UnpackIsConstant(),
|
vertexAttrib.UnpackIsConstant(),
|
||||||
format);
|
format);
|
||||||
@@ -954,6 +967,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
|
|
||||||
bool drawIndexed = _drawState.DrawIndexed;
|
bool drawIndexed = _drawState.DrawIndexed;
|
||||||
bool drawIndirect = _drawState.DrawIndirect;
|
bool drawIndirect = _drawState.DrawIndirect;
|
||||||
|
uint vbEnableMask = 0;
|
||||||
|
|
||||||
for (int index = 0; index < Constants.TotalVertexBuffers; index++)
|
for (int index = 0; index < Constants.TotalVertexBuffers; index++)
|
||||||
{
|
{
|
||||||
@@ -971,6 +985,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
|
|
||||||
ulong address = vertexBuffer.Address.Pack();
|
ulong address = vertexBuffer.Address.Pack();
|
||||||
|
|
||||||
|
if (_channel.MemoryManager.IsMapped(address))
|
||||||
|
{
|
||||||
|
vbEnableMask |= 1u << index;
|
||||||
|
}
|
||||||
|
|
||||||
int stride = vertexBuffer.UnpackStride();
|
int stride = vertexBuffer.UnpackStride();
|
||||||
|
|
||||||
bool instanced = _state.State.VertexBufferInstanced[index];
|
bool instanced = _state.State.VertexBufferInstanced[index];
|
||||||
@@ -1017,6 +1036,12 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
_pipeline.VertexBuffers[index] = new BufferPipelineDescriptor(_channel.MemoryManager.IsMapped(address), stride, divisor);
|
_pipeline.VertexBuffers[index] = new BufferPipelineDescriptor(_channel.MemoryManager.IsMapped(address), stride, divisor);
|
||||||
_channel.BufferManager.SetVertexBuffer(index, address, size, stride, divisor);
|
_channel.BufferManager.SetVertexBuffer(index, address, size, stride, divisor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_vbEnableMask != vbEnableMask)
|
||||||
|
{
|
||||||
|
_vbEnableMask = vbEnableMask;
|
||||||
|
UpdateVertexAttribState();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -1232,88 +1257,24 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
UpdateUserClipState();
|
UpdateUserClipState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UpdateShaderBindings(gs.Bindings);
|
||||||
|
|
||||||
for (int stageIndex = 0; stageIndex < Constants.ShaderStages; stageIndex++)
|
for (int stageIndex = 0; stageIndex < Constants.ShaderStages; stageIndex++)
|
||||||
{
|
{
|
||||||
UpdateStageBindings(stageIndex, gs.Shaders[stageIndex + 1]?.Info);
|
_currentProgramInfo[stageIndex] = gs.Shaders[stageIndex + 1]?.Info;
|
||||||
}
|
}
|
||||||
|
|
||||||
_context.Renderer.Pipeline.SetProgram(gs.HostProgram);
|
_context.Renderer.Pipeline.SetProgram(gs.HostProgram);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Updates bindings consumed by the shader stage on the texture and buffer managers.
|
/// Updates bindings consumed by the shader on the texture and buffer managers.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="stage">Shader stage to have the bindings updated</param>
|
/// <param name="bindings">Bindings for the active shader</param>
|
||||||
/// <param name="info">Shader stage bindings info</param>
|
private void UpdateShaderBindings(CachedShaderBindings bindings)
|
||||||
private void UpdateStageBindings(int stage, ShaderProgramInfo info)
|
|
||||||
{
|
{
|
||||||
_currentProgramInfo[stage] = info;
|
_channel.TextureManager.SetGraphicsBindings(bindings);
|
||||||
|
_channel.BufferManager.SetGraphicsBufferBindings(bindings);
|
||||||
if (info == null)
|
|
||||||
{
|
|
||||||
_channel.TextureManager.RentGraphicsTextureBindings(stage, 0);
|
|
||||||
_channel.TextureManager.RentGraphicsImageBindings(stage, 0);
|
|
||||||
_channel.BufferManager.SetGraphicsStorageBufferBindings(stage, null);
|
|
||||||
_channel.BufferManager.SetGraphicsUniformBufferBindings(stage, null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int maxTextureBinding = -1;
|
|
||||||
int maxImageBinding = -1;
|
|
||||||
|
|
||||||
Span<TextureBindingInfo> textureBindings = _channel.TextureManager.RentGraphicsTextureBindings(stage, info.Textures.Count);
|
|
||||||
|
|
||||||
if (info.UsesRtLayer)
|
|
||||||
{
|
|
||||||
_vtgWritesRtLayer = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int index = 0; index < info.Textures.Count; index++)
|
|
||||||
{
|
|
||||||
var descriptor = info.Textures[index];
|
|
||||||
|
|
||||||
Target target = ShaderTexture.GetTarget(descriptor.Type);
|
|
||||||
|
|
||||||
textureBindings[index] = new TextureBindingInfo(
|
|
||||||
target,
|
|
||||||
descriptor.Binding,
|
|
||||||
descriptor.CbufSlot,
|
|
||||||
descriptor.HandleIndex,
|
|
||||||
descriptor.Flags);
|
|
||||||
|
|
||||||
if (descriptor.Binding > maxTextureBinding)
|
|
||||||
{
|
|
||||||
maxTextureBinding = descriptor.Binding;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TextureBindingInfo[] imageBindings = _channel.TextureManager.RentGraphicsImageBindings(stage, info.Images.Count);
|
|
||||||
|
|
||||||
for (int index = 0; index < info.Images.Count; index++)
|
|
||||||
{
|
|
||||||
var descriptor = info.Images[index];
|
|
||||||
|
|
||||||
Target target = ShaderTexture.GetTarget(descriptor.Type);
|
|
||||||
Format format = ShaderTexture.GetFormat(descriptor.Format);
|
|
||||||
|
|
||||||
imageBindings[index] = new TextureBindingInfo(
|
|
||||||
target,
|
|
||||||
format,
|
|
||||||
descriptor.Binding,
|
|
||||||
descriptor.CbufSlot,
|
|
||||||
descriptor.HandleIndex,
|
|
||||||
descriptor.Flags);
|
|
||||||
|
|
||||||
if (descriptor.Binding > maxImageBinding)
|
|
||||||
{
|
|
||||||
maxImageBinding = descriptor.Binding;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_channel.TextureManager.SetGraphicsMaxBindings(maxTextureBinding, maxImageBinding);
|
|
||||||
|
|
||||||
_channel.BufferManager.SetGraphicsStorageBufferBindings(stage, info.SBuffers);
|
|
||||||
_channel.BufferManager.SetGraphicsUniformBufferBindings(stage, info.CBuffers);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@@ -69,6 +69,12 @@ namespace Ryujinx.Graphics.Gpu
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
internal List<Action> SyncpointActions { get; }
|
internal List<Action> SyncpointActions { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Buffer migrations that are currently in-flight. These are checked whenever sync is created to determine if buffer migration
|
||||||
|
/// copies have completed on the GPU, and their data can be freed.
|
||||||
|
/// </summary>
|
||||||
|
internal List<BufferMigration> BufferMigrations { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Queue with deferred actions that must run on the render thread.
|
/// Queue with deferred actions that must run on the render thread.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -90,6 +96,7 @@ namespace Ryujinx.Graphics.Gpu
|
|||||||
public event Action<ShaderCacheState, int, int> ShaderCacheStateChanged;
|
public event Action<ShaderCacheState, int, int> ShaderCacheStateChanged;
|
||||||
|
|
||||||
private Thread _gpuThread;
|
private Thread _gpuThread;
|
||||||
|
private bool _pendingSync;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new instance of the GPU emulation context.
|
/// Creates a new instance of the GPU emulation context.
|
||||||
@@ -109,6 +116,7 @@ namespace Ryujinx.Graphics.Gpu
|
|||||||
|
|
||||||
SyncActions = new List<Action>();
|
SyncActions = new List<Action>();
|
||||||
SyncpointActions = new List<Action>();
|
SyncpointActions = new List<Action>();
|
||||||
|
BufferMigrations = new List<BufferMigration>();
|
||||||
|
|
||||||
DeferredActions = new Queue<Action>();
|
DeferredActions = new Queue<Action>();
|
||||||
|
|
||||||
@@ -273,6 +281,17 @@ namespace Ryujinx.Graphics.Gpu
|
|||||||
SequenceNumber++;
|
SequenceNumber++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Registers a buffer migration. These are checked to see if they can be disposed when the sync number increases,
|
||||||
|
/// and the migration copy has completed.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="migration">The buffer migration</param>
|
||||||
|
internal void RegisterBufferMigration(BufferMigration migration)
|
||||||
|
{
|
||||||
|
BufferMigrations.Add(migration);
|
||||||
|
_pendingSync = true;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Registers an action to be performed the next time a syncpoint is incremented.
|
/// Registers an action to be performed the next time a syncpoint is incremented.
|
||||||
/// This will also ensure a host sync object is created, and <see cref="SyncNumber"/> is incremented.
|
/// This will also ensure a host sync object is created, and <see cref="SyncNumber"/> is incremented.
|
||||||
@@ -288,6 +307,7 @@ namespace Ryujinx.Graphics.Gpu
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
SyncActions.Add(action);
|
SyncActions.Add(action);
|
||||||
|
_pendingSync = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -298,7 +318,24 @@ namespace Ryujinx.Graphics.Gpu
|
|||||||
/// <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)
|
public void CreateHostSyncIfNeeded(bool syncpoint)
|
||||||
{
|
{
|
||||||
if (SyncActions.Count > 0 || (syncpoint && SyncpointActions.Count > 0))
|
if (BufferMigrations.Count > 0)
|
||||||
|
{
|
||||||
|
ulong currentSyncNumber = Renderer.GetCurrentSync();
|
||||||
|
|
||||||
|
for (int i = 0; i < BufferMigrations.Count; i++)
|
||||||
|
{
|
||||||
|
BufferMigration migration = BufferMigrations[i];
|
||||||
|
long diff = (long)(currentSyncNumber - migration.SyncNumber);
|
||||||
|
|
||||||
|
if (diff >= 0)
|
||||||
|
{
|
||||||
|
migration.Dispose();
|
||||||
|
BufferMigrations.RemoveAt(i--);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_pendingSync || (syncpoint && SyncpointActions.Count > 0))
|
||||||
{
|
{
|
||||||
Renderer.CreateSync(SyncNumber);
|
Renderer.CreateSync(SyncNumber);
|
||||||
|
|
||||||
@@ -317,6 +354,8 @@ namespace Ryujinx.Graphics.Gpu
|
|||||||
SyncActions.Clear();
|
SyncActions.Clear();
|
||||||
SyncpointActions.Clear();
|
SyncpointActions.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_pendingSync = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@@ -37,8 +37,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
private TexturePool _cachedTexturePool;
|
private TexturePool _cachedTexturePool;
|
||||||
private SamplerPool _cachedSamplerPool;
|
private SamplerPool _cachedSamplerPool;
|
||||||
|
|
||||||
private readonly TextureBindingInfo[][] _textureBindings;
|
private TextureBindingInfo[][] _textureBindings;
|
||||||
private readonly TextureBindingInfo[][] _imageBindings;
|
private TextureBindingInfo[][] _imageBindings;
|
||||||
|
|
||||||
private struct TextureState
|
private struct TextureState
|
||||||
{
|
{
|
||||||
@@ -56,9 +56,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
private TextureState[] _textureState;
|
private TextureState[] _textureState;
|
||||||
private TextureState[] _imageState;
|
private TextureState[] _imageState;
|
||||||
|
|
||||||
private int[] _textureBindingsCount;
|
|
||||||
private int[] _imageBindingsCount;
|
|
||||||
|
|
||||||
private int _texturePoolSequence;
|
private int _texturePoolSequence;
|
||||||
private int _samplerPoolSequence;
|
private int _samplerPoolSequence;
|
||||||
|
|
||||||
@@ -101,9 +98,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
_textureState = new TextureState[InitialTextureStateSize];
|
_textureState = new TextureState[InitialTextureStateSize];
|
||||||
_imageState = new TextureState[InitialImageStateSize];
|
_imageState = new TextureState[InitialImageStateSize];
|
||||||
|
|
||||||
_textureBindingsCount = new int[stages];
|
|
||||||
_imageBindingsCount = new int[stages];
|
|
||||||
|
|
||||||
for (int stage = 0; stage < stages; stage++)
|
for (int stage = 0; stage < stages; stage++)
|
||||||
{
|
{
|
||||||
_textureBindings[stage] = new TextureBindingInfo[InitialTextureStateSize];
|
_textureBindings[stage] = new TextureBindingInfo[InitialTextureStateSize];
|
||||||
@@ -112,39 +106,15 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Rents the texture bindings array for a given stage, so that they can be modified.
|
/// Sets the texture and image bindings.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="stage">Shader stage number, or 0 for compute shaders</param>
|
/// <param name="bindings">Bindings for the active shader</param>
|
||||||
/// <param name="count">The number of bindings needed</param>
|
public void SetBindings(CachedShaderBindings bindings)
|
||||||
/// <returns>The texture bindings array</returns>
|
|
||||||
public TextureBindingInfo[] RentTextureBindings(int stage, int count)
|
|
||||||
{
|
{
|
||||||
if (count > _textureBindings[stage].Length)
|
_textureBindings = bindings.TextureBindings;
|
||||||
{
|
_imageBindings = bindings.ImageBindings;
|
||||||
Array.Resize(ref _textureBindings[stage], count);
|
|
||||||
}
|
|
||||||
|
|
||||||
_textureBindingsCount[stage] = count;
|
SetMaxBindings(bindings.MaxTextureBinding, bindings.MaxImageBinding);
|
||||||
|
|
||||||
return _textureBindings[stage];
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Rents the image bindings array for a given stage, so that they can be modified.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="stage">Shader stage number, or 0 for compute shaders</param>
|
|
||||||
/// <param name="count">The number of bindings needed</param>
|
|
||||||
/// <returns>The image bindings array</returns>
|
|
||||||
public TextureBindingInfo[] RentImageBindings(int stage, int count)
|
|
||||||
{
|
|
||||||
if (count > _imageBindings[stage].Length)
|
|
||||||
{
|
|
||||||
Array.Resize(ref _imageBindings[stage], count);
|
|
||||||
}
|
|
||||||
|
|
||||||
_imageBindingsCount[stage] = count;
|
|
||||||
|
|
||||||
return _imageBindings[stage];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -257,7 +227,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
case ShaderStage.Vertex:
|
case ShaderStage.Vertex:
|
||||||
int fragmentIndex = (int)ShaderStage.Fragment - 1;
|
int fragmentIndex = (int)ShaderStage.Fragment - 1;
|
||||||
index += _textureBindingsCount[fragmentIndex] + _imageBindingsCount[fragmentIndex];
|
index += _textureBindings[fragmentIndex].Length + _imageBindings[fragmentIndex].Length;
|
||||||
|
|
||||||
result = texture.ScaleFactor;
|
result = texture.ScaleFactor;
|
||||||
break;
|
break;
|
||||||
@@ -284,7 +254,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private bool VertexRequiresScale()
|
private bool VertexRequiresScale()
|
||||||
{
|
{
|
||||||
for (int i = 0; i < _textureBindingsCount[0]; i++)
|
for (int i = 0; i < _textureBindings[0].Length; i++)
|
||||||
{
|
{
|
||||||
if ((_textureBindings[0][i].Flags & TextureUsageFlags.NeedsScaleValue) != 0)
|
if ((_textureBindings[0][i].Flags & TextureUsageFlags.NeedsScaleValue) != 0)
|
||||||
{
|
{
|
||||||
@@ -292,7 +262,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < _imageBindingsCount[0]; i++)
|
for (int i = 0; i < _imageBindings[0].Length; i++)
|
||||||
{
|
{
|
||||||
if ((_imageBindings[0][i].Flags & TextureUsageFlags.NeedsScaleValue) != 0)
|
if ((_imageBindings[0][i].Flags & TextureUsageFlags.NeedsScaleValue) != 0)
|
||||||
{
|
{
|
||||||
@@ -309,10 +279,10 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
private void CommitRenderScale()
|
private void CommitRenderScale()
|
||||||
{
|
{
|
||||||
// Stage 0 total: Compute or Vertex.
|
// Stage 0 total: Compute or Vertex.
|
||||||
int total = _textureBindingsCount[0] + _imageBindingsCount[0];
|
int total = _textureBindings[0].Length + _imageBindings[0].Length;
|
||||||
|
|
||||||
int fragmentIndex = (int)ShaderStage.Fragment - 1;
|
int fragmentIndex = (int)ShaderStage.Fragment - 1;
|
||||||
int fragmentTotal = _isCompute ? 0 : (_textureBindingsCount[fragmentIndex] + _imageBindingsCount[fragmentIndex]);
|
int fragmentTotal = _isCompute ? 0 : (_textureBindings[fragmentIndex].Length + _imageBindings[fragmentIndex].Length);
|
||||||
|
|
||||||
if (total != 0 && fragmentTotal != _lastFragmentTotal && VertexRequiresScale())
|
if (total != 0 && fragmentTotal != _lastFragmentTotal && VertexRequiresScale())
|
||||||
{
|
{
|
||||||
@@ -481,7 +451,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
bool poolModified,
|
bool poolModified,
|
||||||
ShaderSpecializationState specState)
|
ShaderSpecializationState specState)
|
||||||
{
|
{
|
||||||
int textureCount = _textureBindingsCount[stageIndex];
|
int textureCount = _textureBindings[stageIndex].Length;
|
||||||
if (textureCount == 0)
|
if (textureCount == 0)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
@@ -609,7 +579,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// <returns>True if all bound images match the current shader specialiation state, false otherwise</returns>
|
/// <returns>True if all bound images match the current shader specialiation state, false otherwise</returns>
|
||||||
private bool CommitImageBindings(TexturePool pool, ShaderStage stage, int stageIndex, bool poolModified, ShaderSpecializationState specState)
|
private bool CommitImageBindings(TexturePool pool, ShaderStage stage, int stageIndex, bool poolModified, ShaderSpecializationState specState)
|
||||||
{
|
{
|
||||||
int imageCount = _imageBindingsCount[stageIndex];
|
int imageCount = _imageBindings[stageIndex].Length;
|
||||||
if (imageCount == 0)
|
if (imageCount == 0)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
@@ -622,7 +592,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Scales for images appear after the texture ones.
|
// Scales for images appear after the texture ones.
|
||||||
int baseScaleIndex = _textureBindingsCount[stageIndex];
|
int baseScaleIndex = _textureBindings[stageIndex].Length;
|
||||||
|
|
||||||
int cachedTextureBufferIndex = -1;
|
int cachedTextureBufferIndex = -1;
|
||||||
int cachedSamplerBufferIndex = -1;
|
int cachedSamplerBufferIndex = -1;
|
||||||
|
@@ -57,45 +57,21 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Rents the texture bindings array of the compute pipeline.
|
/// Sets the texture and image bindings for the compute pipeline.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="count">The number of bindings needed</param>
|
/// <param name="bindings">Bindings for the active shader</param>
|
||||||
/// <returns>The texture bindings array</returns>
|
public void SetComputeBindings(CachedShaderBindings bindings)
|
||||||
public TextureBindingInfo[] RentComputeTextureBindings(int count)
|
|
||||||
{
|
{
|
||||||
return _cpBindingsManager.RentTextureBindings(0, count);
|
_cpBindingsManager.SetBindings(bindings);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Rents the texture bindings array for a given stage on the graphics pipeline.
|
/// Sets the texture and image bindings for the graphics pipeline.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="stage">The index of the shader stage to bind the textures</param>
|
/// <param name="bindings">Bindings for the active shader</param>
|
||||||
/// <param name="count">The number of bindings needed</param>
|
public void SetGraphicsBindings(CachedShaderBindings bindings)
|
||||||
/// <returns>The texture bindings array</returns>
|
|
||||||
public TextureBindingInfo[] RentGraphicsTextureBindings(int stage, int count)
|
|
||||||
{
|
{
|
||||||
return _gpBindingsManager.RentTextureBindings(stage, count);
|
_gpBindingsManager.SetBindings(bindings);
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Rents the image bindings array of the compute pipeline.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="count">The number of bindings needed</param>
|
|
||||||
/// <returns>The image bindings array</returns>
|
|
||||||
public TextureBindingInfo[] RentComputeImageBindings(int count)
|
|
||||||
{
|
|
||||||
return _cpBindingsManager.RentImageBindings(0, count);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Rents the image bindings array for a given stage on the graphics pipeline.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="stage">The index of the shader stage to bind the images</param>
|
|
||||||
/// <param name="count">The number of bindings needed</param>
|
|
||||||
/// <returns>The image bindings array</returns>
|
|
||||||
public TextureBindingInfo[] RentGraphicsImageBindings(int stage, int count)
|
|
||||||
{
|
|
||||||
return _gpBindingsManager.RentImageBindings(stage, count);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -107,16 +83,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
_cpBindingsManager.SetTextureBufferIndex(index);
|
_cpBindingsManager.SetTextureBufferIndex(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sets the max binding indexes on the compute pipeline.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="maxTextureBinding">The maximum texture binding</param>
|
|
||||||
/// <param name="maxImageBinding">The maximum image binding</param>
|
|
||||||
public void SetComputeMaxBindings(int maxTextureBinding, int maxImageBinding)
|
|
||||||
{
|
|
||||||
_cpBindingsManager.SetMaxBindings(maxTextureBinding, maxImageBinding);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets the texture constant buffer index on the graphics pipeline.
|
/// Sets the texture constant buffer index on the graphics pipeline.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -126,16 +92,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
_gpBindingsManager.SetTextureBufferIndex(index);
|
_gpBindingsManager.SetTextureBufferIndex(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sets the max binding indexes on the graphics pipeline.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="maxTextureBinding">The maximum texture binding</param>
|
|
||||||
/// <param name="maxImageBinding">The maximum image binding</param>
|
|
||||||
public void SetGraphicsMaxBindings(int maxTextureBinding, int maxImageBinding)
|
|
||||||
{
|
|
||||||
_gpBindingsManager.SetMaxBindings(maxTextureBinding, maxImageBinding);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets the current sampler pool on the compute pipeline.
|
/// Sets the current sampler pool on the compute pipeline.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@@ -65,6 +65,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
private bool _useGranular;
|
private bool _useGranular;
|
||||||
private bool _syncActionRegistered;
|
private bool _syncActionRegistered;
|
||||||
|
|
||||||
|
private int _referenceCount = 1;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new instance of the buffer.
|
/// Creates a new instance of the buffer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -229,7 +231,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
{
|
{
|
||||||
if (_modifiedRanges == null)
|
if (_modifiedRanges == null)
|
||||||
{
|
{
|
||||||
_modifiedRanges = new BufferModifiedRangeList(_context);
|
_modifiedRanges = new BufferModifiedRangeList(_context, this, Flush);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -290,7 +292,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// <param name="from">The buffer to inherit from</param>
|
/// <param name="from">The buffer to inherit from</param>
|
||||||
public void InheritModifiedRanges(Buffer from)
|
public void InheritModifiedRanges(Buffer from)
|
||||||
{
|
{
|
||||||
if (from._modifiedRanges != null)
|
if (from._modifiedRanges != null && from._modifiedRanges.HasRanges)
|
||||||
{
|
{
|
||||||
if (from._syncActionRegistered && !_syncActionRegistered)
|
if (from._syncActionRegistered && !_syncActionRegistered)
|
||||||
{
|
{
|
||||||
@@ -310,17 +312,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (_modifiedRanges == null)
|
EnsureRangeList();
|
||||||
{
|
|
||||||
_modifiedRanges = from._modifiedRanges;
|
|
||||||
_modifiedRanges.ReregisterRanges(registerRangeAction);
|
|
||||||
|
|
||||||
from._modifiedRanges = null;
|
_modifiedRanges.InheritRanges(from._modifiedRanges, registerRangeAction);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_modifiedRanges.InheritRanges(from._modifiedRanges, registerRangeAction);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -456,7 +450,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
if (ranges != null)
|
if (ranges != null)
|
||||||
{
|
{
|
||||||
(address, size) = PageAlign(address, size);
|
(address, size) = PageAlign(address, size);
|
||||||
ranges.WaitForAndGetRanges(address, size, Flush);
|
ranges.WaitForAndFlushRanges(address, size);
|
||||||
}
|
}
|
||||||
}, true);
|
}, true);
|
||||||
}
|
}
|
||||||
@@ -508,6 +502,25 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
UnmappedSequence++;
|
UnmappedSequence++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Increments the buffer reference count.
|
||||||
|
/// </summary>
|
||||||
|
public void IncrementReferenceCount()
|
||||||
|
{
|
||||||
|
_referenceCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decrements the buffer reference count.
|
||||||
|
/// </summary>
|
||||||
|
public void DecrementReferenceCount()
|
||||||
|
{
|
||||||
|
if (--_referenceCount == 0)
|
||||||
|
{
|
||||||
|
DisposeData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Disposes the host buffer's data, not its tracking handles.
|
/// Disposes the host buffer's data, not its tracking handles.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -528,7 +541,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
_memoryTrackingGranular?.Dispose();
|
_memoryTrackingGranular?.Dispose();
|
||||||
_memoryTracking?.Dispose();
|
_memoryTracking?.Dispose();
|
||||||
|
|
||||||
DisposeData();
|
DecrementReferenceCount();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -273,7 +273,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
buffer.CopyTo(newBuffer, dstOffset);
|
buffer.CopyTo(newBuffer, dstOffset);
|
||||||
newBuffer.InheritModifiedRanges(buffer);
|
newBuffer.InheritModifiedRanges(buffer);
|
||||||
|
|
||||||
buffer.DisposeData();
|
buffer.DecrementReferenceCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
newBuffer.SynchronizeMemory(address, newSize);
|
newBuffer.SynchronizeMemory(address, newSize);
|
||||||
|
@@ -1,10 +1,10 @@
|
|||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
using Ryujinx.Graphics.Gpu.Image;
|
using Ryujinx.Graphics.Gpu.Image;
|
||||||
|
using Ryujinx.Graphics.Gpu.Shader;
|
||||||
using Ryujinx.Graphics.Shader;
|
using Ryujinx.Graphics.Shader;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu.Memory
|
namespace Ryujinx.Graphics.Gpu.Memory
|
||||||
@@ -34,7 +34,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Shader buffer binding information.
|
/// Shader buffer binding information.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public BufferDescriptor[] Bindings { get; }
|
public BufferDescriptor[] Bindings { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Buffer regions.
|
/// Buffer regions.
|
||||||
@@ -78,7 +78,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// Sets shader buffer binding information.
|
/// Sets shader buffer binding information.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="descriptors">Buffer binding information</param>
|
/// <param name="descriptors">Buffer binding information</param>
|
||||||
public void SetBindings(ReadOnlyCollection<BufferDescriptor> descriptors)
|
public void SetBindings(BufferDescriptor[] descriptors)
|
||||||
{
|
{
|
||||||
if (descriptors == null)
|
if (descriptors == null)
|
||||||
{
|
{
|
||||||
@@ -86,8 +86,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
descriptors.CopyTo(Bindings, 0);
|
if ((Count = descriptors.Length) != 0)
|
||||||
Count = descriptors.Count;
|
{
|
||||||
|
Bindings = descriptors;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -320,41 +322,26 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets the binding points for the storage buffers bound on the compute pipeline.
|
/// Sets the binding points for the storage buffers bound on the compute pipeline.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="descriptors">Buffer descriptors with the binding point values</param>
|
/// <param name="bindings">Bindings for the active shader</param>
|
||||||
public void SetComputeStorageBufferBindings(ReadOnlyCollection<BufferDescriptor> descriptors)
|
public void SetComputeBufferBindings(CachedShaderBindings bindings)
|
||||||
{
|
{
|
||||||
_cpStorageBuffers.SetBindings(descriptors);
|
_cpStorageBuffers.SetBindings(bindings.StorageBufferBindings[0]);
|
||||||
|
_cpUniformBuffers.SetBindings(bindings.ConstantBufferBindings[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets the binding points for the storage buffers bound on the graphics pipeline.
|
/// Sets the binding points for the storage buffers bound on the graphics pipeline.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="stage">Index of the shader stage</param>
|
/// <param name="bindings">Bindings for the active shader</param>
|
||||||
/// <param name="descriptors">Buffer descriptors with the binding point values</param>
|
public void SetGraphicsBufferBindings(CachedShaderBindings bindings)
|
||||||
public void SetGraphicsStorageBufferBindings(int stage, ReadOnlyCollection<BufferDescriptor> descriptors)
|
|
||||||
{
|
{
|
||||||
_gpStorageBuffers[stage].SetBindings(descriptors);
|
for (int i = 0; i < Constants.ShaderStages; i++)
|
||||||
|
{
|
||||||
|
_gpStorageBuffers[i].SetBindings(bindings.StorageBufferBindings[i]);
|
||||||
|
_gpUniformBuffers[i].SetBindings(bindings.ConstantBufferBindings[i]);
|
||||||
|
}
|
||||||
|
|
||||||
_gpStorageBuffersDirty = true;
|
_gpStorageBuffersDirty = true;
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sets the binding points for the uniform buffers bound on the compute pipeline.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="descriptors">Buffer descriptors with the binding point values</param>
|
|
||||||
public void SetComputeUniformBufferBindings(ReadOnlyCollection<BufferDescriptor> descriptors)
|
|
||||||
{
|
|
||||||
_cpUniformBuffers.SetBindings(descriptors);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sets the enabled uniform buffers mask on the graphics pipeline.
|
|
||||||
/// Each bit set on the mask indicates that the respective buffer index is enabled.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="stage">Index of the shader stage</param>
|
|
||||||
/// <param name="descriptors">Buffer descriptors with the binding point values</param>
|
|
||||||
public void SetGraphicsUniformBufferBindings(int stage, ReadOnlyCollection<BufferDescriptor> descriptors)
|
|
||||||
{
|
|
||||||
_gpUniformBuffers[stage].SetBindings(descriptors);
|
|
||||||
_gpUniformBuffersDirty = true;
|
_gpUniformBuffersDirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
125
Ryujinx.Graphics.Gpu/Memory/BufferMigration.cs
Normal file
125
Ryujinx.Graphics.Gpu/Memory/BufferMigration.cs
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gpu.Memory
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A record of when buffer data was copied from one buffer to another, along with the SyncNumber when the migration will be complete.
|
||||||
|
/// Keeps the source buffer alive for data flushes until the migration is complete.
|
||||||
|
/// </summary>
|
||||||
|
internal class BufferMigration : IDisposable
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The offset for the migrated region.
|
||||||
|
/// </summary>
|
||||||
|
private readonly ulong _offset;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The size for the migrated region.
|
||||||
|
/// </summary>
|
||||||
|
private readonly ulong _size;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The buffer that was migrated from.
|
||||||
|
/// </summary>
|
||||||
|
private readonly Buffer _buffer;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The source range action, to be called on overlap with an unreached sync number.
|
||||||
|
/// </summary>
|
||||||
|
private readonly Action<ulong, ulong> _sourceRangeAction;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The source range list.
|
||||||
|
/// </summary>
|
||||||
|
private readonly BufferModifiedRangeList _source;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The destination range list. This range list must be updated when flushing the source.
|
||||||
|
/// </summary>
|
||||||
|
public readonly BufferModifiedRangeList Destination;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The sync number that needs to be reached for this migration to be removed. This is set to the pending sync number on creation.
|
||||||
|
/// </summary>
|
||||||
|
public readonly ulong SyncNumber;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a record for a buffer migration.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="buffer">The source buffer for this migration</param>
|
||||||
|
/// <param name="sourceRangeAction">The flush action for the source buffer</param>
|
||||||
|
/// <param name="source">The modified range list for the source buffer</param>
|
||||||
|
/// <param name="dest">The modified range list for the destination buffer</param>
|
||||||
|
/// <param name="syncNumber">The sync number for when the migration is complete</param>
|
||||||
|
public BufferMigration(
|
||||||
|
Buffer buffer,
|
||||||
|
Action<ulong, ulong> sourceRangeAction,
|
||||||
|
BufferModifiedRangeList source,
|
||||||
|
BufferModifiedRangeList dest,
|
||||||
|
ulong syncNumber)
|
||||||
|
{
|
||||||
|
_offset = buffer.Address;
|
||||||
|
_size = buffer.Size;
|
||||||
|
_buffer = buffer;
|
||||||
|
_sourceRangeAction = sourceRangeAction;
|
||||||
|
_source = source;
|
||||||
|
Destination = dest;
|
||||||
|
SyncNumber = syncNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determine if the given range overlaps this migration, and has not been completed yet.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="offset">Start offset</param>
|
||||||
|
/// <param name="size">Range size</param>
|
||||||
|
/// <param name="syncNumber">The sync number that was waited on</param>
|
||||||
|
/// <returns>True if overlapping and in progress, false otherwise</returns>
|
||||||
|
public bool Overlaps(ulong offset, ulong size, ulong syncNumber)
|
||||||
|
{
|
||||||
|
ulong end = offset + size;
|
||||||
|
ulong destEnd = _offset + _size;
|
||||||
|
long syncDiff = (long)(syncNumber - SyncNumber); // syncNumber is less if the copy has not completed.
|
||||||
|
|
||||||
|
return !(end <= _offset || offset >= destEnd) && syncDiff < 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determine if the given range matches this migration.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="offset">Start offset</param>
|
||||||
|
/// <param name="size">Range size</param>
|
||||||
|
/// <returns>True if the range exactly matches, false otherwise</returns>
|
||||||
|
public bool FullyMatches(ulong offset, ulong size)
|
||||||
|
{
|
||||||
|
return _offset == offset && _size == size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Perform the migration source's range action on the range provided, clamped to the bounds of the source buffer.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="offset">Start offset</param>
|
||||||
|
/// <param name="size">Range size</param>
|
||||||
|
/// <param name="syncNumber">Current sync number</param>
|
||||||
|
/// <param name="parent">The modified range list that originally owned this range</param>
|
||||||
|
public void RangeActionWithMigration(ulong offset, ulong size, ulong syncNumber, BufferModifiedRangeList parent)
|
||||||
|
{
|
||||||
|
ulong end = offset + size;
|
||||||
|
end = Math.Min(_offset + _size, end);
|
||||||
|
offset = Math.Max(_offset, offset);
|
||||||
|
|
||||||
|
size = end - offset;
|
||||||
|
|
||||||
|
_source.RangeActionWithMigration(offset, size, syncNumber, parent, _sourceRangeAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes this reference to the range list, potentially allowing for the source buffer to be disposed.
|
||||||
|
/// </summary>
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Destination.RemoveMigration(this);
|
||||||
|
|
||||||
|
_buffer.DecrementReferenceCount();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,6 +1,8 @@
|
|||||||
using Ryujinx.Common.Pools;
|
using Ryujinx.Common.Logging;
|
||||||
|
using Ryujinx.Common.Pools;
|
||||||
using Ryujinx.Memory.Range;
|
using Ryujinx.Memory.Range;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu.Memory
|
namespace Ryujinx.Graphics.Gpu.Memory
|
||||||
@@ -30,17 +32,24 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public ulong SyncNumber { get; internal set; }
|
public ulong SyncNumber { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The range list that originally owned this range.
|
||||||
|
/// </summary>
|
||||||
|
public BufferModifiedRangeList Parent { get; internal set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new instance of a modified range.
|
/// Creates a new instance of a modified range.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="address">Start address of the range</param>
|
/// <param name="address">Start address of the range</param>
|
||||||
/// <param name="size">Size of the range in bytes</param>
|
/// <param name="size">Size of the range in bytes</param>
|
||||||
/// <param name="syncNumber">The GPU sync number at the time of creation</param>
|
/// <param name="syncNumber">The GPU sync number at the time of creation</param>
|
||||||
public BufferModifiedRange(ulong address, ulong size, ulong syncNumber)
|
/// <param name="parent">The range list that owns this range</param>
|
||||||
|
public BufferModifiedRange(ulong address, ulong size, ulong syncNumber, BufferModifiedRangeList parent)
|
||||||
{
|
{
|
||||||
Address = address;
|
Address = address;
|
||||||
Size = size;
|
Size = size;
|
||||||
SyncNumber = syncNumber;
|
SyncNumber = syncNumber;
|
||||||
|
Parent = parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -63,16 +72,39 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
private const int BackingInitialSize = 8;
|
private const int BackingInitialSize = 8;
|
||||||
|
|
||||||
private GpuContext _context;
|
private GpuContext _context;
|
||||||
|
private Buffer _parent;
|
||||||
|
private Action<ulong, ulong> _flushAction;
|
||||||
|
|
||||||
|
private List<BufferMigration> _sources;
|
||||||
|
private BufferMigration _migrationTarget;
|
||||||
|
|
||||||
private object _lock = new object();
|
private object _lock = new object();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether the modified range list has any entries or not.
|
||||||
|
/// </summary>
|
||||||
|
public bool HasRanges
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
return Count > 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new instance of a modified range list.
|
/// Creates a new instance of a modified range list.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context">GPU context that the buffer range list belongs to</param>
|
/// <param name="context">GPU context that the buffer range list belongs to</param>
|
||||||
public BufferModifiedRangeList(GpuContext context) : base(BackingInitialSize)
|
/// <param name="parent">The parent buffer that owns this range list</param>
|
||||||
|
/// <param name="flushAction">The flush action for the parent buffer</param>
|
||||||
|
public BufferModifiedRangeList(GpuContext context, Buffer parent, Action<ulong, ulong> flushAction) : base(BackingInitialSize)
|
||||||
{
|
{
|
||||||
_context = context;
|
_context = context;
|
||||||
|
_parent = parent;
|
||||||
|
_flushAction = flushAction;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -142,6 +174,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
{
|
{
|
||||||
// Region already exists. Just update the existing sync number.
|
// Region already exists. Just update the existing sync number.
|
||||||
overlap.SyncNumber = syncNumber;
|
overlap.SyncNumber = syncNumber;
|
||||||
|
overlap.Parent = this;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -152,18 +185,18 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
{
|
{
|
||||||
// A split item must be created behind this overlap.
|
// A split item must be created behind this overlap.
|
||||||
|
|
||||||
Add(new BufferModifiedRange(overlap.Address, address - overlap.Address, overlap.SyncNumber));
|
Add(new BufferModifiedRange(overlap.Address, address - overlap.Address, overlap.SyncNumber, overlap.Parent));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (overlap.Address < endAddress && overlap.EndAddress > endAddress)
|
if (overlap.Address < endAddress && overlap.EndAddress > endAddress)
|
||||||
{
|
{
|
||||||
// A split item must be created after this overlap.
|
// A split item must be created after this overlap.
|
||||||
|
|
||||||
Add(new BufferModifiedRange(endAddress, overlap.EndAddress - endAddress, overlap.SyncNumber));
|
Add(new BufferModifiedRange(endAddress, overlap.EndAddress - endAddress, overlap.SyncNumber, overlap.Parent));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Add(new BufferModifiedRange(address, size, syncNumber));
|
Add(new BufferModifiedRange(address, size, syncNumber, this));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,9 +240,102 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Performs the given range action, or one from a migration that overlaps and has not synced yet.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="offset">The offset to pass to the action</param>
|
||||||
|
/// <param name="size">The size to pass to the action</param>
|
||||||
|
/// <param name="syncNumber">The sync number that has been reached</param>
|
||||||
|
/// <param name="parent">The modified range list that originally owned this range</param>
|
||||||
|
/// <param name="rangeAction">The action to perform</param>
|
||||||
|
public void RangeActionWithMigration(ulong offset, ulong size, ulong syncNumber, BufferModifiedRangeList parent, Action<ulong, ulong> rangeAction)
|
||||||
|
{
|
||||||
|
bool firstSource = true;
|
||||||
|
|
||||||
|
if (parent != this)
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
if (_sources != null)
|
||||||
|
{
|
||||||
|
foreach (BufferMigration source in _sources)
|
||||||
|
{
|
||||||
|
if (source.Overlaps(offset, size, syncNumber))
|
||||||
|
{
|
||||||
|
if (firstSource && !source.FullyMatches(offset, size))
|
||||||
|
{
|
||||||
|
// Perform this buffer's action first. The migrations will run after.
|
||||||
|
rangeAction(offset, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
source.RangeActionWithMigration(offset, size, syncNumber, parent);
|
||||||
|
|
||||||
|
firstSource = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (firstSource)
|
||||||
|
{
|
||||||
|
// No overlapping migrations, or they are not meant for this range, flush the data using the given action.
|
||||||
|
rangeAction(offset, size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes modified ranges ready by the sync number from the list, and flushes their buffer data within a given address range.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="overlaps">Overlapping ranges to check</param>
|
||||||
|
/// <param name="rangeCount">Number of overlapping ranges</param>
|
||||||
|
/// <param name="highestDiff">The highest difference between an overlapping range's sync number and the current one</param>
|
||||||
|
/// <param name="currentSync">The current sync number</param>
|
||||||
|
/// <param name="address">The start address of the flush range</param>
|
||||||
|
/// <param name="endAddress">The end address of the flush range</param>
|
||||||
|
private void RemoveRangesAndFlush(
|
||||||
|
BufferModifiedRange[] overlaps,
|
||||||
|
int rangeCount,
|
||||||
|
long highestDiff,
|
||||||
|
ulong currentSync,
|
||||||
|
ulong address,
|
||||||
|
ulong endAddress)
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
if (_migrationTarget == null)
|
||||||
|
{
|
||||||
|
ulong waitSync = currentSync + (ulong)highestDiff;
|
||||||
|
|
||||||
|
for (int i = 0; i < rangeCount; i++)
|
||||||
|
{
|
||||||
|
BufferModifiedRange overlap = overlaps[i];
|
||||||
|
|
||||||
|
long diff = (long)(overlap.SyncNumber - currentSync);
|
||||||
|
|
||||||
|
if (diff <= highestDiff)
|
||||||
|
{
|
||||||
|
ulong clampAddress = Math.Max(address, overlap.Address);
|
||||||
|
ulong clampEnd = Math.Min(endAddress, overlap.EndAddress);
|
||||||
|
|
||||||
|
ClearPart(overlap, clampAddress, clampEnd);
|
||||||
|
|
||||||
|
RangeActionWithMigration(clampAddress, clampEnd - clampAddress, waitSync, overlap.Parent, _flushAction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// There is a migration target to call instead. This can't be changed after set so accessing it outside the lock is fine.
|
||||||
|
|
||||||
|
_migrationTarget.Destination.RemoveRangesAndFlush(overlaps, rangeCount, highestDiff, currentSync, address, endAddress);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets modified ranges within the specified region, waits on ones from a previous sync number,
|
/// Gets modified ranges within the specified region, waits on ones from a previous sync number,
|
||||||
/// and then fires the given action for each range individually.
|
/// and then fires the flush action for each range individually.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// This function assumes it is called from the background thread.
|
/// This function assumes it is called from the background thread.
|
||||||
@@ -218,8 +344,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// </remarks>
|
/// </remarks>
|
||||||
/// <param name="address">Start address to query</param>
|
/// <param name="address">Start address to query</param>
|
||||||
/// <param name="size">Size to query</param>
|
/// <param name="size">Size to query</param>
|
||||||
/// <param name="rangeAction">The action to call for each modified range</param>
|
public void WaitForAndFlushRanges(ulong address, ulong size)
|
||||||
public void WaitForAndGetRanges(ulong address, ulong size, Action<ulong, ulong> rangeAction)
|
|
||||||
{
|
{
|
||||||
ulong endAddress = address + size;
|
ulong endAddress = address + size;
|
||||||
ulong currentSync = _context.SyncNumber;
|
ulong currentSync = _context.SyncNumber;
|
||||||
@@ -231,10 +356,23 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
// Range list must be consistent for this operation
|
// Range list must be consistent for this operation
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
rangeCount = FindOverlapsNonOverlapping(address, size, ref overlaps);
|
if (_migrationTarget != null)
|
||||||
|
{
|
||||||
|
rangeCount = -1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rangeCount = FindOverlapsNonOverlapping(address, size, ref overlaps);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rangeCount == 0)
|
if (rangeCount == -1)
|
||||||
|
{
|
||||||
|
_migrationTarget.Destination.WaitForAndFlushRanges(address, size);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (rangeCount == 0)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -264,47 +402,37 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
// Wait for the syncpoint.
|
// Wait for the syncpoint.
|
||||||
_context.Renderer.WaitSync(currentSync + (ulong)highestDiff);
|
_context.Renderer.WaitSync(currentSync + (ulong)highestDiff);
|
||||||
|
|
||||||
// Flush and remove all regions with the older syncpoint.
|
RemoveRangesAndFlush(overlaps, rangeCount, highestDiff, currentSync, address, endAddress);
|
||||||
lock (_lock)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < rangeCount; i++)
|
|
||||||
{
|
|
||||||
BufferModifiedRange overlap = overlaps[i];
|
|
||||||
|
|
||||||
long diff = (long)(overlap.SyncNumber - currentSync);
|
|
||||||
|
|
||||||
if (diff <= highestDiff)
|
|
||||||
{
|
|
||||||
ulong clampAddress = Math.Max(address, overlap.Address);
|
|
||||||
ulong clampEnd = Math.Min(endAddress, overlap.EndAddress);
|
|
||||||
|
|
||||||
ClearPart(overlap, clampAddress, clampEnd);
|
|
||||||
|
|
||||||
rangeAction(clampAddress, clampEnd - clampAddress);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Inherit ranges from another modified range list.
|
/// Inherit ranges from another modified range list.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="ranges">The range list to inherit from</param>
|
/// <param name="ranges">The range list to inherit from</param>
|
||||||
/// <param name="rangeAction">The action to call for each modified range</param>
|
/// <param name="registerRangeAction">The action to call for each modified range</param>
|
||||||
public void InheritRanges(BufferModifiedRangeList ranges, Action<ulong, ulong> rangeAction)
|
public void InheritRanges(BufferModifiedRangeList ranges, Action<ulong, ulong> registerRangeAction)
|
||||||
{
|
{
|
||||||
BufferModifiedRange[] inheritRanges;
|
BufferModifiedRange[] inheritRanges;
|
||||||
|
|
||||||
lock (ranges._lock)
|
lock (ranges._lock)
|
||||||
{
|
{
|
||||||
inheritRanges = ranges.ToArray();
|
BufferMigration migration = new(ranges._parent, ranges._flushAction, ranges, this, _context.SyncNumber);
|
||||||
}
|
|
||||||
|
|
||||||
lock (_lock)
|
ranges._parent.IncrementReferenceCount();
|
||||||
{
|
ranges._migrationTarget = migration;
|
||||||
foreach (BufferModifiedRange range in inheritRanges)
|
|
||||||
|
_context.RegisterBufferMigration(migration);
|
||||||
|
|
||||||
|
inheritRanges = ranges.ToArray();
|
||||||
|
|
||||||
|
lock (_lock)
|
||||||
{
|
{
|
||||||
Add(range);
|
(_sources ??= new List<BufferMigration>()).Add(migration);
|
||||||
|
|
||||||
|
foreach (BufferModifiedRange range in inheritRanges)
|
||||||
|
{
|
||||||
|
Add(range);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -313,44 +441,20 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
{
|
{
|
||||||
if (range.SyncNumber != currentSync)
|
if (range.SyncNumber != currentSync)
|
||||||
{
|
{
|
||||||
rangeAction(range.Address, range.Size);
|
registerRangeAction(range.Address, range.Size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Calls the given action for modified ranges that aren't from the current sync number.
|
/// Removes a source buffer migration, indicating its copy has completed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="rangeAction">The action to call for each modified range</param>
|
/// <param name="migration">The migration to remove</param>
|
||||||
public void ReregisterRanges(Action<ulong, ulong> rangeAction)
|
public void RemoveMigration(BufferMigration migration)
|
||||||
{
|
{
|
||||||
ref var ranges = ref ThreadStaticArray<BufferModifiedRange>.Get();
|
|
||||||
int count;
|
|
||||||
|
|
||||||
// Range list must be consistent for this operation.
|
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
count = Count;
|
_sources.Remove(migration);
|
||||||
if (ranges.Length < count)
|
|
||||||
{
|
|
||||||
Array.Resize(ref ranges, count);
|
|
||||||
}
|
|
||||||
|
|
||||||
int i = 0;
|
|
||||||
foreach (BufferModifiedRange range in this)
|
|
||||||
{
|
|
||||||
ranges[i++] = range;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ulong currentSync = _context.SyncNumber;
|
|
||||||
for (int i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
BufferModifiedRange range = ranges[i];
|
|
||||||
if (range.SyncNumber != currentSync)
|
|
||||||
{
|
|
||||||
rangeAction(range.Address, range.Size);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -362,12 +466,12 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
|
|
||||||
if (overlap.Address < address)
|
if (overlap.Address < address)
|
||||||
{
|
{
|
||||||
Add(new BufferModifiedRange(overlap.Address, address - overlap.Address, overlap.SyncNumber));
|
Add(new BufferModifiedRange(overlap.Address, address - overlap.Address, overlap.SyncNumber, overlap.Parent));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (overlap.EndAddress > endAddress)
|
if (overlap.EndAddress > endAddress)
|
||||||
{
|
{
|
||||||
Add(new BufferModifiedRange(endAddress, overlap.EndAddress - endAddress, overlap.SyncNumber));
|
Add(new BufferModifiedRange(endAddress, overlap.EndAddress - endAddress, overlap.SyncNumber, overlap.Parent));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
103
Ryujinx.Graphics.Gpu/Shader/CachedShaderBindings.cs
Normal file
103
Ryujinx.Graphics.Gpu/Shader/CachedShaderBindings.cs
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
using Ryujinx.Graphics.GAL;
|
||||||
|
using Ryujinx.Graphics.Gpu.Engine;
|
||||||
|
using Ryujinx.Graphics.Gpu.Image;
|
||||||
|
using Ryujinx.Graphics.Shader;
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gpu.Shader
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A collection of shader bindings ready for insertion into the buffer and texture managers.
|
||||||
|
/// </summary>
|
||||||
|
internal class CachedShaderBindings
|
||||||
|
{
|
||||||
|
public TextureBindingInfo[][] TextureBindings { get; }
|
||||||
|
public TextureBindingInfo[][] ImageBindings { get; }
|
||||||
|
public BufferDescriptor[][] ConstantBufferBindings { get; }
|
||||||
|
public BufferDescriptor[][] StorageBufferBindings { get; }
|
||||||
|
|
||||||
|
public int MaxTextureBinding { get; }
|
||||||
|
public int MaxImageBinding { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a new cached shader bindings collection.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="isCompute">Whether the shader is for compute</param>
|
||||||
|
/// <param name="stages">The stages used by the shader</param>
|
||||||
|
public CachedShaderBindings(bool isCompute, CachedShaderStage[] stages)
|
||||||
|
{
|
||||||
|
int stageCount = isCompute ? 1 : Constants.ShaderStages;
|
||||||
|
|
||||||
|
TextureBindings = new TextureBindingInfo[stageCount][];
|
||||||
|
ImageBindings = new TextureBindingInfo[stageCount][];
|
||||||
|
ConstantBufferBindings = new BufferDescriptor[stageCount][];
|
||||||
|
StorageBufferBindings = new BufferDescriptor[stageCount][];
|
||||||
|
|
||||||
|
int maxTextureBinding = -1;
|
||||||
|
int maxImageBinding = -1;
|
||||||
|
int offset = isCompute ? 0 : 1;
|
||||||
|
|
||||||
|
for (int i = 0; i < stageCount; i++)
|
||||||
|
{
|
||||||
|
CachedShaderStage stage = stages[i + offset];
|
||||||
|
|
||||||
|
if (stage == null)
|
||||||
|
{
|
||||||
|
TextureBindings[i] = Array.Empty<TextureBindingInfo>();
|
||||||
|
ImageBindings[i] = Array.Empty<TextureBindingInfo>();
|
||||||
|
ConstantBufferBindings[i] = Array.Empty<BufferDescriptor>();
|
||||||
|
StorageBufferBindings[i] = Array.Empty<BufferDescriptor>();
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
TextureBindings[i] = stage.Info.Textures.Select(descriptor =>
|
||||||
|
{
|
||||||
|
Target target = ShaderTexture.GetTarget(descriptor.Type);
|
||||||
|
|
||||||
|
var result = new TextureBindingInfo(
|
||||||
|
target,
|
||||||
|
descriptor.Binding,
|
||||||
|
descriptor.CbufSlot,
|
||||||
|
descriptor.HandleIndex,
|
||||||
|
descriptor.Flags);
|
||||||
|
|
||||||
|
if (descriptor.Binding > maxTextureBinding)
|
||||||
|
{
|
||||||
|
maxTextureBinding = descriptor.Binding;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}).ToArray();
|
||||||
|
|
||||||
|
ImageBindings[i] = stage.Info.Images.Select(descriptor =>
|
||||||
|
{
|
||||||
|
Target target = ShaderTexture.GetTarget(descriptor.Type);
|
||||||
|
Format format = ShaderTexture.GetFormat(descriptor.Format);
|
||||||
|
|
||||||
|
var result = new TextureBindingInfo(
|
||||||
|
target,
|
||||||
|
format,
|
||||||
|
descriptor.Binding,
|
||||||
|
descriptor.CbufSlot,
|
||||||
|
descriptor.HandleIndex,
|
||||||
|
descriptor.Flags);
|
||||||
|
|
||||||
|
if (descriptor.Binding > maxImageBinding)
|
||||||
|
{
|
||||||
|
maxImageBinding = descriptor.Binding;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}).ToArray();
|
||||||
|
|
||||||
|
ConstantBufferBindings[i] = stage.Info.CBuffers.ToArray();
|
||||||
|
StorageBufferBindings[i] = stage.Info.SBuffers.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
MaxTextureBinding = maxTextureBinding;
|
||||||
|
MaxImageBinding = maxImageBinding;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -24,6 +24,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public CachedShaderStage[] Shaders { get; }
|
public CachedShaderStage[] Shaders { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Cached shader bindings, ready for placing into the bindings manager.
|
||||||
|
/// </summary>
|
||||||
|
public CachedShaderBindings Bindings { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new instance of the shader bundle.
|
/// Creates a new instance of the shader bundle.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -37,6 +42,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
Shaders = shaders;
|
Shaders = shaders;
|
||||||
|
|
||||||
SpecializationState.Prepare(shaders);
|
SpecializationState.Prepare(shaders);
|
||||||
|
Bindings = new CachedShaderBindings(shaders.Length == 1, shaders);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@@ -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 = 3897;
|
private const uint CodeGenVersion = 3943;
|
||||||
|
|
||||||
private const string SharedTocFileName = "shared.toc";
|
private const string SharedTocFileName = "shared.toc";
|
||||||
private const string SharedDataFileName = "shared.data";
|
private const string SharedDataFileName = "shared.data";
|
||||||
|
@@ -238,6 +238,11 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
_sync.Wait(id);
|
_sync.Wait(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ulong GetCurrentSync()
|
||||||
|
{
|
||||||
|
return _sync.GetCurrent();
|
||||||
|
}
|
||||||
|
|
||||||
public void Screenshot()
|
public void Screenshot()
|
||||||
{
|
{
|
||||||
_window.ScreenCaptureRequested = true;
|
_window.ScreenCaptureRequested = true;
|
||||||
|
@@ -40,6 +40,37 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ulong GetCurrent()
|
||||||
|
{
|
||||||
|
lock (_handles)
|
||||||
|
{
|
||||||
|
ulong lastHandle = _firstHandle;
|
||||||
|
|
||||||
|
foreach (SyncHandle handle in _handles)
|
||||||
|
{
|
||||||
|
lock (handle)
|
||||||
|
{
|
||||||
|
if (handle.Handle == IntPtr.Zero)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (handle.ID > lastHandle)
|
||||||
|
{
|
||||||
|
WaitSyncStatus syncResult = GL.ClientWaitSync(handle.Handle, _syncFlags, 0);
|
||||||
|
|
||||||
|
if (syncResult == WaitSyncStatus.AlreadySignaled)
|
||||||
|
{
|
||||||
|
lastHandle = handle.ID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return lastHandle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void Wait(ulong id)
|
public void Wait(ulong id)
|
||||||
{
|
{
|
||||||
SyncHandle result = null;
|
SyncHandle result = null;
|
||||||
|
@@ -10,13 +10,9 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
{
|
{
|
||||||
public int Handle { get; private set; }
|
public int Handle { get; private set; }
|
||||||
|
|
||||||
private bool _needsAttribsUpdate;
|
|
||||||
|
|
||||||
private readonly VertexAttribDescriptor[] _vertexAttribs;
|
private readonly VertexAttribDescriptor[] _vertexAttribs;
|
||||||
private readonly VertexBufferDescriptor[] _vertexBuffers;
|
private readonly VertexBufferDescriptor[] _vertexBuffers;
|
||||||
|
|
||||||
private int _vertexAttribsCount;
|
|
||||||
private int _vertexBuffersCount;
|
|
||||||
private int _minVertexCount;
|
private int _minVertexCount;
|
||||||
|
|
||||||
private uint _vertexAttribsInUse;
|
private uint _vertexAttribsInUse;
|
||||||
@@ -76,9 +72,7 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
_vertexBuffers[bindingIndex] = vb;
|
_vertexBuffers[bindingIndex] = vb;
|
||||||
}
|
}
|
||||||
|
|
||||||
_vertexBuffersCount = bindingIndex;
|
|
||||||
_minVertexCount = minVertexCount;
|
_minVertexCount = minVertexCount;
|
||||||
_needsAttribsUpdate = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetVertexAttributes(ReadOnlySpan<VertexAttribDescriptor> vertexAttribs)
|
public void SetVertexAttributes(ReadOnlySpan<VertexAttribDescriptor> vertexAttribs)
|
||||||
@@ -131,8 +125,6 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
_vertexAttribs[index] = attrib;
|
_vertexAttribs[index] = attrib;
|
||||||
}
|
}
|
||||||
|
|
||||||
_vertexAttribsCount = index;
|
|
||||||
|
|
||||||
for (; index < Constants.MaxVertexAttribs; index++)
|
for (; index < Constants.MaxVertexAttribs; index++)
|
||||||
{
|
{
|
||||||
DisableVertexAttrib(index);
|
DisableVertexAttrib(index);
|
||||||
@@ -160,13 +152,11 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
public void PreDraw(int vertexCount)
|
public void PreDraw(int vertexCount)
|
||||||
{
|
{
|
||||||
LimitVertexBuffers(vertexCount);
|
LimitVertexBuffers(vertexCount);
|
||||||
Validate();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void PreDrawVbUnbounded()
|
public void PreDrawVbUnbounded()
|
||||||
{
|
{
|
||||||
UnlimitVertexBuffers();
|
UnlimitVertexBuffers();
|
||||||
Validate();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void LimitVertexBuffers(int vertexCount)
|
public void LimitVertexBuffers(int vertexCount)
|
||||||
@@ -252,36 +242,6 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
_vertexBuffersLimited = 0;
|
_vertexBuffersLimited = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Validate()
|
|
||||||
{
|
|
||||||
for (int attribIndex = 0; attribIndex < _vertexAttribsCount; attribIndex++)
|
|
||||||
{
|
|
||||||
VertexAttribDescriptor attrib = _vertexAttribs[attribIndex];
|
|
||||||
|
|
||||||
if (!attrib.IsZero)
|
|
||||||
{
|
|
||||||
if ((uint)attrib.BufferIndex >= _vertexBuffersCount)
|
|
||||||
{
|
|
||||||
DisableVertexAttrib(attribIndex);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_vertexBuffers[attrib.BufferIndex].Buffer.Handle == BufferHandle.Null)
|
|
||||||
{
|
|
||||||
DisableVertexAttrib(attribIndex);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_needsAttribsUpdate)
|
|
||||||
{
|
|
||||||
EnableVertexAttrib(attribIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_needsAttribsUpdate = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private void EnableVertexAttrib(int index)
|
private void EnableVertexAttrib(int index)
|
||||||
{
|
{
|
||||||
|
@@ -234,7 +234,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
var source = operation.GetSource(0);
|
var source = operation.GetSource(0);
|
||||||
|
|
||||||
var uvec4Type = context.TypeVector(context.TypeU32(), 4);
|
var uvec4Type = context.TypeVector(context.TypeU32(), 4);
|
||||||
var execution = context.Constant(context.TypeU32(), 3); // Subgroup
|
var execution = context.Constant(context.TypeU32(), Scope.Subgroup);
|
||||||
|
|
||||||
var maskVector = context.GroupNonUniformBallot(uvec4Type, execution, context.Get(AggregateType.Bool, source));
|
var maskVector = context.GroupNonUniformBallot(uvec4Type, execution, context.Get(AggregateType.Bool, source));
|
||||||
var mask = context.CompositeExtract(context.TypeU32(), maskVector, (SpvLiteralInteger)0);
|
var mask = context.CompositeExtract(context.TypeU32(), maskVector, (SpvLiteralInteger)0);
|
||||||
@@ -1233,7 +1233,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
var maxThreadId = context.BitwiseOr(context.TypeU32(), minThreadId, clampNotSegMask);
|
var maxThreadId = context.BitwiseOr(context.TypeU32(), minThreadId, clampNotSegMask);
|
||||||
var srcThreadId = context.BitwiseOr(context.TypeU32(), indexNotSegMask, minThreadId);
|
var srcThreadId = context.BitwiseOr(context.TypeU32(), indexNotSegMask, minThreadId);
|
||||||
var valid = context.ULessThanEqual(context.TypeBool(), srcThreadId, maxThreadId);
|
var valid = context.ULessThanEqual(context.TypeBool(), srcThreadId, maxThreadId);
|
||||||
var value = context.SubgroupReadInvocationKHR(context.TypeFP32(), x, srcThreadId);
|
var value = context.GroupNonUniformShuffle(context.TypeFP32(), context.Constant(context.TypeU32(), (int)Scope.Subgroup), x, srcThreadId);
|
||||||
var result = context.Select(context.TypeFP32(), valid, value, x);
|
var result = context.Select(context.TypeFP32(), valid, value, x);
|
||||||
|
|
||||||
var validLocal = (AstOperand)operation.GetSource(3);
|
var validLocal = (AstOperand)operation.GetSource(3);
|
||||||
@@ -1263,7 +1263,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
var maxThreadId = context.BitwiseOr(context.TypeU32(), minThreadId, clampNotSegMask);
|
var maxThreadId = context.BitwiseOr(context.TypeU32(), minThreadId, clampNotSegMask);
|
||||||
var srcThreadId = context.IAdd(context.TypeU32(), threadId, index);
|
var srcThreadId = context.IAdd(context.TypeU32(), threadId, index);
|
||||||
var valid = context.ULessThanEqual(context.TypeBool(), srcThreadId, maxThreadId);
|
var valid = context.ULessThanEqual(context.TypeBool(), srcThreadId, maxThreadId);
|
||||||
var value = context.SubgroupReadInvocationKHR(context.TypeFP32(), x, srcThreadId);
|
var value = context.GroupNonUniformShuffle(context.TypeFP32(), context.Constant(context.TypeU32(), (int)Scope.Subgroup), x, srcThreadId);
|
||||||
var result = context.Select(context.TypeFP32(), valid, value, x);
|
var result = context.Select(context.TypeFP32(), valid, value, x);
|
||||||
|
|
||||||
var validLocal = (AstOperand)operation.GetSource(3);
|
var validLocal = (AstOperand)operation.GetSource(3);
|
||||||
@@ -1289,7 +1289,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
var minThreadId = context.BitwiseAnd(context.TypeU32(), threadId, segMask);
|
var minThreadId = context.BitwiseAnd(context.TypeU32(), threadId, segMask);
|
||||||
var srcThreadId = context.ISub(context.TypeU32(), threadId, index);
|
var srcThreadId = context.ISub(context.TypeU32(), threadId, index);
|
||||||
var valid = context.SGreaterThanEqual(context.TypeBool(), srcThreadId, minThreadId);
|
var valid = context.SGreaterThanEqual(context.TypeBool(), srcThreadId, minThreadId);
|
||||||
var value = context.SubgroupReadInvocationKHR(context.TypeFP32(), x, srcThreadId);
|
var value = context.GroupNonUniformShuffle(context.TypeFP32(), context.Constant(context.TypeU32(), (int)Scope.Subgroup), x, srcThreadId);
|
||||||
var result = context.Select(context.TypeFP32(), valid, value, x);
|
var result = context.Select(context.TypeFP32(), valid, value, x);
|
||||||
|
|
||||||
var validLocal = (AstOperand)operation.GetSource(3);
|
var validLocal = (AstOperand)operation.GetSource(3);
|
||||||
@@ -1319,7 +1319,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
var maxThreadId = context.BitwiseOr(context.TypeU32(), minThreadId, clampNotSegMask);
|
var maxThreadId = context.BitwiseOr(context.TypeU32(), minThreadId, clampNotSegMask);
|
||||||
var srcThreadId = context.BitwiseXor(context.TypeU32(), threadId, index);
|
var srcThreadId = context.BitwiseXor(context.TypeU32(), threadId, index);
|
||||||
var valid = context.ULessThanEqual(context.TypeBool(), srcThreadId, maxThreadId);
|
var valid = context.ULessThanEqual(context.TypeBool(), srcThreadId, maxThreadId);
|
||||||
var value = context.SubgroupReadInvocationKHR(context.TypeFP32(), x, srcThreadId);
|
var value = context.GroupNonUniformShuffle(context.TypeFP32(), context.Constant(context.TypeU32(), (int)Scope.Subgroup), x, srcThreadId);
|
||||||
var result = context.Select(context.TypeFP32(), valid, value, x);
|
var result = context.Select(context.TypeFP32(), valid, value, x);
|
||||||
|
|
||||||
var validLocal = (AstOperand)operation.GetSource(3);
|
var validLocal = (AstOperand)operation.GetSource(3);
|
||||||
@@ -1861,19 +1861,22 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
|
|
||||||
private static OperationResult GenerateVoteAll(CodeGenContext context, AstOperation operation)
|
private static OperationResult GenerateVoteAll(CodeGenContext context, AstOperation operation)
|
||||||
{
|
{
|
||||||
var result = context.SubgroupAllKHR(context.TypeBool(), context.Get(AggregateType.Bool, operation.GetSource(0)));
|
var execution = context.Constant(context.TypeU32(), Scope.Subgroup);
|
||||||
|
var result = context.GroupNonUniformAll(context.TypeBool(), execution, context.Get(AggregateType.Bool, operation.GetSource(0)));
|
||||||
return new OperationResult(AggregateType.Bool, result);
|
return new OperationResult(AggregateType.Bool, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static OperationResult GenerateVoteAllEqual(CodeGenContext context, AstOperation operation)
|
private static OperationResult GenerateVoteAllEqual(CodeGenContext context, AstOperation operation)
|
||||||
{
|
{
|
||||||
var result = context.SubgroupAllEqualKHR(context.TypeBool(), context.Get(AggregateType.Bool, operation.GetSource(0)));
|
var execution = context.Constant(context.TypeU32(), Scope.Subgroup);
|
||||||
|
var result = context.GroupNonUniformAllEqual(context.TypeBool(), execution, context.Get(AggregateType.Bool, operation.GetSource(0)));
|
||||||
return new OperationResult(AggregateType.Bool, result);
|
return new OperationResult(AggregateType.Bool, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static OperationResult GenerateVoteAny(CodeGenContext context, AstOperation operation)
|
private static OperationResult GenerateVoteAny(CodeGenContext context, AstOperation operation)
|
||||||
{
|
{
|
||||||
var result = context.SubgroupAnyKHR(context.TypeBool(), context.Get(AggregateType.Bool, operation.GetSource(0)));
|
var execution = context.Constant(context.TypeU32(), Scope.Subgroup);
|
||||||
|
var result = context.GroupNonUniformAny(context.TypeBool(), execution, context.Get(AggregateType.Bool, operation.GetSource(0)));
|
||||||
return new OperationResult(AggregateType.Bool, result);
|
return new OperationResult(AggregateType.Bool, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -50,12 +50,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
CodeGenContext context = new CodeGenContext(info, config, instPool, integerPool);
|
CodeGenContext context = new CodeGenContext(info, config, instPool, integerPool);
|
||||||
|
|
||||||
context.AddCapability(Capability.GroupNonUniformBallot);
|
context.AddCapability(Capability.GroupNonUniformBallot);
|
||||||
|
context.AddCapability(Capability.GroupNonUniformShuffle);
|
||||||
|
context.AddCapability(Capability.GroupNonUniformVote);
|
||||||
context.AddCapability(Capability.ImageBuffer);
|
context.AddCapability(Capability.ImageBuffer);
|
||||||
context.AddCapability(Capability.ImageGatherExtended);
|
context.AddCapability(Capability.ImageGatherExtended);
|
||||||
context.AddCapability(Capability.ImageQuery);
|
context.AddCapability(Capability.ImageQuery);
|
||||||
context.AddCapability(Capability.SampledBuffer);
|
context.AddCapability(Capability.SampledBuffer);
|
||||||
context.AddCapability(Capability.SubgroupBallotKHR);
|
|
||||||
context.AddCapability(Capability.SubgroupVoteKHR);
|
|
||||||
|
|
||||||
if (config.TransformFeedbackEnabled && config.LastInVertexPipeline)
|
if (config.TransformFeedbackEnabled && config.LastInVertexPipeline)
|
||||||
{
|
{
|
||||||
@@ -94,9 +94,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
context.AddCapability(Capability.DrawParameters);
|
context.AddCapability(Capability.DrawParameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
context.AddExtension("SPV_KHR_shader_ballot");
|
|
||||||
context.AddExtension("SPV_KHR_subgroup_vote");
|
|
||||||
|
|
||||||
Declarations.DeclareAll(context, info);
|
Declarations.DeclareAll(context, info);
|
||||||
|
|
||||||
if ((info.HelperFunctionsMask & NeedsInvocationIdMask) != 0)
|
if ((info.HelperFunctionsMask & NeedsInvocationIdMask) != 0)
|
||||||
|
@@ -12,12 +12,12 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
private const int MaxUpdateBufferSize = 0x10000;
|
private const int MaxUpdateBufferSize = 0x10000;
|
||||||
|
|
||||||
public const AccessFlags DefaultAccessFlags =
|
public const AccessFlags DefaultAccessFlags =
|
||||||
AccessFlags.AccessIndirectCommandReadBit |
|
AccessFlags.IndirectCommandReadBit |
|
||||||
AccessFlags.AccessShaderReadBit |
|
AccessFlags.ShaderReadBit |
|
||||||
AccessFlags.AccessShaderWriteBit |
|
AccessFlags.ShaderWriteBit |
|
||||||
AccessFlags.AccessTransferReadBit |
|
AccessFlags.TransferReadBit |
|
||||||
AccessFlags.AccessTransferWriteBit |
|
AccessFlags.TransferWriteBit |
|
||||||
AccessFlags.AccessUniformReadBit;
|
AccessFlags.UniformReadBit;
|
||||||
|
|
||||||
private readonly VulkanRenderer _gd;
|
private readonly VulkanRenderer _gd;
|
||||||
private readonly Device _device;
|
private readonly Device _device;
|
||||||
@@ -87,9 +87,9 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
_gd.Api.CmdPipelineBarrier(
|
_gd.Api.CmdPipelineBarrier(
|
||||||
commandBuffer,
|
commandBuffer,
|
||||||
PipelineStageFlags.PipelineStageAllCommandsBit,
|
PipelineStageFlags.AllCommandsBit,
|
||||||
PipelineStageFlags.PipelineStageAllCommandsBit,
|
PipelineStageFlags.AllCommandsBit,
|
||||||
DependencyFlags.DependencyDeviceGroupBit,
|
DependencyFlags.DeviceGroupBit,
|
||||||
1,
|
1,
|
||||||
memoryBarrier,
|
memoryBarrier,
|
||||||
0,
|
0,
|
||||||
@@ -273,9 +273,9 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
cbs.CommandBuffer,
|
cbs.CommandBuffer,
|
||||||
dstBuffer,
|
dstBuffer,
|
||||||
BufferHolder.DefaultAccessFlags,
|
BufferHolder.DefaultAccessFlags,
|
||||||
AccessFlags.AccessTransferWriteBit,
|
AccessFlags.TransferWriteBit,
|
||||||
PipelineStageFlags.PipelineStageAllCommandsBit,
|
PipelineStageFlags.AllCommandsBit,
|
||||||
PipelineStageFlags.PipelineStageTransferBit,
|
PipelineStageFlags.TransferBit,
|
||||||
dstOffset,
|
dstOffset,
|
||||||
data.Length);
|
data.Length);
|
||||||
|
|
||||||
@@ -293,10 +293,10 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
_gd,
|
_gd,
|
||||||
cbs.CommandBuffer,
|
cbs.CommandBuffer,
|
||||||
dstBuffer,
|
dstBuffer,
|
||||||
AccessFlags.AccessTransferWriteBit,
|
AccessFlags.TransferWriteBit,
|
||||||
BufferHolder.DefaultAccessFlags,
|
BufferHolder.DefaultAccessFlags,
|
||||||
PipelineStageFlags.PipelineStageTransferBit,
|
PipelineStageFlags.TransferBit,
|
||||||
PipelineStageFlags.PipelineStageAllCommandsBit,
|
PipelineStageFlags.AllCommandsBit,
|
||||||
dstOffset,
|
dstOffset,
|
||||||
data.Length);
|
data.Length);
|
||||||
|
|
||||||
@@ -320,9 +320,9 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
cbs.CommandBuffer,
|
cbs.CommandBuffer,
|
||||||
dstBuffer,
|
dstBuffer,
|
||||||
BufferHolder.DefaultAccessFlags,
|
BufferHolder.DefaultAccessFlags,
|
||||||
AccessFlags.AccessTransferWriteBit,
|
AccessFlags.TransferWriteBit,
|
||||||
PipelineStageFlags.PipelineStageAllCommandsBit,
|
PipelineStageFlags.AllCommandsBit,
|
||||||
PipelineStageFlags.PipelineStageTransferBit,
|
PipelineStageFlags.TransferBit,
|
||||||
dstOffset,
|
dstOffset,
|
||||||
size);
|
size);
|
||||||
|
|
||||||
@@ -334,10 +334,10 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
gd,
|
gd,
|
||||||
cbs.CommandBuffer,
|
cbs.CommandBuffer,
|
||||||
dstBuffer,
|
dstBuffer,
|
||||||
AccessFlags.AccessTransferWriteBit,
|
AccessFlags.TransferWriteBit,
|
||||||
BufferHolder.DefaultAccessFlags,
|
BufferHolder.DefaultAccessFlags,
|
||||||
PipelineStageFlags.PipelineStageTransferBit,
|
PipelineStageFlags.TransferBit,
|
||||||
PipelineStageFlags.PipelineStageAllCommandsBit,
|
PipelineStageFlags.AllCommandsBit,
|
||||||
dstOffset,
|
dstOffset,
|
||||||
size);
|
size);
|
||||||
}
|
}
|
||||||
|
@@ -10,28 +10,28 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
class BufferManager : IDisposable
|
class BufferManager : IDisposable
|
||||||
{
|
{
|
||||||
private const MemoryPropertyFlags DefaultBufferMemoryFlags =
|
private const MemoryPropertyFlags DefaultBufferMemoryFlags =
|
||||||
MemoryPropertyFlags.MemoryPropertyHostVisibleBit |
|
MemoryPropertyFlags.HostVisibleBit |
|
||||||
MemoryPropertyFlags.MemoryPropertyHostCoherentBit |
|
MemoryPropertyFlags.HostCoherentBit |
|
||||||
MemoryPropertyFlags.MemoryPropertyHostCachedBit;
|
MemoryPropertyFlags.HostCachedBit;
|
||||||
|
|
||||||
private const MemoryPropertyFlags DeviceLocalBufferMemoryFlags =
|
private const MemoryPropertyFlags DeviceLocalBufferMemoryFlags =
|
||||||
MemoryPropertyFlags.MemoryPropertyDeviceLocalBit;
|
MemoryPropertyFlags.DeviceLocalBit;
|
||||||
|
|
||||||
private const MemoryPropertyFlags FlushableDeviceLocalBufferMemoryFlags =
|
private const MemoryPropertyFlags FlushableDeviceLocalBufferMemoryFlags =
|
||||||
MemoryPropertyFlags.MemoryPropertyHostVisibleBit |
|
MemoryPropertyFlags.HostVisibleBit |
|
||||||
MemoryPropertyFlags.MemoryPropertyHostCoherentBit |
|
MemoryPropertyFlags.HostCoherentBit |
|
||||||
MemoryPropertyFlags.MemoryPropertyDeviceLocalBit;
|
MemoryPropertyFlags.DeviceLocalBit;
|
||||||
|
|
||||||
private const BufferUsageFlags DefaultBufferUsageFlags =
|
private const BufferUsageFlags DefaultBufferUsageFlags =
|
||||||
BufferUsageFlags.BufferUsageTransferSrcBit |
|
BufferUsageFlags.TransferSrcBit |
|
||||||
BufferUsageFlags.BufferUsageTransferDstBit |
|
BufferUsageFlags.TransferDstBit |
|
||||||
BufferUsageFlags.BufferUsageUniformTexelBufferBit |
|
BufferUsageFlags.UniformTexelBufferBit |
|
||||||
BufferUsageFlags.BufferUsageStorageTexelBufferBit |
|
BufferUsageFlags.StorageTexelBufferBit |
|
||||||
BufferUsageFlags.BufferUsageUniformBufferBit |
|
BufferUsageFlags.UniformBufferBit |
|
||||||
BufferUsageFlags.BufferUsageStorageBufferBit |
|
BufferUsageFlags.StorageBufferBit |
|
||||||
BufferUsageFlags.BufferUsageIndexBufferBit |
|
BufferUsageFlags.IndexBufferBit |
|
||||||
BufferUsageFlags.BufferUsageVertexBufferBit |
|
BufferUsageFlags.VertexBufferBit |
|
||||||
BufferUsageFlags.BufferUsageTransformFeedbackBufferBitExt;
|
BufferUsageFlags.TransformFeedbackBufferBitExt;
|
||||||
|
|
||||||
private readonly PhysicalDevice _physicalDevice;
|
private readonly PhysicalDevice _physicalDevice;
|
||||||
private readonly Device _device;
|
private readonly Device _device;
|
||||||
@@ -76,11 +76,11 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
if (forConditionalRendering && gd.Capabilities.SupportsConditionalRendering)
|
if (forConditionalRendering && gd.Capabilities.SupportsConditionalRendering)
|
||||||
{
|
{
|
||||||
usage |= BufferUsageFlags.BufferUsageConditionalRenderingBitExt;
|
usage |= BufferUsageFlags.ConditionalRenderingBitExt;
|
||||||
}
|
}
|
||||||
else if (gd.Capabilities.SupportsIndirectParameters)
|
else if (gd.Capabilities.SupportsIndirectParameters)
|
||||||
{
|
{
|
||||||
usage |= BufferUsageFlags.BufferUsageIndirectBufferBit;
|
usage |= BufferUsageFlags.IndirectBufferBit;
|
||||||
}
|
}
|
||||||
|
|
||||||
var bufferCreateInfo = new BufferCreateInfo()
|
var bufferCreateInfo = new BufferCreateInfo()
|
||||||
|
@@ -71,8 +71,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
SType = StructureType.CommandPoolCreateInfo,
|
SType = StructureType.CommandPoolCreateInfo,
|
||||||
QueueFamilyIndex = queueFamilyIndex,
|
QueueFamilyIndex = queueFamilyIndex,
|
||||||
Flags = CommandPoolCreateFlags.CommandPoolCreateTransientBit |
|
Flags = CommandPoolCreateFlags.TransientBit |
|
||||||
CommandPoolCreateFlags.CommandPoolCreateResetCommandBufferBit
|
CommandPoolCreateFlags.ResetCommandBufferBit
|
||||||
};
|
};
|
||||||
|
|
||||||
api.CreateCommandPool(device, commandPoolCreateInfo, null, out _pool).ThrowOnError();
|
api.CreateCommandPool(device, commandPoolCreateInfo, null, out _pool).ThrowOnError();
|
||||||
|
@@ -229,7 +229,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
}
|
}
|
||||||
else if (texture is TextureView view)
|
else if (texture is TextureView view)
|
||||||
{
|
{
|
||||||
view.Storage.InsertBarrier(cbs, AccessFlags.AccessShaderReadBit, stage.ConvertToPipelineStageFlags());
|
view.Storage.InsertBarrier(cbs, AccessFlags.ShaderReadBit, stage.ConvertToPipelineStageFlags());
|
||||||
|
|
||||||
_textureRefs[binding] = view.GetImageView();
|
_textureRefs[binding] = view.GetImageView();
|
||||||
_samplerRefs[binding] = ((SamplerHolder)sampler)?.GetSampler();
|
_samplerRefs[binding] = ((SamplerHolder)sampler)?.GetSampler();
|
||||||
|
@@ -12,12 +12,12 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
return stage switch
|
return stage switch
|
||||||
{
|
{
|
||||||
ShaderStage.Vertex => ShaderStageFlags.ShaderStageVertexBit,
|
ShaderStage.Vertex => ShaderStageFlags.VertexBit,
|
||||||
ShaderStage.Geometry => ShaderStageFlags.ShaderStageGeometryBit,
|
ShaderStage.Geometry => ShaderStageFlags.GeometryBit,
|
||||||
ShaderStage.TessellationControl => ShaderStageFlags.ShaderStageTessellationControlBit,
|
ShaderStage.TessellationControl => ShaderStageFlags.TessellationControlBit,
|
||||||
ShaderStage.TessellationEvaluation => ShaderStageFlags.ShaderStageTessellationEvaluationBit,
|
ShaderStage.TessellationEvaluation => ShaderStageFlags.TessellationEvaluationBit,
|
||||||
ShaderStage.Fragment => ShaderStageFlags.ShaderStageFragmentBit,
|
ShaderStage.Fragment => ShaderStageFlags.FragmentBit,
|
||||||
ShaderStage.Compute => ShaderStageFlags.ShaderStageComputeBit,
|
ShaderStage.Compute => ShaderStageFlags.ComputeBit,
|
||||||
_ => LogInvalidAndReturn(stage, nameof(ShaderStage), (ShaderStageFlags)0)
|
_ => LogInvalidAndReturn(stage, nameof(ShaderStage), (ShaderStageFlags)0)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -26,12 +26,12 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
return stage switch
|
return stage switch
|
||||||
{
|
{
|
||||||
ShaderStage.Vertex => PipelineStageFlags.PipelineStageVertexShaderBit,
|
ShaderStage.Vertex => PipelineStageFlags.VertexShaderBit,
|
||||||
ShaderStage.Geometry => PipelineStageFlags.PipelineStageGeometryShaderBit,
|
ShaderStage.Geometry => PipelineStageFlags.GeometryShaderBit,
|
||||||
ShaderStage.TessellationControl => PipelineStageFlags.PipelineStageTessellationControlShaderBit,
|
ShaderStage.TessellationControl => PipelineStageFlags.TessellationControlShaderBit,
|
||||||
ShaderStage.TessellationEvaluation => PipelineStageFlags.PipelineStageTessellationEvaluationShaderBit,
|
ShaderStage.TessellationEvaluation => PipelineStageFlags.TessellationEvaluationShaderBit,
|
||||||
ShaderStage.Fragment => PipelineStageFlags.PipelineStageFragmentShaderBit,
|
ShaderStage.Fragment => PipelineStageFlags.FragmentShaderBit,
|
||||||
ShaderStage.Compute => PipelineStageFlags.PipelineStageComputeShaderBit,
|
ShaderStage.Compute => PipelineStageFlags.ComputeShaderBit,
|
||||||
_ => LogInvalidAndReturn(stage, nameof(ShaderStage), (PipelineStageFlags)0)
|
_ => LogInvalidAndReturn(stage, nameof(ShaderStage), (PipelineStageFlags)0)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -112,10 +112,10 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
return face switch
|
return face switch
|
||||||
{
|
{
|
||||||
Face.Back => CullModeFlags.CullModeBackBit,
|
Face.Back => CullModeFlags.BackBit,
|
||||||
Face.Front => CullModeFlags.CullModeFrontBit,
|
Face.Front => CullModeFlags.FrontBit,
|
||||||
Face.FrontAndBack => CullModeFlags.CullModeFrontAndBack,
|
Face.FrontAndBack => CullModeFlags.FrontAndBack,
|
||||||
_ => LogInvalidAndReturn(face, nameof(Face), CullModeFlags.CullModeBackBit)
|
_ => LogInvalidAndReturn(face, nameof(Face), CullModeFlags.BackBit)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,14 +223,14 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
Target.Texture1D or
|
Target.Texture1D or
|
||||||
Target.Texture1DArray or
|
Target.Texture1DArray or
|
||||||
Target.TextureBuffer => ImageType.ImageType1D,
|
Target.TextureBuffer => ImageType.Type1D,
|
||||||
Target.Texture2D or
|
Target.Texture2D or
|
||||||
Target.Texture2DArray or
|
Target.Texture2DArray or
|
||||||
Target.Texture2DMultisample or
|
Target.Texture2DMultisample or
|
||||||
Target.Cubemap or
|
Target.Cubemap or
|
||||||
Target.CubemapArray => ImageType.ImageType2D,
|
Target.CubemapArray => ImageType.Type2D,
|
||||||
Target.Texture3D => ImageType.ImageType3D,
|
Target.Texture3D => ImageType.Type3D,
|
||||||
_ => LogInvalidAndReturn(target, nameof(Target), ImageType.ImageType2D)
|
_ => LogInvalidAndReturn(target, nameof(Target), ImageType.Type2D)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -238,14 +238,14 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
return target switch
|
return target switch
|
||||||
{
|
{
|
||||||
Target.Texture1D => ImageViewType.ImageViewType1D,
|
Target.Texture1D => ImageViewType.Type1D,
|
||||||
Target.Texture2D or Target.Texture2DMultisample => ImageViewType.ImageViewType2D,
|
Target.Texture2D or Target.Texture2DMultisample => ImageViewType.Type2D,
|
||||||
Target.Texture3D => ImageViewType.ImageViewType3D,
|
Target.Texture3D => ImageViewType.Type3D,
|
||||||
Target.Texture1DArray => ImageViewType.ImageViewType1DArray,
|
Target.Texture1DArray => ImageViewType.Type1DArray,
|
||||||
Target.Texture2DArray => ImageViewType.ImageViewType2DArray,
|
Target.Texture2DArray => ImageViewType.Type2DArray,
|
||||||
Target.Cubemap => ImageViewType.Cube,
|
Target.Cubemap => ImageViewType.TypeCube,
|
||||||
Target.CubemapArray => ImageViewType.CubeArray,
|
Target.CubemapArray => ImageViewType.TypeCubeArray,
|
||||||
_ => LogInvalidAndReturn(target, nameof(Target), ImageViewType.ImageViewType2D)
|
_ => LogInvalidAndReturn(target, nameof(Target), ImageViewType.Type2D)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -253,12 +253,12 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
return format switch
|
return format switch
|
||||||
{
|
{
|
||||||
GAL.Format.D16Unorm or GAL.Format.D32Float => ImageAspectFlags.ImageAspectDepthBit,
|
GAL.Format.D16Unorm or GAL.Format.D32Float => ImageAspectFlags.DepthBit,
|
||||||
GAL.Format.S8Uint => ImageAspectFlags.ImageAspectStencilBit,
|
GAL.Format.S8Uint => ImageAspectFlags.StencilBit,
|
||||||
GAL.Format.D24UnormS8Uint or
|
GAL.Format.D24UnormS8Uint or
|
||||||
GAL.Format.D32FloatS8Uint or
|
GAL.Format.D32FloatS8Uint or
|
||||||
GAL.Format.S8UintD24Unorm => ImageAspectFlags.ImageAspectDepthBit | ImageAspectFlags.ImageAspectStencilBit,
|
GAL.Format.S8UintD24Unorm => ImageAspectFlags.DepthBit | ImageAspectFlags.StencilBit,
|
||||||
_ => ImageAspectFlags.ImageAspectColorBit
|
_ => ImageAspectFlags.ColorBit
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -266,12 +266,12 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
return format switch
|
return format switch
|
||||||
{
|
{
|
||||||
GAL.Format.D16Unorm or GAL.Format.D32Float => ImageAspectFlags.ImageAspectDepthBit,
|
GAL.Format.D16Unorm or GAL.Format.D32Float => ImageAspectFlags.DepthBit,
|
||||||
GAL.Format.S8Uint => ImageAspectFlags.ImageAspectStencilBit,
|
GAL.Format.S8Uint => ImageAspectFlags.StencilBit,
|
||||||
GAL.Format.D24UnormS8Uint or
|
GAL.Format.D24UnormS8Uint or
|
||||||
GAL.Format.D32FloatS8Uint or
|
GAL.Format.D32FloatS8Uint or
|
||||||
GAL.Format.S8UintD24Unorm => depthStencilMode == DepthStencilMode.Stencil ? ImageAspectFlags.ImageAspectStencilBit : ImageAspectFlags.ImageAspectDepthBit,
|
GAL.Format.S8UintD24Unorm => depthStencilMode == DepthStencilMode.Stencil ? ImageAspectFlags.StencilBit : ImageAspectFlags.DepthBit,
|
||||||
_ => ImageAspectFlags.ImageAspectColorBit
|
_ => ImageAspectFlags.ColorBit
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -83,22 +83,22 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
var format = FormatTable.GetFormat(srcFormat);
|
var format = FormatTable.GetFormat(srcFormat);
|
||||||
|
|
||||||
var requiredFeatures = FormatFeatureFlags.FormatFeatureSampledImageBit |
|
var requiredFeatures = FormatFeatureFlags.SampledImageBit |
|
||||||
FormatFeatureFlags.FormatFeatureTransferSrcBit |
|
FormatFeatureFlags.TransferSrcBit |
|
||||||
FormatFeatureFlags.FormatFeatureTransferDstBit;
|
FormatFeatureFlags.TransferDstBit;
|
||||||
|
|
||||||
if (srcFormat.IsDepthOrStencil())
|
if (srcFormat.IsDepthOrStencil())
|
||||||
{
|
{
|
||||||
requiredFeatures |= FormatFeatureFlags.FormatFeatureDepthStencilAttachmentBit;
|
requiredFeatures |= FormatFeatureFlags.DepthStencilAttachmentBit;
|
||||||
}
|
}
|
||||||
else if (srcFormat.IsRtColorCompatible())
|
else if (srcFormat.IsRtColorCompatible())
|
||||||
{
|
{
|
||||||
requiredFeatures |= FormatFeatureFlags.FormatFeatureColorAttachmentBit;
|
requiredFeatures |= FormatFeatureFlags.ColorAttachmentBit;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (srcFormat.IsImageCompatible())
|
if (srcFormat.IsImageCompatible())
|
||||||
{
|
{
|
||||||
requiredFeatures |= FormatFeatureFlags.FormatFeatureStorageImageBit;
|
requiredFeatures |= FormatFeatureFlags.StorageImageBit;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!OptimalFormatSupports(requiredFeatures, srcFormat) || (IsD24S8(srcFormat) && VulkanConfiguration.ForceD24S8Unsupported))
|
if (!OptimalFormatSupports(requiredFeatures, srcFormat) || (IsD24S8(srcFormat) && VulkanConfiguration.ForceD24S8Unsupported))
|
||||||
@@ -125,7 +125,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
var format = FormatTable.GetFormat(srcFormat);
|
var format = FormatTable.GetFormat(srcFormat);
|
||||||
|
|
||||||
if (!BufferFormatSupports(FormatFeatureFlags.FormatFeatureVertexBufferBit, srcFormat) ||
|
if (!BufferFormatSupports(FormatFeatureFlags.VertexBufferBit, srcFormat) ||
|
||||||
(IsRGB16IntFloat(srcFormat) && VulkanConfiguration.ForceRGB16IntFloatUnsupported))
|
(IsRGB16IntFloat(srcFormat) && VulkanConfiguration.ForceRGB16IntFloatUnsupported))
|
||||||
{
|
{
|
||||||
// The format is not supported. Can we convert it to an alternative format?
|
// The format is not supported. Can we convert it to an alternative format?
|
||||||
|
@@ -190,14 +190,14 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
for (int index = 0; index < _colors.Length; index++)
|
for (int index = 0; index < _colors.Length; index++)
|
||||||
{
|
{
|
||||||
_colors[index].Storage.SetModification(
|
_colors[index].Storage.SetModification(
|
||||||
AccessFlags.AccessColorAttachmentWriteBit,
|
AccessFlags.ColorAttachmentWriteBit,
|
||||||
PipelineStageFlags.PipelineStageColorAttachmentOutputBit);
|
PipelineStageFlags.ColorAttachmentOutputBit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_depthStencil?.Storage.SetModification(
|
_depthStencil?.Storage.SetModification(
|
||||||
AccessFlags.AccessDepthStencilAttachmentWriteBit,
|
AccessFlags.DepthStencilAttachmentWriteBit,
|
||||||
PipelineStageFlags.PipelineStageColorAttachmentOutputBit);
|
PipelineStageFlags.ColorAttachmentOutputBit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -349,8 +349,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
var srcBuffer = srcBufferAuto.Get(cbs, srcOffset, size).Value;
|
var srcBuffer = srcBufferAuto.Get(cbs, srcOffset, size).Value;
|
||||||
var dstBuffer = dstBufferAuto.Get(cbs, 0, newSize).Value;
|
var dstBuffer = dstBufferAuto.Get(cbs, 0, newSize).Value;
|
||||||
|
|
||||||
var access = supportsUint8 ? AccessFlags.AccessShaderWriteBit : AccessFlags.AccessTransferWriteBit;
|
var access = supportsUint8 ? AccessFlags.ShaderWriteBit : AccessFlags.TransferWriteBit;
|
||||||
var stage = supportsUint8 ? PipelineStageFlags.PipelineStageComputeShaderBit : PipelineStageFlags.PipelineStageTransferBit;
|
var stage = supportsUint8 ? PipelineStageFlags.ComputeShaderBit : PipelineStageFlags.TransferBit;
|
||||||
|
|
||||||
BufferHolder.InsertBufferBarrier(
|
BufferHolder.InsertBufferBarrier(
|
||||||
gd,
|
gd,
|
||||||
@@ -358,7 +358,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
dstBuffer,
|
dstBuffer,
|
||||||
BufferHolder.DefaultAccessFlags,
|
BufferHolder.DefaultAccessFlags,
|
||||||
access,
|
access,
|
||||||
PipelineStageFlags.PipelineStageAllCommandsBit,
|
PipelineStageFlags.AllCommandsBit,
|
||||||
stage,
|
stage,
|
||||||
0,
|
0,
|
||||||
newSize);
|
newSize);
|
||||||
@@ -420,7 +420,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
access,
|
access,
|
||||||
BufferHolder.DefaultAccessFlags,
|
BufferHolder.DefaultAccessFlags,
|
||||||
stage,
|
stage,
|
||||||
PipelineStageFlags.PipelineStageAllCommandsBit,
|
PipelineStageFlags.AllCommandsBit,
|
||||||
0,
|
0,
|
||||||
newSize);
|
newSize);
|
||||||
}
|
}
|
||||||
@@ -484,9 +484,9 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
cbs.CommandBuffer,
|
cbs.CommandBuffer,
|
||||||
dstBuffer,
|
dstBuffer,
|
||||||
BufferHolder.DefaultAccessFlags,
|
BufferHolder.DefaultAccessFlags,
|
||||||
AccessFlags.AccessTransferWriteBit,
|
AccessFlags.TransferWriteBit,
|
||||||
PipelineStageFlags.PipelineStageAllCommandsBit,
|
PipelineStageFlags.AllCommandsBit,
|
||||||
PipelineStageFlags.PipelineStageTransferBit,
|
PipelineStageFlags.TransferBit,
|
||||||
0,
|
0,
|
||||||
convertedCount * outputIndexSize);
|
convertedCount * outputIndexSize);
|
||||||
|
|
||||||
@@ -499,10 +499,10 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
gd,
|
gd,
|
||||||
cbs.CommandBuffer,
|
cbs.CommandBuffer,
|
||||||
dstBuffer,
|
dstBuffer,
|
||||||
AccessFlags.AccessTransferWriteBit,
|
AccessFlags.TransferWriteBit,
|
||||||
BufferHolder.DefaultAccessFlags,
|
BufferHolder.DefaultAccessFlags,
|
||||||
PipelineStageFlags.PipelineStageTransferBit,
|
PipelineStageFlags.TransferBit,
|
||||||
PipelineStageFlags.PipelineStageAllCommandsBit,
|
PipelineStageFlags.AllCommandsBit,
|
||||||
0,
|
0,
|
||||||
convertedCount * outputIndexSize);
|
convertedCount * outputIndexSize);
|
||||||
}
|
}
|
||||||
@@ -548,10 +548,10 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
cbs.CommandBuffer,
|
cbs.CommandBuffer,
|
||||||
src.GetImage().Get(cbs).Value,
|
src.GetImage().Get(cbs).Value,
|
||||||
TextureStorage.DefaultAccessMask,
|
TextureStorage.DefaultAccessMask,
|
||||||
AccessFlags.AccessShaderReadBit,
|
AccessFlags.ShaderReadBit,
|
||||||
PipelineStageFlags.PipelineStageAllCommandsBit,
|
PipelineStageFlags.AllCommandsBit,
|
||||||
PipelineStageFlags.PipelineStageComputeShaderBit,
|
PipelineStageFlags.ComputeShaderBit,
|
||||||
ImageAspectFlags.ImageAspectColorBit,
|
ImageAspectFlags.ColorBit,
|
||||||
src.FirstLayer + srcLayer,
|
src.FirstLayer + srcLayer,
|
||||||
src.FirstLevel,
|
src.FirstLevel,
|
||||||
depth,
|
depth,
|
||||||
@@ -610,11 +610,11 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
gd.Api,
|
gd.Api,
|
||||||
cbs.CommandBuffer,
|
cbs.CommandBuffer,
|
||||||
dst.GetImage().Get(cbs).Value,
|
dst.GetImage().Get(cbs).Value,
|
||||||
AccessFlags.AccessShaderWriteBit,
|
AccessFlags.ShaderWriteBit,
|
||||||
TextureStorage.DefaultAccessMask,
|
TextureStorage.DefaultAccessMask,
|
||||||
PipelineStageFlags.PipelineStageComputeShaderBit,
|
PipelineStageFlags.ComputeShaderBit,
|
||||||
PipelineStageFlags.PipelineStageAllCommandsBit,
|
PipelineStageFlags.AllCommandsBit,
|
||||||
ImageAspectFlags.ImageAspectColorBit,
|
ImageAspectFlags.ColorBit,
|
||||||
dst.FirstLayer + dstLayer,
|
dst.FirstLayer + dstLayer,
|
||||||
dst.FirstLevel,
|
dst.FirstLevel,
|
||||||
depth,
|
depth,
|
||||||
@@ -770,9 +770,9 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
cbs.CommandBuffer,
|
cbs.CommandBuffer,
|
||||||
srcIndirectBuffer.GetBuffer().Get(cbs, srcIndirectBufferOffset, indirectDataSize).Value,
|
srcIndirectBuffer.GetBuffer().Get(cbs, srcIndirectBufferOffset, indirectDataSize).Value,
|
||||||
BufferHolder.DefaultAccessFlags,
|
BufferHolder.DefaultAccessFlags,
|
||||||
AccessFlags.AccessShaderReadBit,
|
AccessFlags.ShaderReadBit,
|
||||||
PipelineStageFlags.PipelineStageAllCommandsBit,
|
PipelineStageFlags.AllCommandsBit,
|
||||||
PipelineStageFlags.PipelineStageComputeShaderBit,
|
PipelineStageFlags.ComputeShaderBit,
|
||||||
srcIndirectBufferOffset,
|
srcIndirectBufferOffset,
|
||||||
indirectDataSize);
|
indirectDataSize);
|
||||||
|
|
||||||
@@ -786,10 +786,10 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
gd,
|
gd,
|
||||||
cbs.CommandBuffer,
|
cbs.CommandBuffer,
|
||||||
patternBufferAuto.Get(cbs, ParamsIndirectDispatchOffset, ParamsIndirectDispatchSize).Value,
|
patternBufferAuto.Get(cbs, ParamsIndirectDispatchOffset, ParamsIndirectDispatchSize).Value,
|
||||||
AccessFlags.AccessShaderWriteBit,
|
AccessFlags.ShaderWriteBit,
|
||||||
AccessFlags.AccessIndirectCommandReadBit,
|
AccessFlags.IndirectCommandReadBit,
|
||||||
PipelineStageFlags.PipelineStageComputeShaderBit,
|
PipelineStageFlags.ComputeShaderBit,
|
||||||
PipelineStageFlags.PipelineStageDrawIndirectBit,
|
PipelineStageFlags.DrawIndirectBit,
|
||||||
ParamsIndirectDispatchOffset,
|
ParamsIndirectDispatchOffset,
|
||||||
ParamsIndirectDispatchSize);
|
ParamsIndirectDispatchSize);
|
||||||
|
|
||||||
@@ -798,9 +798,9 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
cbs.CommandBuffer,
|
cbs.CommandBuffer,
|
||||||
dstBuffer,
|
dstBuffer,
|
||||||
BufferHolder.DefaultAccessFlags,
|
BufferHolder.DefaultAccessFlags,
|
||||||
AccessFlags.AccessTransferWriteBit,
|
AccessFlags.TransferWriteBit,
|
||||||
PipelineStageFlags.PipelineStageAllCommandsBit,
|
PipelineStageFlags.AllCommandsBit,
|
||||||
PipelineStageFlags.PipelineStageTransferBit,
|
PipelineStageFlags.TransferBit,
|
||||||
0,
|
0,
|
||||||
convertedCount * outputIndexSize);
|
convertedCount * outputIndexSize);
|
||||||
|
|
||||||
@@ -814,10 +814,10 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
gd,
|
gd,
|
||||||
cbs.CommandBuffer,
|
cbs.CommandBuffer,
|
||||||
dstBuffer,
|
dstBuffer,
|
||||||
AccessFlags.AccessTransferWriteBit,
|
AccessFlags.TransferWriteBit,
|
||||||
BufferHolder.DefaultAccessFlags,
|
BufferHolder.DefaultAccessFlags,
|
||||||
PipelineStageFlags.PipelineStageTransferBit,
|
PipelineStageFlags.TransferBit,
|
||||||
PipelineStageFlags.PipelineStageAllCommandsBit,
|
PipelineStageFlags.AllCommandsBit,
|
||||||
0,
|
0,
|
||||||
convertedCount * outputIndexSize);
|
convertedCount * outputIndexSize);
|
||||||
|
|
||||||
|
@@ -33,7 +33,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
return default;
|
return default;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool map = flags.HasFlag(MemoryPropertyFlags.MemoryPropertyHostVisibleBit);
|
bool map = flags.HasFlag(MemoryPropertyFlags.HostVisibleBit);
|
||||||
return Allocate(memoryTypeIndex, requirements.Size, requirements.Alignment, map);
|
return Allocate(memoryTypeIndex, requirements.Size, requirements.Alignment, map);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -128,14 +128,14 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
MemoryBarrier memoryBarrier = new MemoryBarrier()
|
MemoryBarrier memoryBarrier = new MemoryBarrier()
|
||||||
{
|
{
|
||||||
SType = StructureType.MemoryBarrier,
|
SType = StructureType.MemoryBarrier,
|
||||||
SrcAccessMask = AccessFlags.AccessMemoryReadBit | AccessFlags.AccessMemoryWriteBit,
|
SrcAccessMask = AccessFlags.MemoryReadBit | AccessFlags.MemoryWriteBit,
|
||||||
DstAccessMask = AccessFlags.AccessMemoryReadBit | AccessFlags.AccessMemoryWriteBit
|
DstAccessMask = AccessFlags.MemoryReadBit | AccessFlags.MemoryWriteBit
|
||||||
};
|
};
|
||||||
|
|
||||||
Gd.Api.CmdPipelineBarrier(
|
Gd.Api.CmdPipelineBarrier(
|
||||||
CommandBuffer,
|
CommandBuffer,
|
||||||
PipelineStageFlags.PipelineStageFragmentShaderBit,
|
PipelineStageFlags.FragmentShaderBit,
|
||||||
PipelineStageFlags.PipelineStageFragmentShaderBit,
|
PipelineStageFlags.FragmentShaderBit,
|
||||||
0,
|
0,
|
||||||
1,
|
1,
|
||||||
memoryBarrier,
|
memoryBarrier,
|
||||||
@@ -161,9 +161,9 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
Cbs.CommandBuffer,
|
Cbs.CommandBuffer,
|
||||||
dst,
|
dst,
|
||||||
BufferHolder.DefaultAccessFlags,
|
BufferHolder.DefaultAccessFlags,
|
||||||
AccessFlags.AccessTransferWriteBit,
|
AccessFlags.TransferWriteBit,
|
||||||
PipelineStageFlags.PipelineStageAllCommandsBit,
|
PipelineStageFlags.AllCommandsBit,
|
||||||
PipelineStageFlags.PipelineStageTransferBit,
|
PipelineStageFlags.TransferBit,
|
||||||
offset,
|
offset,
|
||||||
size);
|
size);
|
||||||
|
|
||||||
@@ -173,10 +173,10 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
Gd,
|
Gd,
|
||||||
Cbs.CommandBuffer,
|
Cbs.CommandBuffer,
|
||||||
dst,
|
dst,
|
||||||
AccessFlags.AccessTransferWriteBit,
|
AccessFlags.TransferWriteBit,
|
||||||
BufferHolder.DefaultAccessFlags,
|
BufferHolder.DefaultAccessFlags,
|
||||||
PipelineStageFlags.PipelineStageTransferBit,
|
PipelineStageFlags.TransferBit,
|
||||||
PipelineStageFlags.PipelineStageAllCommandsBit,
|
PipelineStageFlags.AllCommandsBit,
|
||||||
offset,
|
offset,
|
||||||
size);
|
size);
|
||||||
}
|
}
|
||||||
@@ -196,7 +196,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
BeginRenderPass();
|
BeginRenderPass();
|
||||||
|
|
||||||
var clearValue = new ClearValue(new ClearColorValue(color.Red, color.Green, color.Blue, color.Alpha));
|
var clearValue = new ClearValue(new ClearColorValue(color.Red, color.Green, color.Blue, color.Alpha));
|
||||||
var attachment = new ClearAttachment(ImageAspectFlags.ImageAspectColorBit, (uint)index, clearValue);
|
var attachment = new ClearAttachment(ImageAspectFlags.ColorBit, (uint)index, clearValue);
|
||||||
var clearRect = FramebufferParams.GetClearRect(ClearScissor, layer, layerCount);
|
var clearRect = FramebufferParams.GetClearRect(ClearScissor, layer, layerCount);
|
||||||
|
|
||||||
Gd.Api.CmdClearAttachments(CommandBuffer, 1, &attachment, 1, &clearRect);
|
Gd.Api.CmdClearAttachments(CommandBuffer, 1, &attachment, 1, &clearRect);
|
||||||
@@ -219,11 +219,11 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
BeginRenderPass();
|
BeginRenderPass();
|
||||||
|
|
||||||
var clearValue = new ClearValue(null, new ClearDepthStencilValue(depthValue, (uint)stencilValue));
|
var clearValue = new ClearValue(null, new ClearDepthStencilValue(depthValue, (uint)stencilValue));
|
||||||
var flags = depthMask ? ImageAspectFlags.ImageAspectDepthBit : 0;
|
var flags = depthMask ? ImageAspectFlags.DepthBit : 0;
|
||||||
|
|
||||||
if (stencilMask != 0)
|
if (stencilMask != 0)
|
||||||
{
|
{
|
||||||
flags |= ImageAspectFlags.ImageAspectStencilBit;
|
flags |= ImageAspectFlags.StencilBit;
|
||||||
}
|
}
|
||||||
|
|
||||||
var attachment = new ClearAttachment(flags, 0, clearValue);
|
var attachment = new ClearAttachment(flags, 0, clearValue);
|
||||||
@@ -238,13 +238,13 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
SType = StructureType.MemoryBarrier,
|
SType = StructureType.MemoryBarrier,
|
||||||
SrcAccessMask = BufferHolder.DefaultAccessFlags,
|
SrcAccessMask = BufferHolder.DefaultAccessFlags,
|
||||||
DstAccessMask = AccessFlags.AccessIndirectCommandReadBit
|
DstAccessMask = AccessFlags.IndirectCommandReadBit
|
||||||
};
|
};
|
||||||
|
|
||||||
Gd.Api.CmdPipelineBarrier(
|
Gd.Api.CmdPipelineBarrier(
|
||||||
CommandBuffer,
|
CommandBuffer,
|
||||||
PipelineStageFlags.PipelineStageAllCommandsBit,
|
PipelineStageFlags.AllCommandsBit,
|
||||||
PipelineStageFlags.PipelineStageDrawIndirectBit,
|
PipelineStageFlags.DrawIndirectBit,
|
||||||
0,
|
0,
|
||||||
1,
|
1,
|
||||||
memoryBarrier,
|
memoryBarrier,
|
||||||
@@ -624,7 +624,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
var oldViewports = DynamicState.Viewports;
|
var oldViewports = DynamicState.Viewports;
|
||||||
var oldViewportsCount = _newState.ViewportsCount;
|
var oldViewportsCount = _newState.ViewportsCount;
|
||||||
|
|
||||||
_newState.CullMode = CullModeFlags.CullModeNone;
|
_newState.CullMode = CullModeFlags.None;
|
||||||
_newState.StencilTestEnable = false;
|
_newState.StencilTestEnable = false;
|
||||||
_newState.DepthTestEnable = false;
|
_newState.DepthTestEnable = false;
|
||||||
_newState.DepthWriteEnable = false;
|
_newState.DepthWriteEnable = false;
|
||||||
@@ -737,7 +737,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
public void SetFaceCulling(bool enable, Face face)
|
public void SetFaceCulling(bool enable, Face face)
|
||||||
{
|
{
|
||||||
_newState.CullMode = enable ? face.Convert() : CullModeFlags.CullModeNone;
|
_newState.CullMode = enable ? face.Convert() : CullModeFlags.None;
|
||||||
SignalStateChange();
|
SignalStateChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1200,14 +1200,14 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
MemoryBarrier memoryBarrier = new MemoryBarrier()
|
MemoryBarrier memoryBarrier = new MemoryBarrier()
|
||||||
{
|
{
|
||||||
SType = StructureType.MemoryBarrier,
|
SType = StructureType.MemoryBarrier,
|
||||||
SrcAccessMask = AccessFlags.AccessMemoryReadBit | AccessFlags.AccessMemoryWriteBit,
|
SrcAccessMask = AccessFlags.MemoryReadBit | AccessFlags.MemoryWriteBit,
|
||||||
DstAccessMask = AccessFlags.AccessMemoryReadBit | AccessFlags.AccessMemoryWriteBit
|
DstAccessMask = AccessFlags.MemoryReadBit | AccessFlags.MemoryWriteBit
|
||||||
};
|
};
|
||||||
|
|
||||||
Gd.Api.CmdPipelineBarrier(
|
Gd.Api.CmdPipelineBarrier(
|
||||||
CommandBuffer,
|
CommandBuffer,
|
||||||
PipelineStageFlags.PipelineStageFragmentShaderBit,
|
PipelineStageFlags.FragmentShaderBit,
|
||||||
PipelineStageFlags.PipelineStageFragmentShaderBit,
|
PipelineStageFlags.FragmentShaderBit,
|
||||||
0,
|
0,
|
||||||
1,
|
1,
|
||||||
memoryBarrier,
|
memoryBarrier,
|
||||||
|
@@ -6,8 +6,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
static class PipelineConverter
|
static class PipelineConverter
|
||||||
{
|
{
|
||||||
private const AccessFlags SubpassSrcAccessMask = AccessFlags.AccessMemoryReadBit | AccessFlags.AccessMemoryWriteBit | AccessFlags.AccessColorAttachmentWriteBit;
|
private const AccessFlags SubpassSrcAccessMask = AccessFlags.MemoryReadBit | AccessFlags.MemoryWriteBit | AccessFlags.ColorAttachmentWriteBit;
|
||||||
private const AccessFlags SubpassDstAccessMask = AccessFlags.AccessMemoryReadBit | AccessFlags.AccessMemoryWriteBit | AccessFlags.AccessShaderReadBit;
|
private const AccessFlags SubpassDstAccessMask = AccessFlags.MemoryReadBit | AccessFlags.MemoryWriteBit | AccessFlags.ShaderReadBit;
|
||||||
|
|
||||||
public static unsafe DisposableRenderPass ToRenderPass(this ProgramPipelineState state, VulkanRenderer gd, Device device)
|
public static unsafe DisposableRenderPass ToRenderPass(this ProgramPipelineState state, VulkanRenderer gd, Device device)
|
||||||
{
|
{
|
||||||
@@ -129,8 +129,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
return new SubpassDependency(
|
return new SubpassDependency(
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
PipelineStageFlags.PipelineStageAllGraphicsBit,
|
PipelineStageFlags.AllGraphicsBit,
|
||||||
PipelineStageFlags.PipelineStageAllGraphicsBit,
|
PipelineStageFlags.AllGraphicsBit,
|
||||||
SubpassSrcAccessMask,
|
SubpassSrcAccessMask,
|
||||||
SubpassDstAccessMask,
|
SubpassDstAccessMask,
|
||||||
0);
|
0);
|
||||||
@@ -143,8 +143,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
null,
|
null,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
PipelineStageFlags.PipelineStageAllGraphicsBit,
|
PipelineStageFlags.AllGraphicsBit,
|
||||||
PipelineStageFlags.PipelineStageAllGraphicsBit,
|
PipelineStageFlags.AllGraphicsBit,
|
||||||
SubpassSrcAccessMask,
|
SubpassSrcAccessMask,
|
||||||
SubpassDstAccessMask,
|
SubpassDstAccessMask,
|
||||||
0);
|
0);
|
||||||
@@ -157,7 +157,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
// It is assumed that Dynamic State is enabled when this conversion is used.
|
// It is assumed that Dynamic State is enabled when this conversion is used.
|
||||||
|
|
||||||
pipeline.CullMode = state.CullEnable ? state.CullMode.Convert() : CullModeFlags.CullModeNone;
|
pipeline.CullMode = state.CullEnable ? state.CullMode.Convert() : CullModeFlags.None;
|
||||||
|
|
||||||
pipeline.DepthBoundsTestEnable = false; // Not implemented.
|
pipeline.DepthBoundsTestEnable = false; // Not implemented.
|
||||||
|
|
||||||
|
@@ -145,12 +145,12 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
private void RecordStencilMasks(Vk api, CommandBuffer commandBuffer)
|
private void RecordStencilMasks(Vk api, CommandBuffer commandBuffer)
|
||||||
{
|
{
|
||||||
api.CmdSetStencilCompareMask(commandBuffer, StencilFaceFlags.StencilFaceBackBit, _backCompareMask);
|
api.CmdSetStencilCompareMask(commandBuffer, StencilFaceFlags.FaceBackBit, _backCompareMask);
|
||||||
api.CmdSetStencilWriteMask(commandBuffer, StencilFaceFlags.StencilFaceBackBit, _backWriteMask);
|
api.CmdSetStencilWriteMask(commandBuffer, StencilFaceFlags.FaceBackBit, _backWriteMask);
|
||||||
api.CmdSetStencilReference(commandBuffer, StencilFaceFlags.StencilFaceBackBit, _backReference);
|
api.CmdSetStencilReference(commandBuffer, StencilFaceFlags.FaceBackBit, _backReference);
|
||||||
api.CmdSetStencilCompareMask(commandBuffer, StencilFaceFlags.StencilFaceFrontBit, _frontCompareMask);
|
api.CmdSetStencilCompareMask(commandBuffer, StencilFaceFlags.FaceFrontBit, _frontCompareMask);
|
||||||
api.CmdSetStencilWriteMask(commandBuffer, StencilFaceFlags.StencilFaceFrontBit, _frontWriteMask);
|
api.CmdSetStencilWriteMask(commandBuffer, StencilFaceFlags.FaceFrontBit, _frontWriteMask);
|
||||||
api.CmdSetStencilReference(commandBuffer, StencilFaceFlags.StencilFaceFrontBit, _frontReference);
|
api.CmdSetStencilReference(commandBuffer, StencilFaceFlags.FaceFrontBit, _frontReference);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RecordViewport(Vk api, CommandBuffer commandBuffer)
|
private void RecordViewport(Vk api, CommandBuffer commandBuffer)
|
||||||
|
@@ -125,7 +125,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
if (Gd.Capabilities.SupportsConditionalRendering)
|
if (Gd.Capabilities.SupportsConditionalRendering)
|
||||||
{
|
{
|
||||||
var buffer = evt.GetBuffer().Get(Cbs, 0, sizeof(long)).Value;
|
var buffer = evt.GetBuffer().Get(Cbs, 0, sizeof(long)).Value;
|
||||||
var flags = isEqual ? ConditionalRenderingFlagsEXT.ConditionalRenderingInvertedBitExt : 0;
|
var flags = isEqual ? ConditionalRenderingFlagsEXT.InvertedBitExt : 0;
|
||||||
|
|
||||||
var conditionalRenderingBeginInfo = new ConditionalRenderingBeginInfoEXT()
|
var conditionalRenderingBeginInfo = new ConditionalRenderingBeginInfoEXT()
|
||||||
{
|
{
|
||||||
|
@@ -8,9 +8,9 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
static class PipelineLayoutFactory
|
static class PipelineLayoutFactory
|
||||||
{
|
{
|
||||||
private const ShaderStageFlags SupportBufferStages =
|
private const ShaderStageFlags SupportBufferStages =
|
||||||
ShaderStageFlags.ShaderStageVertexBit |
|
ShaderStageFlags.VertexBit |
|
||||||
ShaderStageFlags.ShaderStageFragmentBit |
|
ShaderStageFlags.FragmentBit |
|
||||||
ShaderStageFlags.ShaderStageComputeBit;
|
ShaderStageFlags.ComputeBit;
|
||||||
|
|
||||||
public static unsafe DescriptorSetLayout[] Create(VulkanRenderer gd, Device device, uint stages, bool usePd, out PipelineLayout layout)
|
public static unsafe DescriptorSetLayout[] Create(VulkanRenderer gd, Device device, uint stages, bool usePd, out PipelineLayout layout)
|
||||||
{
|
{
|
||||||
@@ -42,11 +42,11 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
var stageFlags = stage switch
|
var stageFlags = stage switch
|
||||||
{
|
{
|
||||||
1 => ShaderStageFlags.ShaderStageFragmentBit,
|
1 => ShaderStageFlags.FragmentBit,
|
||||||
2 => ShaderStageFlags.ShaderStageGeometryBit,
|
2 => ShaderStageFlags.GeometryBit,
|
||||||
3 => ShaderStageFlags.ShaderStageTessellationControlBit,
|
3 => ShaderStageFlags.TessellationControlBit,
|
||||||
4 => ShaderStageFlags.ShaderStageTessellationEvaluationBit,
|
4 => ShaderStageFlags.TessellationEvaluationBit,
|
||||||
_ => ShaderStageFlags.ShaderStageVertexBit | ShaderStageFlags.ShaderStageComputeBit
|
_ => ShaderStageFlags.VertexBit | ShaderStageFlags.ComputeBit
|
||||||
};
|
};
|
||||||
|
|
||||||
void Set(DescriptorSetLayoutBinding* bindings, int maxPerStage, DescriptorType type, int start, int skip)
|
void Set(DescriptorSetLayoutBinding* bindings, int maxPerStage, DescriptorType type, int start, int skip)
|
||||||
@@ -93,7 +93,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
SType = StructureType.DescriptorSetLayoutCreateInfo,
|
SType = StructureType.DescriptorSetLayoutCreateInfo,
|
||||||
PBindings = uLayoutBindings,
|
PBindings = uLayoutBindings,
|
||||||
BindingCount = (uint)uCount,
|
BindingCount = (uint)uCount,
|
||||||
Flags = usePd ? DescriptorSetLayoutCreateFlags.DescriptorSetLayoutCreatePushDescriptorBitKhr : 0
|
Flags = usePd ? DescriptorSetLayoutCreateFlags.PushDescriptorBitKhr : 0
|
||||||
};
|
};
|
||||||
|
|
||||||
var sDescriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo()
|
var sDescriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo()
|
||||||
|
@@ -41,7 +41,7 @@ namespace Ryujinx.Graphics.Vulkan.Queries
|
|||||||
if (_isSupported)
|
if (_isSupported)
|
||||||
{
|
{
|
||||||
QueryPipelineStatisticFlags flags = type == CounterType.PrimitivesGenerated ?
|
QueryPipelineStatisticFlags flags = type == CounterType.PrimitivesGenerated ?
|
||||||
QueryPipelineStatisticFlags.QueryPipelineStatisticGeometryShaderPrimitivesBit : 0;
|
QueryPipelineStatisticFlags.GeometryShaderPrimitivesBit : 0;
|
||||||
|
|
||||||
var queryPoolCreateInfo = new QueryPoolCreateInfo()
|
var queryPoolCreateInfo = new QueryPoolCreateInfo()
|
||||||
{
|
{
|
||||||
@@ -175,11 +175,11 @@ namespace Ryujinx.Graphics.Vulkan.Queries
|
|||||||
{
|
{
|
||||||
var buffer = _buffer.GetBuffer(cbs.CommandBuffer, true).Get(cbs, 0, sizeof(long)).Value;
|
var buffer = _buffer.GetBuffer(cbs.CommandBuffer, true).Get(cbs, 0, sizeof(long)).Value;
|
||||||
|
|
||||||
QueryResultFlags flags = QueryResultFlags.QueryResultWaitBit;
|
QueryResultFlags flags = QueryResultFlags.ResultWaitBit;
|
||||||
|
|
||||||
if (!_result32Bit)
|
if (!_result32Bit)
|
||||||
{
|
{
|
||||||
flags |= QueryResultFlags.QueryResult64Bit;
|
flags |= QueryResultFlags.Result64Bit;
|
||||||
}
|
}
|
||||||
|
|
||||||
_api.CmdCopyQueryPoolResults(
|
_api.CmdCopyQueryPoolResults(
|
||||||
|
@@ -15,9 +15,9 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="OpenTK.Windowing.GraphicsLibraryFramework" Version="4.7.5" />
|
<PackageReference Include="OpenTK.Windowing.GraphicsLibraryFramework" Version="4.7.5" />
|
||||||
<PackageReference Include="shaderc.net" Version="0.1.0" />
|
<PackageReference Include="shaderc.net" Version="0.1.0" />
|
||||||
<PackageReference Include="Silk.NET.Vulkan" Version="2.10.1" />
|
<PackageReference Include="Silk.NET.Vulkan" Version="2.16.0" />
|
||||||
<PackageReference Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.10.1" />
|
<PackageReference Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.16.0" />
|
||||||
<PackageReference Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.10.1" />
|
<PackageReference Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.16.0" />
|
||||||
<PackageReference Include="System.IO.FileSystem.Primitives" Version="4.3.0" />
|
<PackageReference Include="System.IO.FileSystem.Primitives" Version="4.3.0" />
|
||||||
<PackageReference Include="System.Net.NameResolution" Version="4.3.0" />
|
<PackageReference Include="System.Net.NameResolution" Version="4.3.0" />
|
||||||
<PackageReference Include="System.Threading.ThreadPool" Version="4.3.0" />
|
<PackageReference Include="System.Threading.ThreadPool" Version="4.3.0" />
|
||||||
|
@@ -82,14 +82,14 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
stages |= 1u << shader.StageFlags switch
|
stages |= 1u << shader.StageFlags switch
|
||||||
{
|
{
|
||||||
ShaderStageFlags.ShaderStageFragmentBit => 1,
|
ShaderStageFlags.FragmentBit => 1,
|
||||||
ShaderStageFlags.ShaderStageGeometryBit => 2,
|
ShaderStageFlags.GeometryBit => 2,
|
||||||
ShaderStageFlags.ShaderStageTessellationControlBit => 3,
|
ShaderStageFlags.TessellationControlBit => 3,
|
||||||
ShaderStageFlags.ShaderStageTessellationEvaluationBit => 4,
|
ShaderStageFlags.TessellationEvaluationBit => 4,
|
||||||
_ => 0
|
_ => 0
|
||||||
};
|
};
|
||||||
|
|
||||||
if (shader.StageFlags == ShaderStageFlags.ShaderStageComputeBit)
|
if (shader.StageFlags == ShaderStageFlags.ComputeBit)
|
||||||
{
|
{
|
||||||
IsCompute = true;
|
IsCompute = true;
|
||||||
}
|
}
|
||||||
|
@@ -11,6 +11,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
public ulong ID;
|
public ulong ID;
|
||||||
public MultiFenceHolder Waitable;
|
public MultiFenceHolder Waitable;
|
||||||
|
public bool Signalled;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ulong _firstHandle = 0;
|
private ulong _firstHandle = 0;
|
||||||
@@ -45,6 +46,37 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ulong GetCurrent()
|
||||||
|
{
|
||||||
|
lock (_handles)
|
||||||
|
{
|
||||||
|
ulong lastHandle = _firstHandle;
|
||||||
|
|
||||||
|
foreach (SyncHandle handle in _handles)
|
||||||
|
{
|
||||||
|
lock (handle)
|
||||||
|
{
|
||||||
|
if (handle.Waitable == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (handle.ID > lastHandle)
|
||||||
|
{
|
||||||
|
bool signaled = handle.Signalled || handle.Waitable.WaitForFences(_gd.Api, _device, 0);
|
||||||
|
if (signaled)
|
||||||
|
{
|
||||||
|
lastHandle = handle.ID;
|
||||||
|
handle.Signalled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return lastHandle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void Wait(ulong id)
|
public void Wait(ulong id)
|
||||||
{
|
{
|
||||||
SyncHandle result = null;
|
SyncHandle result = null;
|
||||||
@@ -75,11 +107,15 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool signaled = result.Waitable.WaitForFences(_gd.Api, _device, 1000000000);
|
bool signaled = result.Signalled || result.Waitable.WaitForFences(_gd.Api, _device, 1000000000);
|
||||||
if (!signaled)
|
if (!signaled)
|
||||||
{
|
{
|
||||||
Logger.Error?.PrintMsg(LogClass.Gpu, $"VK Sync Object {result.ID} failed to signal within 1000ms. Continuing...");
|
Logger.Error?.PrintMsg(LogClass.Gpu, $"VK Sync Object {result.ID} failed to signal within 1000ms. Continuing...");
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result.Signalled = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -60,9 +60,9 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
commandBuffer,
|
commandBuffer,
|
||||||
srcImage,
|
srcImage,
|
||||||
TextureStorage.DefaultAccessMask,
|
TextureStorage.DefaultAccessMask,
|
||||||
AccessFlags.AccessTransferReadBit,
|
AccessFlags.TransferReadBit,
|
||||||
PipelineStageFlags.PipelineStageAllCommandsBit,
|
PipelineStageFlags.AllCommandsBit,
|
||||||
PipelineStageFlags.PipelineStageTransferBit,
|
PipelineStageFlags.TransferBit,
|
||||||
srcAspectFlags,
|
srcAspectFlags,
|
||||||
srcLayer,
|
srcLayer,
|
||||||
srcLevel,
|
srcLevel,
|
||||||
@@ -103,10 +103,10 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
api,
|
api,
|
||||||
commandBuffer,
|
commandBuffer,
|
||||||
dstImage,
|
dstImage,
|
||||||
AccessFlags.AccessTransferWriteBit,
|
AccessFlags.TransferWriteBit,
|
||||||
TextureStorage.DefaultAccessMask,
|
TextureStorage.DefaultAccessMask,
|
||||||
PipelineStageFlags.PipelineStageTransferBit,
|
PipelineStageFlags.TransferBit,
|
||||||
PipelineStageFlags.PipelineStageAllCommandsBit,
|
PipelineStageFlags.AllCommandsBit,
|
||||||
dstAspectFlags,
|
dstAspectFlags,
|
||||||
dstLayer,
|
dstLayer,
|
||||||
dstLevel,
|
dstLevel,
|
||||||
@@ -285,9 +285,9 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
commandBuffer,
|
commandBuffer,
|
||||||
srcImage,
|
srcImage,
|
||||||
TextureStorage.DefaultAccessMask,
|
TextureStorage.DefaultAccessMask,
|
||||||
AccessFlags.AccessTransferReadBit,
|
AccessFlags.TransferReadBit,
|
||||||
PipelineStageFlags.PipelineStageAllCommandsBit,
|
PipelineStageFlags.AllCommandsBit,
|
||||||
PipelineStageFlags.PipelineStageTransferBit,
|
PipelineStageFlags.TransferBit,
|
||||||
srcAspect,
|
srcAspect,
|
||||||
srcViewLayer + srcLayer,
|
srcViewLayer + srcLayer,
|
||||||
srcViewLevel + srcLevel,
|
srcViewLevel + srcLevel,
|
||||||
@@ -345,10 +345,10 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
api,
|
api,
|
||||||
commandBuffer,
|
commandBuffer,
|
||||||
dstImage,
|
dstImage,
|
||||||
AccessFlags.AccessTransferWriteBit,
|
AccessFlags.TransferWriteBit,
|
||||||
TextureStorage.DefaultAccessMask,
|
TextureStorage.DefaultAccessMask,
|
||||||
PipelineStageFlags.PipelineStageTransferBit,
|
PipelineStageFlags.TransferBit,
|
||||||
PipelineStageFlags.PipelineStageAllCommandsBit,
|
PipelineStageFlags.AllCommandsBit,
|
||||||
dstAspect,
|
dstAspect,
|
||||||
dstViewLayer + dstLayer,
|
dstViewLayer + dstLayer,
|
||||||
dstViewLevel + dstLevel,
|
dstViewLevel + dstLevel,
|
||||||
@@ -370,8 +370,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
SType = StructureType.SubpassDescriptionDepthStencilResolve,
|
SType = StructureType.SubpassDescriptionDepthStencilResolve,
|
||||||
PDepthStencilResolveAttachment = &dsResolveAttachmentReference,
|
PDepthStencilResolveAttachment = &dsResolveAttachmentReference,
|
||||||
DepthResolveMode = ResolveModeFlags.ResolveModeSampleZeroBit,
|
DepthResolveMode = ResolveModeFlags.SampleZeroBit,
|
||||||
StencilResolveMode = ResolveModeFlags.ResolveModeSampleZeroBit
|
StencilResolveMode = ResolveModeFlags.SampleZeroBit
|
||||||
};
|
};
|
||||||
|
|
||||||
var subpass = new SubpassDescription2()
|
var subpass = new SubpassDescription2()
|
||||||
|
@@ -12,22 +12,22 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
class TextureStorage : IDisposable
|
class TextureStorage : IDisposable
|
||||||
{
|
{
|
||||||
private const MemoryPropertyFlags DefaultImageMemoryFlags =
|
private const MemoryPropertyFlags DefaultImageMemoryFlags =
|
||||||
MemoryPropertyFlags.MemoryPropertyDeviceLocalBit;
|
MemoryPropertyFlags.DeviceLocalBit;
|
||||||
|
|
||||||
private const ImageUsageFlags DefaultUsageFlags =
|
private const ImageUsageFlags DefaultUsageFlags =
|
||||||
ImageUsageFlags.ImageUsageSampledBit |
|
ImageUsageFlags.SampledBit |
|
||||||
ImageUsageFlags.ImageUsageTransferSrcBit |
|
ImageUsageFlags.TransferSrcBit |
|
||||||
ImageUsageFlags.ImageUsageTransferDstBit;
|
ImageUsageFlags.TransferDstBit;
|
||||||
|
|
||||||
public const AccessFlags DefaultAccessMask =
|
public const AccessFlags DefaultAccessMask =
|
||||||
AccessFlags.AccessShaderReadBit |
|
AccessFlags.ShaderReadBit |
|
||||||
AccessFlags.AccessShaderWriteBit |
|
AccessFlags.ShaderWriteBit |
|
||||||
AccessFlags.AccessColorAttachmentReadBit |
|
AccessFlags.ColorAttachmentReadBit |
|
||||||
AccessFlags.AccessColorAttachmentWriteBit |
|
AccessFlags.ColorAttachmentWriteBit |
|
||||||
AccessFlags.AccessDepthStencilAttachmentReadBit |
|
AccessFlags.DepthStencilAttachmentReadBit |
|
||||||
AccessFlags.AccessDepthStencilAttachmentWriteBit |
|
AccessFlags.DepthStencilAttachmentWriteBit |
|
||||||
AccessFlags.AccessTransferReadBit |
|
AccessFlags.TransferReadBit |
|
||||||
AccessFlags.AccessTransferWriteBit;
|
AccessFlags.TransferWriteBit;
|
||||||
|
|
||||||
private readonly VulkanRenderer _gd;
|
private readonly VulkanRenderer _gd;
|
||||||
|
|
||||||
@@ -83,32 +83,32 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
if (info.Format.IsDepthOrStencil())
|
if (info.Format.IsDepthOrStencil())
|
||||||
{
|
{
|
||||||
usage |= ImageUsageFlags.ImageUsageDepthStencilAttachmentBit;
|
usage |= ImageUsageFlags.DepthStencilAttachmentBit;
|
||||||
}
|
}
|
||||||
else if (info.Format.IsRtColorCompatible())
|
else if (info.Format.IsRtColorCompatible())
|
||||||
{
|
{
|
||||||
usage |= ImageUsageFlags.ImageUsageColorAttachmentBit;
|
usage |= ImageUsageFlags.ColorAttachmentBit;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (info.Format.IsImageCompatible())
|
if (info.Format.IsImageCompatible())
|
||||||
{
|
{
|
||||||
usage |= ImageUsageFlags.ImageUsageStorageBit;
|
usage |= ImageUsageFlags.StorageBit;
|
||||||
}
|
}
|
||||||
|
|
||||||
var flags = ImageCreateFlags.ImageCreateMutableFormatBit;
|
var flags = ImageCreateFlags.CreateMutableFormatBit;
|
||||||
|
|
||||||
// This flag causes mipmapped texture arrays to break on AMD GCN, so for that copy dependencies are forced for aliasing as cube.
|
// This flag causes mipmapped texture arrays to break on AMD GCN, so for that copy dependencies are forced for aliasing as cube.
|
||||||
bool isCube = info.Target == Target.Cubemap || info.Target == Target.CubemapArray;
|
bool isCube = info.Target == Target.Cubemap || info.Target == Target.CubemapArray;
|
||||||
bool cubeCompatible = gd.IsAmdGcn ? isCube : (info.Width == info.Height && layers >= 6);
|
bool cubeCompatible = gd.IsAmdGcn ? isCube : (info.Width == info.Height && layers >= 6);
|
||||||
|
|
||||||
if (type == ImageType.ImageType2D && cubeCompatible)
|
if (type == ImageType.Type2D && cubeCompatible)
|
||||||
{
|
{
|
||||||
flags |= ImageCreateFlags.ImageCreateCubeCompatibleBit;
|
flags |= ImageCreateFlags.CreateCubeCompatibleBit;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type == ImageType.ImageType3D)
|
if (type == ImageType.Type3D)
|
||||||
{
|
{
|
||||||
flags |= ImageCreateFlags.ImageCreate2DArrayCompatibleBit;
|
flags |= ImageCreateFlags.Create2DArrayCompatibleBit;
|
||||||
}
|
}
|
||||||
|
|
||||||
var imageCreateInfo = new ImageCreateInfo()
|
var imageCreateInfo = new ImageCreateInfo()
|
||||||
@@ -290,8 +290,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
_gd.Api.CmdPipelineBarrier(
|
_gd.Api.CmdPipelineBarrier(
|
||||||
cbs.CommandBuffer,
|
cbs.CommandBuffer,
|
||||||
PipelineStageFlags.PipelineStageTopOfPipeBit,
|
PipelineStageFlags.TopOfPipeBit,
|
||||||
PipelineStageFlags.PipelineStageAllCommandsBit,
|
PipelineStageFlags.AllCommandsBit,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
null,
|
null,
|
||||||
@@ -308,9 +308,9 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
public static SampleCountFlags ConvertToSampleCountFlags(uint samples)
|
public static SampleCountFlags ConvertToSampleCountFlags(uint samples)
|
||||||
{
|
{
|
||||||
if (samples == 0 || samples > (uint)SampleCountFlags.SampleCount64Bit)
|
if (samples == 0 || samples > (uint)SampleCountFlags.Count64Bit)
|
||||||
{
|
{
|
||||||
return SampleCountFlags.SampleCount1Bit;
|
return SampleCountFlags.Count1Bit;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Round up to the nearest power of two.
|
// Round up to the nearest power of two.
|
||||||
@@ -428,7 +428,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
public void InsertBarrier(CommandBufferScoped cbs, AccessFlags dstAccessFlags, PipelineStageFlags dstStageFlags)
|
public void InsertBarrier(CommandBufferScoped cbs, AccessFlags dstAccessFlags, PipelineStageFlags dstStageFlags)
|
||||||
{
|
{
|
||||||
if (_lastModificationAccess != AccessFlags.AccessNoneKhr)
|
if (_lastModificationAccess != AccessFlags.NoneKhr)
|
||||||
{
|
{
|
||||||
ImageAspectFlags aspectFlags;
|
ImageAspectFlags aspectFlags;
|
||||||
|
|
||||||
@@ -436,20 +436,20 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
if (_info.Format == GAL.Format.S8Uint)
|
if (_info.Format == GAL.Format.S8Uint)
|
||||||
{
|
{
|
||||||
aspectFlags = ImageAspectFlags.ImageAspectStencilBit;
|
aspectFlags = ImageAspectFlags.StencilBit;
|
||||||
}
|
}
|
||||||
else if (_info.Format == GAL.Format.D16Unorm || _info.Format == GAL.Format.D32Float)
|
else if (_info.Format == GAL.Format.D16Unorm || _info.Format == GAL.Format.D32Float)
|
||||||
{
|
{
|
||||||
aspectFlags = ImageAspectFlags.ImageAspectDepthBit;
|
aspectFlags = ImageAspectFlags.DepthBit;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
aspectFlags = ImageAspectFlags.ImageAspectDepthBit | ImageAspectFlags.ImageAspectStencilBit;
|
aspectFlags = ImageAspectFlags.DepthBit | ImageAspectFlags.StencilBit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
aspectFlags = ImageAspectFlags.ImageAspectColorBit;
|
aspectFlags = ImageAspectFlags.ColorBit;
|
||||||
}
|
}
|
||||||
|
|
||||||
TextureView.InsertImageBarrier(
|
TextureView.InsertImageBarrier(
|
||||||
@@ -466,7 +466,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
_info.GetLayers(),
|
_info.GetLayers(),
|
||||||
_info.Levels);
|
_info.Levels);
|
||||||
|
|
||||||
_lastModificationAccess = AccessFlags.AccessNoneKhr;
|
_lastModificationAccess = AccessFlags.NoneKhr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -126,7 +126,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
subresourceRange = new ImageSubresourceRange(aspectFlags, (uint)firstLevel, levels, (uint)firstLayer, (uint)info.Depth);
|
subresourceRange = new ImageSubresourceRange(aspectFlags, (uint)firstLevel, levels, (uint)firstLayer, (uint)info.Depth);
|
||||||
|
|
||||||
_imageView2dArray = CreateImageView(identityComponentMapping, subresourceRange, ImageViewType.ImageViewType2DArray);
|
_imageView2dArray = CreateImageView(identityComponentMapping, subresourceRange, ImageViewType.Type2DArray);
|
||||||
}
|
}
|
||||||
|
|
||||||
Valid = true;
|
Valid = true;
|
||||||
@@ -322,8 +322,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (_gd.FormatCapabilities.OptimalFormatSupports(FormatFeatureFlags.FormatFeatureBlitSrcBit, srcFormat) &&
|
else if (_gd.FormatCapabilities.OptimalFormatSupports(FormatFeatureFlags.BlitSrcBit, srcFormat) &&
|
||||||
_gd.FormatCapabilities.OptimalFormatSupports(FormatFeatureFlags.FormatFeatureBlitDstBit, dstFormat))
|
_gd.FormatCapabilities.OptimalFormatSupports(FormatFeatureFlags.BlitDstBit, dstFormat))
|
||||||
{
|
{
|
||||||
TextureCopy.Blit(
|
TextureCopy.Blit(
|
||||||
_gd.Api,
|
_gd.Api,
|
||||||
@@ -402,8 +402,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
layers,
|
layers,
|
||||||
levels,
|
levels,
|
||||||
linearFilter,
|
linearFilter,
|
||||||
ImageAspectFlags.ImageAspectColorBit,
|
ImageAspectFlags.ColorBit,
|
||||||
ImageAspectFlags.ImageAspectColorBit);
|
ImageAspectFlags.ColorBit);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void BlitDepthStencilWithBuffer(
|
private static void BlitDepthStencilWithBuffer(
|
||||||
@@ -471,10 +471,10 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
gd,
|
gd,
|
||||||
cbs.CommandBuffer,
|
cbs.CommandBuffer,
|
||||||
srcTempBuffer.GetBuffer().Get(cbs, 0, srcSize).Value,
|
srcTempBuffer.GetBuffer().Get(cbs, 0, srcSize).Value,
|
||||||
AccessFlags.AccessTransferWriteBit,
|
AccessFlags.TransferWriteBit,
|
||||||
AccessFlags.AccessTransferReadBit,
|
AccessFlags.TransferReadBit,
|
||||||
PipelineStageFlags.PipelineStageTransferBit,
|
PipelineStageFlags.TransferBit,
|
||||||
PipelineStageFlags.PipelineStageTransferBit,
|
PipelineStageFlags.TransferBit,
|
||||||
0,
|
0,
|
||||||
srcSize);
|
srcSize);
|
||||||
|
|
||||||
@@ -498,10 +498,10 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
gd.Api,
|
gd.Api,
|
||||||
cbs.CommandBuffer,
|
cbs.CommandBuffer,
|
||||||
srcTemp.GetImage().Get(cbs).Value,
|
srcTemp.GetImage().Get(cbs).Value,
|
||||||
AccessFlags.AccessTransferWriteBit,
|
AccessFlags.TransferWriteBit,
|
||||||
AccessFlags.AccessTransferReadBit,
|
AccessFlags.TransferReadBit,
|
||||||
PipelineStageFlags.PipelineStageTransferBit,
|
PipelineStageFlags.TransferBit,
|
||||||
PipelineStageFlags.PipelineStageTransferBit,
|
PipelineStageFlags.TransferBit,
|
||||||
aspectFlags,
|
aspectFlags,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
@@ -531,10 +531,10 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
gd.Api,
|
gd.Api,
|
||||||
cbs.CommandBuffer,
|
cbs.CommandBuffer,
|
||||||
dstTemp.GetImage().Get(cbs).Value,
|
dstTemp.GetImage().Get(cbs).Value,
|
||||||
AccessFlags.AccessTransferWriteBit,
|
AccessFlags.TransferWriteBit,
|
||||||
AccessFlags.AccessTransferReadBit,
|
AccessFlags.TransferReadBit,
|
||||||
PipelineStageFlags.PipelineStageTransferBit,
|
PipelineStageFlags.TransferBit,
|
||||||
PipelineStageFlags.PipelineStageTransferBit,
|
PipelineStageFlags.TransferBit,
|
||||||
aspectFlags,
|
aspectFlags,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
@@ -561,10 +561,10 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
gd,
|
gd,
|
||||||
cbs.CommandBuffer,
|
cbs.CommandBuffer,
|
||||||
dstTempBuffer.GetBuffer().Get(cbs, 0, dstSize).Value,
|
dstTempBuffer.GetBuffer().Get(cbs, 0, dstSize).Value,
|
||||||
AccessFlags.AccessTransferWriteBit,
|
AccessFlags.TransferWriteBit,
|
||||||
AccessFlags.AccessTransferReadBit,
|
AccessFlags.TransferReadBit,
|
||||||
PipelineStageFlags.PipelineStageTransferBit,
|
PipelineStageFlags.TransferBit,
|
||||||
PipelineStageFlags.PipelineStageTransferBit,
|
PipelineStageFlags.TransferBit,
|
||||||
0,
|
0,
|
||||||
dstSize);
|
dstSize);
|
||||||
|
|
||||||
@@ -585,8 +585,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
false);
|
false);
|
||||||
}
|
}
|
||||||
|
|
||||||
SlowBlit(d32SrcStorage, d32DstStorage, ImageAspectFlags.ImageAspectDepthBit);
|
SlowBlit(d32SrcStorage, d32DstStorage, ImageAspectFlags.DepthBit);
|
||||||
SlowBlit(s8SrcStorage, s8DstStorage, ImageAspectFlags.ImageAspectStencilBit);
|
SlowBlit(s8SrcStorage, s8DstStorage, ImageAspectFlags.StencilBit);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static unsafe void InsertImageBarrier(
|
public static unsafe void InsertImageBarrier(
|
||||||
@@ -631,7 +631,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
private bool SupportsBlitFromD32FS8ToD32FAndS8()
|
private bool SupportsBlitFromD32FS8ToD32FAndS8()
|
||||||
{
|
{
|
||||||
var formatFeatureFlags = FormatFeatureFlags.FormatFeatureBlitSrcBit | FormatFeatureFlags.FormatFeatureBlitDstBit;
|
var formatFeatureFlags = FormatFeatureFlags.BlitSrcBit | FormatFeatureFlags.BlitDstBit;
|
||||||
return _gd.FormatCapabilities.OptimalFormatSupports(formatFeatureFlags, GAL.Format.D32Float) &&
|
return _gd.FormatCapabilities.OptimalFormatSupports(formatFeatureFlags, GAL.Format.D32Float) &&
|
||||||
_gd.FormatCapabilities.OptimalFormatSupports(formatFeatureFlags, GAL.Format.S8Uint);
|
_gd.FormatCapabilities.OptimalFormatSupports(formatFeatureFlags, GAL.Format.S8Uint);
|
||||||
}
|
}
|
||||||
@@ -903,9 +903,9 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
var aspectFlags = Info.Format.ConvertAspectFlags();
|
var aspectFlags = Info.Format.ConvertAspectFlags();
|
||||||
|
|
||||||
if (aspectFlags == (ImageAspectFlags.ImageAspectDepthBit | ImageAspectFlags.ImageAspectStencilBit))
|
if (aspectFlags == (ImageAspectFlags.DepthBit | ImageAspectFlags.StencilBit))
|
||||||
{
|
{
|
||||||
aspectFlags = ImageAspectFlags.ImageAspectDepthBit;
|
aspectFlags = ImageAspectFlags.DepthBit;
|
||||||
}
|
}
|
||||||
|
|
||||||
var sl = new ImageSubresourceLayers(
|
var sl = new ImageSubresourceLayers(
|
||||||
@@ -962,9 +962,9 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
var aspectFlags = Info.Format.ConvertAspectFlags();
|
var aspectFlags = Info.Format.ConvertAspectFlags();
|
||||||
|
|
||||||
if (aspectFlags == (ImageAspectFlags.ImageAspectDepthBit | ImageAspectFlags.ImageAspectStencilBit))
|
if (aspectFlags == (ImageAspectFlags.DepthBit | ImageAspectFlags.StencilBit))
|
||||||
{
|
{
|
||||||
aspectFlags = ImageAspectFlags.ImageAspectDepthBit;
|
aspectFlags = ImageAspectFlags.DepthBit;
|
||||||
}
|
}
|
||||||
|
|
||||||
var sl = new ImageSubresourceLayers(aspectFlags, (uint)(FirstLevel + dstLevel), (uint)(FirstLayer + dstLayer), 1);
|
var sl = new ImageSubresourceLayers(aspectFlags, (uint)(FirstLevel + dstLevel), (uint)(FirstLayer + dstLayer), 1);
|
||||||
|
@@ -11,9 +11,10 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
Unknown
|
Unknown
|
||||||
}
|
}
|
||||||
|
|
||||||
static class VendorUtils
|
static partial class VendorUtils
|
||||||
{
|
{
|
||||||
public static Regex AmdGcnRegex = new Regex(@"Radeon (((HD|R(5|7|9|X)) )?((M?[2-6]\d{2}(\D|$))|([7-8]\d{3}(\D|$))|Fury|Nano))|(Pro Duo)");
|
[GeneratedRegex("Radeon (((HD|R(5|7|9|X)) )?((M?[2-6]\\d{2}(\\D|$))|([7-8]\\d{3}(\\D|$))|Fury|Nano))|(Pro Duo)")]
|
||||||
|
public static partial Regex AmdGcnRegex();
|
||||||
|
|
||||||
public static Vendor FromId(uint id)
|
public static Vendor FromId(uint id)
|
||||||
{
|
{
|
||||||
|
@@ -37,7 +37,6 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
public static string[] RequiredExtensions { get; } = new string[]
|
public static string[] RequiredExtensions { get; } = new string[]
|
||||||
{
|
{
|
||||||
KhrSwapchain.ExtensionName,
|
KhrSwapchain.ExtensionName,
|
||||||
"VK_EXT_shader_subgroup_vote",
|
|
||||||
ExtTransformFeedback.ExtensionName
|
ExtTransformFeedback.ExtensionName
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -160,20 +159,20 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityErrorBitExt))
|
if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt))
|
||||||
{
|
{
|
||||||
Logger.Error?.Print(LogClass.Gpu, msg);
|
Logger.Error?.Print(LogClass.Gpu, msg);
|
||||||
//throw new Exception(msg);
|
//throw new Exception(msg);
|
||||||
}
|
}
|
||||||
else if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityWarningBitExt))
|
else if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.WarningBitExt))
|
||||||
{
|
{
|
||||||
Logger.Warning?.Print(LogClass.Gpu, msg);
|
Logger.Warning?.Print(LogClass.Gpu, msg);
|
||||||
}
|
}
|
||||||
else if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityInfoBitExt))
|
else if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.InfoBitExt))
|
||||||
{
|
{
|
||||||
Logger.Info?.Print(LogClass.Gpu, msg);
|
Logger.Info?.Print(LogClass.Gpu, msg);
|
||||||
}
|
}
|
||||||
else // if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityVerboseBitExt))
|
else // if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.VerboseBitExt))
|
||||||
{
|
{
|
||||||
Logger.Debug?.Print(LogClass.Gpu, msg);
|
Logger.Debug?.Print(LogClass.Gpu, msg);
|
||||||
}
|
}
|
||||||
@@ -318,7 +317,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
internal static uint FindSuitableQueueFamily(Vk api, PhysicalDevice physicalDevice, SurfaceKHR surface, out uint queueCount)
|
internal static uint FindSuitableQueueFamily(Vk api, PhysicalDevice physicalDevice, SurfaceKHR surface, out uint queueCount)
|
||||||
{
|
{
|
||||||
const QueueFlags RequiredFlags = QueueFlags.QueueGraphicsBit | QueueFlags.QueueComputeBit;
|
const QueueFlags RequiredFlags = QueueFlags.GraphicsBit | QueueFlags.ComputeBit;
|
||||||
|
|
||||||
var khrSurface = new KhrSurface(api.Context);
|
var khrSurface = new KhrSurface(api.Context);
|
||||||
|
|
||||||
@@ -562,24 +561,24 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
var filterLogType = logLevel switch
|
var filterLogType = logLevel switch
|
||||||
{
|
{
|
||||||
GraphicsDebugLevel.Error => DebugUtilsMessageTypeFlagsEXT.DebugUtilsMessageTypeValidationBitExt,
|
GraphicsDebugLevel.Error => DebugUtilsMessageTypeFlagsEXT.ValidationBitExt,
|
||||||
GraphicsDebugLevel.Slowdowns => DebugUtilsMessageTypeFlagsEXT.DebugUtilsMessageTypeValidationBitExt |
|
GraphicsDebugLevel.Slowdowns => DebugUtilsMessageTypeFlagsEXT.ValidationBitExt |
|
||||||
DebugUtilsMessageTypeFlagsEXT.DebugUtilsMessageTypePerformanceBitExt,
|
DebugUtilsMessageTypeFlagsEXT.PerformanceBitExt,
|
||||||
GraphicsDebugLevel.All => DebugUtilsMessageTypeFlagsEXT.DebugUtilsMessageTypeGeneralBitExt |
|
GraphicsDebugLevel.All => DebugUtilsMessageTypeFlagsEXT.GeneralBitExt |
|
||||||
DebugUtilsMessageTypeFlagsEXT.DebugUtilsMessageTypeValidationBitExt |
|
DebugUtilsMessageTypeFlagsEXT.ValidationBitExt |
|
||||||
DebugUtilsMessageTypeFlagsEXT.DebugUtilsMessageTypePerformanceBitExt,
|
DebugUtilsMessageTypeFlagsEXT.PerformanceBitExt,
|
||||||
_ => throw new ArgumentException($"Invalid log level \"{logLevel}\".")
|
_ => throw new ArgumentException($"Invalid log level \"{logLevel}\".")
|
||||||
};
|
};
|
||||||
|
|
||||||
var filterLogSeverity = logLevel switch
|
var filterLogSeverity = logLevel switch
|
||||||
{
|
{
|
||||||
GraphicsDebugLevel.Error => DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityErrorBitExt,
|
GraphicsDebugLevel.Error => DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt,
|
||||||
GraphicsDebugLevel.Slowdowns => DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityErrorBitExt |
|
GraphicsDebugLevel.Slowdowns => DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt |
|
||||||
DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityWarningBitExt,
|
DebugUtilsMessageSeverityFlagsEXT.WarningBitExt,
|
||||||
GraphicsDebugLevel.All => DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityInfoBitExt |
|
GraphicsDebugLevel.All => DebugUtilsMessageSeverityFlagsEXT.InfoBitExt |
|
||||||
DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityWarningBitExt |
|
DebugUtilsMessageSeverityFlagsEXT.WarningBitExt |
|
||||||
DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityVerboseBitExt |
|
DebugUtilsMessageSeverityFlagsEXT.VerboseBitExt |
|
||||||
DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityErrorBitExt,
|
DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt,
|
||||||
_ => throw new ArgumentException($"Invalid log level \"{logLevel}\".")
|
_ => throw new ArgumentException($"Invalid log level \"{logLevel}\".")
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -341,11 +341,11 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
public unsafe Capabilities GetCapabilities()
|
public unsafe Capabilities GetCapabilities()
|
||||||
{
|
{
|
||||||
FormatFeatureFlags compressedFormatFeatureFlags =
|
FormatFeatureFlags compressedFormatFeatureFlags =
|
||||||
FormatFeatureFlags.FormatFeatureSampledImageBit |
|
FormatFeatureFlags.SampledImageBit |
|
||||||
FormatFeatureFlags.FormatFeatureSampledImageFilterLinearBit |
|
FormatFeatureFlags.SampledImageFilterLinearBit |
|
||||||
FormatFeatureFlags.FormatFeatureBlitSrcBit |
|
FormatFeatureFlags.BlitSrcBit |
|
||||||
FormatFeatureFlags.FormatFeatureTransferSrcBit |
|
FormatFeatureFlags.TransferSrcBit |
|
||||||
FormatFeatureFlags.FormatFeatureTransferDstBit;
|
FormatFeatureFlags.TransferDstBit;
|
||||||
|
|
||||||
bool supportsBc123CompressionFormat = FormatCapabilities.OptimalFormatsSupport(compressedFormatFeatureFlags,
|
bool supportsBc123CompressionFormat = FormatCapabilities.OptimalFormatsSupport(compressedFormatFeatureFlags,
|
||||||
GAL.Format.Bc1RgbaSrgb,
|
GAL.Format.Bc1RgbaSrgb,
|
||||||
@@ -471,7 +471,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
GpuRenderer = Marshal.PtrToStringAnsi((IntPtr)properties.DeviceName);
|
GpuRenderer = Marshal.PtrToStringAnsi((IntPtr)properties.DeviceName);
|
||||||
GpuVersion = $"Vulkan v{ParseStandardVulkanVersion(properties.ApiVersion)}, Driver v{ParseDriverVersion(ref properties)}";
|
GpuVersion = $"Vulkan v{ParseStandardVulkanVersion(properties.ApiVersion)}, Driver v{ParseDriverVersion(ref properties)}";
|
||||||
|
|
||||||
IsAmdGcn = Vendor == Vendor.Amd && VendorUtils.AmdGcnRegex.IsMatch(GpuRenderer);
|
IsAmdGcn = Vendor == Vendor.Amd && VendorUtils.AmdGcnRegex().IsMatch(GpuRenderer);
|
||||||
|
|
||||||
Logger.Notice.Print(LogClass.Gpu, $"{GpuVendor} {GpuRenderer} ({GpuVersion})");
|
Logger.Notice.Print(LogClass.Gpu, $"{GpuVendor} {GpuRenderer} ({GpuVersion})");
|
||||||
}
|
}
|
||||||
@@ -565,6 +565,11 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
_syncManager.Wait(id);
|
_syncManager.Wait(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ulong GetCurrentSync()
|
||||||
|
{
|
||||||
|
return _syncManager.GetCurrent();
|
||||||
|
}
|
||||||
|
|
||||||
public void Screenshot()
|
public void Screenshot()
|
||||||
{
|
{
|
||||||
_window.ScreenCaptureRequested = true;
|
_window.ScreenCaptureRequested = true;
|
||||||
|
@@ -109,11 +109,11 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
ImageFormat = surfaceFormat.Format,
|
ImageFormat = surfaceFormat.Format,
|
||||||
ImageColorSpace = surfaceFormat.ColorSpace,
|
ImageColorSpace = surfaceFormat.ColorSpace,
|
||||||
ImageExtent = extent,
|
ImageExtent = extent,
|
||||||
ImageUsage = ImageUsageFlags.ImageUsageColorAttachmentBit | ImageUsageFlags.ImageUsageTransferDstBit,
|
ImageUsage = ImageUsageFlags.ColorAttachmentBit | ImageUsageFlags.TransferDstBit,
|
||||||
ImageSharingMode = SharingMode.Exclusive,
|
ImageSharingMode = SharingMode.Exclusive,
|
||||||
ImageArrayLayers = 1,
|
ImageArrayLayers = 1,
|
||||||
PreTransform = capabilities.CurrentTransform,
|
PreTransform = capabilities.CurrentTransform,
|
||||||
CompositeAlpha = CompositeAlphaFlagsKHR.CompositeAlphaOpaqueBitKhr,
|
CompositeAlpha = CompositeAlphaFlagsKHR.OpaqueBitKhr,
|
||||||
PresentMode = ChooseSwapPresentMode(presentModes, _vsyncEnabled),
|
PresentMode = ChooseSwapPresentMode(presentModes, _vsyncEnabled),
|
||||||
Clipped = true,
|
Clipped = true,
|
||||||
OldSwapchain = oldSwapchain
|
OldSwapchain = oldSwapchain
|
||||||
@@ -146,7 +146,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
ComponentSwizzle.B,
|
ComponentSwizzle.B,
|
||||||
ComponentSwizzle.A);
|
ComponentSwizzle.A);
|
||||||
|
|
||||||
var aspectFlags = ImageAspectFlags.ImageAspectColorBit;
|
var aspectFlags = ImageAspectFlags.ColorBit;
|
||||||
|
|
||||||
var subresourceRange = new ImageSubresourceRange(aspectFlags, 0, 1, 0, 1);
|
var subresourceRange = new ImageSubresourceRange(aspectFlags, 0, 1, 0, 1);
|
||||||
|
|
||||||
@@ -154,7 +154,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
SType = StructureType.ImageViewCreateInfo,
|
SType = StructureType.ImageViewCreateInfo,
|
||||||
Image = swapchainImage,
|
Image = swapchainImage,
|
||||||
ViewType = ImageViewType.ImageViewType2D,
|
ViewType = ImageViewType.Type2D,
|
||||||
Format = format,
|
Format = format,
|
||||||
Components = componentMapping,
|
Components = componentMapping,
|
||||||
SubresourceRange = subresourceRange
|
SubresourceRange = subresourceRange
|
||||||
@@ -168,12 +168,12 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
if (availableFormats.Length == 1 && availableFormats[0].Format == VkFormat.Undefined)
|
if (availableFormats.Length == 1 && availableFormats[0].Format == VkFormat.Undefined)
|
||||||
{
|
{
|
||||||
return new SurfaceFormatKHR(VkFormat.B8G8R8A8Unorm, ColorSpaceKHR.ColorspaceSrgbNonlinearKhr);
|
return new SurfaceFormatKHR(VkFormat.B8G8R8A8Unorm, ColorSpaceKHR.PaceSrgbNonlinearKhr);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var format in availableFormats)
|
foreach (var format in availableFormats)
|
||||||
{
|
{
|
||||||
if (format.Format == VkFormat.B8G8R8A8Unorm && format.ColorSpace == ColorSpaceKHR.ColorspaceSrgbNonlinearKhr)
|
if (format.Format == VkFormat.B8G8R8A8Unorm && format.ColorSpace == ColorSpaceKHR.PaceSrgbNonlinearKhr)
|
||||||
{
|
{
|
||||||
return format;
|
return format;
|
||||||
}
|
}
|
||||||
@@ -184,21 +184,21 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
private static PresentModeKHR ChooseSwapPresentMode(PresentModeKHR[] availablePresentModes, bool vsyncEnabled)
|
private static PresentModeKHR ChooseSwapPresentMode(PresentModeKHR[] availablePresentModes, bool vsyncEnabled)
|
||||||
{
|
{
|
||||||
if (!vsyncEnabled && availablePresentModes.Contains(PresentModeKHR.PresentModeImmediateKhr))
|
if (!vsyncEnabled && availablePresentModes.Contains(PresentModeKHR.ImmediateKhr))
|
||||||
{
|
{
|
||||||
return PresentModeKHR.PresentModeImmediateKhr;
|
return PresentModeKHR.ImmediateKhr;
|
||||||
}
|
}
|
||||||
else if (availablePresentModes.Contains(PresentModeKHR.PresentModeMailboxKhr))
|
else if (availablePresentModes.Contains(PresentModeKHR.MailboxKhr))
|
||||||
{
|
{
|
||||||
return PresentModeKHR.PresentModeMailboxKhr;
|
return PresentModeKHR.MailboxKhr;
|
||||||
}
|
}
|
||||||
else if (availablePresentModes.Contains(PresentModeKHR.PresentModeFifoKhr))
|
else if (availablePresentModes.Contains(PresentModeKHR.FifoKhr))
|
||||||
{
|
{
|
||||||
return PresentModeKHR.PresentModeFifoKhr;
|
return PresentModeKHR.FifoKhr;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return PresentModeKHR.PresentModeFifoKhr;
|
return PresentModeKHR.FifoKhr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -254,7 +254,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
cbs.CommandBuffer,
|
cbs.CommandBuffer,
|
||||||
swapchainImage,
|
swapchainImage,
|
||||||
0,
|
0,
|
||||||
AccessFlags.AccessTransferWriteBit,
|
AccessFlags.TransferWriteBit,
|
||||||
ImageLayout.Undefined,
|
ImageLayout.Undefined,
|
||||||
ImageLayout.General);
|
ImageLayout.General);
|
||||||
|
|
||||||
@@ -339,7 +339,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
_gd.CommandBufferPool.Return(
|
_gd.CommandBufferPool.Return(
|
||||||
cbs,
|
cbs,
|
||||||
stackalloc[] { _imageAvailableSemaphore },
|
stackalloc[] { _imageAvailableSemaphore },
|
||||||
stackalloc[] { PipelineStageFlags.PipelineStageColorAttachmentOutputBit },
|
stackalloc[] { PipelineStageFlags.ColorAttachmentOutputBit },
|
||||||
stackalloc[] { _renderFinishedSemaphore });
|
stackalloc[] { _renderFinishedSemaphore });
|
||||||
|
|
||||||
// TODO: Present queue.
|
// TODO: Present queue.
|
||||||
@@ -373,7 +373,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
ImageLayout srcLayout,
|
ImageLayout srcLayout,
|
||||||
ImageLayout dstLayout)
|
ImageLayout dstLayout)
|
||||||
{
|
{
|
||||||
var subresourceRange = new ImageSubresourceRange(ImageAspectFlags.ImageAspectColorBit, 0, 1, 0, 1);
|
var subresourceRange = new ImageSubresourceRange(ImageAspectFlags.ColorBit, 0, 1, 0, 1);
|
||||||
|
|
||||||
var barrier = new ImageMemoryBarrier()
|
var barrier = new ImageMemoryBarrier()
|
||||||
{
|
{
|
||||||
@@ -390,8 +390,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
_gd.Api.CmdPipelineBarrier(
|
_gd.Api.CmdPipelineBarrier(
|
||||||
commandBuffer,
|
commandBuffer,
|
||||||
PipelineStageFlags.PipelineStageTopOfPipeBit,
|
PipelineStageFlags.TopOfPipeBit,
|
||||||
PipelineStageFlags.PipelineStageAllCommandsBit,
|
PipelineStageFlags.AllCommandsBit,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
null,
|
null,
|
||||||
|
@@ -18,7 +18,7 @@ using System.Text.RegularExpressions;
|
|||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Applets.Error
|
namespace Ryujinx.HLE.HOS.Applets.Error
|
||||||
{
|
{
|
||||||
internal class ErrorApplet : IApplet
|
internal partial class ErrorApplet : IApplet
|
||||||
{
|
{
|
||||||
private const long ErrorMessageBinaryTitleId = 0x0100000000000801;
|
private const long ErrorMessageBinaryTitleId = 0x0100000000000801;
|
||||||
|
|
||||||
@@ -30,6 +30,9 @@ namespace Ryujinx.HLE.HOS.Applets.Error
|
|||||||
|
|
||||||
public event EventHandler AppletStateChanged;
|
public event EventHandler AppletStateChanged;
|
||||||
|
|
||||||
|
[GeneratedRegex(@"[^\u0000\u0009\u000A\u000D\u0020-\uFFFF]..")]
|
||||||
|
private static partial Regex CleanTextRegex();
|
||||||
|
|
||||||
public ErrorApplet(Horizon horizon)
|
public ErrorApplet(Horizon horizon)
|
||||||
{
|
{
|
||||||
_horizon = horizon;
|
_horizon = horizon;
|
||||||
@@ -101,7 +104,7 @@ namespace Ryujinx.HLE.HOS.Applets.Error
|
|||||||
|
|
||||||
private static string CleanText(string value)
|
private static string CleanText(string value)
|
||||||
{
|
{
|
||||||
return Regex.Replace(value, @"[^\u0000\u0009\u000A\u000D\u0020-\uFFFF]..", "").Replace("\0", "");
|
return CleanTextRegex().Replace(value, "").Replace("\0", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetMessageText(uint module, uint description, string key)
|
private string GetMessageText(uint module, uint description, string key)
|
||||||
|
@@ -2,18 +2,30 @@
|
|||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres.Proxy
|
namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres.Proxy
|
||||||
{
|
{
|
||||||
static class DnsBlacklist
|
static partial class DnsBlacklist
|
||||||
{
|
{
|
||||||
const RegexOptions RegexOpts = RegexOptions.CultureInvariant | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture | RegexOptions.Compiled;
|
const RegexOptions RegexOpts = RegexOptions.CultureInvariant | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture;
|
||||||
|
|
||||||
private static readonly Regex[] BlockedHosts = new Regex[]
|
[GeneratedRegex(@"^(.*)\-lp1\.(n|s)\.n\.srv\.nintendo\.net$", RegexOpts)]
|
||||||
{
|
private static partial Regex BlockedHost1();
|
||||||
new Regex(@"^(.*)\-lp1\.(n|s)\.n\.srv\.nintendo\.net$", RegexOpts),
|
[GeneratedRegex(@"^(.*)\-lp1\.lp1\.t\.npln\.srv\.nintendo\.net$", RegexOpts)]
|
||||||
new Regex(@"^(.*)\-lp1\.lp1\.t\.npln\.srv\.nintendo\.net$", RegexOpts),
|
private static partial Regex BlockedHost2();
|
||||||
new Regex(@"^(.*)\-lp1\.(znc|p)\.srv\.nintendo\.net$", RegexOpts),
|
[GeneratedRegex(@"^(.*)\-lp1\.(znc|p)\.srv\.nintendo\.net$", RegexOpts)]
|
||||||
new Regex(@"^(.*)\-sb\-api\.accounts\.nintendo\.com$", RegexOpts),
|
private static partial Regex BlockedHost3();
|
||||||
new Regex(@"^(.*)\-sb\.accounts\.nintendo\.com$", RegexOpts),
|
[GeneratedRegex(@"^(.*)\-sb\-api\.accounts\.nintendo\.com$", RegexOpts)]
|
||||||
new Regex(@"^accounts\.nintendo\.com$", RegexOpts)
|
private static partial Regex BlockedHost4();
|
||||||
|
[GeneratedRegex(@"^(.*)\-sb\.accounts\.nintendo\.com$", RegexOpts)]
|
||||||
|
private static partial Regex BlockedHost5();
|
||||||
|
[GeneratedRegex(@"^accounts\.nintendo\.com$", RegexOpts)]
|
||||||
|
private static partial Regex BlockedHost6();
|
||||||
|
|
||||||
|
private static readonly Regex[] BlockedHosts = {
|
||||||
|
BlockedHost1(),
|
||||||
|
BlockedHost2(),
|
||||||
|
BlockedHost3(),
|
||||||
|
BlockedHost4(),
|
||||||
|
BlockedHost5(),
|
||||||
|
BlockedHost6()
|
||||||
};
|
};
|
||||||
|
|
||||||
public static bool IsHostBlocked(string host)
|
public static bool IsHostBlocked(string host)
|
||||||
|
@@ -9,7 +9,7 @@ using System.Text.RegularExpressions;
|
|||||||
|
|
||||||
namespace Ryujinx.HLE.Loaders.Executables
|
namespace Ryujinx.HLE.Loaders.Executables
|
||||||
{
|
{
|
||||||
class NsoExecutable : IExecutable
|
partial class NsoExecutable : IExecutable
|
||||||
{
|
{
|
||||||
public byte[] Program { get; }
|
public byte[] Program { get; }
|
||||||
public Span<byte> Text => Program.AsSpan((int)TextOffset, (int)TextSize);
|
public Span<byte> Text => Program.AsSpan((int)TextOffset, (int)TextSize);
|
||||||
@@ -29,6 +29,13 @@ namespace Ryujinx.HLE.Loaders.Executables
|
|||||||
public string Name;
|
public string Name;
|
||||||
public Array32<byte> BuildId;
|
public Array32<byte> BuildId;
|
||||||
|
|
||||||
|
[GeneratedRegex(@"[a-z]:[\\/][ -~]{5,}\.nss", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant)]
|
||||||
|
private static partial Regex ModuleRegex();
|
||||||
|
[GeneratedRegex(@"sdk_version: ([0-9.]*)")]
|
||||||
|
private static partial Regex FsSdkRegex();
|
||||||
|
[GeneratedRegex(@"SDK MW[ -~]*")]
|
||||||
|
private static partial Regex SdkMwRegex();
|
||||||
|
|
||||||
public NsoExecutable(IStorage inStorage, string name = null)
|
public NsoExecutable(IStorage inStorage, string name = null)
|
||||||
{
|
{
|
||||||
NsoReader reader = new NsoReader();
|
NsoReader reader = new NsoReader();
|
||||||
@@ -83,7 +90,7 @@ namespace Ryujinx.HLE.Loaders.Executables
|
|||||||
|
|
||||||
if (string.IsNullOrEmpty(modulePath))
|
if (string.IsNullOrEmpty(modulePath))
|
||||||
{
|
{
|
||||||
Match moduleMatch = Regex.Match(rawTextBuffer, @"[a-z]:[\\/][ -~]{5,}\.nss", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Compiled);
|
Match moduleMatch = ModuleRegex().Match(rawTextBuffer);
|
||||||
if (moduleMatch.Success)
|
if (moduleMatch.Success)
|
||||||
{
|
{
|
||||||
modulePath = moduleMatch.Value;
|
modulePath = moduleMatch.Value;
|
||||||
@@ -92,13 +99,13 @@ namespace Ryujinx.HLE.Loaders.Executables
|
|||||||
|
|
||||||
stringBuilder.AppendLine($" Module: {modulePath}");
|
stringBuilder.AppendLine($" Module: {modulePath}");
|
||||||
|
|
||||||
Match fsSdkMatch = Regex.Match(rawTextBuffer, @"sdk_version: ([0-9.]*)", RegexOptions.Compiled);
|
Match fsSdkMatch = FsSdkRegex().Match(rawTextBuffer);
|
||||||
if (fsSdkMatch.Success)
|
if (fsSdkMatch.Success)
|
||||||
{
|
{
|
||||||
stringBuilder.AppendLine($" FS SDK Version: {fsSdkMatch.Value.Replace("sdk_version: ", "")}");
|
stringBuilder.AppendLine($" FS SDK Version: {fsSdkMatch.Value.Replace("sdk_version: ", "")}");
|
||||||
}
|
}
|
||||||
|
|
||||||
MatchCollection sdkMwMatches = Regex.Matches(rawTextBuffer, @"SDK MW[ -~]*", RegexOptions.Compiled);
|
MatchCollection sdkMwMatches = SdkMwRegex().Matches(rawTextBuffer);
|
||||||
if (sdkMwMatches.Count != 0)
|
if (sdkMwMatches.Count != 0)
|
||||||
{
|
{
|
||||||
string libHeader = " SDK Libraries: ";
|
string libHeader = " SDK Libraries: ";
|
||||||
|
@@ -638,16 +638,7 @@ namespace Ryujinx.Headless.SDL2
|
|||||||
|
|
||||||
Translator.IsReadyForTranslation.Reset();
|
Translator.IsReadyForTranslation.Reset();
|
||||||
|
|
||||||
Thread windowThread = new Thread(() =>
|
ExecutionEntrypoint();
|
||||||
{
|
|
||||||
ExecutionEntrypoint();
|
|
||||||
})
|
|
||||||
{
|
|
||||||
Name = "GUI.WindowThread"
|
|
||||||
};
|
|
||||||
|
|
||||||
windowThread.Start();
|
|
||||||
windowThread.Join();
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@@ -168,14 +168,6 @@ namespace Ryujinx.Headless.SDL2
|
|||||||
|
|
||||||
public void Render()
|
public void Render()
|
||||||
{
|
{
|
||||||
InitializeWindowRenderer();
|
|
||||||
|
|
||||||
Device.Gpu.Renderer.Initialize(_glLogLevel);
|
|
||||||
|
|
||||||
InitializeRenderer();
|
|
||||||
|
|
||||||
_gpuVendorName = GetGpuVendorName();
|
|
||||||
|
|
||||||
Device.Gpu.Renderer.RunLoop(() =>
|
Device.Gpu.Renderer.RunLoop(() =>
|
||||||
{
|
{
|
||||||
Device.Gpu.SetGpuThread();
|
Device.Gpu.SetGpuThread();
|
||||||
@@ -323,6 +315,14 @@ namespace Ryujinx.Headless.SDL2
|
|||||||
|
|
||||||
InitializeWindow();
|
InitializeWindow();
|
||||||
|
|
||||||
|
InitializeWindowRenderer();
|
||||||
|
|
||||||
|
Device.Gpu.Renderer.Initialize(_glLogLevel);
|
||||||
|
|
||||||
|
InitializeRenderer();
|
||||||
|
|
||||||
|
_gpuVendorName = GetGpuVendorName();
|
||||||
|
|
||||||
Thread renderLoopThread = new Thread(Render)
|
Thread renderLoopThread = new Thread(Render)
|
||||||
{
|
{
|
||||||
Name = "GUI.RenderLoop"
|
Name = "GUI.RenderLoop"
|
||||||
|
@@ -41,7 +41,7 @@ namespace Ryujinx.Memory.Tracking
|
|||||||
{
|
{
|
||||||
for (int i = 0; i < Masks.Length; i++)
|
for (int i = 0; i < Masks.Length; i++)
|
||||||
{
|
{
|
||||||
if (Volatile.Read(ref Masks[i]) != 0)
|
if (Interlocked.Read(ref Masks[i]) != 0)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -62,7 +62,7 @@ namespace Ryujinx.Memory.Tracking
|
|||||||
|
|
||||||
long wordMask = 1L << wordBit;
|
long wordMask = 1L << wordBit;
|
||||||
|
|
||||||
return (Volatile.Read(ref Masks[wordIndex]) & wordMask) != 0;
|
return (Interlocked.Read(ref Masks[wordIndex]) & wordMask) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -86,7 +86,7 @@ namespace Ryujinx.Memory.Tracking
|
|||||||
int endBit = end & IntMask;
|
int endBit = end & IntMask;
|
||||||
long endMask = (long)(ulong.MaxValue >> (IntMask - endBit));
|
long endMask = (long)(ulong.MaxValue >> (IntMask - endBit));
|
||||||
|
|
||||||
long startValue = Volatile.Read(ref Masks[startIndex]);
|
long startValue = Interlocked.Read(ref Masks[startIndex]);
|
||||||
|
|
||||||
if (startIndex == endIndex)
|
if (startIndex == endIndex)
|
||||||
{
|
{
|
||||||
@@ -100,13 +100,13 @@ namespace Ryujinx.Memory.Tracking
|
|||||||
|
|
||||||
for (int i = startIndex + 1; i < endIndex; i++)
|
for (int i = startIndex + 1; i < endIndex; i++)
|
||||||
{
|
{
|
||||||
if (Volatile.Read(ref Masks[i]) != 0)
|
if (Interlocked.Read(ref Masks[i]) != 0)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
long endValue = Volatile.Read(ref Masks[endIndex]);
|
long endValue = Interlocked.Read(ref Masks[endIndex]);
|
||||||
|
|
||||||
if ((endValue & endMask) != 0)
|
if ((endValue & endMask) != 0)
|
||||||
{
|
{
|
||||||
@@ -128,23 +128,14 @@ namespace Ryujinx.Memory.Tracking
|
|||||||
|
|
||||||
long wordMask = 1L << wordBit;
|
long wordMask = 1L << wordBit;
|
||||||
|
|
||||||
long existing;
|
if (value)
|
||||||
long newValue;
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
{
|
||||||
existing = Volatile.Read(ref Masks[wordIndex]);
|
Interlocked.Or(ref Masks[wordIndex], wordMask);
|
||||||
|
}
|
||||||
if (value)
|
else
|
||||||
{
|
{
|
||||||
newValue = existing | wordMask;
|
Interlocked.And(ref Masks[wordIndex], ~wordMask);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
newValue = existing & ~wordMask;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
while (Interlocked.CompareExchange(ref Masks[wordIndex], newValue, existing) != existing);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -154,7 +145,7 @@ namespace Ryujinx.Memory.Tracking
|
|||||||
{
|
{
|
||||||
for (int i = 0; i < Masks.Length; i++)
|
for (int i = 0; i < Masks.Length; i++)
|
||||||
{
|
{
|
||||||
Volatile.Write(ref Masks[i], 0);
|
Interlocked.Exchange(ref Masks[i], 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -28,6 +28,8 @@ namespace Ryujinx.SDL2.Common
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Action<Action> MainThreadDispatcher { get; set; }
|
||||||
|
|
||||||
private const uint SdlInitFlags = SDL_INIT_EVENTS | SDL_INIT_GAMECONTROLLER | SDL_INIT_JOYSTICK | SDL_INIT_AUDIO | SDL_INIT_VIDEO;
|
private const uint SdlInitFlags = SDL_INIT_EVENTS | SDL_INIT_GAMECONTROLLER | SDL_INIT_JOYSTICK | SDL_INIT_AUDIO | SDL_INIT_VIDEO;
|
||||||
|
|
||||||
private bool _isRunning;
|
private bool _isRunning;
|
||||||
@@ -69,7 +71,7 @@ namespace Ryujinx.SDL2.Common
|
|||||||
|
|
||||||
if (SDL_Init(SdlInitFlags) != 0)
|
if (SDL_Init(SdlInitFlags) != 0)
|
||||||
{
|
{
|
||||||
string errorMessage = $"SDL2 initlaization failed with error \"{SDL_GetError()}\"";
|
string errorMessage = $"SDL2 initialization failed with error \"{SDL_GetError()}\"";
|
||||||
|
|
||||||
Logger.Error?.Print(LogClass.Application, errorMessage);
|
Logger.Error?.Print(LogClass.Application, errorMessage);
|
||||||
|
|
||||||
@@ -154,10 +156,13 @@ namespace Ryujinx.SDL2.Common
|
|||||||
|
|
||||||
while (_isRunning)
|
while (_isRunning)
|
||||||
{
|
{
|
||||||
while (SDL_PollEvent(out SDL_Event evnt) != 0)
|
MainThreadDispatcher?.Invoke(() =>
|
||||||
{
|
{
|
||||||
HandleSDLEvent(ref evnt);
|
while (SDL_PollEvent(out SDL_Event evnt) != 0)
|
||||||
}
|
{
|
||||||
|
HandleSDLEvent(ref evnt);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
waitHandle.Wait(WaitTimeMs);
|
waitHandle.Wait(WaitTimeMs);
|
||||||
}
|
}
|
||||||
|
@@ -444,7 +444,10 @@ namespace Ryujinx.Ui.App.Common
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
ApplicationMetadata appMetadata = LoadAndSaveMetaData(titleId);
|
ApplicationMetadata appMetadata = LoadAndSaveMetaData(titleId, appMetadata =>
|
||||||
|
{
|
||||||
|
appMetadata.Title = titleName;
|
||||||
|
});
|
||||||
|
|
||||||
if (appMetadata.LastPlayed != "Never" && !DateTime.TryParse(appMetadata.LastPlayed, out _))
|
if (appMetadata.LastPlayed != "Never" && !DateTime.TryParse(appMetadata.LastPlayed, out _))
|
||||||
{
|
{
|
||||||
|
@@ -2,6 +2,7 @@
|
|||||||
{
|
{
|
||||||
public class ApplicationMetadata
|
public class ApplicationMetadata
|
||||||
{
|
{
|
||||||
|
public string Title { get; set; }
|
||||||
public bool Favorite { get; set; }
|
public bool Favorite { get; set; }
|
||||||
public double TimePlayed { get; set; }
|
public double TimePlayed { get; set; }
|
||||||
public string LastPlayed { get; set; } = "Never";
|
public string LastPlayed { get; set; } = "Never";
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user