Compare commits

...

29 Commits

Author SHA1 Message Date
Ethan Page
5c0fb0cec3 ui: Disallow checking for updates while emulation active (#3886)
* Disallow updating while game is running

* Reflected change on Avalonia

* Git has gone wonky

* Fix accidental indent
2022-12-04 20:17:11 +01:00
gdkchan
17a1cab5d2 Allow SNorm buffer texture formats on Vulkan (#3957)
* Allow SNorm buffer texture formats on Vulkan

* Shader cache version bump
2022-12-04 15:36:03 -03:00
gdkchan
73aed239c3 Implement non-MS to MS copies with draws (#3958)
* Implement non-MS to MS copies with draws, simplify MS to non-MS copies and supports any host sample count

* Remove unused program
2022-12-04 15:07:11 -03:00
riperiperi
9ac66336a2 GPU: Use lazy checks for specialization state (#4004)
* GPU: Use lazy checks for specialization state

This PR adds a new class, the SpecializationStateUpdater, that allows elements of specialization state to be updated individually, and signal the state is checked when it changes between draws, instead of building and checking it on every draw. This also avoids building spec state when

Most state updates have been moved behind the shader state update, so that their specialization state updates make it in before shaders are fetched.

Downside: Fields in GpuChannelGraphicsState are no longer readonly. To counteract copies that might be caused this I pass it as `ref` when possible, though maybe `in` would be better? Not really sure about the quirks of `in` and the difference probably won't show on a benchmark.

The result is around 2 extra FPS on SMO in the usual spot. Not much right now, but it will remove costs when we're doing more expensive specialization checks, such as fragment output type specialization for macos. It may also help more on other games with more draws.

* Address Feedback

* Oops
2022-12-04 18:41:17 +01:00
riperiperi
4965681e06 GPU: Swap bindings array instead of copying (#4003)
* GPU: Swap bindings array instead of copying

Reduces work on UpdateShaderState. Now the cost is a few reference moves for arrays, rather than copying data.

Downside: bindings arrays are no longer readonly.

* Micro optimisation

* Add missing docs

* Address Feedback
2022-12-04 18:18:40 +01:00
Andrey Sukharev
3868a00206 Use source generated regular expressions (#4005) 2022-12-04 00:43:23 +00:00
mageven
933e5144a9 Query Available RAM on macOS (#4000) 2022-12-04 00:25:07 +00:00
Ac_K
73a42c85c4 Add crowdin badge and information in readme (#3990)
* Add crowdin bagde and informations in readme.

* Update README.md

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

* Update README.md

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>
2022-12-02 23:57:38 +01:00
Ac_K
39ba11054b Update Crowdin configuration file 2022-12-02 15:22:21 +01:00
Ac_K
c250e3392c Fix using in Ava 2022-12-02 15:08:57 +01:00
Ac_K
e56b069081 Update Crowdin configuration file 2022-12-02 14:54:56 +01:00
merry
204c031fef SDL2Driver: Invoke dispatcher on main thread (#3818) 2022-12-02 14:37:22 +01:00
Emmanuel Hansen
d9053bbe37 Avalonia - Save Manager (#3476)
* Add save manager to account selector

* add fallback to app metadata for titlename if app is not in gamelist

* Allow recovering lost accounts
2022-12-02 13:16:43 +00:00
Mary-nyan
c25e8427aa amadeus: Fix wrong SendCommands logic (#3969)
* amadeus: Fix wrong SendCommands logic

Might help with audio desync, might cause audio stutters, will see!

* Address gdkchan's comment
2022-12-02 13:01:19 +00:00
Isaac Marovitz
21a081b185 Add back locales removed in #3955 (#3983)
Add back `SettingsButtonSave` & `SettingsButtonClose` removed in #3955

Fixes #3982
2022-12-02 12:46:18 +00:00
Isaac Marovitz
b540ea80d1 Ava GUI: Make Dialogue More Intuitive (#3955)
* Adjust button position and locales

* Shortcuts + Highlight default action

* Update Locales - Corrections Welcome

* Move `Apply` button back to right side

* OS Reactive Button layout

* Fix reversed boolean :)

* Fix accented button styling
2022-12-02 03:31:21 +01:00
Mary
d692a9b83e Revert "nuget: bump SixLabors.ImageSharp from 1.0.4 to 2.1.3 (#3976)"
This reverts commit 9677ddaa5d.

SixLabors.ImageShar switched to a shady and vague license starting with 2.x
without mentioning it on their changelog.

As a result we are staying on 1.x (licensed under Apache-2) and will
seak an alternative package.
2022-12-01 23:06:55 +01:00
dependabot[bot]
9677ddaa5d nuget: bump SixLabors.ImageSharp from 1.0.4 to 2.1.3 (#3976)
* nuget: bump SixLabors.ImageSharp from 1.0.4 to 2.1.3

Bumps [SixLabors.ImageSharp](https://github.com/SixLabors/ImageSharp) from 1.0.4 to 2.1.3.
- [Release notes](https://github.com/SixLabors/ImageSharp/releases)
- [Commits](https://github.com/SixLabors/ImageSharp/compare/v1.0.4...v2.1.3)

---
updated-dependencies:
- dependency-name: SixLabors.ImageSharp
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update for 2.x changes

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Mary <mary@mary.zone>
2022-12-01 21:54:41 +00:00
Mary-nyan
ce92e8cd04 chore: Update Silk.NET to 2.16.0 (#3953) 2022-12-01 19:11:56 +01:00
Isaac Marovitz
456fc04007 Better SDL2 Audio Init Error Logging (#3967)
* Better SDL2 Audio Init Error Logging

* Update Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs

Co-authored-by: Mary-nyan <thog@protonmail.com>

* Update Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs

Co-authored-by: Mary-nyan <thog@protonmail.com>

* Update Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs

Co-authored-by: Mary-nyan <thog@protonmail.com>

* Update Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs

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

* Update Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs

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

* Update SDL2HardwareDeviceDriver.cs

* Update SDL2HardwareDeviceDriver.cs

Co-authored-by: Mary-nyan <thog@protonmail.com>
Co-authored-by: Ac_K <Acoustik666@gmail.com>
2022-12-01 17:53:13 +01:00
riperiperi
458452279c GPU: Track buffer migrations and flush source on incomplete copy (#3952)
* Track buffer migrations and flush source on incomplete copy

Makes sure that the modified range list is always from the latest iteration of the buffer, and flushes earlier iterations of a buffer if the data has not been migrated yet.

* Cleanup 1

* Reduce cost for redundant signal checks on Vulkan

* Only inherit the range list if there are pending ranges.

* Fix OpenGL

* Address Feedback

* Whoops
2022-12-01 16:30:13 +01:00
Mary-nyan
817b89767a infra: Add distribution files for macOS (#3934)
This upstream macOS packing and distribution files
2022-12-01 14:08:43 +01:00
TSRBerry
3fb583c98c Avalonia: Clean up leftover RenderTimer & Fix minimum and initial window size (#3935)
* ava: Cleanup RenderTimer

* ava: Remove ContentControl from RendererHost

* ava: Remove unused actual scale factor

* ava: Enable UseGpu for Linux

* ava: Set better initial size & Scale the window properly

* ava: Realign properties

* ava: Use explicit type & specify where the note applies
2022-11-30 23:34:25 +01:00
dependabot[bot]
d2686e0a5b nuget: bump DiscordRichPresence from 1.0.175 to 1.1.3.18 (#3965)
Bumps [DiscordRichPresence](https://github.com/Lachee/discord-rpc-csharp) from 1.0.175 to 1.1.3.18.
- [Release notes](https://github.com/Lachee/discord-rpc-csharp/releases)
- [Commits](https://github.com/Lachee/discord-rpc-csharp/commits)

---
updated-dependencies:
- dependency-name: DiscordRichPresence
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-30 21:58:45 +00:00
gdkchan
4905101df1 Remove shader dependency on SPV_KHR_shader_ballot and SPV_KHR_subgroup_vote extensions (#3943)
* Remove shader dependency on SPV_KHR_shader_ballot and SPV_KHR_subgroup_vote extensions

* Shader cache version bump
2022-11-30 18:24:15 -03:00
gdkchan
8750b90a7f Ensure that vertex attribute buffer index is valid on GPU (#3942)
* Ensure that vertex attribute buffer index is valid on GPU

* Remove vertex buffer validation code from OpenGL

* Remove some fields that are no longer necessary
2022-11-30 18:06:40 -03:00
dependabot[bot]
af01100050 nuget: bump System.Management from 6.0.0 to 7.0.0 (#3949)
Bumps [System.Management](https://github.com/dotnet/runtime) from 6.0.0 to 7.0.0.
- [Release notes](https://github.com/dotnet/runtime/releases)
- [Commits](https://github.com/dotnet/runtime/compare/v6.0.0...v7.0.0)

---
updated-dependencies:
- dependency-name: System.Management
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-29 23:30:33 +01:00
EmulationFanatic
c0821fee1f Update README.MD (#3946)
Updates the readme to include usage of Metal via MoltenVK, updated game compatibility statistics.
2022-11-29 17:20:26 +01:00
merry
a5c2aead67 ConcurrentBitmap: Use Interlocked Or/And (#3937) 2022-11-29 13:47:57 +00:00
134 changed files with 3892 additions and 1552 deletions

View File

@@ -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**

View File

@@ -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;

View File

@@ -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);

View File

@@ -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
{ {

View File

@@ -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",

View File

@@ -168,6 +168,8 @@
"SettingsTabInputDirectKeyboardAccess": "Άμεση Πρόσβαση στο Πληκτρολόγιο", "SettingsTabInputDirectKeyboardAccess": "Άμεση Πρόσβαση στο Πληκτρολόγιο",
"SettingsButtonSave": "Αποθήκευση", "SettingsButtonSave": "Αποθήκευση",
"SettingsButtonClose": "Κλείσιμο", "SettingsButtonClose": "Κλείσιμο",
"SettingsButtonOk": "OK",
"SettingsButtonCancel": "Ακύρωση",
"SettingsButtonApply": "Εφαρμογή", "SettingsButtonApply": "Εφαρμογή",
"ControllerSettingsPlayer": "Παίχτης", "ControllerSettingsPlayer": "Παίχτης",
"ControllerSettingsPlayer1": "Παίχτης 1", "ControllerSettingsPlayer1": "Παίχτης 1",

View File

@@ -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"
} }

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -168,6 +168,8 @@
"SettingsTabInputDirectKeyboardAccess": "キーボード直接アクセス", "SettingsTabInputDirectKeyboardAccess": "キーボード直接アクセス",
"SettingsButtonSave": "セーブ", "SettingsButtonSave": "セーブ",
"SettingsButtonClose": "閉じる", "SettingsButtonClose": "閉じる",
"SettingsButtonOk": "オーケー",
"SettingsButtonCancel": "キャンセル",
"SettingsButtonApply": "適用", "SettingsButtonApply": "適用",
"ControllerSettingsPlayer": "プレイヤー", "ControllerSettingsPlayer": "プレイヤー",
"ControllerSettingsPlayer1": "プレイヤー 1", "ControllerSettingsPlayer1": "プレイヤー 1",

View File

@@ -167,6 +167,8 @@
"SettingsTabInputDirectKeyboardAccess": "직접 키보드 액세스", "SettingsTabInputDirectKeyboardAccess": "직접 키보드 액세스",
"SettingsButtonSave": "구하다", "SettingsButtonSave": "구하다",
"SettingsButtonClose": "출구", "SettingsButtonClose": "출구",
"SettingsButtonOk": "좋아",
"SettingsButtonCancel": "취소",
"SettingsButtonApply": "적용하다", "SettingsButtonApply": "적용하다",
"ControllerSettingsPlayer": "플레이어", "ControllerSettingsPlayer": "플레이어",
"ControllerSettingsPlayer1": "플레이어 1", "ControllerSettingsPlayer1": "플레이어 1",

View File

@@ -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",

View File

@@ -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",

View File

@@ -167,6 +167,8 @@
"SettingsTabInputDirectKeyboardAccess": "Прямой доступ с клавиатуры", "SettingsTabInputDirectKeyboardAccess": "Прямой доступ с клавиатуры",
"SettingsButtonSave": "Сохранить", "SettingsButtonSave": "Сохранить",
"SettingsButtonClose": "Закрыть", "SettingsButtonClose": "Закрыть",
"SettingsButtonOk": "OK",
"SettingsButtonCancel": "Отмена",
"SettingsButtonApply": "Применить", "SettingsButtonApply": "Применить",
"ControllerSettingsPlayer": "Игрок", "ControllerSettingsPlayer": "Игрок",
"ControllerSettingsPlayer1": "Игрок 1", "ControllerSettingsPlayer1": "Игрок 1",

View File

@@ -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",

View File

@@ -168,6 +168,8 @@
"SettingsTabInputDirectKeyboardAccess": "直通键盘控制", "SettingsTabInputDirectKeyboardAccess": "直通键盘控制",
"SettingsButtonSave": "保存", "SettingsButtonSave": "保存",
"SettingsButtonClose": "关闭", "SettingsButtonClose": "关闭",
"SettingsButtonOk": "批准",
"SettingsButtonCancel": "取消",
"SettingsButtonApply": "应用", "SettingsButtonApply": "应用",
"ControllerSettingsPlayer": "玩家", "ControllerSettingsPlayer": "玩家",
"ControllerSettingsPlayer1": "玩家 1", "ControllerSettingsPlayer1": "玩家 1",

View File

@@ -168,6 +168,8 @@
"SettingsTabInputDirectKeyboardAccess": "直通鍵盤控制", "SettingsTabInputDirectKeyboardAccess": "直通鍵盤控制",
"SettingsButtonSave": "儲存", "SettingsButtonSave": "儲存",
"SettingsButtonClose": "關閉", "SettingsButtonClose": "關閉",
"SettingsButtonOk": "嘛好",
"SettingsButtonCancel": "取消",
"SettingsButtonApply": "套用", "SettingsButtonApply": "套用",
"ControllerSettingsPlayer": "玩家", "ControllerSettingsPlayer": "玩家",
"ControllerSettingsPlayer1": "玩家 1", "ControllerSettingsPlayer1": "玩家 1",

View File

@@ -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>

View File

@@ -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>

View File

@@ -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))

View File

@@ -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();
} }
} }
} }

View File

@@ -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" />

View File

@@ -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"],

View File

@@ -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();
}
}
}

View File

@@ -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>

View File

@@ -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()

View 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>

View 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;
}
}
}

View File

@@ -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

View File

@@ -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
{ {

View 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>

View 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;
}
}
}
}

View File

@@ -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>

View 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();
}
}
}
}

View File

@@ -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(){}

View File

@@ -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));
}
} }
} }

View File

@@ -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;
}); });

View File

@@ -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();
}
} }
} }

View File

@@ -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));
}
} }
} }

View File

@@ -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"

View File

@@ -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)
@@ -261,6 +266,7 @@ namespace Ryujinx.Ava.Ui.Windows
return; return;
} }
CanUpdate = false;
ViewModel.LoadHeading = string.IsNullOrWhiteSpace(titleName) ? string.Format(LocaleManager.Instance["LoadingHeading"], AppHost.Device.Application.TitleName) : titleName; ViewModel.LoadHeading = string.IsNullOrWhiteSpace(titleName) ? string.Format(LocaleManager.Instance["LoadingHeading"], AppHost.Device.Application.TitleName) : titleName;
ViewModel.TitleName = string.IsNullOrWhiteSpace(titleName) ? AppHost.Device.Application.TitleName : titleName; ViewModel.TitleName = string.IsNullOrWhiteSpace(titleName) ? AppHost.Device.Application.TitleName : titleName;
@@ -366,6 +372,7 @@ namespace Ryujinx.Ava.Ui.Windows
ViewModel.ShowContent = true; ViewModel.ShowContent = true;
ViewModel.ShowLoadProgress = false; ViewModel.ShowLoadProgress = false;
ViewModel.IsLoadingIndeterminate = false; ViewModel.IsLoadingIndeterminate = false;
CanUpdate = true;
Cursor = Cursor.Default; Cursor = Cursor.Default;
if (MainContent.Content != _mainViewContent) if (MainContent.Content != _mainViewContent)

View File

@@ -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>

View File

@@ -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();

View File

@@ -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>

View File

@@ -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);
} }
} }

View File

@@ -17,6 +17,7 @@ namespace Ryujinx.Graphics.GAL
public readonly bool Supports3DTextureCompression; public readonly bool Supports3DTextureCompression;
public readonly bool SupportsBgraFormat; public readonly bool SupportsBgraFormat;
public readonly bool SupportsR4G4Format; public readonly bool SupportsR4G4Format;
public readonly bool SupportsSnormBufferTextureFormat;
public readonly bool SupportsFragmentShaderInterlock; public readonly bool SupportsFragmentShaderInterlock;
public readonly bool SupportsFragmentShaderOrderingIntel; public readonly bool SupportsFragmentShaderOrderingIntel;
public readonly bool SupportsGeometryShaderPassthrough; public readonly bool SupportsGeometryShaderPassthrough;
@@ -52,6 +53,7 @@ namespace Ryujinx.Graphics.GAL
bool supports3DTextureCompression, bool supports3DTextureCompression,
bool supportsBgraFormat, bool supportsBgraFormat,
bool supportsR4G4Format, bool supportsR4G4Format,
bool supportsSnormBufferTextureFormat,
bool supportsFragmentShaderInterlock, bool supportsFragmentShaderInterlock,
bool supportsFragmentShaderOrderingIntel, bool supportsFragmentShaderOrderingIntel,
bool supportsGeometryShaderPassthrough, bool supportsGeometryShaderPassthrough,
@@ -84,6 +86,7 @@ namespace Ryujinx.Graphics.GAL
Supports3DTextureCompression = supports3DTextureCompression; Supports3DTextureCompression = supports3DTextureCompression;
SupportsBgraFormat = supportsBgraFormat; SupportsBgraFormat = supportsBgraFormat;
SupportsR4G4Format = supportsR4G4Format; SupportsR4G4Format = supportsR4G4Format;
SupportsSnormBufferTextureFormat = supportsSnormBufferTextureFormat;
SupportsFragmentShaderInterlock = supportsFragmentShaderInterlock; SupportsFragmentShaderInterlock = supportsFragmentShaderInterlock;
SupportsFragmentShaderOrderingIntel = supportsFragmentShaderOrderingIntel; SupportsFragmentShaderOrderingIntel = supportsFragmentShaderOrderingIntel;
SupportsGeometryShaderPassthrough = supportsGeometryShaderPassthrough; SupportsGeometryShaderPassthrough = supportsGeometryShaderPassthrough;

View File

@@ -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);

View File

@@ -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();

View File

@@ -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);

View File

@@ -19,6 +19,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
private readonly GpuChannel _channel; private readonly GpuChannel _channel;
private readonly DeviceStateWithShadow<ThreedClassState> _state; private readonly DeviceStateWithShadow<ThreedClassState> _state;
private readonly DrawState _drawState; private readonly DrawState _drawState;
private readonly SpecializationStateUpdater _currentSpecState;
private bool _topologySet; private bool _topologySet;
private bool _instancedDrawPending; private bool _instancedDrawPending;
@@ -44,12 +45,14 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// <param name="channel">GPU channel</param> /// <param name="channel">GPU channel</param>
/// <param name="state">Channel state</param> /// <param name="state">Channel state</param>
/// <param name="drawState">Draw state</param> /// <param name="drawState">Draw state</param>
public DrawManager(GpuContext context, GpuChannel channel, DeviceStateWithShadow<ThreedClassState> state, DrawState drawState) /// <param name="spec">Specialization state updater</param>
public DrawManager(GpuContext context, GpuChannel channel, DeviceStateWithShadow<ThreedClassState> state, DrawState drawState, SpecializationStateUpdater spec)
{ {
_context = context; _context = context;
_channel = channel; _channel = channel;
_state = state; _state = state;
_drawState = drawState; _drawState = drawState;
_currentSpecState = spec;
} }
/// <summary> /// <summary>
@@ -132,6 +135,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
_drawState.FirstIndex = firstIndex; _drawState.FirstIndex = firstIndex;
_drawState.IndexCount = indexCount; _drawState.IndexCount = indexCount;
_currentSpecState.SetHasConstantBufferDrawParameters(false);
engine.UpdateState(); engine.UpdateState();
@@ -256,6 +260,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
if (_drawState.Topology != topology || !_topologySet) if (_drawState.Topology != topology || !_topologySet)
{ {
_context.Renderer.Pipeline.SetPrimitiveTopology(topology); _context.Renderer.Pipeline.SetPrimitiveTopology(topology);
_currentSpecState.SetTopology(topology);
_drawState.Topology = topology; _drawState.Topology = topology;
_topologySet = true; _topologySet = true;
} }
@@ -452,7 +457,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
_state.State.FirstInstance = (uint)firstInstance; _state.State.FirstInstance = (uint)firstInstance;
_drawState.DrawIndexed = indexed; _drawState.DrawIndexed = indexed;
_drawState.HasConstantBufferDrawParameters = true; _currentSpecState.SetHasConstantBufferDrawParameters(true);
engine.UpdateState(); engine.UpdateState();
@@ -469,7 +474,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
_state.State.FirstInstance = 0; _state.State.FirstInstance = 0;
_drawState.DrawIndexed = false; _drawState.DrawIndexed = false;
_drawState.HasConstantBufferDrawParameters = false;
if (renderEnable == ConditionalRenderEnabled.Host) if (renderEnable == ConditionalRenderEnabled.Host)
{ {
@@ -527,7 +531,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
_drawState.DrawIndexed = indexed; _drawState.DrawIndexed = indexed;
_drawState.DrawIndirect = true; _drawState.DrawIndirect = true;
_drawState.HasConstantBufferDrawParameters = true; _currentSpecState.SetHasConstantBufferDrawParameters(true);
engine.UpdateState(); engine.UpdateState();
@@ -561,7 +565,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
_drawState.DrawIndexed = false; _drawState.DrawIndexed = false;
_drawState.DrawIndirect = false; _drawState.DrawIndirect = false;
_drawState.HasConstantBufferDrawParameters = false;
if (renderEnable == ConditionalRenderEnabled.Host) if (renderEnable == ConditionalRenderEnabled.Host)
{ {

View File

@@ -0,0 +1,280 @@
using Ryujinx.Common.Memory;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Shader;
using Ryujinx.Graphics.Shader;
namespace Ryujinx.Graphics.Gpu.Engine.Threed
{
/// <summary>
/// Maintains a "current" specialiation state, and provides a flag to check if it has changed meaningfully.
/// </summary>
internal class SpecializationStateUpdater
{
private GpuChannelGraphicsState _graphics;
private GpuChannelPoolState _pool;
private bool _usesDrawParameters;
private bool _usesTopology;
private bool _changed;
/// <summary>
/// Signal that the specialization state has changed.
/// </summary>
private void Signal()
{
_changed = true;
}
/// <summary>
/// Checks if the specialization state has changed since the last check.
/// </summary>
/// <returns>True if it has changed, false otherwise</returns>
public bool HasChanged()
{
if (_changed)
{
_changed = false;
return true;
}
else
{
return false;
}
}
/// <summary>
/// Sets the active shader, clearing the dirty state and recording if certain specializations are noteworthy.
/// </summary>
/// <param name="gs">The active shader</param>
public void SetShader(CachedShaderProgram gs)
{
_usesDrawParameters = gs.Shaders[1]?.Info.UsesDrawParameters ?? false;
_usesTopology = gs.SpecializationState.IsPrimitiveTopologyQueried();
_changed = false;
}
/// <summary>
/// Get the current graphics state.
/// </summary>
/// <returns>GPU graphics state</returns>
public ref GpuChannelGraphicsState GetGraphicsState()
{
return ref _graphics;
}
/// <summary>
/// Get the current pool state.
/// </summary>
/// <returns>GPU pool state</returns>
public ref GpuChannelPoolState GetPoolState()
{
return ref _pool;
}
/// <summary>
/// Early Z force enable.
/// </summary>
/// <param name="value">The new value</param>
public void SetEarlyZForce(bool value)
{
_graphics.EarlyZForce = value;
Signal();
}
/// <summary>
/// Primitive topology of current draw.
/// </summary>
/// <param name="value">The new value</param>
public void SetTopology(PrimitiveTopology value)
{
if (value != _graphics.Topology)
{
_graphics.Topology = value;
if (_usesTopology)
{
Signal();
}
}
}
/// <summary>
/// Tessellation mode.
/// </summary>
/// <param name="value">The new value</param>
public void SetTessellationMode(TessMode value)
{
if (value.Packed != _graphics.TessellationMode.Packed)
{
_graphics.TessellationMode = value;
Signal();
}
}
/// <summary>
/// Updates alpha-to-coverage state, and sets it as changed.
/// </summary>
/// <param name="enable">Whether alpha-to-coverage is enabled</param>
/// <param name="ditherEnable">Whether alpha-to-coverage dithering is enabled</param>
public void SetAlphaToCoverageEnable(bool enable, bool ditherEnable)
{
_graphics.AlphaToCoverageEnable = enable;
_graphics.AlphaToCoverageDitherEnable = ditherEnable;
Signal();
}
/// <summary>
/// Indicates whether the viewport transform is disabled.
/// </summary>
/// <param name="value">The new value</param>
public void SetViewportTransformDisable(bool value)
{
if (value != _graphics.ViewportTransformDisable)
{
_graphics.ViewportTransformDisable = value;
Signal();
}
}
/// <summary>
/// Depth mode zero to one or minus one to one.
/// </summary>
/// <param name="value">The new value</param>
public void SetDepthMode(bool value)
{
if (value != _graphics.DepthMode)
{
_graphics.DepthMode = value;
Signal();
}
}
/// <summary>
/// Indicates if the point size is set on the shader or is fixed.
/// </summary>
/// <param name="value">The new value</param>
public void SetProgramPointSizeEnable(bool value)
{
if (value != _graphics.ProgramPointSizeEnable)
{
_graphics.ProgramPointSizeEnable = value;
Signal();
}
}
/// <summary>
/// Point size used if <see cref="SetProgramPointSizeEnable" /> is provided false.
/// </summary>
/// <param name="value">The new value</param>
public void SetPointSize(float value)
{
if (value != _graphics.PointSize)
{
_graphics.PointSize = value;
Signal();
}
}
/// <summary>
/// Updates alpha test specialization state, and sets it as changed.
/// </summary>
/// <param name="enable">Whether alpha test is enabled</param>
/// <param name="reference">The value to compare with the fragment output alpha</param>
/// <param name="op">The comparison that decides if the fragment should be discarded</param>
public void SetAlphaTest(bool enable, float reference, CompareOp op)
{
_graphics.AlphaTestEnable = enable;
_graphics.AlphaTestReference = reference;
_graphics.AlphaTestCompare = op;
Signal();
}
/// <summary>
/// Updates the type of the vertex attributes consumed by the shader.
/// </summary>
/// <param name="state">The new state</param>
public void SetAttributeTypes(ref Array32<VertexAttribState> state)
{
bool changed = false;
ref Array32<AttributeType> attributeTypes = ref _graphics.AttributeTypes;
for (int location = 0; location < state.Length; location++)
{
VertexAttribType type = state[location].UnpackType();
AttributeType value = type switch
{
VertexAttribType.Sint => AttributeType.Sint,
VertexAttribType.Uint => AttributeType.Uint,
_ => AttributeType.Float
};
if (attributeTypes[location] != value)
{
attributeTypes[location] = value;
changed = true;
}
}
if (changed)
{
Signal();
}
}
/// <summary>
/// Indicates that the draw is writing the base vertex, base instance and draw index to Constant Buffer 0.
/// </summary>
/// <param name="value">The new value</param>
public void SetHasConstantBufferDrawParameters(bool value)
{
if (value != _graphics.HasConstantBufferDrawParameters)
{
_graphics.HasConstantBufferDrawParameters = value;
if (_usesDrawParameters)
{
Signal();
}
}
}
/// <summary>
/// Indicates that any storage buffer use is unaligned.
/// </summary>
/// <param name="value">The new value</param>
public void SetHasUnalignedStorageBuffer(bool value)
{
if (value != _graphics.HasUnalignedStorageBuffer)
{
_graphics.HasUnalignedStorageBuffer = value;
Signal();
}
}
/// <summary>
/// Sets the GPU pool state.
/// </summary>
/// <param name="state">The new state</param>
public void SetPoolState(GpuChannelPoolState state)
{
if (!state.Equals(_pool))
{
_pool = state;
Signal();
}
}
}
}

View File

@@ -1,6 +1,7 @@
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Common.Memory; using Ryujinx.Common.Memory;
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Engine.GPFifo;
using Ryujinx.Graphics.Gpu.Engine.Types; using Ryujinx.Graphics.Gpu.Engine.Types;
using Ryujinx.Graphics.Gpu.Image; using Ryujinx.Graphics.Gpu.Image;
using Ryujinx.Graphics.Gpu.Shader; using Ryujinx.Graphics.Gpu.Shader;
@@ -16,9 +17,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// </summary> /// </summary>
class StateUpdater class StateUpdater
{ {
public const int ShaderStateIndex = 16; public const int ShaderStateIndex = 26;
public const int RasterizerStateIndex = 15; public const int RasterizerStateIndex = 15;
public const int ScissorStateIndex = 18; public const int ScissorStateIndex = 16;
public const int VertexBufferStateIndex = 0; public const int VertexBufferStateIndex = 0;
public const int PrimitiveRestartStateIndex = 12; public const int PrimitiveRestartStateIndex = 12;
@@ -31,12 +32,14 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
private readonly ShaderProgramInfo[] _currentProgramInfo; private readonly ShaderProgramInfo[] _currentProgramInfo;
private ShaderSpecializationState _shaderSpecState; private ShaderSpecializationState _shaderSpecState;
private SpecializationStateUpdater _currentSpecState;
private ProgramPipelineState _pipeline; private ProgramPipelineState _pipeline;
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;
@@ -53,15 +56,17 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// <param name="channel">GPU channel</param> /// <param name="channel">GPU channel</param>
/// <param name="state">3D engine state</param> /// <param name="state">3D engine state</param>
/// <param name="drawState">Draw state</param> /// <param name="drawState">Draw state</param>
public StateUpdater(GpuContext context, GpuChannel channel, DeviceStateWithShadow<ThreedClassState> state, DrawState drawState) /// <param name="spec">Specialization state updater</param>
public StateUpdater(GpuContext context, GpuChannel channel, DeviceStateWithShadow<ThreedClassState> state, DrawState drawState, SpecializationStateUpdater spec)
{ {
_context = context; _context = context;
_channel = channel; _channel = channel;
_state = state; _state = state;
_drawState = drawState; _drawState = drawState;
_currentProgramInfo = new ShaderProgramInfo[Constants.ShaderStages]; _currentProgramInfo = new ShaderProgramInfo[Constants.ShaderStages];
_currentSpecState = spec;
// ShaderState must be updated after other state updates, as pipeline state is sent to the backend when compiling new shaders. // ShaderState must be updated after other state updates, as specialization/pipeline state is used when fetching shaders.
// Render target state must appear after shader state as it depends on information from the currently bound shader. // Render target state must appear after shader state as it depends on information from the currently bound shader.
// Rasterizer and scissor states are checked by render target clear, their indexes // Rasterizer and scissor states are checked by render target clear, their indexes
// must be updated on the constants "RasterizerStateIndex" and "ScissorStateIndex" if modified. // must be updated on the constants "RasterizerStateIndex" and "ScissorStateIndex" if modified.
@@ -76,6 +81,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,
@@ -99,6 +105,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
nameof(ThreedClassState.DepthTestFunc)), nameof(ThreedClassState.DepthTestFunc)),
new StateUpdateCallbackEntry(UpdateTessellationState, new StateUpdateCallbackEntry(UpdateTessellationState,
nameof(ThreedClassState.TessMode),
nameof(ThreedClassState.TessOuterLevel), nameof(ThreedClassState.TessOuterLevel),
nameof(ThreedClassState.TessInnerLevel), nameof(ThreedClassState.TessInnerLevel),
nameof(ThreedClassState.PatchVertices)), nameof(ThreedClassState.PatchVertices)),
@@ -136,17 +143,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
new StateUpdateCallbackEntry(UpdateRasterizerState, nameof(ThreedClassState.RasterizeEnable)), new StateUpdateCallbackEntry(UpdateRasterizerState, nameof(ThreedClassState.RasterizeEnable)),
new StateUpdateCallbackEntry(UpdateShaderState,
nameof(ThreedClassState.ShaderBaseAddress),
nameof(ThreedClassState.ShaderState)),
new StateUpdateCallbackEntry(UpdateRenderTargetState,
nameof(ThreedClassState.RtColorState),
nameof(ThreedClassState.RtDepthStencilState),
nameof(ThreedClassState.RtControl),
nameof(ThreedClassState.RtDepthStencilSize),
nameof(ThreedClassState.RtDepthStencilEnable)),
new StateUpdateCallbackEntry(UpdateScissorState, new StateUpdateCallbackEntry(UpdateScissorState,
nameof(ThreedClassState.ScissorState), nameof(ThreedClassState.ScissorState),
nameof(ThreedClassState.ScreenScissorState)), nameof(ThreedClassState.ScreenScissorState)),
@@ -177,7 +173,21 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
new StateUpdateCallbackEntry(UpdateMultisampleState, new StateUpdateCallbackEntry(UpdateMultisampleState,
nameof(ThreedClassState.AlphaToCoverageDitherEnable), nameof(ThreedClassState.AlphaToCoverageDitherEnable),
nameof(ThreedClassState.MultisampleControl)) nameof(ThreedClassState.MultisampleControl)),
new StateUpdateCallbackEntry(UpdateEarlyZState,
nameof(ThreedClassState.EarlyZForce)),
new StateUpdateCallbackEntry(UpdateShaderState,
nameof(ThreedClassState.ShaderBaseAddress),
nameof(ThreedClassState.ShaderState)),
new StateUpdateCallbackEntry(UpdateRenderTargetState,
nameof(ThreedClassState.RtColorState),
nameof(ThreedClassState.RtDepthStencilState),
nameof(ThreedClassState.RtControl),
nameof(ThreedClassState.RtDepthStencilSize),
nameof(ThreedClassState.RtDepthStencilEnable)),
}); });
} }
@@ -207,17 +217,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Update() public void Update()
{ {
// If any state that the shader depends on changed,
// then we may need to compile/bind a different version
// of the shader for the new state.
if (_shaderSpecState != null)
{
if (!_shaderSpecState.MatchesGraphics(_channel, GetPoolState(), GetGraphicsState(), _vsUsesDrawParameters, false))
{
ForceShaderUpdate();
}
}
// The vertex buffer size is calculated using a different // The vertex buffer size is calculated using a different
// method when doing indexed draws, so we need to make sure // method when doing indexed draws, so we need to make sure
// to update the vertex buffers if we are doing a regular // to update the vertex buffers if we are doing a regular
@@ -269,6 +268,18 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
_updateTracker.Update(ulong.MaxValue); _updateTracker.Update(ulong.MaxValue);
// If any state that the shader depends on changed,
// then we may need to compile/bind a different version
// of the shader for the new state.
if (_shaderSpecState != null && _currentSpecState.HasChanged())
{
if (!_shaderSpecState.MatchesGraphics(_channel, ref _currentSpecState.GetPoolState(), ref _currentSpecState.GetGraphicsState(), _vsUsesDrawParameters, false))
{
// Shader must be reloaded. _vtgWritesRtLayer should not change.
UpdateShaderState();
}
}
CommitBindings(); CommitBindings();
if (tfEnable && !_prevTfEnable) if (tfEnable && !_prevTfEnable)
@@ -300,7 +311,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
if (!_channel.TextureManager.CommitGraphicsBindings(_shaderSpecState) || (buffers.HasUnalignedStorageBuffers != hasUnaligned)) if (!_channel.TextureManager.CommitGraphicsBindings(_shaderSpecState) || (buffers.HasUnalignedStorageBuffers != hasUnaligned))
{ {
// Shader must be reloaded. _currentSpecState.SetHasUnalignedStorageBuffer(buffers.HasUnalignedStorageBuffers);
// Shader must be reloaded. _vtgWritesRtLayer should not change.
UpdateShaderState(); UpdateShaderState();
} }
@@ -349,6 +361,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
_state.State.PatchVertices, _state.State.PatchVertices,
_state.State.TessOuterLevel.AsSpan(), _state.State.TessOuterLevel.AsSpan(),
_state.State.TessInnerLevel.AsSpan()); _state.State.TessInnerLevel.AsSpan());
_currentSpecState.SetTessellationMode(_state.State.TessMode);
} }
/// <summary> /// <summary>
@@ -609,6 +623,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
_state.State.AlphaTestEnable, _state.State.AlphaTestEnable,
_state.State.AlphaTestRef, _state.State.AlphaTestRef,
_state.State.AlphaTestFunc); _state.State.AlphaTestFunc);
_currentSpecState.SetAlphaTest(
_state.State.AlphaTestEnable,
_state.State.AlphaTestRef,
_state.State.AlphaTestFunc);
} }
/// <summary> /// <summary>
@@ -708,6 +727,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
_context.Renderer.Pipeline.SetDepthMode(GetDepthMode()); _context.Renderer.Pipeline.SetDepthMode(GetDepthMode());
_context.Renderer.Pipeline.SetViewports(viewports, disableTransform); _context.Renderer.Pipeline.SetViewports(viewports, disableTransform);
_currentSpecState.SetViewportTransformDisable(_state.State.ViewportTransformEnable == 0);
_currentSpecState.SetDepthMode(GetDepthMode() == DepthMode.MinusOneToOne);
} }
/// <summary> /// <summary>
@@ -845,6 +867,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
_channel.TextureManager.SetGraphicsTexturePool(texturePool.Address.Pack(), texturePool.MaximumId); _channel.TextureManager.SetGraphicsTexturePool(texturePool.Address.Pack(), texturePool.MaximumId);
_channel.TextureManager.SetGraphicsTextureBufferIndex((int)_state.State.TextureBufferIndex); _channel.TextureManager.SetGraphicsTextureBufferIndex((int)_state.State.TextureBufferIndex);
_currentSpecState.SetPoolState(GetPoolState());
} }
/// <summary> /// <summary>
@@ -852,12 +876,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 +901,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);
@@ -874,6 +909,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
_pipeline.SetVertexAttribs(vertexAttribs); _pipeline.SetVertexAttribs(vertexAttribs);
_context.Renderer.Pipeline.SetVertexAttribs(vertexAttribs); _context.Renderer.Pipeline.SetVertexAttribs(vertexAttribs);
_currentSpecState.SetAttributeTypes(ref _state.State.VertexAttribState);
} }
/// <summary> /// <summary>
@@ -901,6 +937,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
Origin origin = (_state.State.PointCoordReplace & 4) == 0 ? Origin.LowerLeft : Origin.UpperLeft; Origin origin = (_state.State.PointCoordReplace & 4) == 0 ? Origin.LowerLeft : Origin.UpperLeft;
_context.Renderer.Pipeline.SetPointParameters(size, isProgramPointSize, enablePointSprite, origin); _context.Renderer.Pipeline.SetPointParameters(size, isProgramPointSize, enablePointSprite, origin);
_currentSpecState.SetProgramPointSizeEnable(isProgramPointSize);
_currentSpecState.SetPointSize(size);
} }
/// <summary> /// <summary>
@@ -954,6 +993,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 +1011,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 +1062,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>
@@ -1187,6 +1238,16 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
alphaToCoverageEnable, alphaToCoverageEnable,
_state.State.AlphaToCoverageDitherEnable, _state.State.AlphaToCoverageDitherEnable,
alphaToOneEnable)); alphaToOneEnable));
_currentSpecState.SetAlphaToCoverageEnable(alphaToCoverageEnable, _state.State.AlphaToCoverageDitherEnable);
}
/// <summary>
/// Updates the early z flag, based on guest state.
/// </summary>
private void UpdateEarlyZState()
{
_currentSpecState.SetEarlyZForce(_state.State.EarlyZForce);
} }
/// <summary> /// <summary>
@@ -1214,10 +1275,10 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
addressesSpan[index] = baseAddress + shader.Offset; addressesSpan[index] = baseAddress + shader.Offset;
} }
GpuChannelPoolState poolState = GetPoolState(); CachedShaderProgram gs = shaderCache.GetGraphicsShader(ref _state.State, ref _pipeline, _channel, ref _currentSpecState.GetPoolState(), ref _currentSpecState.GetGraphicsState(), addresses);
GpuChannelGraphicsState graphicsState = GetGraphicsState();
CachedShaderProgram gs = shaderCache.GetGraphicsShader(ref _state.State, ref _pipeline, _channel, poolState, graphicsState, addresses); // Consume the modified flag for spec state so that it isn't checked again.
_currentSpecState.SetShader(gs);
_shaderSpecState = gs.SpecializationState; _shaderSpecState = gs.SpecializationState;
@@ -1232,88 +1293,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>
@@ -1328,46 +1325,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
(int)_state.State.TextureBufferIndex); (int)_state.State.TextureBufferIndex);
} }
/// <summary>
/// Gets the current GPU channel state for shader creation or compatibility verification.
/// </summary>
/// <returns>Current GPU channel state</returns>
private GpuChannelGraphicsState GetGraphicsState()
{
ref var vertexAttribState = ref _state.State.VertexAttribState;
Array32<AttributeType> attributeTypes = new Array32<AttributeType>();
for (int location = 0; location < attributeTypes.Length; location++)
{
VertexAttribType type = vertexAttribState[location].UnpackType();
attributeTypes[location] = type switch
{
VertexAttribType.Sint => AttributeType.Sint,
VertexAttribType.Uint => AttributeType.Uint,
_ => AttributeType.Float
};
}
return new GpuChannelGraphicsState(
_state.State.EarlyZForce,
_drawState.Topology,
_state.State.TessMode,
(_state.State.MultisampleControl & 1) != 0,
_state.State.AlphaToCoverageDitherEnable,
_state.State.ViewportTransformEnable == 0,
GetDepthMode() == DepthMode.MinusOneToOne,
_state.State.VertexProgramPointSize,
_state.State.PointSize,
_state.State.AlphaTestEnable,
_state.State.AlphaTestFunc,
_state.State.AlphaTestRef,
ref attributeTypes,
_drawState.HasConstantBufferDrawParameters,
_channel.BufferManager.HasUnalignedStorageBuffers);
}
/// <summary> /// <summary>
/// Gets the depth mode that is currently being used (zero to one or minus one to one). /// Gets the depth mode that is currently being used (zero to one or minus one to one).
/// </summary> /// </summary>

View File

@@ -67,12 +67,13 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
_i2mClass = new InlineToMemoryClass(context, channel, initializeState: false); _i2mClass = new InlineToMemoryClass(context, channel, initializeState: false);
var spec = new SpecializationStateUpdater();
var drawState = new DrawState(); var drawState = new DrawState();
_drawManager = new DrawManager(context, channel, _state, drawState); _drawManager = new DrawManager(context, channel, _state, drawState, spec);
_semaphoreUpdater = new SemaphoreUpdater(context, channel, _state); _semaphoreUpdater = new SemaphoreUpdater(context, channel, _state);
_cbUpdater = new ConstantBufferUpdater(channel, _state); _cbUpdater = new ConstantBufferUpdater(channel, _state);
_stateUpdater = new StateUpdater(context, channel, _state, drawState); _stateUpdater = new StateUpdater(context, channel, _state, drawState, spec);
// This defaults to "always", even without any register write. // This defaults to "always", even without any register write.
// Reads just return 0, regardless of what was set there. // Reads just return 0, regardless of what was set there.

View File

@@ -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>

View File

@@ -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;

View File

@@ -1088,10 +1088,9 @@ namespace Ryujinx.Graphics.Gpu.Image
{ {
FormatInfo formatInfo = TextureCompatibility.ToHostCompatibleFormat(info, caps); FormatInfo formatInfo = TextureCompatibility.ToHostCompatibleFormat(info, caps);
if (info.Target == Target.TextureBuffer) if (info.Target == Target.TextureBuffer && !caps.SupportsSnormBufferTextureFormat)
{ {
// We assume that the host does not support signed normalized format // If the host does not support signed normalized formats, we use a signed integer format instead.
// (as is the case with OpenGL), so we just use a unsigned format.
// The shader will need the appropriate conversion code to compensate. // The shader will need the appropriate conversion code to compensate.
switch (formatInfo.Format) switch (formatInfo.Format)
{ {

View File

@@ -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>

View File

@@ -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();
} }
} }
} }

View File

@@ -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);

View File

@@ -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;
} }

View 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();
}
}
}

View File

@@ -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));
} }
} }

View 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;
}
}
}

View File

@@ -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>

View File

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

View File

@@ -134,6 +134,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
public bool QueryHostSupportsShaderBallot() => _context.Capabilities.SupportsShaderBallot; public bool QueryHostSupportsShaderBallot() => _context.Capabilities.SupportsShaderBallot;
public bool QueryHostSupportsSnormBufferTextureFormat() => _context.Capabilities.SupportsSnormBufferTextureFormat;
public bool QueryHostSupportsTextureShadowLod() => _context.Capabilities.SupportsTextureShadowLod; public bool QueryHostSupportsTextureShadowLod() => _context.Capabilities.SupportsTextureShadowLod;
public bool QueryHostSupportsViewportIndex() => _context.Capabilities.SupportsViewportIndex; public bool QueryHostSupportsViewportIndex() => _context.Capabilities.SupportsViewportIndex;

View File

@@ -15,62 +15,62 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <summary> /// <summary>
/// Early Z force enable. /// Early Z force enable.
/// </summary> /// </summary>
public readonly bool EarlyZForce; public bool EarlyZForce;
/// <summary> /// <summary>
/// Primitive topology of current draw. /// Primitive topology of current draw.
/// </summary> /// </summary>
public readonly PrimitiveTopology Topology; public PrimitiveTopology Topology;
/// <summary> /// <summary>
/// Tessellation mode. /// Tessellation mode.
/// </summary> /// </summary>
public readonly TessMode TessellationMode; public TessMode TessellationMode;
/// <summary> /// <summary>
/// Indicates whether alpha-to-coverage is enabled. /// Indicates whether alpha-to-coverage is enabled.
/// </summary> /// </summary>
public readonly bool AlphaToCoverageEnable; public bool AlphaToCoverageEnable;
/// <summary> /// <summary>
/// Indicates whether alpha-to-coverage dithering is enabled. /// Indicates whether alpha-to-coverage dithering is enabled.
/// </summary> /// </summary>
public readonly bool AlphaToCoverageDitherEnable; public bool AlphaToCoverageDitherEnable;
/// <summary> /// <summary>
/// Indicates whether the viewport transform is disabled. /// Indicates whether the viewport transform is disabled.
/// </summary> /// </summary>
public readonly bool ViewportTransformDisable; public bool ViewportTransformDisable;
/// <summary> /// <summary>
/// Depth mode zero to one or minus one to one. /// Depth mode zero to one or minus one to one.
/// </summary> /// </summary>
public readonly bool DepthMode; public bool DepthMode;
/// <summary> /// <summary>
/// Indicates if the point size is set on the shader or is fixed. /// Indicates if the point size is set on the shader or is fixed.
/// </summary> /// </summary>
public readonly bool ProgramPointSizeEnable; public bool ProgramPointSizeEnable;
/// <summary> /// <summary>
/// Point size used if <see cref="ProgramPointSizeEnable" /> is false. /// Point size used if <see cref="ProgramPointSizeEnable" /> is false.
/// </summary> /// </summary>
public readonly float PointSize; public float PointSize;
/// <summary> /// <summary>
/// Indicates whether alpha test is enabled. /// Indicates whether alpha test is enabled.
/// </summary> /// </summary>
public readonly bool AlphaTestEnable; public bool AlphaTestEnable;
/// <summary> /// <summary>
/// When alpha test is enabled, indicates the comparison that decides if the fragment should be discarded. /// When alpha test is enabled, indicates the comparison that decides if the fragment should be discarded.
/// </summary> /// </summary>
public readonly CompareOp AlphaTestCompare; public CompareOp AlphaTestCompare;
/// <summary> /// <summary>
/// When alpha test is enabled, indicates the value to compare with the fragment output alpha. /// When alpha test is enabled, indicates the value to compare with the fragment output alpha.
/// </summary> /// </summary>
public readonly float AlphaTestReference; public float AlphaTestReference;
/// <summary> /// <summary>
/// Type of the vertex attributes consumed by the shader. /// Type of the vertex attributes consumed by the shader.
@@ -80,12 +80,12 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <summary> /// <summary>
/// Indicates that the draw is writing the base vertex, base instance and draw index to Constant Buffer 0. /// Indicates that the draw is writing the base vertex, base instance and draw index to Constant Buffer 0.
/// </summary> /// </summary>
public readonly bool HasConstantBufferDrawParameters; public bool HasConstantBufferDrawParameters;
/// <summary> /// <summary>
/// Indicates that any storage buffer use is unaligned. /// Indicates that any storage buffer use is unaligned.
/// </summary> /// </summary>
public readonly bool HasUnalignedStorageBuffer; public bool HasUnalignedStorageBuffer;
/// <summary> /// <summary>
/// Creates a new GPU graphics state. /// Creates a new GPU graphics state.

View File

@@ -1,9 +1,11 @@
using System;
namespace Ryujinx.Graphics.Gpu.Shader namespace Ryujinx.Graphics.Gpu.Shader
{ {
/// <summary> /// <summary>
/// State used by the <see cref="GpuAccessor"/>. /// State used by the <see cref="GpuAccessor"/>.
/// </summary> /// </summary>
struct GpuChannelPoolState struct GpuChannelPoolState : IEquatable<GpuChannelPoolState>
{ {
/// <summary> /// <summary>
/// GPU virtual address of the texture pool. /// GPU virtual address of the texture pool.
@@ -32,5 +34,17 @@ namespace Ryujinx.Graphics.Gpu.Shader
TexturePoolMaximumId = texturePoolMaximumId; TexturePoolMaximumId = texturePoolMaximumId;
TextureBufferIndex = textureBufferIndex; TextureBufferIndex = textureBufferIndex;
} }
/// <summary>
/// Check if the pool states are equal.
/// </summary>
/// <param name="other">Pool state to compare with</param>
/// <returns>True if they are equal, false otherwise</returns>
public bool Equals(GpuChannelPoolState other)
{
return TexturePoolGpuVa == other.TexturePoolGpuVa &&
TexturePoolMaximumId == other.TexturePoolMaximumId &&
TextureBufferIndex == other.TextureBufferIndex;
}
} }
} }

View File

@@ -300,16 +300,16 @@ namespace Ryujinx.Graphics.Gpu.Shader
ref ThreedClassState state, ref ThreedClassState state,
ref ProgramPipelineState pipeline, ref ProgramPipelineState pipeline,
GpuChannel channel, GpuChannel channel,
GpuChannelPoolState poolState, ref GpuChannelPoolState poolState,
GpuChannelGraphicsState graphicsState, ref GpuChannelGraphicsState graphicsState,
ShaderAddresses addresses) ShaderAddresses addresses)
{ {
if (_gpPrograms.TryGetValue(addresses, out var gpShaders) && IsShaderEqual(channel, poolState, graphicsState, gpShaders, addresses)) if (_gpPrograms.TryGetValue(addresses, out var gpShaders) && IsShaderEqual(channel, ref poolState, ref graphicsState, gpShaders, addresses))
{ {
return gpShaders; return gpShaders;
} }
if (_graphicsShaderCache.TryFind(channel, poolState, graphicsState, addresses, out gpShaders, out var cachedGuestCode)) if (_graphicsShaderCache.TryFind(channel, ref poolState, ref graphicsState, addresses, out gpShaders, out var cachedGuestCode))
{ {
_gpPrograms[addresses] = gpShaders; _gpPrograms[addresses] = gpShaders;
return gpShaders; return gpShaders;
@@ -498,7 +498,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
{ {
if (IsShaderEqual(channel.MemoryManager, cpShader.Shaders[0], gpuVa)) if (IsShaderEqual(channel.MemoryManager, cpShader.Shaders[0], gpuVa))
{ {
return cpShader.SpecializationState.MatchesCompute(channel, poolState, computeState, true); return cpShader.SpecializationState.MatchesCompute(channel, ref poolState, computeState, true);
} }
return false; return false;
@@ -515,8 +515,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <returns>True if the code is different, false otherwise</returns> /// <returns>True if the code is different, false otherwise</returns>
private static bool IsShaderEqual( private static bool IsShaderEqual(
GpuChannel channel, GpuChannel channel,
GpuChannelPoolState poolState, ref GpuChannelPoolState poolState,
GpuChannelGraphicsState graphicsState, ref GpuChannelGraphicsState graphicsState,
CachedShaderProgram gpShaders, CachedShaderProgram gpShaders,
ShaderAddresses addresses) ShaderAddresses addresses)
{ {
@@ -536,7 +536,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
bool usesDrawParameters = gpShaders.Shaders[1]?.Info.UsesDrawParameters ?? false; bool usesDrawParameters = gpShaders.Shaders[1]?.Info.UsesDrawParameters ?? false;
return gpShaders.SpecializationState.MatchesGraphics(channel, poolState, graphicsState, usesDrawParameters, true); return gpShaders.SpecializationState.MatchesGraphics(channel, ref poolState, ref graphicsState, usesDrawParameters, true);
} }
/// <summary> /// <summary>

View File

@@ -215,8 +215,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <returns>True if a cached host program was found, false otherwise</returns> /// <returns>True if a cached host program was found, false otherwise</returns>
public bool TryFind( public bool TryFind(
GpuChannel channel, GpuChannel channel,
GpuChannelPoolState poolState, ref GpuChannelPoolState poolState,
GpuChannelGraphicsState graphicsState, ref GpuChannelGraphicsState graphicsState,
ShaderAddresses addresses, ShaderAddresses addresses,
out CachedShaderProgram program, out CachedShaderProgram program,
out CachedGraphicsGuestCode guestCode) out CachedGraphicsGuestCode guestCode)
@@ -236,7 +236,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
if (found && _shaderPrograms.TryGetValue(idTable, out ShaderSpecializationList specList)) if (found && _shaderPrograms.TryGetValue(idTable, out ShaderSpecializationList specList))
{ {
return specList.TryFindForGraphics(channel, poolState, graphicsState, out program); return specList.TryFindForGraphics(channel, ref poolState, ref graphicsState, out program);
} }
return false; return false;

View File

@@ -29,15 +29,15 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <returns>True if a compatible program is found, false otherwise</returns> /// <returns>True if a compatible program is found, false otherwise</returns>
public bool TryFindForGraphics( public bool TryFindForGraphics(
GpuChannel channel, GpuChannel channel,
GpuChannelPoolState poolState, ref GpuChannelPoolState poolState,
GpuChannelGraphicsState graphicsState, ref GpuChannelGraphicsState graphicsState,
out CachedShaderProgram program) out CachedShaderProgram program)
{ {
foreach (var entry in _entries) foreach (var entry in _entries)
{ {
bool usesDrawParameters = entry.Shaders[1]?.Info.UsesDrawParameters ?? false; bool usesDrawParameters = entry.Shaders[1]?.Info.UsesDrawParameters ?? false;
if (entry.SpecializationState.MatchesGraphics(channel, poolState, graphicsState, usesDrawParameters, true)) if (entry.SpecializationState.MatchesGraphics(channel, ref poolState, ref graphicsState, usesDrawParameters, true))
{ {
program = entry; program = entry;
return true; return true;
@@ -60,7 +60,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
{ {
foreach (var entry in _entries) foreach (var entry in _entries)
{ {
if (entry.SpecializationState.MatchesCompute(channel, poolState, computeState, true)) if (entry.SpecializationState.MatchesCompute(channel, ref poolState, computeState, true))
{ {
program = entry; program = entry;
return true; return true;

View File

@@ -392,6 +392,15 @@ namespace Ryujinx.Graphics.Gpu.Shader
state.Value.QueriedFlags |= QueriedTextureStateFlags.CoordNormalized; state.Value.QueriedFlags |= QueriedTextureStateFlags.CoordNormalized;
} }
/// <summary>
/// Checks if primitive topology was queried by the shader.
/// </summary>
/// <returns>True if queried, false otherwise</returns>
public bool IsPrimitiveTopologyQueried()
{
return _queriedState.HasFlag(QueriedStateFlags.PrimitiveTopology);
}
/// <summary> /// <summary>
/// Checks if a given texture was registerd on this specialization state. /// Checks if a given texture was registerd on this specialization state.
/// </summary> /// </summary>
@@ -486,8 +495,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <returns>True if the state matches, false otherwise</returns> /// <returns>True if the state matches, false otherwise</returns>
public bool MatchesGraphics( public bool MatchesGraphics(
GpuChannel channel, GpuChannel channel,
GpuChannelPoolState poolState, ref GpuChannelPoolState poolState,
GpuChannelGraphicsState graphicsState, ref GpuChannelGraphicsState graphicsState,
bool usesDrawParameters, bool usesDrawParameters,
bool checkTextures) bool checkTextures)
{ {
@@ -536,7 +545,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
return false; return false;
} }
return Matches(channel, poolState, checkTextures, isCompute: false); return Matches(channel, ref poolState, checkTextures, isCompute: false);
} }
/// <summary> /// <summary>
@@ -547,14 +556,14 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <param name="computeState">Compute state</param> /// <param name="computeState">Compute state</param>
/// <param name="checkTextures">Indicates whether texture descriptors should be checked</param> /// <param name="checkTextures">Indicates whether texture descriptors should be checked</param>
/// <returns>True if the state matches, false otherwise</returns> /// <returns>True if the state matches, false otherwise</returns>
public bool MatchesCompute(GpuChannel channel, GpuChannelPoolState poolState, GpuChannelComputeState computeState, bool checkTextures) public bool MatchesCompute(GpuChannel channel, ref GpuChannelPoolState poolState, GpuChannelComputeState computeState, bool checkTextures)
{ {
if (computeState.HasUnalignedStorageBuffer != ComputeState.HasUnalignedStorageBuffer) if (computeState.HasUnalignedStorageBuffer != ComputeState.HasUnalignedStorageBuffer)
{ {
return false; return false;
} }
return Matches(channel, poolState, checkTextures, isCompute: true); return Matches(channel, ref poolState, checkTextures, isCompute: true);
} }
/// <summary> /// <summary>
@@ -618,7 +627,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <param name="checkTextures">Indicates whether texture descriptors should be checked</param> /// <param name="checkTextures">Indicates whether texture descriptors should be checked</param>
/// <param name="isCompute">Indicates whenever the check is requested by the 3D or compute engine</param> /// <param name="isCompute">Indicates whenever the check is requested by the 3D or compute engine</param>
/// <returns>True if the state matches, false otherwise</returns> /// <returns>True if the state matches, false otherwise</returns>
private bool Matches(GpuChannel channel, GpuChannelPoolState poolState, bool checkTextures, bool isCompute) private bool Matches(GpuChannel channel, ref GpuChannelPoolState poolState, bool checkTextures, bool isCompute)
{ {
int constantBufferUsePerStageMask = _constantBufferUsePerStage; int constantBufferUsePerStageMask = _constantBufferUsePerStage;

View File

@@ -113,6 +113,7 @@ namespace Ryujinx.Graphics.OpenGL
supports3DTextureCompression: false, supports3DTextureCompression: false,
supportsBgraFormat: false, supportsBgraFormat: false,
supportsR4G4Format: false, supportsR4G4Format: false,
supportsSnormBufferTextureFormat: false,
supportsFragmentShaderInterlock: HwCapabilities.SupportsFragmentShaderInterlock, supportsFragmentShaderInterlock: HwCapabilities.SupportsFragmentShaderInterlock,
supportsFragmentShaderOrderingIntel: HwCapabilities.SupportsFragmentShaderOrdering, supportsFragmentShaderOrderingIntel: HwCapabilities.SupportsFragmentShaderOrdering,
supportsGeometryShaderPassthrough: HwCapabilities.SupportsGeometryShaderPassthrough, supportsGeometryShaderPassthrough: HwCapabilities.SupportsGeometryShaderPassthrough,
@@ -238,6 +239,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;

View File

@@ -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;

View File

@@ -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)
{ {

View File

@@ -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);
} }

View File

@@ -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)

View File

@@ -285,6 +285,15 @@ namespace Ryujinx.Graphics.Shader
return true; return true;
} }
/// <summary>
/// Queries host GPU support for signed normalized buffer texture formats.
/// </summary>
/// <returns>True if the GPU and driver supports the formats, false otherwise</returns>
bool QueryHostSupportsSnormBufferTextureFormat()
{
return true;
}
/// <summary> /// <summary>
/// Queries host GPU texture shadow LOD support. /// Queries host GPU texture shadow LOD support.
/// </summary> /// </summary>

View File

@@ -14,6 +14,7 @@ namespace Ryujinx.Graphics.Shader.Translation
{ {
bool isVertexShader = config.Stage == ShaderStage.Vertex; bool isVertexShader = config.Stage == ShaderStage.Vertex;
bool hasConstantBufferDrawParameters = config.GpuAccessor.QueryHasConstantBufferDrawParameters(); bool hasConstantBufferDrawParameters = config.GpuAccessor.QueryHasConstantBufferDrawParameters();
bool supportsSnormBufferTextureFormat = config.GpuAccessor.QueryHostSupportsSnormBufferTextureFormat();
for (int blkIndex = 0; blkIndex < blocks.Length; blkIndex++) for (int blkIndex = 0; blkIndex < blocks.Length; blkIndex++)
{ {
@@ -52,7 +53,7 @@ namespace Ryujinx.Graphics.Shader.Translation
{ {
node = RewriteTextureSample(node, config); node = RewriteTextureSample(node, config);
if (texOp.Type == SamplerType.TextureBuffer) if (texOp.Type == SamplerType.TextureBuffer && !supportsSnormBufferTextureFormat)
{ {
node = InsertSnormNormalization(node, config); node = InsertSnormNormalization(node, config);
} }

View File

@@ -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);
} }

View File

@@ -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()

View File

@@ -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();

View File

@@ -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();

View File

@@ -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
}; };
} }

View File

@@ -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?

View File

@@ -32,6 +32,7 @@ namespace Ryujinx.Graphics.Vulkan
Auto<DisposableImageView> view, Auto<DisposableImageView> view,
uint width, uint width,
uint height, uint height,
uint samples,
bool isDepthStencil, bool isDepthStencil,
VkFormat format) VkFormat format)
{ {
@@ -43,7 +44,7 @@ namespace Ryujinx.Graphics.Vulkan
Height = height; Height = height;
Layers = 1; Layers = 1;
AttachmentSamples = new[] { 1u }; AttachmentSamples = new[] { samples };
AttachmentFormats = new[] { format }; AttachmentFormats = new[] { format };
AttachmentIndices = new[] { 0 }; AttachmentIndices = new[] { 0 };
@@ -190,14 +191,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);
} }
} }
} }

View File

@@ -22,6 +22,7 @@ namespace Ryujinx.Graphics.Vulkan
public readonly uint MinSubgroupSize; public readonly uint MinSubgroupSize;
public readonly uint MaxSubgroupSize; public readonly uint MaxSubgroupSize;
public readonly ShaderStageFlags RequiredSubgroupSizeStages; public readonly ShaderStageFlags RequiredSubgroupSizeStages;
public readonly SampleCountFlags SupportedSampleCounts;
public HardwareCapabilities( public HardwareCapabilities(
bool supportsIndexTypeUint8, bool supportsIndexTypeUint8,
@@ -41,7 +42,8 @@ namespace Ryujinx.Graphics.Vulkan
bool supportsGeometryShader, bool supportsGeometryShader,
uint minSubgroupSize, uint minSubgroupSize,
uint maxSubgroupSize, uint maxSubgroupSize,
ShaderStageFlags requiredSubgroupSizeStages) ShaderStageFlags requiredSubgroupSizeStages,
SampleCountFlags supportedSampleCounts)
{ {
SupportsIndexTypeUint8 = supportsIndexTypeUint8; SupportsIndexTypeUint8 = supportsIndexTypeUint8;
SupportsCustomBorderColor = supportsCustomBorderColor; SupportsCustomBorderColor = supportsCustomBorderColor;
@@ -61,6 +63,7 @@ namespace Ryujinx.Graphics.Vulkan
MinSubgroupSize = minSubgroupSize; MinSubgroupSize = minSubgroupSize;
MaxSubgroupSize = maxSubgroupSize; MaxSubgroupSize = maxSubgroupSize;
RequiredSubgroupSizeStages = requiredSubgroupSizeStages; RequiredSubgroupSizeStages = requiredSubgroupSizeStages;
SupportedSampleCounts = supportedSampleCounts;
} }
} }
} }

View File

@@ -20,9 +20,10 @@ namespace Ryujinx.Graphics.Vulkan
private readonly IProgram _programColorBlitClearAlpha; private readonly IProgram _programColorBlitClearAlpha;
private readonly IProgram _programColorClear; private readonly IProgram _programColorClear;
private readonly IProgram _programStrideChange; private readonly IProgram _programStrideChange;
private readonly IProgram _programColorCopyBetweenMsNonMs;
private readonly IProgram _programConvertIndexBuffer; private readonly IProgram _programConvertIndexBuffer;
private readonly IProgram _programConvertIndirectData; private readonly IProgram _programConvertIndirectData;
private readonly IProgram _programColorCopyToNonMs;
private readonly IProgram _programColorDrawToMs;
public HelperShader(VulkanRenderer gd, Device device) public HelperShader(VulkanRenderer gd, Device device)
{ {
@@ -32,13 +33,13 @@ namespace Ryujinx.Graphics.Vulkan
_samplerLinear = gd.CreateSampler(GAL.SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear)); _samplerLinear = gd.CreateSampler(GAL.SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear));
_samplerNearest = gd.CreateSampler(GAL.SamplerCreateInfo.Create(MinFilter.Nearest, MagFilter.Nearest)); _samplerNearest = gd.CreateSampler(GAL.SamplerCreateInfo.Create(MinFilter.Nearest, MagFilter.Nearest));
var vertexBindings = new ShaderBindings( var colorBlitVertexBindings = new ShaderBindings(
new[] { 1 }, new[] { 1 },
Array.Empty<int>(), Array.Empty<int>(),
Array.Empty<int>(), Array.Empty<int>(),
Array.Empty<int>()); Array.Empty<int>());
var fragmentBindings = new ShaderBindings( var colorBlitFragmentBindings = new ShaderBindings(
Array.Empty<int>(), Array.Empty<int>(),
Array.Empty<int>(), Array.Empty<int>(),
new[] { 0 }, new[] { 0 },
@@ -46,17 +47,17 @@ namespace Ryujinx.Graphics.Vulkan
_programColorBlit = gd.CreateProgramWithMinimalLayout(new[] _programColorBlit = gd.CreateProgramWithMinimalLayout(new[]
{ {
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, vertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv), new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, colorBlitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
new ShaderSource(ShaderBinaries.ColorBlitFragmentShaderSource, fragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv), new ShaderSource(ShaderBinaries.ColorBlitFragmentShaderSource, colorBlitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
}); });
_programColorBlitClearAlpha = gd.CreateProgramWithMinimalLayout(new[] _programColorBlitClearAlpha = gd.CreateProgramWithMinimalLayout(new[]
{ {
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, vertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv), new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, colorBlitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
new ShaderSource(ShaderBinaries.ColorBlitClearAlphaFragmentShaderSource, fragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv), new ShaderSource(ShaderBinaries.ColorBlitClearAlphaFragmentShaderSource, colorBlitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
}); });
var fragmentBindings2 = new ShaderBindings( var colorClearFragmentBindings = new ShaderBindings(
Array.Empty<int>(), Array.Empty<int>(),
Array.Empty<int>(), Array.Empty<int>(),
Array.Empty<int>(), Array.Empty<int>(),
@@ -64,8 +65,8 @@ namespace Ryujinx.Graphics.Vulkan
_programColorClear = gd.CreateProgramWithMinimalLayout(new[] _programColorClear = gd.CreateProgramWithMinimalLayout(new[]
{ {
new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, vertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv), new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, colorBlitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
new ShaderSource(ShaderBinaries.ColorClearFragmentShaderSource, fragmentBindings2, ShaderStage.Fragment, TargetLanguage.Spirv), new ShaderSource(ShaderBinaries.ColorClearFragmentShaderSource, colorClearFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
}); });
var strideChangeBindings = new ShaderBindings( var strideChangeBindings = new ShaderBindings(
@@ -79,18 +80,33 @@ namespace Ryujinx.Graphics.Vulkan
new ShaderSource(ShaderBinaries.ChangeBufferStrideShaderSource, strideChangeBindings, ShaderStage.Compute, TargetLanguage.Spirv), new ShaderSource(ShaderBinaries.ChangeBufferStrideShaderSource, strideChangeBindings, ShaderStage.Compute, TargetLanguage.Spirv),
}); });
var colorCopyMSBindings = new ShaderBindings( var colorCopyToNonMsBindings = new ShaderBindings(
new[] { 0 }, new[] { 0 },
Array.Empty<int>(), Array.Empty<int>(),
new[] { 0 }, new[] { 0 },
new[] { 0 }); new[] { 0 });
_programColorCopyBetweenMsNonMs = gd.CreateProgramWithMinimalLayout(new[] _programColorCopyToNonMs = gd.CreateProgramWithMinimalLayout(new[]
{ {
new ShaderSource(ShaderBinaries.ColorCopyBetweenMsNonMs, colorCopyMSBindings, ShaderStage.Compute, TargetLanguage.Spirv), new ShaderSource(ShaderBinaries.ColorCopyToNonMsComputeShaderSource, colorCopyToNonMsBindings, ShaderStage.Compute, TargetLanguage.Spirv),
}, new[] });
var colorDrawToMsVertexBindings = new ShaderBindings(
Array.Empty<int>(),
Array.Empty<int>(),
Array.Empty<int>(),
Array.Empty<int>());
var colorDrawToMsFragmentBindings = new ShaderBindings(
new[] { 0 },
Array.Empty<int>(),
new[] { 0 },
Array.Empty<int>());
_programColorDrawToMs = gd.CreateProgramWithMinimalLayout(new[]
{ {
new SpecDescription((0, SpecConstType.Int32)) new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, colorDrawToMsVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
new ShaderSource(ShaderBinaries.ColorDrawToMsFragmentShaderSource, colorDrawToMsFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
}); });
var convertIndexBufferBindings = new ShaderBindings( var convertIndexBufferBindings = new ShaderBindings(
@@ -349,8 +365,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 +374,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 +436,7 @@ namespace Ryujinx.Graphics.Vulkan
access, access,
BufferHolder.DefaultAccessFlags, BufferHolder.DefaultAccessFlags,
stage, stage,
PipelineStageFlags.PipelineStageAllCommandsBit, PipelineStageFlags.AllCommandsBit,
0, 0,
newSize); newSize);
} }
@@ -484,9 +500,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,45 +515,27 @@ 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);
} }
public void CopyMSToNonMS(VulkanRenderer gd, CommandBufferScoped cbs, TextureView src, TextureView dst, int srcLayer, int dstLayer, int depth) public void CopyMSToNonMS(VulkanRenderer gd, CommandBufferScoped cbs, TextureView src, TextureView dst, int srcLayer, int dstLayer, int depth)
{
CopyMS(gd, cbs, src, dst, srcLayer, dstLayer, depth, src.Info.Samples, dst.Info.Width, dst.Info.Height);
}
public void CopyNonMSToMS(VulkanRenderer gd, CommandBufferScoped cbs, TextureView src, TextureView dst, int srcLayer, int dstLayer, int depth)
{
CopyMS(gd, cbs, src, dst, srcLayer, dstLayer, depth, dst.Info.Samples, src.Info.Width, src.Info.Height);
}
private void CopyMS(
VulkanRenderer gd,
CommandBufferScoped cbs,
TextureView src,
TextureView dst,
int srcLayer,
int dstLayer,
int depth,
int samples,
int nonMSWidth,
int nonMSHeight)
{ {
const int ParamsBufferSize = 16; const int ParamsBufferSize = 16;
Span<int> shaderParams = stackalloc int[ParamsBufferSize / sizeof(int)]; Span<int> shaderParams = stackalloc int[ParamsBufferSize / sizeof(int)];
int samples = src.Info.Samples;
// X and Y are the expected texture samples. // X and Y are the expected texture samples.
// Z and W are the actual texture samples used. // Z and W are the actual texture samples used.
// They may differ if the GPU does not support the samples count requested and we had to use a lower amount. // They may differ if the GPU does not support the samples count requested and we had to use a lower amount.
(shaderParams[0], shaderParams[1]) = GetSampleCountXYLog2(samples); (shaderParams[0], shaderParams[1]) = GetSampleCountXYLog2(samples);
(shaderParams[2], shaderParams[3]) = GetSampleCountXYLog2((int)TextureStorage.ConvertToSampleCountFlags((uint)samples)); (shaderParams[2], shaderParams[3]) = GetSampleCountXYLog2((int)TextureStorage.ConvertToSampleCountFlags(gd.Capabilities.SupportedSampleCounts, (uint)samples));
var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize, false); var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize, false);
@@ -548,10 +546,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,
@@ -559,17 +557,12 @@ namespace Ryujinx.Graphics.Vulkan
_pipeline.SetCommandBuffer(cbs); _pipeline.SetCommandBuffer(cbs);
_pipeline.SetProgram(_programColorCopyBetweenMsNonMs); _pipeline.SetProgram(_programColorCopyToNonMs);
var format = GetFormat(src.Info.BytesPerPixel); var format = GetFormat(src.Info.BytesPerPixel);
int dispatchX = (nonMSWidth + 31) / 32; int dispatchX = (dst.Info.Width + 31) / 32;
int dispatchY = (nonMSHeight + 31) / 32; int dispatchY = (dst.Info.Height + 31) / 32;
// Specialize shader.
bool srcIsMs = src.Info.Target.IsMultisample();
int conversionType = srcIsMs ? src.Info.BytesPerPixel : -src.Info.BytesPerPixel;
_pipeline.Specialize(conversionType);
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, new BufferRange(bufferHandle, 0, ParamsBufferSize)) }); _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, new BufferRange(bufferHandle, 0, ParamsBufferSize)) });
@@ -610,9 +603,132 @@ 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.AllCommandsBit,
ImageAspectFlags.ColorBit,
dst.FirstLayer + dstLayer,
dst.FirstLevel,
depth,
1);
}
public void CopyNonMSToMS(VulkanRenderer gd, CommandBufferScoped cbs, TextureView src, TextureView dst, int srcLayer, int dstLayer, int depth)
{
const int ParamsBufferSize = 16;
Span<int> shaderParams = stackalloc int[ParamsBufferSize / sizeof(int)];
int samples = dst.Info.Samples;
// X and Y are the expected texture samples.
// Z and W are the actual texture samples used.
// They may differ if the GPU does not support the samples count requested and we had to use a lower amount.
(shaderParams[0], shaderParams[1]) = GetSampleCountXYLog2(samples);
(shaderParams[2], shaderParams[3]) = GetSampleCountXYLog2((int)TextureStorage.ConvertToSampleCountFlags(gd.Capabilities.SupportedSampleCounts, (uint)samples));
var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize, false);
gd.BufferManager.SetData<int>(bufferHandle, 0, shaderParams);
TextureView.InsertImageBarrier(
gd.Api,
cbs.CommandBuffer,
src.GetImage().Get(cbs).Value,
TextureStorage.DefaultAccessMask,
AccessFlags.AccessShaderReadBit,
PipelineStageFlags.PipelineStageAllCommandsBit,
PipelineStageFlags.PipelineStageFragmentShaderBit,
ImageAspectFlags.ImageAspectColorBit,
src.FirstLayer + srcLayer,
src.FirstLevel,
depth,
1);
_pipeline.SetCommandBuffer(cbs);
_pipeline.SetProgram(_programColorDrawToMs);
Span<GAL.Viewport> viewports = stackalloc GAL.Viewport[1];
var rect = new Rectangle<float>(0, 0, dst.Width, dst.Height);
viewports[0] = new GAL.Viewport(
rect,
ViewportSwizzle.PositiveX,
ViewportSwizzle.PositiveY,
ViewportSwizzle.PositiveZ,
ViewportSwizzle.PositiveW,
0f,
1f);
Span<Rectangle<int>> scissors = stackalloc Rectangle<int>[1];
scissors[0] = new Rectangle<int>(0, 0, dst.Width, dst.Height);
_pipeline.SetRenderTargetColorMasks(new uint[] { 0xf });
_pipeline.SetScissors(scissors);
_pipeline.SetViewports(viewports, false);
_pipeline.SetPrimitiveTopology(GAL.PrimitiveTopology.TriangleStrip);
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, new BufferRange(bufferHandle, 0, ParamsBufferSize)) });
var format = GetFormat(src.Info.BytesPerPixel);
var vkFormat = FormatTable.GetFormat(format);
if (src.Info.Target == Target.Texture2DMultisampleArray ||
dst.Info.Target == Target.Texture2DMultisampleArray)
{
for (int z = 0; z < depth; z++)
{
var srcView = Create2DLayerView(src, srcLayer + z, format);
var dstView = Create2DLayerView(dst, dstLayer + z);
_pipeline.SetTextureAndSampler(ShaderStage.Fragment, 0, srcView, null);
_pipeline.SetRenderTarget(
((TextureView)dstView).GetView(format).GetImageViewForAttachment(),
(uint)dst.Width,
(uint)dst.Height,
(uint)samples,
false,
vkFormat);
_pipeline.Draw(4, 1, 0, 0);
srcView.Release();
dstView.Release();
}
}
else
{
var srcView = Create2DLayerView(src, srcLayer, format);
_pipeline.SetTextureAndSampler(ShaderStage.Fragment, 0, srcView, null);
_pipeline.SetRenderTarget(
dst.GetView(format).GetImageViewForAttachment(),
(uint)dst.Width,
(uint)dst.Height,
(uint)samples,
false,
vkFormat);
_pipeline.Draw(4, 1, 0, 0);
srcView.Release();
}
gd.BufferManager.Delete(bufferHandle);
_pipeline.Finish(gd, cbs);
TextureView.InsertImageBarrier(
gd.Api,
cbs.CommandBuffer,
dst.GetImage().Get(cbs).Value,
AccessFlags.AccessColorAttachmentWriteBit,
TextureStorage.DefaultAccessMask,
PipelineStageFlags.PipelineStageFragmentShaderBit,
PipelineStageFlags.PipelineStageAllCommandsBit, PipelineStageFlags.PipelineStageAllCommandsBit,
ImageAspectFlags.ImageAspectColorBit, ImageAspectFlags.ImageAspectColorBit,
dst.FirstLayer + dstLayer, dst.FirstLayer + dstLayer,
@@ -770,9 +886,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 +902,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 +914,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 +930,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);
@@ -834,9 +950,10 @@ namespace Ryujinx.Graphics.Vulkan
_programColorBlit.Dispose(); _programColorBlit.Dispose();
_programColorClear.Dispose(); _programColorClear.Dispose();
_programStrideChange.Dispose(); _programStrideChange.Dispose();
_programColorCopyBetweenMsNonMs.Dispose();
_programConvertIndexBuffer.Dispose(); _programConvertIndexBuffer.Dispose();
_programConvertIndirectData.Dispose(); _programConvertIndirectData.Dispose();
_programColorCopyToNonMs.Dispose();
_programColorDrawToMs.Dispose();
_samplerNearest.Dispose(); _samplerNearest.Dispose();
_samplerLinear.Dispose(); _samplerLinear.Dispose();
_pipeline.Dispose(); _pipeline.Dispose();

View File

@@ -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);
} }

View File

@@ -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,
@@ -1263,7 +1263,6 @@ namespace Ryujinx.Graphics.Vulkan
{ {
FramebufferParams = new FramebufferParams(Device, colors, depthStencil); FramebufferParams = new FramebufferParams(Device, colors, depthStencil);
UpdatePipelineAttachmentFormats(); UpdatePipelineAttachmentFormats();
_newState.SamplesCount = FramebufferParams.AttachmentSamples.Length != 0 ? FramebufferParams.AttachmentSamples[0] : 1;
} }
protected void UpdatePipelineAttachmentFormats() protected void UpdatePipelineAttachmentFormats()
@@ -1279,6 +1278,7 @@ namespace Ryujinx.Graphics.Vulkan
_newState.ColorBlendAttachmentStateCount = (uint)(FramebufferParams.MaxColorAttachmentIndex + 1); _newState.ColorBlendAttachmentStateCount = (uint)(FramebufferParams.MaxColorAttachmentIndex + 1);
_newState.HasDepthStencil = FramebufferParams.HasDepthStencil; _newState.HasDepthStencil = FramebufferParams.HasDepthStencil;
_newState.SamplesCount = FramebufferParams.AttachmentSamples.Length != 0 ? FramebufferParams.AttachmentSamples[0] : 1;
} }
protected unsafe void CreateRenderPass() protected unsafe void CreateRenderPass()
@@ -1307,7 +1307,7 @@ namespace Ryujinx.Graphics.Vulkan
attachmentDescs[i] = new AttachmentDescription( attachmentDescs[i] = new AttachmentDescription(
0, 0,
FramebufferParams.AttachmentFormats[i], FramebufferParams.AttachmentFormats[i],
TextureStorage.ConvertToSampleCountFlags(FramebufferParams.AttachmentSamples[i]), TextureStorage.ConvertToSampleCountFlags(Gd.Capabilities.SupportedSampleCounts, FramebufferParams.AttachmentSamples[i]),
AttachmentLoadOp.Load, AttachmentLoadOp.Load,
AttachmentStoreOp.Store, AttachmentStoreOp.Store,
AttachmentLoadOp.Load, AttachmentLoadOp.Load,

View File

@@ -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)
{ {
@@ -58,7 +58,7 @@ namespace Ryujinx.Graphics.Vulkan
attachmentDescs[i] = new AttachmentDescription( attachmentDescs[i] = new AttachmentDescription(
0, 0,
attachmentFormats[i], attachmentFormats[i],
TextureStorage.ConvertToSampleCountFlags((uint)state.SamplesCount), TextureStorage.ConvertToSampleCountFlags(gd.Capabilities.SupportedSampleCounts, (uint)state.SamplesCount),
AttachmentLoadOp.Load, AttachmentLoadOp.Load,
AttachmentStoreOp.Store, AttachmentStoreOp.Store,
AttachmentLoadOp.Load, AttachmentLoadOp.Load,
@@ -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.

View File

@@ -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)

View File

@@ -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()
{ {

View File

@@ -11,14 +11,19 @@ namespace Ryujinx.Graphics.Vulkan
public void SetRenderTarget(Auto<DisposableImageView> view, uint width, uint height, bool isDepthStencil, VkFormat format) public void SetRenderTarget(Auto<DisposableImageView> view, uint width, uint height, bool isDepthStencil, VkFormat format)
{ {
CreateFramebuffer(view, width, height, isDepthStencil, format); SetRenderTarget(view, width, height, 1u, isDepthStencil, format);
}
public void SetRenderTarget(Auto<DisposableImageView> view, uint width, uint height, uint samples, bool isDepthStencil, VkFormat format)
{
CreateFramebuffer(view, width, height, samples, isDepthStencil, format);
CreateRenderPass(); CreateRenderPass();
SignalStateChange(); SignalStateChange();
} }
private void CreateFramebuffer(Auto<DisposableImageView> view, uint width, uint height, bool isDepthStencil, VkFormat format) private void CreateFramebuffer(Auto<DisposableImageView> view, uint width, uint height, uint samples, bool isDepthStencil, VkFormat format)
{ {
FramebufferParams = new FramebufferParams(Device, view, width, height, isDepthStencil, format); FramebufferParams = new FramebufferParams(Device, view, width, height, samples, isDepthStencil, format);
UpdatePipelineAttachmentFormats(); UpdatePipelineAttachmentFormats();
} }

View File

@@ -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()

View File

@@ -439,7 +439,7 @@ namespace Ryujinx.Graphics.Vulkan
{ {
SType = StructureType.PipelineMultisampleStateCreateInfo, SType = StructureType.PipelineMultisampleStateCreateInfo,
SampleShadingEnable = false, SampleShadingEnable = false,
RasterizationSamples = TextureStorage.ConvertToSampleCountFlags(SamplesCount), RasterizationSamples = TextureStorage.ConvertToSampleCountFlags(gd.Capabilities.SupportedSampleCounts, SamplesCount),
MinSampleShading = 1, MinSampleShading = 1,
AlphaToCoverageEnable = AlphaToCoverageEnable, AlphaToCoverageEnable = AlphaToCoverageEnable,
AlphaToOneEnable = AlphaToOneEnable AlphaToOneEnable = AlphaToOneEnable

View File

@@ -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(

View File

@@ -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" />

View File

@@ -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;
} }

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