From de16d8fa3ed707aee066c120740f1ce844b882a2 Mon Sep 17 00:00:00 2001 From: Evan Husted Date: Wed, 19 Feb 2025 18:24:24 -0600 Subject: [PATCH 01/24] UI: Remove the ability to select Swedish & Norwegian for System language Switch doesn't natively support these languages, they should only be UI language. --- src/Ryujinx/Assets/locales.json | 52 +------------------ .../Views/Settings/SettingsSystemView.axaml | 6 --- 2 files changed, 1 insertion(+), 57 deletions(-) diff --git a/src/Ryujinx/Assets/locales.json b/src/Ryujinx/Assets/locales.json index 698d31730..3b56aba7b 100644 --- a/src/Ryujinx/Assets/locales.json +++ b/src/Ryujinx/Assets/locales.json @@ -4647,56 +4647,6 @@ "zh_TW": "正體中文 (建議)" } }, - { - "ID": "SettingsTabSystemSystemLanguageSwedish", - "Translations": { - "ar_SA": "", - "de_DE": "", - "el_GR": "", - "en_US": "Swedish", - "es_ES": "", - "fr_FR": "", - "he_IL": "", - "it_IT": "", - "ja_JP": "", - "ko_KR": "스웨덴어", - "no_NO": "Svensk", - "pl_PL": "", - "pt_BR": "Sueco", - "ru_RU": "", - "sv_SE": "Svenska", - "th_TH": "", - "tr_TR": "", - "uk_UA": "", - "zh_CN": "瑞典语", - "zh_TW": "" - } - }, - { - "ID": "SettingsTabSystemSystemLanguageNorwegian", - "Translations": { - "ar_SA": "", - "de_DE": "", - "el_GR": "", - "en_US": "Norwegian", - "es_ES": "", - "fr_FR": "", - "he_IL": "", - "it_IT": "", - "ja_JP": "", - "ko_KR": "노르웨이어", - "no_NO": "Norsk", - "pl_PL": "", - "pt_BR": "Norueguês", - "ru_RU": "", - "sv_SE": "Norska", - "th_TH": "", - "tr_TR": "", - "uk_UA": "", - "zh_CN": "挪威语", - "zh_TW": "" - } - }, { "ID": "SettingsTabSystemSystemTimeZone", "Translations": { @@ -24123,4 +24073,4 @@ } } ] -} +} \ No newline at end of file diff --git a/src/Ryujinx/UI/Views/Settings/SettingsSystemView.axaml b/src/Ryujinx/UI/Views/Settings/SettingsSystemView.axaml index 7a9a55848..5daa7f69f 100644 --- a/src/Ryujinx/UI/Views/Settings/SettingsSystemView.axaml +++ b/src/Ryujinx/UI/Views/Settings/SettingsSystemView.axaml @@ -133,12 +133,6 @@ - - - - - - Date: Wed, 19 Feb 2025 23:07:50 -0600 Subject: [PATCH 02/24] UI: `--install-firmware` startup flag. Has the normal UI flow, this is just for systems where the file picker doesn't show up. --- Directory.Packages.props | 2 +- .../UI/ViewModels/MainWindowViewModel.cs | 2 +- src/Ryujinx/UI/Windows/MainWindow.axaml.cs | 17 +++++++++++++++-- src/Ryujinx/Utilities/CommandLineState.cs | 15 +++++++++++++++ 4 files changed, 32 insertions(+), 4 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index f1d7cac61..64867fc5b 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -42,7 +42,7 @@ - + diff --git a/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs b/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs index 7d7674c4e..1dd4a6b6d 100644 --- a/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs @@ -793,7 +793,7 @@ namespace Ryujinx.Ava.UI.ViewModels return false; } - private async Task HandleFirmwareInstallation(string filename) + public async Task HandleFirmwareInstallation(string filename) { try { diff --git a/src/Ryujinx/UI/Windows/MainWindow.axaml.cs b/src/Ryujinx/UI/Windows/MainWindow.axaml.cs index 96b3d08aa..c5f886ff3 100644 --- a/src/Ryujinx/UI/Windows/MainWindow.axaml.cs +++ b/src/Ryujinx/UI/Windows/MainWindow.axaml.cs @@ -139,8 +139,21 @@ namespace Ryujinx.Ava.UI.Windows base.OnApplyTemplate(e); NotificationHelper.SetNotificationManager(this); - - Executor.ExecuteBackgroundAsync(ShowIntelMacWarningAsync); + + Executor.ExecuteBackgroundAsync(async () => + { + await ShowIntelMacWarningAsync(); + FilePath firmwarePath = CommandLineState.FirmwareToInstallPathArg; + if (firmwarePath is not null) + { + if ((firmwarePath.ExistsAsFile && firmwarePath.Extension is "xci" or "zip") || + firmwarePath.ExistsAsDirectory) + await Dispatcher.UIThread.InvokeAsync(() => + ViewModel.HandleFirmwareInstallation(firmwarePath)); + else + Logger.Notice.Print(LogClass.UI, "Invalid firmware type provided. Path must be a directory, or a .zip or .xci file."); + } + }); } private void OnScalingChanged(object sender, EventArgs e) diff --git a/src/Ryujinx/Utilities/CommandLineState.cs b/src/Ryujinx/Utilities/CommandLineState.cs index 3eafb8d01..d6e8f669c 100644 --- a/src/Ryujinx/Utilities/CommandLineState.cs +++ b/src/Ryujinx/Utilities/CommandLineState.cs @@ -1,3 +1,4 @@ +using Gommon; using Ryujinx.Common.Logging; using System.Collections.Generic; @@ -13,6 +14,7 @@ namespace Ryujinx.Ava.Utilities public static string OverrideBackendThreading { get; private set; } public static string OverrideHideCursor { get; private set; } public static string BaseDirPathArg { get; private set; } + public static FilePath FirmwareToInstallPathArg { get; private set; } public static string Profile { get; private set; } public static string LaunchPathArg { get; private set; } public static string LaunchApplicationId { get; private set; } @@ -41,6 +43,19 @@ namespace Ryujinx.Ava.Utilities BaseDirPathArg = args[++i]; + arguments.Add(arg); + arguments.Add(args[i]); + break; + case "--install-firmware": + if (i + 1 >= args.Length) + { + Logger.Error?.Print(LogClass.Application, $"Invalid option '{arg}'"); + + continue; + } + + FirmwareToInstallPathArg = new FilePath(args[++i]); + arguments.Add(arg); arguments.Add(args[i]); break; From 3f59bade94fec8406973e7a64df4b79890425c73 Mon Sep 17 00:00:00 2001 From: Milihraim <112830494+Milihraim@users.noreply.github.com> Date: Thu, 20 Feb 2025 23:32:35 +0300 Subject: [PATCH 03/24] Update Russian Translation (#695) --- src/Ryujinx/Assets/locales.json | 176 ++++++++++++++++---------------- 1 file changed, 88 insertions(+), 88 deletions(-) diff --git a/src/Ryujinx/Assets/locales.json b/src/Ryujinx/Assets/locales.json index 3b56aba7b..ec6f6313c 100644 --- a/src/Ryujinx/Assets/locales.json +++ b/src/Ryujinx/Assets/locales.json @@ -463,7 +463,7 @@ "no_NO": "", "pl_PL": "", "pt_BR": "Abrir Pasta de Capturas de Tela", - "ru_RU": "", + "ru_RU": "Открыть папку со скриншотами", "sv_SE": "", "th_TH": "", "tr_TR": "", @@ -613,7 +613,7 @@ "no_NO": "Start Spillet med UI Gjemt", "pl_PL": "", "pt_BR": "Iniciar Jogos Ocultando a Interface", - "ru_RU": "", + "ru_RU": "Запускать игры скрывая интерфейс", "sv_SE": "Starta spel med dolt användargränssnitt", "th_TH": "", "tr_TR": "", @@ -688,7 +688,7 @@ "no_NO": "_Administrere Brukerprofiler", "pl_PL": "_Zarządzaj profilami użytkowników", "pt_BR": "_Gerenciar Perfis de Usuário", - "ru_RU": "_Менеджер учетных записей", + "ru_RU": "_Менеджер учётных записей", "sv_SE": "_Hantera användarprofiler", "th_TH": "_จัดการโปรไฟล์ผู้ใช้งาน", "tr_TR": "_Kullanıcı Profillerini Yönet", @@ -1563,7 +1563,7 @@ "no_NO": "Utviklet av {0}", "pl_PL": "", "pt_BR": "Desenvolvido por {0}", - "ru_RU": "", + "ru_RU": "Разработана {0}", "sv_SE": "", "th_TH": "", "tr_TR": "", @@ -1863,7 +1863,7 @@ "no_NO": "Kompatibilitet", "pl_PL": "", "pt_BR": "Compatibilidade:", - "ru_RU": "", + "ru_RU": "Совместимость:", "sv_SE": "", "th_TH": "", "tr_TR": "", @@ -1888,7 +1888,7 @@ "no_NO": "Tittel ID:", "pl_PL": "", "pt_BR": "ID do Título:", - "ru_RU": "", + "ru_RU": "ID приложения", "sv_SE": "", "th_TH": "", "tr_TR": "", @@ -1913,7 +1913,7 @@ "no_NO": "Spill som Arrangeres: {0}", "pl_PL": "", "pt_BR": "Jogos Hospedados: {0}", - "ru_RU": "", + "ru_RU": "Запущенно игр: {0}", "sv_SE": "", "th_TH": "", "tr_TR": "", @@ -1938,7 +1938,7 @@ "no_NO": "Online-spillere: {0}", "pl_PL": "", "pt_BR": "Jogadores Online: {0}", - "ru_RU": "", + "ru_RU": "Игроков онлайн: {0}", "sv_SE": "", "th_TH": "", "tr_TR": "", @@ -2288,7 +2288,7 @@ "no_NO": "Tøm PPTC-bufferen", "pl_PL": "", "pt_BR": "Limpar Cache PPTC", - "ru_RU": "", + "ru_RU": "Очистить кэш PPTC", "sv_SE": "", "th_TH": "", "tr_TR": "", @@ -2313,7 +2313,7 @@ "no_NO": "Sletter alle PPTC-cache-filer for applikasjonen", "pl_PL": "", "pt_BR": "Apaga os arquivos de cache PPTC do aplicativo", - "ru_RU": "", + "ru_RU": "Удаляет все файлы кэша PPTC для приложения", "sv_SE": "", "th_TH": "", "tr_TR": "", @@ -2638,7 +2638,7 @@ "no_NO": "Pakk ut RomFS filene fra valgt DLC fil", "pl_PL": "", "pt_BR": "Extraia o RomFS de um arquivo DLC selecionado", - "ru_RU": "", + "ru_RU": "Извлекает файлы RomFS из выбранного файла DLC", "sv_SE": "Extrahera RomFS från en vald DLC-fil", "th_TH": "", "tr_TR": "", @@ -2788,7 +2788,7 @@ "no_NO": "Vis kompatibilitetsoppføring", "pl_PL": "", "pt_BR": "Mostrar Dados de Compatibilidade", - "ru_RU": "", + "ru_RU": "Показать записи о совместимости", "sv_SE": "", "th_TH": "", "tr_TR": "", @@ -2813,7 +2813,7 @@ "no_NO": "Vis det valgte spillet i kompatibilitetslisten, som du vanligvis får tilgang til via Hjelp-menyen.", "pl_PL": "", "pt_BR": "Exibe o jogo selecionado na Lista de Compatibilidade, que normalmente pode ser acessada pelo menu Ajuda.", - "ru_RU": "", + "ru_RU": "Отобразить выбранную игру в списке совместимости, доступ к которому вы обычно можете получить через меню Справки.", "sv_SE": "", "th_TH": "", "tr_TR": "", @@ -2838,7 +2838,7 @@ "no_NO": "Vis Spill Info", "pl_PL": "", "pt_BR": "Mostrar Informações do Jogo", - "ru_RU": "", + "ru_RU": "Показать информацию об игре", "sv_SE": "", "th_TH": "", "tr_TR": "", @@ -2863,7 +2863,7 @@ "no_NO": "Vis statistikk og detaljer om det valgte spillet.", "pl_PL": "", "pt_BR": "Exibe estatísticas e detalhes sobre o jogo selecionado.", - "ru_RU": "", + "ru_RU": "Показывать статистику и подробную информацию о выбранной игре.", "sv_SE": "", "th_TH": "", "tr_TR": "", @@ -3388,7 +3388,7 @@ "no_NO": "Se etter Oppdateringer:", "pl_PL": "", "pt_BR": "Verificar Atualizações:", - "ru_RU": "", + "ru_RU": "Проверка наличия обновлений", "sv_SE": "", "th_TH": "", "tr_TR": "", @@ -3413,7 +3413,7 @@ "no_NO": "Av", "pl_PL": "", "pt_BR": "Desligado", - "ru_RU": "", + "ru_RU": "Отключить", "sv_SE": "", "th_TH": "", "tr_TR": "", @@ -3438,7 +3438,7 @@ "no_NO": "Spør", "pl_PL": "", "pt_BR": "Ao Abrir", - "ru_RU": "", + "ru_RU": "При запуске", "sv_SE": "", "th_TH": "", "tr_TR": "", @@ -3463,7 +3463,7 @@ "no_NO": "Bakgrunn", "pl_PL": "", "pt_BR": "2° Plano", - "ru_RU": "", + "ru_RU": "В фоне", "sv_SE": "", "th_TH": "", "tr_TR": "", @@ -3488,7 +3488,7 @@ "no_NO": "På Emulator Fokus Tapt:", "pl_PL": "", "pt_BR": "Ao Perder o Foco:", - "ru_RU": "", + "ru_RU": "При выходе эмулятора из фокуса", "sv_SE": "", "th_TH": "", "tr_TR": "", @@ -3513,7 +3513,7 @@ "no_NO": "Gjør Ingenting", "pl_PL": "", "pt_BR": "Não Fazer Nada", - "ru_RU": "", + "ru_RU": "Ничего не делать", "sv_SE": "", "th_TH": "", "tr_TR": "", @@ -3538,7 +3538,7 @@ "no_NO": "Blokkinngang", "pl_PL": "", "pt_BR": "Bloquear Controles", - "ru_RU": "", + "ru_RU": "Блокировать управление", "sv_SE": "", "th_TH": "", "tr_TR": "", @@ -3563,7 +3563,7 @@ "no_NO": "Demp Lyd", "pl_PL": "", "pt_BR": "Ficar Mudo", - "ru_RU": "", + "ru_RU": "Отключить звук", "sv_SE": "", "th_TH": "", "tr_TR": "", @@ -3588,7 +3588,7 @@ "no_NO": "Blokker Inputs og demp Volumet", "pl_PL": "", "pt_BR": "Bloquear Controles & Ficar Mudo", - "ru_RU": "", + "ru_RU": "Блокировать управление и отключить звук", "sv_SE": "", "th_TH": "", "tr_TR": "", @@ -3613,7 +3613,7 @@ "no_NO": "Pause Emulatoren", "pl_PL": "", "pt_BR": "Pausar a Emulação", - "ru_RU": "", + "ru_RU": "Поставить паузу", "sv_SE": "", "th_TH": "", "tr_TR": "", @@ -3688,7 +3688,7 @@ "no_NO": "Deaktiver inndata når vinduet er ute av fokus", "pl_PL": "", "pt_BR": "Desativar Controles Quando Estiver Fora de Foco", - "ru_RU": "", + "ru_RU": "Отключает управление при выходе из фокуса", "sv_SE": "", "th_TH": "", "tr_TR": "", @@ -4713,7 +4713,7 @@ "no_NO": "Match systemtid", "pl_PL": "", "pt_BR": "Sincronizar com o Sistema PC", - "ru_RU": "", + "ru_RU": "Соответствовать времени в системе", "sv_SE": "", "th_TH": "", "tr_TR": "", @@ -5138,7 +5138,7 @@ "no_NO": "", "pl_PL": "", "pt_BR": "Ignorar Applet do Controlador", - "ru_RU": "", + "ru_RU": "Игнорировать апплет контроллера", "sv_SE": "", "th_TH": "", "tr_TR": "", @@ -5988,7 +5988,7 @@ "no_NO": "Aktivere UI-logger", "pl_PL": "", "pt_BR": "Habilitar Logs da IU", - "ru_RU": "", + "ru_RU": "Включить журнал интерфейса", "sv_SE": "", "th_TH": "", "tr_TR": "", @@ -6388,7 +6388,7 @@ "no_NO": "Tilbakestill innstillinger", "pl_PL": "", "pt_BR": "Redefinir Configurações", - "ru_RU": "", + "ru_RU": "Сбросить настройки", "sv_SE": "", "th_TH": "", "tr_TR": "", @@ -6413,7 +6413,7 @@ "no_NO": "Jeg vil tilbakestille innstillingene mine.", "pl_PL": "", "pt_BR": "Quero redefinir minhas configurações.", - "ru_RU": "", + "ru_RU": "Я хочу сбросить свои настройки.", "sv_SE": "", "th_TH": "", "tr_TR": "", @@ -8313,7 +8313,7 @@ "no_NO": "", "pl_PL": "", "pt_BR": "", - "ru_RU": "", + "ru_RU": "LED-подсветка", "sv_SE": "", "th_TH": "", "tr_TR": "", @@ -8338,7 +8338,7 @@ "no_NO": "Deaktiver", "pl_PL": "", "pt_BR": "Desabilitar", - "ru_RU": "", + "ru_RU": "Отключить", "sv_SE": "Inaktivera", "th_TH": "", "tr_TR": "", @@ -8363,7 +8363,7 @@ "no_NO": "Regnbue", "pl_PL": "", "pt_BR": "Arco-íris", - "ru_RU": "", + "ru_RU": "Радужная", "sv_SE": "Regnbåge", "th_TH": "", "tr_TR": "", @@ -8388,7 +8388,7 @@ "no_NO": "Regnbue Hastighet", "pl_PL": "", "pt_BR": "Velocidade do Arco-íris", - "ru_RU": "", + "ru_RU": "Скорость переливания", "sv_SE": "", "th_TH": "", "tr_TR": "", @@ -8413,7 +8413,7 @@ "no_NO": "Farge", "pl_PL": "", "pt_BR": "Cor", - "ru_RU": "", + "ru_RU": "Цвет", "sv_SE": "Färg", "th_TH": "", "tr_TR": "", @@ -9438,7 +9438,7 @@ "no_NO": "Numerisk 1", "pl_PL": "", "pt_BR": "", - "ru_RU": "1 (цифровий блок)", + "ru_RU": "Блок цифр 1", "sv_SE": "", "th_TH": "", "tr_TR": "", @@ -11838,7 +11838,7 @@ "no_NO": "Vis/Skjul favorittstatus for spillet", "pl_PL": "Przełącz status Ulubionej Gry", "pt_BR": "Marca ou desmarca o jogo como favorito", - "ru_RU": "Добавляет игру в избранное и помечает звездочкой", + "ru_RU": "Добавляет игру в избранное и помечает звёздочкой", "sv_SE": "Växla favoritstatus för spelet", "th_TH": "สลับสถานะเกมที่ชื่นชอบ", "tr_TR": "Oyunu Favorilere Ekle/Çıkar", @@ -11913,7 +11913,7 @@ "no_NO": "Mørk", "pl_PL": "Ciemny", "pt_BR": "Escuro", - "ru_RU": "Темная", + "ru_RU": "Тёмная", "sv_SE": "Mörkt", "th_TH": "มืด", "tr_TR": "Karanlık", @@ -12113,7 +12113,7 @@ "no_NO": "{0} - Bekreftelse", "pl_PL": "{0} - Potwierdzenie", "pt_BR": "{0} - Confirmação", - "ru_RU": "{0} - Подтверждение", + "ru_RU": "{0} — Подтверждение", "sv_SE": "{0} - Bekräftelse", "th_TH": "{0} - ยืนยัน", "tr_TR": "{0} - Onay", @@ -12138,7 +12138,7 @@ "no_NO": "{0} – Oppdaterer", "pl_PL": "{0} - Asystent aktualizacji", "pt_BR": "{0} - Atualizador", - "ru_RU": "{0} - Обновление", + "ru_RU": "{0} — Обновление", "sv_SE": "{0} - Uppdatering", "th_TH": "{0} - อัพเดต", "tr_TR": "{0} - Güncelleyici", @@ -12163,7 +12163,7 @@ "no_NO": "{0} - Feil", "pl_PL": "{0} - Błąd", "pt_BR": "{0} - Erro", - "ru_RU": "{0} - Ошибка", + "ru_RU": "{0} — Ошибка", "sv_SE": "{0} - Fel", "th_TH": "{0} - ผิดพลาด", "tr_TR": "{0} - Hata", @@ -12188,7 +12188,7 @@ "no_NO": "{0} - Advarsel", "pl_PL": "{0} - Ostrzeżenie", "pt_BR": "{0} - Alerta", - "ru_RU": "{0} - Предупреждение", + "ru_RU": "{0} — Предупреждение", "sv_SE": "{0} - Varning", "th_TH": "{0} - คำเตือน", "tr_TR": "{0} - Uyarı", @@ -12213,7 +12213,7 @@ "no_NO": "{0} - Avslutt", "pl_PL": "{0} - Wyjdź", "pt_BR": "{0} - Sair", - "ru_RU": "{0} - Выход", + "ru_RU": "{0} — Выход", "sv_SE": "{0} - Avslut", "th_TH": "{0} - ออก", "tr_TR": "{0} - Çıkış", @@ -12288,7 +12288,7 @@ "no_NO": "Alle ulagrede data vil gå tapt!", "pl_PL": "Wszystkie niezapisane dane zostaną utracone!", "pt_BR": "Todos os dados que não foram salvos serão perdidos!", - "ru_RU": "Все несохраненные данные будут потеряны", + "ru_RU": "Все несохранённые данные будут потеряны", "sv_SE": "Allt data som inte sparats kommer att förloras!", "th_TH": "ข้อมูลทั้งหมดที่ไม่ได้บันทึกทั้งหมดจะสูญหาย!", "tr_TR": "Kaydedilmeyen bütün veriler kaybedilecek!", @@ -13663,7 +13663,7 @@ "no_NO": "Du er i ferd med å slette alle PPTC-data fra:\n\n{0}\n\n\nEr du sikker på at du vil fortsette?", "pl_PL": "", "pt_BR": "Você está prestes a limpar todos os dados PPTC de:\n\n{0}\n\nTem certeza de que deseja continuar?", - "ru_RU": "", + "ru_RU": "Вы собираетесь удалить все данные PPTC из:\n\n{0}\n\nВы уверены, что хотите продолжить?", "sv_SE": "", "th_TH": "", "tr_TR": "", @@ -14213,7 +14213,7 @@ "no_NO": "Du har gjort endringer i denne brukerprofilen som ikke er lagret.", "pl_PL": "Wprowadziłeś zmiany dla tego profilu użytkownika, które nie zostały zapisane.", "pt_BR": "Você fez alterações para este perfil de usuário que não foram salvas.", - "ru_RU": "В эту учетную запись внесены изменения, которые не были сохранены.", + "ru_RU": "В эту учётную запись внесены изменения, которые не были сохранены.", "sv_SE": "Du har gjort ändringar i denna användarprofil som inte har sparats.", "th_TH": "คุณได้ทำการเปลี่ยนแปลงโปรไฟล์ผู้ใช้นี้โดยไม่ได้รับการบันทึก", "tr_TR": "Kullanıcı profilinizde kaydedilmemiş değişiklikler var.", @@ -15763,7 +15763,7 @@ "no_NO": "Velg om Ryujinx skal vises på din \"spillende\" Discord aktivitet eller ikke", "pl_PL": "Wybierz, czy chcesz wyświetlać Ryujinx w swojej \"aktualnie grane\" aktywności Discord", "pt_BR": "Escolha se deseja mostrar Ryujinx ou não na sua atividade do Discord quando estiver usando-o", - "ru_RU": "Включает или отключает отображение статуса \"Играет в игру\" в Discord", + "ru_RU": "Включает или отключает отображение статуса «Играет в игру» в Discord", "sv_SE": "Välj huruvida Ryujinx ska visas på din \"spelar för tillfället\" Discord-aktivitet", "th_TH": "เลือกว่าจะแสดง Ryujinx ในกิจกรรม Discord \"ที่กำลังเล่นอยู่\" ของคุณหรือไม่?", "tr_TR": "Ryujinx'i \"şimdi oynanıyor\" Discord aktivitesinde göstermeyi veya göstermemeyi seçin", @@ -16338,7 +16338,7 @@ "no_NO": "Endre hvordan gjesteminne tilordnes og åpnes. Påvirker emulator CPU-ytelsen veldig mye.\n\nSett til HOST UNCHECKED hvis usikker.", "pl_PL": "Zmień sposób mapowania i uzyskiwania dostępu do pamięci gości. Znacznie wpływa na wydajność emulowanego procesora.\n\nUstaw na HOST UNCHECKED, jeśli nie masz pewności.", "pt_BR": "Altera como a memória do convidado é mapeada e acessada. Afeta muito o desempenho da CPU emulada.\n\nDefina como HÓSPEDE SEM VERIFICAÇÃO se não tiver certeza.", - "ru_RU": "Меняет разметку и доступ к гостевой памяти. Значительно влияет на производительность процессора.\n\nРекомендуется оставить \"Хост не установлен\"", + "ru_RU": "Меняет разметку и доступ к гостевой памяти. Значительно влияет на производительность процессора.\n\nРекомендуется оставить «Хост не установлен»", "sv_SE": "Ändra hur gästminne mappas och ges åtkomst till. Påverkar emulerad CPU-prestanda mycket.\n\nStäll in till \"Värd inte kontrollerad\" om du är osäker.", "th_TH": "เปลี่ยนวิธีการเข้าถึงหน่วยความจำของผู้เยี่ยมชม ส่งผลอย่างมากต่อประสิทธิภาพการทำงานของ CPU ที่จำลอง\n\nตั้งค่าเป็น ไม่ได้ตรวจสอบโฮสต์ หากคุณไม่แน่ใจ", "tr_TR": "Guest hafızasının nasıl tahsis edilip erişildiğini değiştirir. Emüle edilen CPU performansını ciddi biçimde etkiler.\n\nEmin değilseniz HOST UNCHECKED seçeneğine ayarlayın.", @@ -16513,7 +16513,7 @@ "no_NO": "", "pl_PL": "", "pt_BR": "A caixa de diálogo do Applet do controlador não aparecerá se o controle for desconectado enquanto um aplicativo estiver em execução.\n\nDeixe a opção DESLIGADO se não tiver certeza.", - "ru_RU": "", + "ru_RU": "Диалоговое окно апплета контроллера не будет отображаться, если геймпад отключен во время работы приложения.\n\nОставьте выключенным, если не уверены.", "sv_SE": "", "th_TH": "", "tr_TR": "", @@ -16613,7 +16613,7 @@ "no_NO": "Dobler spillets gjengivelse.\n\nNoen få spill fungerer kanskje ikke med dette aktivert og kan se veldig pikselert ut selv når gjengivelsen er økt; for de spillene, så kan det hende du må bruke modifikasjoner som fjerner anti-aliasing eller som øker den interne gjengivelsen. For og bruke sistnenvte, så vil du helst bruke \"Native\".\n\nHa til tanke at 4x er unødig for virituelt alle maskiner.", "pl_PL": "", "pt_BR": "Multiplica a resolução de renderização do jogo.\n\nAlguns jogos podem não funcionar bem com essa opção e apresentar uma aparência pixelada, mesmo com o aumento da resolução; para esses jogos, talvez seja necessário encontrar mods que removam o anti-aliasing ou aumentem a resolução de renderização interna. Ao usar a segunda opção, provavelmente desejará selecionar Nativa.\n\nEssa opção pode ser alterada enquanto um jogo está em execução, clicando em \"Aplicar\" abaixo; basta mover a janela de configurações para o lado e experimentar até encontrar o visual preferido para o jogo.\n\nLembre-se de que 4x é exagerado para praticamente qualquer configuração.", - "ru_RU": "Увеличивает разрешение рендера игры.\n\nНекоторые игры могут не работать с этой настройкой и выглядеть смазано даже когда разрешение увеличено. Для таких игр может потребоваться установка модов, которые убирают сглаживание или увеличивают разрешение рендеринга. \nДля использования последнего, вам нужно будет выбрать опцию \"Нативное\".\n\nЭта опция может быть изменена во время игры по нажатию кнопки \"Применить\" ниже. Вы можете просто переместить окно настроек в сторону и поэкспериментировать, пока не подберете подходящие настройки для конкретной игры.\n\nИмейте в виду, что \"4x\" является излишеством.", + "ru_RU": "Увеличивает разрешение рендера игры.\n\nНекоторые игры могут не работать с этой настройкой и выглядеть смазано даже когда разрешение увеличено. Для таких игр может потребоваться установка модов, которые убирают сглаживание или увеличивают разрешение рендеринга. \nДля использования последнего, вам нужно будет выбрать опцию «Нативное».\n\nЭта опция может быть изменена во время игры по нажатию кнопки «Применить» ниже. Вы можете просто переместить окно настроек в сторону и поэкспериментировать, пока не подберете подходящие настройки для конкретной игры.\n\nИмейте в виду, что «4x» является излишеством.", "sv_SE": "Multiplicerar spelets renderingsupplösning.\n\nNågra spel kanske inte fungerar med detta och ser pixelerade ut även när upplösningen ökas; för dessa spel så kan du behöva hitta moddar som tar bort anti-aliasing eller som ökar deras interna renderingsupplösning. För att använda det senare, kommer du sannolikt vilja välja Inbyggd.\n\nDet här alternativet kan ändras medan ett spel körs genom att klicka på \"Tillämpa\" nedan. du kan helt enkelt flytta inställningsfönstret åt sidan och experimentera tills du hittar ditt föredragna utseende för ett spel.\n\nTänk på att 4x är overkill för praktiskt taget alla maskiner.", "th_TH": "คูณความละเอียดการเรนเดอร์ของเกม\n\nเกมบางเกมอาจไม่สามารถใช้งานได้และดูเป็นพิกเซลแม้ว่าความละเอียดจะเพิ่มขึ้นก็ตาม สำหรับเกมเหล่านั้น คุณอาจต้องค้นหาม็อดที่ลบรอยหยักของภาพหรือเพิ่มความละเอียดในการเรนเดอร์ภายใน หากต้องการใช้อย่างหลัง คุณอาจต้องเลือก Native\n\nตัวเลือกนี้สามารถเปลี่ยนแปลงได้ในขณะที่เกมกำลังทำงานอยู่โดยคลิก \"นำมาใช้\" ด้านล่าง คุณสามารถย้ายหน้าต่างการตั้งค่าไปด้านข้างและทดลองจนกว่าคุณจะพบรูปลักษณ์ที่คุณต้องการสำหรับเกม\n\nโปรดทราบว่า 4x นั้นเกินความจำเป็นสำหรับการตั้งค่าแทบทุกประเภท", "tr_TR": "", @@ -16988,7 +16988,7 @@ "no_NO": "Skriver ut Avalonia (UI)-loggmeldinger i konsollen.", "pl_PL": "", "pt_BR": "Imprime mensagens de log do Avalonia (UI) no console.", - "ru_RU": "", + "ru_RU": "Выводит сообщения журнала Avalonia (интерфейс) в консоли.", "sv_SE": "", "th_TH": "", "tr_TR": "", @@ -17188,7 +17188,7 @@ "no_NO": "", "pl_PL": "", "pt_BR": "Abre a pasta de capturas de tela do Ryujinx", - "ru_RU": "", + "ru_RU": "Открывает папку скриншотов Ryujinx", "sv_SE": "", "th_TH": "", "tr_TR": "", @@ -17213,7 +17213,7 @@ "no_NO": "Åpner mappen hvor logger er lagret", "pl_PL": "Otwiera folder, w którym zapisywane są logi", "pt_BR": "Abre a pasta onde os logs são gravados", - "ru_RU": "Открывает папку в которую записываются логи", + "ru_RU": "Открывает папку, в которую записываются логи", "sv_SE": "Öppnar mappen där loggarna har skrivits till", "th_TH": "เปิดโฟลเดอร์ ที่เก็บไฟล์ประวัติ", "tr_TR": "Log dosyalarının bulunduğu klasörü açar", @@ -17288,7 +17288,7 @@ "no_NO": "Åpne vindu for brukerprofiler", "pl_PL": "Otwórz okno Menedżera Profili Użytkownika", "pt_BR": "Abrir Janela de Gerenciamento de Perfis", - "ru_RU": "Открыть менеджер учетных записей", + "ru_RU": "Открыть менеджер учётных записей", "sv_SE": "Öppna hanterare för användarprofiler", "th_TH": "เปิดหน้าต่างตัวจัดการโปรไฟล์ผู้ใช้", "tr_TR": "Kullanıcı profil yöneticisi penceresini açar", @@ -17938,7 +17938,7 @@ "no_NO": "Oppdatering tilgjengelig!", "pl_PL": "", "pt_BR": "Atualização Disponível!", - "ru_RU": "", + "ru_RU": "Доступно обновление!", "sv_SE": "", "th_TH": "", "tr_TR": "", @@ -19738,7 +19738,7 @@ "no_NO": "LED-innstillinger", "pl_PL": "", "pt_BR": "Configurações de LED", - "ru_RU": "", + "ru_RU": "Настройки LED-подсветки", "sv_SE": "LED-inställningar", "th_TH": "", "tr_TR": "", @@ -20338,7 +20338,7 @@ "no_NO": "Bruker Profiler Behandler", "pl_PL": "Menedżer Profili Użytkowników", "pt_BR": "Gerenciador de Perfis de Usuário", - "ru_RU": "Менеджер учетных записей", + "ru_RU": "Менеджер учётных записей", "sv_SE": "Hanterare för användarprofiler", "th_TH": "จัดการโปรไฟล์ผู้ใช้", "tr_TR": "Kullanıcı Profillerini Yönet", @@ -21663,7 +21663,7 @@ "no_NO": "Velg grafikkkortet som skal brukes sammen med Vulkan grafikkbackenden\n\nPåvirker ikke GPU-er som OpenGL skal bruke.\n\nSett til GPU-merket som \"dGPU\" hvis usikker. Hvis det ikke det er en, la være uberørt.", "pl_PL": "Wybierz kartę graficzną, która będzie używana z backendem graficznym Vulkan.\n\nNie wpływa na GPU używane przez OpenGL.\n\nW razie wątpliwości ustaw flagę GPU jako \"dGPU\". Jeśli żadnej nie ma, pozostaw nietknięte.", "pt_BR": "Selecione a placa de vídeo que será usada com o renderizador gráfico Vulkan.\n\nNão afeta a GPU que OpenGL usará.\n\nSelecione \"dGPU\" em caso de dúvida. Se não houver nenhuma, não mexa.", - "ru_RU": "Выберает видеоадаптер, который будет использоваться графическим бэкендом Vulkan.\n\nЭта настройка не влияет на видеоадаптер, который будет использоваться с OpenGL.\n\nЕсли вы не уверены что нужно выбрать, используйте графический процессор, помеченный как \"dGPU\". Если его нет, оставьте выбор по умолчанию.", + "ru_RU": "Выберает видеоадаптер, который будет использоваться графическим бэкендом Vulkan.\n\nЭта настройка не влияет на видеоадаптер, который будет использоваться с OpenGL.\n\nЕсли вы не уверены что нужно выбрать, используйте графический процессор, помеченный как «dGPU». Если его нет, оставьте выбор по умолчанию.", "sv_SE": "Välj grafikkortet som ska användas med Vulkan-grafikbakänden.\n\nPåverkar inte GPU:n som OpenGL använder.\n\nStäll in till den GPU som flaggats som \"dGPU\" om osäker. Om det inte finns någon, lämna orörd.", "th_TH": "เลือกการ์ดจอที่จะใช้กับแบ็กเอนด์กราฟิก Vulkan\n\nไม่ส่งผลต่อ GPU ที่ OpenGL จะใช้\n\nตั้งค่าเป็น GPU ที่ถูกตั้งค่าสถานะเป็น \"dGPU\" ถ้าหากคุณไม่แน่ใจ ,หากไม่มีก็ปล่อยทิ้งไว้โดยไม่ต้องแตะต้องมัน", "tr_TR": "Vulkan Grafik Arka Ucu ile kullanılacak Ekran Kartını Seçin.\n\nOpenGL'nin kullanacağı GPU'yu etkilemez.\n\n Emin değilseniz \"dGPU\" olarak işaretlenmiş GPU'ya ayarlayın. Eğer yoksa, dokunmadan bırakın.\n", @@ -22163,7 +22163,7 @@ "no_NO": "Gjenopprett tapte kontoer", "pl_PL": "Odzyskaj Utracone Konta", "pt_BR": "Recuperar Contas Perdidas", - "ru_RU": "Восстановить учетные записи", + "ru_RU": "Восстановить учётные записи", "sv_SE": "Återskapa förlorade konton", "th_TH": "กู้คืนบัญชีที่สูญหาย", "tr_TR": "Kayıp Hesapları Kurtar", @@ -22238,7 +22238,7 @@ "no_NO": "Ingen profiler å gjenopprette", "pl_PL": "Brak profili do odzyskania", "pt_BR": "Nenhum perfil para recuperar", - "ru_RU": "Нет учетных записей для восстановления", + "ru_RU": "Нет учётных записей для восстановления", "sv_SE": "Inga profiler att återskapa", "th_TH": "ไม่มีโปรไฟล์ที่สามารถกู้คืนได้", "tr_TR": "Kurtarılacak profil bulunamadı", @@ -22263,7 +22263,7 @@ "no_NO": "Aktiverer anti-aliasing til spill render.\n\nFXAA vil gjøre det meste av bildet, mens SMAA vil forsøke å finne berørte kanter og glatte dem ut.\n\nAnbefales ikke til bruk i forbindelse med FSR-skaleringsfilteret.\n\nDette valget kan endres mens et spill kjører ved å klikke \"Apply\" nedenfor; du kan bare flytte innstillingsvinduet til du finner det foretrukne utseendet til et spill.\n\nForlat på NONE hvis usikker.", "pl_PL": "", "pt_BR": "Aplica anti-aliasing à renderização do jogo.\n\nFXAA borrará a maior parte da imagem, enquanto SMAA tentará identificar e suavizar bordas serrilhadas.\n\nNão é recomendado usar em conjunto com o filtro de escala FSR.\n\nEssa opção pode ser alterada enquanto o jogo está em execução clicando em \"Aplicar\" abaixo; basta mover a janela de configurações para o lado e experimentar até encontrar o visual preferido para o jogo.\n\nDeixe em NENHUM se estiver em dúvida.", - "ru_RU": "Применимое сглаживание для рендера.\n\nFXAA размывает большую часть изображения, SMAA попытается найти \"зазубренные\" края и сгладить их.\n\nНе рекомендуется использовать вместе с масштабирующим фильтром FSR.\n\nЭта опция может быть изменена во время игры по нажатию \"Применить\" ниже; \nВы можете просто переместить окно настроек в сторону и поэкспериментировать, пока не найдёте подходящую настройку игры.\n\nРекомендуется использовать \"Нет\".", + "ru_RU": "Применимое сглаживание для рендера.\n\nFXAA размывает большую часть изображения, SMAA попытается найти «зазубренные» края и сгладить их.\n\nНе рекомендуется использовать вместе с масштабирующим фильтром FSR.\n\nЭта опция может быть изменена во время игры по нажатию «Применить» ниже; \nВы можете просто переместить окно настроек в сторону и поэкспериментировать, пока не найдёте подходящую настройку игры.\n\nРекомендуется использовать «Нет».", "sv_SE": "Tillämpar anti-aliasing på spelrenderaren.\n\nFXAA kommer att sudda det mesta av bilden, medan SMAA kommer att försöka hitta taggiga kanter och släta ut dem.\n\nRekommenderas inte att använda tillsammans med skalfiltret FSR.\n\nDet här alternativet kan ändras medan ett spel körs genom att klicka på \"Tillämpa\" nedan. Du kan helt enkelt flytta inställningsfönstret åt sidan och experimentera tills du hittar ditt föredragna utseende för ett spel.\n\nLämna som INGEN om du är osäker.", "th_TH": "ใช้การลดรอยหยักกับการเรนเดอร์เกม\n\nFXAA จะเบลอภาพส่วนใหญ่ ในขณะที่ SMAA จะพยายามค้นหารอยหยักและปรับให้เรียบ\n\nไม่แนะนำให้ใช้ร่วมกับตัวกรองสเกล FSR\n\nตัวเลือกนี้สามารถเปลี่ยนแปลงได้ในขณะที่เกมกำลังทำงานอยู่โดยคลิก \"นำไปใช้\" ด้านล่าง คุณสามารถย้ายหน้าต่างการตั้งค่าไปด้านข้างและทดลองจนกว่าคุณจะพบรูปลักษณ์ที่คุณต้องการสำหรับเกม\n\nปล่อยไว้ที่ NONE หากไม่แน่ใจ", "tr_TR": "", @@ -22338,7 +22338,7 @@ "no_NO": "Velg det skaleringsfilteret som skal brukes når du bruker oppløsningsskalaen.\n\nBilinear fungerer godt for 3D-spill og er et trygt standardalternativ.\n\nNærmeste anbefales for pixel kunst-spill.\n\nFSR 1.0 er bare et skarpere filter, ikke anbefalt for bruk med FXAA eller SMAA.\n\nOmrådeskalering anbefales når nedskalering er større enn utgangsvinduet. Den kan brukes til å oppnå en superprøvetaket anti-aliasingseffekt når en nedskalerer med mer enn 2x.\n\nDette valget kan endres mens et spill kjører ved å klikke \"Apply\" nedenfor; du kan bare flytte innstillingsvinduet til du finner det foretrukne utseendet til et spill.\n\nLa være på BILINEAR hvis usikker.", "pl_PL": "Choose the scaling filter that will be applied when using resolution scale.\n\nBilinear works well for 3D games and is a safe default option.\n\nNearest is recommended for pixel art games.\n\nFSR 1.0 is merely a sharpening filter, not recommended for use with FXAA or SMAA.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on BILINEAR if unsure.", "pt_BR": "Escolha o filtro de escala que será aplicado ao usar a escala de resolução.\n\nBilinear funciona bem para jogos 3D e é uma opção padrão segura.\n\nNearest é recomendado para jogos em pixel art.\n\nFSR 1.0 é apenas um filtro de nitidez, não recomendado para uso com FXAA ou SMAA.\n\nEssa opção pode ser alterada enquanto o jogo está em execução, clicando em \"Aplicar\" abaixo; basta mover a janela de configurações para o lado e experimentar até encontrar o visual preferido para o jogo.\n\nMantenha em BILINEAR se estiver em dúvida.", - "ru_RU": "Фильтрация текстур, которая будет применяться при масштабировании.\n\nБилинейная хорошо работает для 3D-игр и является настройкой по умолчанию.\n\nСтупенчатая рекомендуется для пиксельных игр.\n\nFSR это фильтр резкости, который не рекомендуется использовать с FXAA или SMAA.\n\nЭта опция может быть изменена во время игры по нажатию кнопки \"Применить\" ниже; \nВы можете просто переместить окно настроек в сторону и поэкспериментировать, пока не подберете подходящие настройки для конкретной игры.\n\nРекомендуется использовать \"Билинейная\".", + "ru_RU": "Фильтрация текстур, которая будет применяться при масштабировании.\n\nБилинейная хорошо работает для 3D-игр и является настройкой по умолчанию.\n\nСтупенчатая рекомендуется для пиксельных игр.\n\nFSR это фильтр резкости, который не рекомендуется использовать с FXAA или SMAA.\n\nЗональная рекомендуется в случае использования разрешения больше разрешения окна. Можно использовать для достижения эффекта суперсемплига (SSAA) при даунскейле более чем в 2 раза.\n\nЭта опция может быть изменена во время игры по нажатию кнопки «Применить» ниже; \nВы можете просто переместить окно настроек в сторону и поэкспериментировать, пока не подберете подходящие настройки для конкретной игры.\n\nРекомендуется использовать «Билинейная».", "sv_SE": "Välj det skalfilter som ska tillämpas vid användning av upplösningsskala.\n\nBilinjär fungerar bra för 3D-spel och är ett säkert standardalternativ.\n\nNärmast rekommenderas för pixel art-spel.\n\nFSR 1.0 är bara ett skarpningsfilter, rekommenderas inte för FXAA eller SMAA.\n\nOmrådesskalning rekommenderas vid nedskalning av upplösning som är större än utdatafönstret. Det kan användas för att uppnå en supersamplad anti-alias-effekt vid nedskalning med mer än 2x.\n\nDetta alternativ kan ändras medan ett spel körs genom att klicka på \"Tillämpa\" nedan. du kan helt enkelt flytta inställningsfönstret åt sidan och experimentera tills du hittar ditt föredragna utseende för ett spel.\n\nLämna som BILINJÄR om du är osäker.", "th_TH": "เลือกตัวกรองสเกลที่จะใช้เมื่อใช้สเกลความละเอียด\n\nBilinear ทำงานได้ดีกับเกม 3D และเป็นตัวเลือกเริ่มต้นที่ปลอดภัย\n\nแนะนำให้ใช้เกมภาพพิกเซลที่ใกล้เคียงที่สุด\n\nFSR 1.0 เป็นเพียงตัวกรองความคมชัด ไม่แนะนำให้ใช้กับ FXAA หรือ SMAA\n\nตัวเลือกนี้สามารถเปลี่ยนแปลงได้ในขณะที่เกมกำลังทำงานอยู่โดยคลิก \"นำไปใช้\" ด้านล่าง คุณสามารถย้ายหน้าต่างการตั้งค่าไปด้านข้างและทดลองจนกว่าคุณจะพบรูปลักษณ์ที่คุณต้องการสำหรับเกม", "tr_TR": "Choose the scaling filter that will be applied when using resolution scale.\n\nBilinear works well for 3D games and is a safe default option.\n\nNearest is recommended for pixel art games.\n\nFSR 1.0 is merely a sharpening filter, not recommended for use with FXAA or SMAA.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on BILINEAR if unsure.", @@ -22488,7 +22488,7 @@ "no_NO": "Definer FSR 1,0 skarpere nivå. Høyere er skarpere.", "pl_PL": "Ustaw poziom ostrzeżenia FSR 1.0. Wyższy jest ostrzejszy.", "pt_BR": "Defina o nível de nitidez do FSR 1.0. Quanto maior, mais nítido.", - "ru_RU": "Выбор режима работы FSR 1.0. Выше - четче.", + "ru_RU": "Выбор режима работы FSR 1.0. Выше — четче.", "sv_SE": "Ställ in nivå för FSR 1.0 sharpening. Högre är skarpare.", "th_TH": "ตั้งค่าระดับความคมชัด FSR 1.0 ยิ่งสูงกว่าจะยิ่งคมชัดกว่า", "tr_TR": "", @@ -22688,7 +22688,7 @@ "no_NO": "Nettverksgrensesnittets grensesnitt brukt for LAN/LDN funksjoner.\n\ni konjuksjon med en VPN eller XLink Kai og ett spill med LAN støtte, kan bli brukt til og spoofe ett \"samme-nettverk\" tilkobling over nettet.\n\nLa være på DEFAULT om usikker.", "pl_PL": "Interfejs sieciowy używany dla funkcji LAN/LDN.\n\nw połączeniu z VPN lub XLink Kai i grą z obsługą sieci LAN, może być użyty do spoofowania połączenia z tą samą siecią przez Internet.\n\nZostaw DOMYŚLNE, jeśli nie ma pewności.", "pt_BR": "A interface de rede usada para recursos de LAN/LDN.\n\nEm conjunto com uma VPN ou XLink Kai e um jogo com suporte a LAN, pode ser usada para simular uma conexão na mesma rede pela Internet.\n\nMantenha em PADRÃO se estiver em dúvida.", - "ru_RU": "Сетевой интерфейс, используемый для функций LAN/LDN.\n\nМожет использоваться для игры через интернет в сочетании с VPN или XLink Kai и игрой с поддержкой LAN.\n\nРекомендуется использовать \"По умолчанию\".", + "ru_RU": "Сетевой интерфейс, используемый для функций LAN/LDN.\n\nМожет использоваться для игры через интернет в сочетании с VPN или XLink Kai и игрой с поддержкой LAN.\n\nРекомендуется использовать «По умолчанию».", "sv_SE": "Nätverksgränssnittet som används för LAN/LDN-funktioner.\n\nTillsammans med en VPN eller XLink Kai och ett spel med LAN-stöd så kan detta användas för att spoofa en same-network-anslutning över internet.\n\nLämna som STANDARD om du är osäker.", "th_TH": "อินเทอร์เฟซเครือข่ายที่ใช้สำหรับคุณสมบัติ LAN/LDN\n\nเมื่อใช้ร่วมกับ VPN หรือ XLink Kai และเกมที่รองรับ LAN สามารถใช้เพื่อปลอมการเชื่อมต่อเครือข่ายเดียวกันผ่านทางอินเทอร์เน็ต\n\nปล่อยให้เป็น ค่าเริ่มต้น หากคุณไม่แน่ใจ", "tr_TR": "", @@ -22863,7 +22863,7 @@ "no_NO": "Endre LDN flerspillermodus.\n\nLdnMitm vil endre lokal trådløst/lokal spillfunksjonalitet i spill som skal fungere som om den var LAN, noe som tillater lokal, samme nettverk forbindelser med andre Ryujinx instanser og hacket Nintendo Switch konsoller som har installert ldn_mitm-modulen.\n\nFlerspiller krever at alle spillerne er på samme versjon (dvs. Super Smash Bros. Ultimat v13.0.1 kan ikke koble til v13.0.0).\n\nForlat DEAKTIVERT hvis usikker.", "pl_PL": "", "pt_BR": "Alterar o modo multiplayer LDN.\n\nLdnMitm modificará a funcionalidade de jogo sem fio/local nos jogos para funcionar como se fosse LAN, permitindo conexões locais, na mesma rede, com outras instâncias do Ryujinx e consoles Nintendo Switch hackeados que possuem o módulo ldn_mitm instalado.\n\nO multiplayer exige que todos os jogadores estejam na mesma versão do jogo (ex.: Super Smash Bros. Ultimate v13.0.1 não consegue se conectar à v13.0.0).\n\nDeixe DESATIVADO se estiver em dúvida.", - "ru_RU": "Меняет многопользовательский режим LDN.\n\nLdnMitm модифицирует функциональность локальной беспроводной/игры на одном устройстве в играх, позволяя играть с другими пользователями Ryujinx или взломанными консолями Nintendo Switch с установленным модулем ldn_mitm, находящимися в одной локальной сети друг с другом.\n\nМногопользовательская игра требует наличия у всех игроков одной и той же версии игры (т.е. Super Smash Bros. Ultimate v13.0.1 не может подключиться к v13.0.0).\n\nРекомендуется оставить отключенным.", + "ru_RU": "Меняет многопользовательский режим LDN.\n\nLdnMitm модифицирует функциональность локальной беспроводной/игры на одном устройстве в играх, позволяя играть с другими пользователями Ryujinx или взломанными консолями Nintendo Switch с установленным модулем ldn_mitm, находящимися в одной локальной сети друг с другом.\n\nМногопользовательская игра требует наличия у всех игроков одной и той же версии игры (т.е. Super Smash Bros. Ultimate v13.0.1 не может подключиться к v13.0.0).\n\nРекомендуется оставить выключенным.", "sv_SE": "Ändra LDN-flerspelarläge\n\nLdnMitm kommer att ändra lokal funktionalitet för trådlös/lokalt spel att fungera som om det vore ett LAN, vilket ger stöd för anslutningar med local och same-network med andra Ryujinx-instanser och hackade Nintendo Switch-konsoller som har modulen ldn_mitm installerad.\n\nFlerspelare kräver att alla spelare har samma spelversion (t.ex. Super Smash Bros. Ultimate v13.0.1 kan inte ansluta till v13.0.0).\n\nLämna INAKTIVERAD om du är osäker.", "th_TH": "เปลี่ยนโหมดผู้เล่นหลายคนของ LDN\n\nLdnMitm จะปรับเปลี่ยนฟังก์ชันการเล่นแบบไร้สาย/ภายใน จะให้เกมทำงานเหมือนกับว่าเป็น LAN ช่วยให้สามารถเชื่อมต่อภายในเครือข่ายเดียวกันกับอินสแตนซ์ Ryujinx อื่น ๆ และคอนโซล Nintendo Switch ที่ถูกแฮ็กซึ่งมีโมดูล ldn_mitm ติดตั้งอยู่\n\nผู้เล่นหลายคนต้องการให้ผู้เล่นทุกคนอยู่ในเกมเวอร์ชันเดียวกัน (เช่น Super Smash Bros. Ultimate v13.0.1 ไม่สามารถเชื่อมต่อกับ v13.0.0)\n\nปล่อยให้ปิดการใช้งานหากไม่แน่ใจ", "tr_TR": "", @@ -23213,7 +23213,7 @@ "no_NO": "Ugyldig passordfrase! Må være i formatet \"Ryujinx-<8 hex tegn>\"", "pl_PL": "", "pt_BR": "Frase-senha inválida! Deve estar no formato \"Ryujinx-<8 hex chars>\"", - "ru_RU": "Неверный пароль! Пароль должен быть в формате \"Ryujinx-<8 шестнадцатеричных символов>\"", + "ru_RU": "Неверный пароль! Пароль должен быть в формате «Ryujinx-<8 шестнадцатеричных символов>»", "sv_SE": "Ogiltig lösenfras! Måste vara i formatet \"Ryujinx-<8 hextecken>\"", "th_TH": "", "tr_TR": "", @@ -23363,7 +23363,7 @@ "no_NO": "Emulert vertikal synkronisering. «Switch» emulerer Switchs oppdateringsfrekvens på 60 Hz. «Ubegrenset» er en ubegrenset oppdateringsfrekvens.", "pl_PL": "", "pt_BR": "Sincronização vertical emulada. 'Switch' emula a taxa de atualização de 60 Hz do Switch. 'Ilimitada' é uma taxa de atualização sem limite.", - "ru_RU": "Эмулированная вертикальная синхронизация. 'Консоль' эмулирует частоту обновления консоли, равную 60 Гц. 'Без ограничений' - неограниченная частота кадров.", + "ru_RU": "Эмулированная вертикальная синхронизация. «Консоль» эмулирует частоту обновления консоли, равную 60 Гц. «Без ограничений» — неограниченная частота кадров.", "sv_SE": "Emulerad vertikal synk. 'Switch' emulerar Switchens uppdateringsfrekvens på 60Hz. 'Obunden' är en obegränsad uppdateringsfrekvens.", "th_TH": "", "tr_TR": "", @@ -23388,7 +23388,7 @@ "no_NO": "Emulert vertikal synkronisering. «Switch» emulerer Switchs oppdateringsfrekvens på 60 Hz. «Ubegrenset» er en ubegrenset oppdateringsfrekvens. «Egendefinert» emulerer den angitte egendefinerte oppdateringsfrekvensen.", "pl_PL": "", "pt_BR": "Sincronização Vertical Emulada. 'Switch' emula a taxa de atualização de 60 Hz do Switch. 'Ilimitada' é uma taxa de atualização sem limite. 'Taxa de atualização personalizada' emula a taxa de atualização personalizada especificada.", - "ru_RU": "Эмулированная вертикальная синхронизация. 'Консоль' эмулирует частоту обновления консоли, равную 60 Гц. 'Без ограничений' - неограниченная частота кадров. 'Пользовательска частота кадров' эмулирует выбранную пользователем частоту кадров.", + "ru_RU": "Эмулированная вертикальная синхронизация. «Консоль» эмулирует частоту обновления консоли, равную 60 Гц. «Без ограничений» — неограниченная частота кадров. «Пользовательска частота кадров» эмулирует выбранную пользователем частоту кадров.", "sv_SE": "Emulerad vertikal synk. 'Switch' emulerar Switchens uppdateringsfrekvens på 60Hz. 'Obunden' är en obegränsad uppdateringsfrekvens. 'Anpassad uppdateringsfrekvens' emulerar den angivna anpassade uppdateringsfrekvensen.", "th_TH": "", "tr_TR": "", @@ -23638,7 +23638,7 @@ "no_NO": "Sist oppdatert: {0}", "pl_PL": "", "pt_BR": "Última atualização: {0}", - "ru_RU": "", + "ru_RU": "Последнее обновление: {0}", "sv_SE": "Senast uppdaterad: {0}", "th_TH": "", "tr_TR": "", @@ -23663,7 +23663,7 @@ "no_NO": "Denne kompatibilitetslisten kan inneholde oppføringer som er tomme for data.\nVær ikke imot å teste spill i statusen «Ingame».", "pl_PL": "", "pt_BR": "Esta lista de compatibilidade pode estar desatualizada.\nNão se oponha a testar os jogos", - "ru_RU": "", + "ru_RU": "В списке совместимости могут содержаться устаревшие записи.\nНе стестняйтесь тестировать игр в статусе «Запускается»", "sv_SE": "Denna kompatibilitetslista kan innehålla utdaterade poster.\nTesta gärna spelen som listas med \"Spelproblem\"-status.", "th_TH": "", "tr_TR": "", @@ -23688,7 +23688,7 @@ "no_NO": "Søk i kompatibilitetsoppføringer...", "pl_PL": "", "pt_BR": "Pesquisa de compatibilidade", - "ru_RU": "", + "ru_RU": "Поиск записей о совместимости...", "sv_SE": "Sök i kompatibilitetsposter...", "th_TH": "", "tr_TR": "", @@ -23713,7 +23713,7 @@ "no_NO": "Åpne kompatibilitetslisten", "pl_PL": "", "pt_BR": "Lista de Compatibilidade", - "ru_RU": "", + "ru_RU": "Открыть список совместимости", "sv_SE": "Öppna kompatibilitetslistan", "th_TH": "", "tr_TR": "", @@ -23738,7 +23738,7 @@ "no_NO": "Vis bare eide spill", "pl_PL": "", "pt_BR": "Mostrar apenas jogos disponíveis", - "ru_RU": "", + "ru_RU": "Показывать только свои игры", "sv_SE": "Visa endast ägda spel", "th_TH": "", "tr_TR": "", @@ -23763,7 +23763,7 @@ "no_NO": "Spillbar", "pl_PL": "", "pt_BR": "Jogável", - "ru_RU": "", + "ru_RU": "Играбельно", "sv_SE": "Spelbart", "th_TH": "", "tr_TR": "", @@ -23788,7 +23788,7 @@ "no_NO": "", "pl_PL": "", "pt_BR": "No jogo", - "ru_RU": "", + "ru_RU": "Запускается", "sv_SE": "Spelproblem", "th_TH": "", "tr_TR": "", @@ -23813,7 +23813,7 @@ "no_NO": "Menyer", "pl_PL": "", "pt_BR": "Menu", - "ru_RU": "", + "ru_RU": "Меню", "sv_SE": "Menyer", "th_TH": "", "tr_TR": "", @@ -23838,7 +23838,7 @@ "no_NO": "Starter", "pl_PL": "", "pt_BR": "Inicializa", - "ru_RU": "", + "ru_RU": "Стартует", "sv_SE": "Startar", "th_TH": "", "tr_TR": "", @@ -23863,7 +23863,7 @@ "no_NO": "Ingenting", "pl_PL": "", "pt_BR": "Nada", - "ru_RU": "", + "ru_RU": "Ничего", "sv_SE": "Ingenting", "th_TH": "", "tr_TR": "", @@ -23888,7 +23888,7 @@ "no_NO": "Starter opp og spiller uten krasj eller GPU-feil av noe slag, og med en hastighet som er rask nok til å ha rimelig glede av på en gjennomsnittlig PC.", "pl_PL": "", "pt_BR": "Inicializa e roda sem travamentos ou bugs de GPU de qualquer tipo, e em uma velocidade rápida o suficiente para ser aproveitado em um PC comum.", - "ru_RU": "", + "ru_RU": "Запускается и работает без любого рода сбоев или графисечких ошибок и на скорости, достаточной для работы на обычном ПК.", "sv_SE": "", "th_TH": "", "tr_TR": "", @@ -23913,7 +23913,7 @@ "no_NO": "Starter og går i gang i spillet, men lider av ett eller flere av følgende: krasjer, fastlåser, GPU-feil, distraherende dårlig lyd eller er rett og slett for tregt. Spillet kan fortsatt spilles helt til ende, men ikke slik det er ment å spilles.", "pl_PL": "", "pt_BR": "Inicializa e entra no jogo, mas sofre de um ou mais dos seguintes: travamentos, deadlocks, bugs de GPU, áudio ruim que distrai ou é simplesmente muito lento. O jogo ainda pode ser jogado até o fim, mas não da forma como foi criado para ser jogado.", - "ru_RU": "", + "ru_RU": "Запускается и работает, но возникает одна или несколько из следующих проблем: сбои, взаимоблокировки, ошибки GPU, отвлекающие звуки или просто слишком медленная работа. Возможно, игру всё же удастся пройти до конца, но не так, как она задумана.", "sv_SE": "", "th_TH": "", "tr_TR": "", @@ -23938,7 +23938,7 @@ "no_NO": "Starter opp og går forbi tittelskjermen, men kommer ikke inn i hovedspillet.", "pl_PL": "", "pt_BR": "Inicializa e passa da tela de título, mas não entra no jogo principal.", - "ru_RU": "", + "ru_RU": "Загружается титульный экран и можно перейти дальше, но сама игра не работает.", "sv_SE": "", "th_TH": "", "tr_TR": "", @@ -23963,7 +23963,7 @@ "no_NO": "Starter, men kommer ikke lenger enn til tittelskjermen.", "pl_PL": "", "pt_BR": "Inizializa, mas não passa da tela de título.", - "ru_RU": "", + "ru_RU": "Загружается, но не проходит дальше титульного экрана.", "sv_SE": "", "th_TH": "", "tr_TR": "", @@ -23988,7 +23988,7 @@ "no_NO": "Starter ikke opp eller viser ingen tegn til aktivitet.", "pl_PL": "", "pt_BR": "Não inicializa ou não mostra sinais de atividade.", - "ru_RU": "", + "ru_RU": "Не запускается или не подаёт признаков жизни.", "sv_SE": "", "th_TH": "", "tr_TR": "", @@ -24013,7 +24013,7 @@ "no_NO": "Velg en DLC og hente ut", "pl_PL": "", "pt_BR": "Selecione um DLC para Extrair", - "ru_RU": "", + "ru_RU": "Выберите DLC для извлечения", "sv_SE": "Välj en DLC att extrahera", "th_TH": "", "tr_TR": "", @@ -24038,7 +24038,7 @@ "no_NO": "Rikt nærværsbilde", "pl_PL": "", "pt_BR": "Imagem da Presença do Discord", - "ru_RU": "", + "ru_RU": "Изображение для статуса активности", "sv_SE": "", "th_TH": "", "tr_TR": "", @@ -24063,7 +24063,7 @@ "no_NO": "Dynamisk og rik tilstedeværelse", "pl_PL": "", "pt_BR": "Presença Dinâmica do Discord", - "ru_RU": "", + "ru_RU": "Динамический статус активности", "sv_SE": "", "th_TH": "", "tr_TR": "", @@ -24073,4 +24073,4 @@ } } ] -} \ No newline at end of file +} From 4a4078865fc41b4895a54ecbbf08b4765c16a6d7 Mon Sep 17 00:00:00 2001 From: FluffyOMC <45863583+FluffyOMC@users.noreply.github.com> Date: Thu, 20 Feb 2025 17:09:14 -0500 Subject: [PATCH 04/24] Add Melatonin to compatibility list (#667) It's playable, no bugs, and can run pretty easily way above intended FPS, which shows there's not really any performance issues lol. ![image](https://github.com/user-attachments/assets/63402c9f-2412-4b43-9e5d-42a19436ac55) --- docs/compatibility.csv | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/compatibility.csv b/docs/compatibility.csv index 4cad8e8db..5f6ead1eb 100644 --- a/docs/compatibility.csv +++ b/docs/compatibility.csv @@ -1800,6 +1800,7 @@ 010005A00B312000,"Megaton Rainfall",gpu;opengl,boots,2022-08-04 18:29:43 0100EA100DF92000,"Meiji Katsugeki Haikara Ryuuseigumi - Seibai Shimaseu, Yonaoshi Kagyou",32-bit;nvdec,playable,2022-12-05 13:19:12 0100B360068B2000,"Mekorama",gpu,boots,2021-06-17 16:37:21 +010012301932A000,"Melatonin",,playable,2025-02-16 04:08:17 01000FA010340000,"Melbits World",nvdec;online,menus,2021-11-26 13:51:22 0100F68019636000,"Melon Journey",,playable,2023-04-23 21:20:01 010079C012896000,"Memories Off -Innocent Fille- for Dearest",,playable,2020-08-04 07:31:22 From e0ddbe55c052ee943d8b7bc541dad2f1c09d12da Mon Sep 17 00:00:00 2001 From: rrondo <46533574+rrondo@users.noreply.github.com> Date: Fri, 21 Feb 2025 02:11:56 +0200 Subject: [PATCH 05/24] Ukrainian localization changes (1.2.82) (#678) Some changes and new lines for Ukrainian (uk_UA) localization. --- src/Ryujinx/Assets/locales.json | 156 ++++++++++++++++---------------- 1 file changed, 78 insertions(+), 78 deletions(-) diff --git a/src/Ryujinx/Assets/locales.json b/src/Ryujinx/Assets/locales.json index ec6f6313c..6e2577182 100644 --- a/src/Ryujinx/Assets/locales.json +++ b/src/Ryujinx/Assets/locales.json @@ -142,7 +142,7 @@ "sv_SE": "Direkt musåtkomst", "th_TH": "เข้าถึงเมาส์ได้โดยตรง", "tr_TR": "Doğrudan Mouse Erişimi", - "uk_UA": "Прямий доступ мишею", + "uk_UA": "Пряме керування мишею", "zh_CN": "直通鼠标操作", "zh_TW": "滑鼠直接存取" } @@ -417,7 +417,7 @@ "sv_SE": "Läs in titeluppdateringar från mapp", "th_TH": "โหลดไฟล์อัพเดตจากโฟลเดอร์", "tr_TR": "", - "uk_UA": "Завантажити оновлення заголовків з теки", + "uk_UA": "Завантажити оновлення ігор з теки", "zh_CN": "从文件夹加载游戏更新", "zh_TW": "從資料夾中載入遊戲更新" } @@ -467,7 +467,7 @@ "sv_SE": "", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Відкрити теку скріншотів", "zh_CN": "", "zh_TW": "" } @@ -617,7 +617,7 @@ "sv_SE": "Starta spel med dolt användargränssnitt", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Запускати ігри з прихованим інтерфейсом", "zh_CN": "启动游戏时隐藏 UI", "zh_TW": "" } @@ -1217,7 +1217,7 @@ "sv_SE": "_Hjälp", "th_TH": "_ช่วยเหลือ", "tr_TR": "_Yardım", - "uk_UA": "_Допомога", + "uk_UA": "_Довідка", "zh_CN": "帮助(_H)", "zh_TW": "說明(_H)" } @@ -1567,7 +1567,7 @@ "sv_SE": "", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Розроблено: {0}", "zh_CN": "由 {0} 开发", "zh_TW": "" } @@ -1867,7 +1867,7 @@ "sv_SE": "", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Сумісність:", "zh_CN": "兼容性:", "zh_TW": "" } @@ -1892,7 +1892,7 @@ "sv_SE": "", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "ID гри:", "zh_CN": "标题 ID:", "zh_TW": "" } @@ -1917,7 +1917,7 @@ "sv_SE": "", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Розміщені ігри: {0}", "zh_CN": "服务的游戏: {0}", "zh_TW": "" } @@ -1942,7 +1942,7 @@ "sv_SE": "", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Гравців онлайн: {0}", "zh_CN": "在线玩家: {0}", "zh_TW": "" } @@ -2292,7 +2292,7 @@ "sv_SE": "", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Очистити кеш PPTC", "zh_CN": "清理 PPTC 缓存", "zh_TW": "" } @@ -2317,7 +2317,7 @@ "sv_SE": "", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Видаляє всі файли кешу PPTC для застосунку", "zh_CN": "删除应用程序的所有 PPTC 缓存", "zh_TW": "" } @@ -2642,7 +2642,7 @@ "sv_SE": "Extrahera RomFS från en vald DLC-fil", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Витягти RomFS з обраного файлу DLC", "zh_CN": "从选定的 DLC 文件中解压 RomFS", "zh_TW": "" } @@ -2792,7 +2792,7 @@ "sv_SE": "", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Iнформація про сумісність", "zh_CN": "显示兼容性项目", "zh_TW": "" } @@ -2817,7 +2817,7 @@ "sv_SE": "", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Показати цю гру в Списку Сумісності. Список сумісності також можна зайти в меню Довідки.", "zh_CN": "在兼容性列表中显示选定的游戏,您通常可以通过帮助菜单访问。", "zh_TW": "" } @@ -2842,7 +2842,7 @@ "sv_SE": "", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Інформація про гру", "zh_CN": "显示游戏信息", "zh_TW": "" } @@ -2867,7 +2867,7 @@ "sv_SE": "", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Показати статистику та деталі обраної гри.", "zh_CN": "显示当前选定游戏的状态与详细信息。", "zh_TW": "" } @@ -3392,7 +3392,7 @@ "sv_SE": "", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Перевірка оновлень:", "zh_CN": "检查更新", "zh_TW": "" } @@ -3417,7 +3417,7 @@ "sv_SE": "", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Вимкнути", "zh_CN": "关闭", "zh_TW": "" } @@ -3442,7 +3442,7 @@ "sv_SE": "", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Запитувати щоразу", "zh_CN": "提示", "zh_TW": "" } @@ -3467,7 +3467,7 @@ "sv_SE": "", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Оновлювати в фоні", "zh_CN": "背景", "zh_TW": "" } @@ -3492,7 +3492,7 @@ "sv_SE": "", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "При втраті фокуса емулятором:", "zh_CN": "当模拟器在后台时:", "zh_TW": "" } @@ -3517,7 +3517,7 @@ "sv_SE": "", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Нічого не робити", "zh_CN": "什么事情也不做", "zh_TW": "" } @@ -3542,7 +3542,7 @@ "sv_SE": "", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Блокувати введення", "zh_CN": "禁用输入", "zh_TW": "" } @@ -3567,7 +3567,7 @@ "sv_SE": "", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Вимкнути звук", "zh_CN": "静音", "zh_TW": "" } @@ -3592,7 +3592,7 @@ "sv_SE": "", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Блокувати введення та Вимкнути звук", "zh_CN": "阻止输入且静音", "zh_TW": "" } @@ -3617,7 +3617,7 @@ "sv_SE": "", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Поставити на паузу", "zh_CN": "暂停模拟", "zh_TW": "" } @@ -3742,7 +3742,7 @@ "sv_SE": "Dölj markör:", "th_TH": "ซ่อน เคอร์เซอร์:", "tr_TR": "İşaretçiyi Gizle:", - "uk_UA": "Сховати вказівник:", + "uk_UA": "Сховати курсор:", "zh_CN": "隐藏鼠标指针:", "zh_TW": "隱藏滑鼠游標:" } @@ -5142,7 +5142,7 @@ "sv_SE": "", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Ігнорувати Аплет Контролера", "zh_CN": "", "zh_TW": "" } @@ -5992,7 +5992,7 @@ "sv_SE": "", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Увімкнути журнали інтерфейсу", "zh_CN": "启用 UI 日志", "zh_TW": "" } @@ -6017,7 +6017,7 @@ "sv_SE": "Aktivera loggar för filsystemsåtkomst", "th_TH": "เปิดใช้งานการบันทึกประวัติการเข้าถึง Fs", "tr_TR": "Fs Erişim Loglarını Etkinleştir", - "uk_UA": "Увімкнути журнали доступу Fs", + "uk_UA": "Увімкнути журнали доступу до файлової системи", "zh_CN": "启用文件访问日志", "zh_TW": "啟用檔案系統存取日誌" } @@ -6042,7 +6042,7 @@ "sv_SE": "Loggläge för global filsystemsåtkomst:", "th_TH": "โหมด การเข้าถึงประวัติส่วนกลาง:", "tr_TR": "Fs Evrensel Erişim Log Modu:", - "uk_UA": "Режим журналу глобального доступу Fs:", + "uk_UA": "Режим журналу глобального доступу файлової системи:", "zh_CN": "文件系统全局访问日志模式:", "zh_TW": "檔案系統全域存取日誌模式:" } @@ -6267,7 +6267,7 @@ "sv_SE": "Inmatning", "th_TH": "ป้อนข้อมูล", "tr_TR": "Giriş Yöntemi", - "uk_UA": "Введення", + "uk_UA": "Керування", "zh_CN": "输入", "zh_TW": "輸入" } @@ -6392,7 +6392,7 @@ "sv_SE": "", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Скинути налаштування", "zh_CN": "重置设置", "zh_TW": "" } @@ -6417,7 +6417,7 @@ "sv_SE": "", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Я хочу скинути налаштування.", "zh_CN": "我要重置我的设置。", "zh_TW": "" } @@ -10692,7 +10692,7 @@ "sv_SE": "", "th_TH": "", "tr_TR": "Rehber", - "uk_UA": "", + "uk_UA": "Меню", "zh_CN": "主页键", "zh_TW": "快顯功能表鍵" } @@ -10842,7 +10842,7 @@ "sv_SE": "", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Сенсорна панель", "zh_CN": "触摸板", "zh_TW": "觸控板" } @@ -13417,7 +13417,7 @@ "sv_SE": "Amiibo-API", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "API Amiibo", "zh_CN": "", "zh_TW": "" } @@ -13667,7 +13667,7 @@ "sv_SE": "", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Ви збираєтесь видалити всі дані PPTC з:\n\n{0}\n\nБажаєте продовжити цю операцію?", "zh_CN": "您正要清理 PPTC 数据:\n\n{0}\n\n您确实要继续吗?", "zh_TW": "" } @@ -13967,7 +13967,7 @@ "sv_SE": "En ogiltig nyckelfil hittades i {0}", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Виявлено неправильний файл ключів у теці {0}", "zh_CN": "在 {0} 发现了一个无效的密匙文件", "zh_TW": "找到無效的金鑰檔案 {0}" } @@ -15642,7 +15642,7 @@ "sv_SE": "Favorit", "th_TH": "สิ่งที่ชื่นชอบ", "tr_TR": "Favori", - "uk_UA": "Вибрані", + "uk_UA": "Обрані", "zh_CN": "收藏", "zh_TW": "我的最愛" } @@ -16067,7 +16067,7 @@ "sv_SE": "Stöd för direkt musåtkomst (HID). Ger spel åtkomst till din mus som pekdon.\n\nFungerar endast med spel som har inbyggt stöd för muskontroller på Switch-hårdvara, som är endast ett fåtal.\n\nViss pekskärmsfunktionalitet kanske inte fungerar när aktiverat.\n\nLämna AV om du är osäker.", "th_TH": "รองรับการเข้าถึงเมาส์โดยตรง (HID) ให้เกมเข้าถึงเมาส์ของคุณเป็นอุปกรณ์ชี้ตำแหน่ง\n\nใช้งานได้เฉพาะกับเกมที่รองรับการควบคุมเมาส์บนฮาร์ดแวร์ของ Switch เท่านั้น ซึ่งมีอยู่ไม่มากนัก\n\nเมื่อเปิดใช้งาน ฟังก์ชั่นหน้าจอสัมผัสอาจไม่ทำงาน\n\nหากคุณไม่แน่ใจให้ปิดใช้งานไว้", "tr_TR": "", - "uk_UA": "Підтримка прямого доступу до миші (HID). Надає іграм доступ до миші, як пристрій вказування.\n\nПрацює тільки з іграми, які підтримують мишу на обладнанні Switch (таких небагато).\n\nФункціонал сенсорного екрану може не працювати, якщо ця функція ввімкнена.\n\nЗалиште вимкненим, якщо не впевнені.", + "uk_UA": "Підтримка прямого доступу до миші (HID). Надає іграм доступ до миші, як пристрій вказування.\n\nПрацює тільки з тими іграми, що підтримують мишу на обладнанні Switch (таких небагато).\n\nФункціонал сенсорного екрану може не працювати, якщо увімкнути цю функцію.\n\nЗалиште вимкненим, якщо не впевнені.", "zh_CN": "直接鼠标访问(HID)支持,游戏可以直接访问鼠标作为指针输入设备。\n\n只适用于在 Switch 硬件上原生支持鼠标控制的游戏,这种游戏很少。\n\n启用后,触屏功能可能无法正常工作。\n\n如果不确定,请保持关闭状态。", "zh_TW": "支援滑鼠直接存取 (HID)。遊戲可將滑鼠作為指向裝置使用。\n\n僅適用於在 Switch 硬體上原生支援滑鼠控制的遊戲,這類遊戲很少。\n\n啟用後,觸控螢幕功能可能無法使用。\n\n如果不確定,請保持關閉狀態。" } @@ -16492,7 +16492,7 @@ "sv_SE": "Ignorerar Horizon OS-tjänster som inte har implementerats. Detta kan avhjälpa krascher när vissa spel startar upp.\n\nLämna AV om du är osäker.", "th_TH": "ละเว้นบริการ Horizon OS ที่ยังไม่ได้ใช้งาน วิธีนี้อาจช่วยในการหลีกเลี่ยงข้อผิดพลาดเมื่อบูตเกมบางเกม\n\nปล่อยให้ปิดหากคุณไม่แน่ใจ", "tr_TR": "Henüz programlanmamış Horizon işletim sistemi servislerini görmezden gelir. Bu seçenek belirli oyunların açılırken çökmesinin önüne geçmeye yardımcı olabilir.\n\nEmin değilseniz devre dışı bırakın.", - "uk_UA": "Ігнорує нереалізовані служби Horizon OS. Це може допомогти в обході збоїв під час завантаження певних ігор.\n\nЗалиште вимкненим, якщо не впевнені.", + "uk_UA": "Ігнорує нереалізовані служби Horizon OS. Це може допомогти в обході збоїв під час завантаження певних ігор.\n\nЗалиште вимкненим якщо не впевнені.", "zh_CN": "开启后,游戏会忽略未实现的系统服务,从而继续运行。\n少部分新发布的游戏由于使用了新的未知系统服务,可能需要此选项来避免闪退。\n模拟器更新完善系统服务之后,则无需开启此选项。\n\n如果不确定,请保持关闭状态。", "zh_TW": "忽略未實現的 Horizon OS 服務。這可能有助於在啟動某些遊戲時避免崩潰。\n\n如果不確定,請保持關閉狀態。" } @@ -16517,7 +16517,7 @@ "sv_SE": "", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Діалогове вікно Аплету Контролера не з'явиться, якщо геймпад було відключено під час роботи програми.\n\nЗалиште вимкненим якщо не впевнені.", "zh_CN": "", "zh_TW": "" } @@ -16542,7 +16542,7 @@ "sv_SE": "Kör kommandon för grafikbakände i en andra tråd.\n\nSnabbar upp shader compilation, minskar stuttering och förbättrar prestandan på GPU-drivrutiner utan stöd för egen multithreading. Något bättre prestanda på drivrutiner med multithreading.\n\nStäll in till AUTO om du är osäker.", "th_TH": "ดำเนินการคำสั่งแบ็กเอนด์กราฟิกบนเธรดที่สอง\n\nเร่งความเร็วการคอมไพล์ ลดการกระตุก และปรับปรุงประสิทธิภาพการทำงานของไดรเวอร์ GPU โดยไม่ต้องรองรับมัลติเธรดในตัว ประสิทธิภาพที่ดีขึ้นเล็กน้อยสำหรับไดรเวอร์ที่มีมัลติเธรด\n\nตั้งเป็น อัตโนมัติ หากคุณไม่แน่ใจ", "tr_TR": "Grafik arka uç komutlarını ikinci bir iş parçacığında işletir.\n\nKendi multithreading desteği olmayan sürücülerde shader derlemeyi hızlandırıp performansı artırır. Multithreading desteği olan sürücülerde çok az daha iyi performans sağlar.\n\nEmin değilseniz Otomatik seçeneğine ayarlayın.", - "uk_UA": "Виконує команди графічного сервера в другому потоці.\n\nПрискорює компіляцію шейдерів, зменшує затримки та покращує продуктивність драйверів GPU без власної підтримки багатопоточності. Трохи краща продуктивність на драйверах з багатопотоковістю.\nВстановіть значення «Авто», якщо не впевнені", + "uk_UA": "Виконує команди графічного сервера в другому потоці.\n\nПрискорює компіляцію шейдерів, зменшує затримки та покращує продуктивність драйверів GPU без власної підтримки багатопоточності. Трохи краща продуктивність на драйверах з багатопотоковістю.\nВстановіть значення «Авто» якщо не впевнені", "zh_CN": "在第二个线程上执行图形引擎指令。\n\n可以加速着色器编译,减少卡顿,提高 GPU 的性能。\n\n如果不确定,请设置为“自动”。", "zh_TW": "在第二個執行緒上執行圖形後端指令。\n\n在本身不支援多執行緒的 GPU 驅動程式上,可加快著色器編譯、減少卡頓並提高效能。在支援多執行緒的驅動程式上效能略有提升。\n\n如果不確定,請設定為自動。" } @@ -16567,7 +16567,7 @@ "sv_SE": "Kör kommandon för grafikbakände i en andra tråd.\n\nSnabbar upp shader compilation, minskar stuttering och förbättrar prestandan på GPU-drivrutiner utan stöd för egen multithreading. Något bättre prestanda på drivrutiner med multithreading.\n\nStäll in till AUTO om du är osäker.", "th_TH": "ดำเนินการคำสั่งแบ็กเอนด์กราฟิกบนเธรดที่สอง\n\nเร่งความเร็วการคอมไพล์เชเดอร์ ลดการกระตุก และปรับปรุงประสิทธิภาพการทำงานของไดรเวอร์ GPU โดยไม่ต้องรองรับมัลติเธรดในตัว ประสิทธิภาพที่ดีขึ้นเล็กน้อยสำหรับไดรเวอร์ที่มีมัลติเธรด\n\nตั้งเป็น อัตโนมัติ หากคุณไม่แน่ใจ", "tr_TR": "Grafik arka uç komutlarını ikinci bir iş parçacığında işletir.\n\nKendi multithreading desteği olmayan sürücülerde shader derlemeyi hızlandırıp performansı artırır. Multithreading desteği olan sürücülerde çok az daha iyi performans sağlar.\n\nEmin değilseniz Otomatik seçeneğine ayarlayın.", - "uk_UA": "Виконує команди графічного сервера в другому потоці.\n\nПрискорює компіляцію шейдерів, зменшує затримки та покращує продуктивність драйверів GPU без власної підтримки багатопоточності. Трохи краща продуктивність на драйверах з багатопотоковістю.\n\nВстановіть значення «Авто», якщо не впевнені.", + "uk_UA": "Виконує команди графічного сервера в другому потоці.\n\nПрискорює компіляцію шейдерів, зменшує затримки та покращує продуктивність драйверів GPU без власної підтримки багатопоточності. Трохи краща продуктивність на драйверах з багатопотоковістю.\n\nВстановіть значення «Авто» якщо не впевнені.", "zh_CN": "在第二个线程上执行图形引擎指令。\n\n可以加速着色器编译,减少卡顿,提高 GPU 的性能。\n\n如果不确定,请设置为“自动”。", "zh_TW": "在第二個執行緒上執行圖形後端指令。\n\n在本身不支援多執行緒的 GPU 驅動程式上,可加快著色器編譯、減少卡頓並提高效能。在支援多執行緒的驅動程式上效能略有提升。\n\n如果不確定,請設定為自動。" } @@ -16592,7 +16592,7 @@ "sv_SE": "Sparar en disk shader cache som minskar stuttering i efterföljande körningar.\n\nLämna PÅ om du är osäker.", "th_TH": "บันทึกแคชแสงเงาของดิสก์ซึ่งช่วยลดการกระตุกในการรันครั้งต่อๆ ไป\n\nเปิดทิ้งไว้หากคุณไม่แน่ใจ", "tr_TR": "Sonraki çalışmalarda takılmaları engelleyen bir gölgelendirici disk önbelleğine kaydeder.", - "uk_UA": "Зберігає кеш дискового шейдера, що зменшує затримки під час наступних запусків.\n\nЗалиште увімкненим, якщо не впевнені.", + "uk_UA": "Зберігає кеш дискового шейдера, що зменшує затримки під час наступних запусків.\n\nЗалиште увімкненим якщо не впевнені.", "zh_CN": "模拟器将已编译的着色器保存到硬盘,可以减少游戏再次渲染相同图形导致的卡顿。\n\n如果不确定,请保持开启状态。", "zh_TW": "儲存磁碟著色器快取,減少後續執行時的卡頓。\n\n如果不確定,請保持開啟狀態。" } @@ -16942,7 +16942,7 @@ "sv_SE": "Aktiverar loggutdata för filsystemsåtkomst i konsollen. Möjliga lägen är 0-3", "th_TH": "เปิดใช้งาน เอาต์พุตประวัติการเข้าถึง FS ไปยังคอนโซล โหมดที่เป็นไปได้คือ 0-3", "tr_TR": "Konsola FS erişim loglarının yazılmasını etkinleştirir. Kullanılabilir modlar 0-3'tür", - "uk_UA": "Вмикає виведення журналу доступу (access log) до FS на консоль. Можливі режими 0-3", + "uk_UA": "Увімкнути виведення журналу доступу (access log) до файлової системи в консоль. Можливі режими: 0-3", "zh_CN": "在控制台中显示文件系统访问日志,可选模式为 0-3。", "zh_TW": "啟用檔案系統存取日誌輸出到控制台中。可能的模式為 0 到 3" } @@ -16992,7 +16992,7 @@ "sv_SE": "", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Виводити повідомлення журналу Avalonia (UI) в консоль", "zh_CN": "在控制台显示 Avalonia (UI) 的日志信息", "zh_TW": "" } @@ -17142,7 +17142,7 @@ "sv_SE": "Öppna en filutforskare för att välja en eller flera mappar att läsa in alla titeluppdateringar från", "th_TH": "เปิดตัวสำรวจไฟล์เพื่อเลือกหนึ่งโฟลเดอร์ขึ้นไปเพื่อโหลดไฟล์อัปเดตจำนวนมาก", "tr_TR": "", - "uk_UA": "Відкриває Файловий провідник для обрання однієї або декількох тек для масового завантаження оновлень", + "uk_UA": "Відкриває Файловий Провідник для обрання однієї або декількох тек для масового завантаження оновлень", "zh_CN": "打开文件资源管理器以选择一个或多个文件夹来批量加载游戏更新。", "zh_TW": "開啟檔案總管,選擇一個或多個資料夾來大量載入遊戲更新" } @@ -17192,7 +17192,7 @@ "sv_SE": "", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Відкрити теку куди зберігаються скріншоти Ryujinx", "zh_CN": "", "zh_TW": "" } @@ -17217,7 +17217,7 @@ "sv_SE": "Öppnar mappen där loggarna har skrivits till", "th_TH": "เปิดโฟลเดอร์ ที่เก็บไฟล์ประวัติ", "tr_TR": "Log dosyalarının bulunduğu klasörü açar", - "uk_UA": "Відкриває теку, куди записуються журнали (logs)", + "uk_UA": "Відкриває теку, куди зберігаються журнали (logs)", "zh_CN": "打开日志存放的目录", "zh_TW": "開啟日誌被寫入的資料夾" } @@ -17242,7 +17242,7 @@ "sv_SE": "Avsluta Ryujinx", "th_TH": "ออกจากโปรแกรม Ryujinx", "tr_TR": "Ryujinx'ten çıkış yapmayı sağlar", - "uk_UA": "Виходить з Ryujinx", + "uk_UA": "Закриває Ryujinx", "zh_CN": "退出 Ryujinx 模拟器", "zh_TW": "結束 Ryujinx" } @@ -17942,7 +17942,7 @@ "sv_SE": "", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Доступне оновлення!", "zh_CN": "有可用的更新!", "zh_TW": "" } @@ -18817,7 +18817,7 @@ "sv_SE": "Cabinet-dialog", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Сповіщення Cabinet", "zh_CN": "档案对话框", "zh_TW": "Cabinet 對話方塊" } @@ -19742,7 +19742,7 @@ "sv_SE": "LED-inställningar", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Налаштування LED", "zh_CN": "LED 设置", "zh_TW": "" } @@ -20692,7 +20692,7 @@ "sv_SE": "Markera visade", "th_TH": "", "tr_TR": "", - "uk_UA": "Вибрати показане", + "uk_UA": "Вибрати показані", "zh_CN": "选定显示的", "zh_TW": "選擇已顯示" } @@ -21542,7 +21542,7 @@ "sv_SE": "Automatiskt", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Автоматично", "zh_CN": "自动", "zh_TW": "自動" } @@ -21567,7 +21567,7 @@ "sv_SE": "Använder Vulkan.\nPå en ARM Mac och vid spel som körs bra på den så används Metal-bakänden.", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Використовує Vulkan.\nНа Mac з ARM-архітектурою, якщо гра добре працює з Vulkan, використовується графічний рушій Metal.", "zh_CN": "使用 Vulkan。\n在 ARM Mac 上,当玩在其下运行良好的游戏时,使用 Metal 后端。", "zh_TW": "使用Vulkan。\n在 ARM Mac 上,如果遊戲執行性能良好時,則將使用 Metal 後端。" } @@ -23542,7 +23542,7 @@ "sv_SE": "Intervall", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Інтервал", "zh_CN": "间隔", "zh_TW": "間隔" } @@ -23642,7 +23642,7 @@ "sv_SE": "Senast uppdaterad: {0}", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Останнє оновлення: {0}", "zh_CN": "最后更新于: {0}", "zh_TW": "上次更新時間: {0}" } @@ -23667,7 +23667,7 @@ "sv_SE": "Denna kompatibilitetslista kan innehålla utdaterade poster.\nTesta gärna spelen som listas med \"Spelproblem\"-status.", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Цей список сумісності може містити застарілі дані.\nНе відмовляйтеся від тестування ігор що мають статус \"Запускаються\".", "zh_CN": "此兼容性列表可能包含过时的条目。\n不要只测试 \"进入游戏\" 状态的游戏。", "zh_TW": "這個相容性列表可能含有已過時的紀錄。\n敬請繼續測試「大致可遊玩 (Ingame)」狀態的遊戲並回報以更新紀錄。" } @@ -23692,7 +23692,7 @@ "sv_SE": "Sök i kompatibilitetsposter...", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Перевірити сумісність гри...", "zh_CN": "正在搜索兼容性条目...", "zh_TW": "搜尋相容性列表紀錄..." } @@ -23717,7 +23717,7 @@ "sv_SE": "Öppna kompatibilitetslistan", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Відкрити Список Сумісності", "zh_CN": "打开兼容性列表", "zh_TW": "開啟相容性列表" } @@ -23742,7 +23742,7 @@ "sv_SE": "Visa endast ägda spel", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Показувати лише ігри в наявності", "zh_CN": "仅显示拥有的游戏", "zh_TW": "只顯示已擁有的遊戲" } @@ -23767,7 +23767,7 @@ "sv_SE": "Spelbart", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Оптимально", "zh_CN": "可游玩", "zh_TW": "可暢順遊玩" } @@ -23792,7 +23792,7 @@ "sv_SE": "Spelproblem", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "З недоліками", "zh_CN": "进入游戏", "zh_TW": "大致可遊玩" } @@ -23817,7 +23817,7 @@ "sv_SE": "Menyer", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Меню", "zh_CN": "菜单", "zh_TW": "只開啟至遊戲開始功能表" } @@ -23842,7 +23842,7 @@ "sv_SE": "Startar", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Запускається", "zh_CN": "启动", "zh_TW": "只能啟動" } @@ -23867,7 +23867,7 @@ "sv_SE": "Ingenting", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Не працює", "zh_CN": "什么都没有", "zh_TW": "無法啟動" } @@ -23892,7 +23892,7 @@ "sv_SE": "", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Запускається та оптимально працює (без збоїв або графічних багів) на середньостатистичному комп'ютері.", "zh_CN": "启动和游戏时不会出现任何崩溃或任何类型的 GPU bug 且速度足够快可以在一般 PC 上尽情游玩。", "zh_TW": "" } @@ -23917,7 +23917,7 @@ "sv_SE": "", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Запускається, але в грі на вас чекатимуть одна або декілька наступних проблем: збої, зависання, графічні баги, спотворений звук або ж гра загалом працюватиме надто повільно. Можливо, її все ще можна пройти, але досвід буде не найкращим.", "zh_CN": "可以成功启动并进入游戏但可能会遇到以下一种或多种问题: 崩溃、卡死、GPU bug、令人无法接受的音频,或者只是太慢。仍然可以继续进行游戏,但是可能无法达到预期。", "zh_TW": "" } @@ -23942,7 +23942,7 @@ "sv_SE": "", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Запускається та проходить початковий екран, але пограти не вийде.", "zh_CN": "可以启动并通过标题画面但是无法进入到主要的游戏流程。", "zh_TW": "" } @@ -23967,7 +23967,7 @@ "sv_SE": "", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Запускається, але не відображає навіть початкового екрану.", "zh_CN": "可以启动但是无法通过标题画面。", "zh_TW": "" } @@ -23992,7 +23992,7 @@ "sv_SE": "", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Взагалі не запускається.", "zh_CN": "无法启动或显示无任何动静。", "zh_TW": "" } @@ -24017,7 +24017,7 @@ "sv_SE": "Välj en DLC att extrahera", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Оберіть DLC які бажаєте вилучити", "zh_CN": "选择一个要解压的 DLC", "zh_TW": "" } @@ -24042,7 +24042,7 @@ "sv_SE": "", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Зображення картки активності Discord", "zh_CN": "Rich Presence 图像", "zh_TW": "" } @@ -24067,7 +24067,7 @@ "sv_SE": "", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Динамічна картка активності Discord", "zh_CN": "动态 Rich Presence", "zh_TW": "" } From d0ac83b49361f545217e40d44a967128ec0f39ec Mon Sep 17 00:00:00 2001 From: Evan Husted Date: Thu, 20 Feb 2025 18:52:37 -0600 Subject: [PATCH 06/24] misc: chore: Prevent firmware installation prompt from showing up multiple times during runtime when using --install-firmware --- src/Ryujinx/UI/Windows/MainWindow.axaml.cs | 3 +++ src/Ryujinx/Utilities/CommandLineState.cs | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Ryujinx/UI/Windows/MainWindow.axaml.cs b/src/Ryujinx/UI/Windows/MainWindow.axaml.cs index c5f886ff3..20e2ac522 100644 --- a/src/Ryujinx/UI/Windows/MainWindow.axaml.cs +++ b/src/Ryujinx/UI/Windows/MainWindow.axaml.cs @@ -148,8 +148,11 @@ namespace Ryujinx.Ava.UI.Windows { if ((firmwarePath.ExistsAsFile && firmwarePath.Extension is "xci" or "zip") || firmwarePath.ExistsAsDirectory) + { await Dispatcher.UIThread.InvokeAsync(() => ViewModel.HandleFirmwareInstallation(firmwarePath)); + CommandLineState.FirmwareToInstallPathArg = null; + } else Logger.Notice.Print(LogClass.UI, "Invalid firmware type provided. Path must be a directory, or a .zip or .xci file."); } diff --git a/src/Ryujinx/Utilities/CommandLineState.cs b/src/Ryujinx/Utilities/CommandLineState.cs index d6e8f669c..71c7b5e73 100644 --- a/src/Ryujinx/Utilities/CommandLineState.cs +++ b/src/Ryujinx/Utilities/CommandLineState.cs @@ -14,7 +14,7 @@ namespace Ryujinx.Ava.Utilities public static string OverrideBackendThreading { get; private set; } public static string OverrideHideCursor { get; private set; } public static string BaseDirPathArg { get; private set; } - public static FilePath FirmwareToInstallPathArg { get; private set; } + public static FilePath FirmwareToInstallPathArg { get; set; } public static string Profile { get; private set; } public static string LaunchPathArg { get; private set; } public static string LaunchApplicationId { get; private set; } From c47448628cf4d86d54b21e1143e6b686bcbbdcab Mon Sep 17 00:00:00 2001 From: Evan Husted Date: Thu, 20 Feb 2025 19:29:18 -0600 Subject: [PATCH 07/24] UI: Print LED setting failed on Debug --- src/Ryujinx.Input.SDL2/SDL2Gamepad.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx.Input.SDL2/SDL2Gamepad.cs b/src/Ryujinx.Input.SDL2/SDL2Gamepad.cs index 3b243eed0..6f1b79d38 100644 --- a/src/Ryujinx.Input.SDL2/SDL2Gamepad.cs +++ b/src/Ryujinx.Input.SDL2/SDL2Gamepad.cs @@ -111,7 +111,7 @@ namespace Ryujinx.Input.SDL2 byte blue = packedRgb > 0 ? (byte)(packedRgb % 256) : (byte)0; if (SDL_GameControllerSetLED(_gamepadHandle, red, green, blue) != 0) - Logger.Error?.Print(LogClass.Hid, "LED setting failed; probably in the middle of disconnecting."); + Logger.Debug?.Print(LogClass.Hid, "LED setting failed; probably in the middle of disconnecting."); } private GamepadFeaturesFlag GetFeaturesFlag() From 6c6580ddcc6a44af652a7debb603cf3c410ccba4 Mon Sep 17 00:00:00 2001 From: Evan Husted Date: Thu, 20 Feb 2025 19:30:00 -0600 Subject: [PATCH 08/24] misc: chore: Move the LDN constants into a SharedConstants class --- src/Ryujinx.Common/SharedConstants.cs | 9 +++++++ .../IUserLocalCommunicationService.cs | 15 ++++------- src/Ryujinx/AppHost.cs | 2 +- src/Ryujinx/Assets/locales.json | 2 +- .../AppLibrary/ApplicationLibrary.cs | 27 +++++-------------- .../LdnGameDataReceivedEventArgs.cs | 10 ++++++- .../Configuration/ConfigurationState.Model.cs | 9 +++++++ 7 files changed, 41 insertions(+), 33 deletions(-) create mode 100644 src/Ryujinx.Common/SharedConstants.cs diff --git a/src/Ryujinx.Common/SharedConstants.cs b/src/Ryujinx.Common/SharedConstants.cs new file mode 100644 index 000000000..f40afeb2b --- /dev/null +++ b/src/Ryujinx.Common/SharedConstants.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.Common +{ + public static class SharedConstants + { + public const string DefaultLanPlayHost = "ryuldn.vudjun.com"; + public const short LanPlayPort = 30456; + public const string DefaultLanPlayWebHost = "ryuldnweb.vudjun.com"; + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/IUserLocalCommunicationService.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/IUserLocalCommunicationService.cs index 5c6ee7732..9f7e6206b 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/IUserLocalCommunicationService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/IUserLocalCommunicationService.cs @@ -23,9 +23,6 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator { class IUserLocalCommunicationService : IpcService, IDisposable { - public static string DefaultLanPlayHost = "ryuldn.vudjun.com"; - public static short LanPlayPort = 30456; - public INetworkClient NetworkClient { get; private set; } private const int NifmRequestID = 90; @@ -1092,20 +1089,18 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator case MultiplayerMode.LdnRyu: try { - string ldnServer = context.Device.Configuration.MultiplayerLdnServer; - if (string.IsNullOrEmpty(ldnServer)) - { - ldnServer = DefaultLanPlayHost; - } + string ldnServer = context.Device.Configuration.MultiplayerLdnServer + ?? throw new InvalidOperationException("Cannot initialize RyuLDN with a null Multiplayer server."); + if (!IPAddress.TryParse(ldnServer, out IPAddress ipAddress)) { ipAddress = Dns.GetHostEntry(ldnServer).AddressList[0]; } - NetworkClient = new LdnMasterProxyClient(ipAddress.ToString(), LanPlayPort, context.Device.Configuration); + NetworkClient = new LdnMasterProxyClient(ipAddress.ToString(), SharedConstants.LanPlayPort, context.Device.Configuration); } catch (Exception ex) { - Logger.Error?.Print(LogClass.ServiceLdn, "Could not locate LdnRyu server. Defaulting to stubbed wireless."); + Logger.Error?.Print(LogClass.ServiceLdn, "Could not locate RyuLDN server. Defaulting to stubbed wireless."); Logger.Error?.Print(LogClass.ServiceLdn, ex.Message); NetworkClient = new LdnDisabledClient(); } diff --git a/src/Ryujinx/AppHost.cs b/src/Ryujinx/AppHost.cs index b26921e6a..4147b8a5e 100644 --- a/src/Ryujinx/AppHost.cs +++ b/src/Ryujinx/AppHost.cs @@ -951,7 +951,7 @@ namespace Ryujinx.Ava ConfigurationState.Instance.Multiplayer.Mode, ConfigurationState.Instance.Multiplayer.DisableP2p, ConfigurationState.Instance.Multiplayer.LdnPassphrase, - ConfigurationState.Instance.Multiplayer.LdnServer, + ConfigurationState.Instance.Multiplayer.GetLdnServer(), ConfigurationState.Instance.Graphics.CustomVSyncInterval.Value, ConfigurationState.Instance.Hacks.ShowDirtyHacks ? ConfigurationState.Instance.Hacks.EnabledHacks : null)); } diff --git a/src/Ryujinx/Assets/locales.json b/src/Ryujinx/Assets/locales.json index 6e2577182..a48e294b9 100644 --- a/src/Ryujinx/Assets/locales.json +++ b/src/Ryujinx/Assets/locales.json @@ -24073,4 +24073,4 @@ } } ] -} +} \ No newline at end of file diff --git a/src/Ryujinx/Utilities/AppLibrary/ApplicationLibrary.cs b/src/Ryujinx/Utilities/AppLibrary/ApplicationLibrary.cs index 79cac1a0e..260fabee5 100644 --- a/src/Ryujinx/Utilities/AppLibrary/ApplicationLibrary.cs +++ b/src/Ryujinx/Utilities/AppLibrary/ApplicationLibrary.cs @@ -42,7 +42,6 @@ namespace Ryujinx.Ava.Utilities.AppLibrary { public class ApplicationLibrary { - public const string DefaultLanPlayWebHost = "ryuldnweb.vudjun.com"; public Language DesiredLanguage { get; set; } public event EventHandler ApplicationCountUpdated; public event Action LdnGameDataReceived; @@ -826,7 +825,6 @@ namespace Ryujinx.Ava.Utilities.AppLibrary public async Task RefreshLdn() { - if (ConfigurationState.Instance.Multiplayer.Mode == MultiplayerMode.LdnRyu) { try @@ -834,33 +832,22 @@ namespace Ryujinx.Ava.Utilities.AppLibrary string ldnWebHost = ConfigurationState.Instance.Multiplayer.LdnServer; if (string.IsNullOrEmpty(ldnWebHost)) { - ldnWebHost = DefaultLanPlayWebHost; + ldnWebHost = SharedConstants.DefaultLanPlayWebHost; } - IEnumerable ldnGameDataArray = Array.Empty(); + using HttpClient httpClient = new(); string ldnGameDataArrayString = await httpClient.GetStringAsync($"https://{ldnWebHost}/api/public_games"); - ldnGameDataArray = JsonHelper.Deserialize(ldnGameDataArrayString, _ldnDataSerializerContext.IEnumerableLdnGameData); - LdnGameDataReceived?.Invoke(new LdnGameDataReceivedEventArgs - { - LdnData = ldnGameDataArray - }); + LdnGameData[] ldnGameDataArray = JsonHelper.Deserialize(ldnGameDataArrayString, _ldnDataSerializerContext.IEnumerableLdnGameData).ToArray(); + LdnGameDataReceived?.Invoke(new LdnGameDataReceivedEventArgs(ldnGameDataArray)); + return; } catch (Exception ex) { Logger.Warning?.Print(LogClass.Application, $"Failed to fetch the public games JSON from the API. Player and game count in the game list will be unavailable.\n{ex.Message}"); - LdnGameDataReceived?.Invoke(new LdnGameDataReceivedEventArgs - { - LdnData = Array.Empty() - }); } } - else - { - LdnGameDataReceived?.Invoke(new LdnGameDataReceivedEventArgs - { - LdnData = Array.Empty() - }); - } + + LdnGameDataReceived?.Invoke(LdnGameDataReceivedEventArgs.Empty); } // Replace the currently stored DLC state for the game with the provided DLC state. diff --git a/src/Ryujinx/Utilities/AppLibrary/LdnGameDataReceivedEventArgs.cs b/src/Ryujinx/Utilities/AppLibrary/LdnGameDataReceivedEventArgs.cs index 97299c9e8..0eaa6ecb3 100644 --- a/src/Ryujinx/Utilities/AppLibrary/LdnGameDataReceivedEventArgs.cs +++ b/src/Ryujinx/Utilities/AppLibrary/LdnGameDataReceivedEventArgs.cs @@ -5,6 +5,14 @@ namespace Ryujinx.Ava.Utilities.AppLibrary { public class LdnGameDataReceivedEventArgs : EventArgs { - public IEnumerable LdnData { get; set; } + public static new readonly LdnGameDataReceivedEventArgs Empty = new(null); + + public LdnGameDataReceivedEventArgs(LdnGameData[] ldnData) + { + LdnData = ldnData ?? []; + } + + + public LdnGameData[] LdnData { get; set; } } } diff --git a/src/Ryujinx/Utilities/Configuration/ConfigurationState.Model.cs b/src/Ryujinx/Utilities/Configuration/ConfigurationState.Model.cs index 3f22c6c0f..ead99fbac 100644 --- a/src/Ryujinx/Utilities/Configuration/ConfigurationState.Model.cs +++ b/src/Ryujinx/Utilities/Configuration/ConfigurationState.Model.cs @@ -1,5 +1,6 @@ using ARMeilleure; using Gommon; +using Ryujinx.Ava.Utilities.AppLibrary; using Ryujinx.Ava.Utilities.Configuration.System; using Ryujinx.Ava.Utilities.Configuration.UI; using Ryujinx.Common; @@ -647,6 +648,14 @@ namespace Ryujinx.Ava.Utilities.Configuration /// public ReactiveObject LdnServer { get; private set; } + public string GetLdnServer() + { + string ldnServer = LdnServer; + return string.IsNullOrEmpty(ldnServer) + ? SharedConstants.DefaultLanPlayHost + : ldnServer; + } + public MultiplayerSection() { LanInterfaceId = new ReactiveObject(); From 2a84656ffcff6124df91db475ac61fe09d788f1a Mon Sep 17 00:00:00 2001 From: Evan Husted Date: Thu, 20 Feb 2025 19:49:17 -0600 Subject: [PATCH 09/24] misc: chore: use new array in LdnGameDataReceivedEventArgs instead of collecting the ldn datas into a list --- src/Ryujinx/UI/Windows/MainWindow.axaml.cs | 9 ++------- src/Ryujinx/Utilities/AppLibrary/LdnGameData.cs | 8 +++++++- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/Ryujinx/UI/Windows/MainWindow.axaml.cs b/src/Ryujinx/UI/Windows/MainWindow.axaml.cs index 20e2ac522..08b2a0585 100644 --- a/src/Ryujinx/UI/Windows/MainWindow.axaml.cs +++ b/src/Ryujinx/UI/Windows/MainWindow.axaml.cs @@ -189,17 +189,12 @@ namespace Ryujinx.Ava.UI.Windows { Dispatcher.UIThread.Post(() => { - List ldnGameDataArray = e.LdnData.ToList(); ViewModel.LdnData.Clear(); foreach (ApplicationData application in ViewModel.Applications.Where(it => it.HasControlHolder)) { ref ApplicationControlProperty controlHolder = ref application.ControlHolder.Value; - - ViewModel.LdnData[application.IdString] = - LdnGameData.GetArrayForApp( - ldnGameDataArray, - ref controlHolder - ); + + ViewModel.LdnData[application.IdString] = e.LdnData.Where(ref controlHolder); UpdateApplicationWithLdnData(application); } diff --git a/src/Ryujinx/Utilities/AppLibrary/LdnGameData.cs b/src/Ryujinx/Utilities/AppLibrary/LdnGameData.cs index 4b9b8fe6c..252c0ecdc 100644 --- a/src/Ryujinx/Utilities/AppLibrary/LdnGameData.cs +++ b/src/Ryujinx/Utilities/AppLibrary/LdnGameData.cs @@ -18,7 +18,7 @@ namespace Ryujinx.Ava.Utilities.AppLibrary public IEnumerable Players { get; set; } public static Array GetArrayForApp( - IEnumerable receivedData, ref ApplicationControlProperty acp) + LdnGameData[] receivedData, ref ApplicationControlProperty acp) { LibHac.Common.FixedArrays.Array8 communicationId = acp.LocalCommunicationId; @@ -40,4 +40,10 @@ namespace Ryujinx.Ava.Utilities.AppLibrary public int GameCount => _ldnDatas.Length; } } + + public static class LdnGameDataHelper + { + public static LdnGameData.Array Where(this LdnGameData[] unfilteredDatas, ref ApplicationControlProperty acp) + => LdnGameData.GetArrayForApp(unfilteredDatas, ref acp); + } } From 9631bdfe165d9bed12722d265cf5dd296f9f2441 Mon Sep 17 00:00:00 2001 From: Evan Husted Date: Thu, 20 Feb 2025 20:56:52 -0600 Subject: [PATCH 10/24] docs: compat: Hogwarts Legacy is an Unreal Engine 4 game --- docs/compatibility.csv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/compatibility.csv b/docs/compatibility.csv index 5f6ead1eb..a3c9aa619 100644 --- a/docs/compatibility.csv +++ b/docs/compatibility.csv @@ -1436,7 +1436,7 @@ 010083A018262000,"Hitman: Blood Money — Reprisal",deadlock,ingame,2024-09-28 16:28:50 01004B100A5CC000,"Hob: The Definitive Edition",,playable,2021-01-13 09:39:19 0100F7300ED2C000,"Hoggy2",,playable,2022-10-10 13:53:35 -0100F7E00C70E000,"Hogwarts Legacy",slow,ingame,2024-09-03 19:53:58 +0100F7E00C70E000,"Hogwarts Legacy",UE4;slow,ingame,2024-09-03 19:53:58 0100633007D48000,"Hollow Knight",nvdec,playable,2023-01-16 15:44:56 0100F2100061E800,"Hollow0",UE4;gpu,ingame,2021-03-03 23:42:56 0100342009E16000,"Holy Potatoes! What The Hell?!",,playable,2020-07-03 10:48:56 From eb6b0e9adca0c2f010fa6ac093c806c4db199283 Mon Sep 17 00:00:00 2001 From: shinyoyo Date: Sat, 22 Feb 2025 16:16:28 +0800 Subject: [PATCH 11/24] Updated Zh-CN Simplified Chinese. (#703) --- src/Ryujinx/Assets/locales.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Ryujinx/Assets/locales.json b/src/Ryujinx/Assets/locales.json index a48e294b9..dd9459cd6 100644 --- a/src/Ryujinx/Assets/locales.json +++ b/src/Ryujinx/Assets/locales.json @@ -468,7 +468,7 @@ "th_TH": "", "tr_TR": "", "uk_UA": "Відкрити теку скріншотів", - "zh_CN": "", + "zh_CN": "打开截图文件夹", "zh_TW": "" } }, @@ -5143,7 +5143,7 @@ "th_TH": "", "tr_TR": "", "uk_UA": "Ігнорувати Аплет Контролера", - "zh_CN": "", + "zh_CN": "忽略控制器小程序", "zh_TW": "" } }, @@ -16518,7 +16518,7 @@ "th_TH": "", "tr_TR": "", "uk_UA": "Діалогове вікно Аплету Контролера не з'явиться, якщо геймпад було відключено під час роботи програми.\n\nЗалиште вимкненим якщо не впевнені.", - "zh_CN": "", + "zh_CN": "在应用程序运行时如果游戏手柄断开连接则不会显示控制器小程序对话框。\n\n如果不确定,请保持关闭状态。", "zh_TW": "" } }, @@ -17193,7 +17193,7 @@ "th_TH": "", "tr_TR": "", "uk_UA": "Відкрити теку куди зберігаються скріншоти Ryujinx", - "zh_CN": "", + "zh_CN": "打开 Ryujinx 截图文件夹", "zh_TW": "" } }, From fe1617ffeae0edd58bf61b1e008304e1221a1f7d Mon Sep 17 00:00:00 2001 From: Evan Husted Date: Sat, 22 Feb 2025 21:26:46 -0600 Subject: [PATCH 12/24] Revert the Metal Experiment (#701) Metal sounded like a good idea to get in the emulator but frankly I underestimated just how experimental and not ready it was. From my write up in the Discord: ``` As is, Metal supports only a few games. The games it does support freeze on first use of not playing them via Vulkan, because shader translation is broken. So you need to use a dirty hack to not delete all your shaders. Not to mention it breaks many games via MoltenVK because of changes to the shared GPU code. Merging Metal seemed like a great idea, because of the few games it does support. But I don't think it's worth it. Many of the games it breaks via MoltenVK *don't work via Metal*. Which effectively makes current Ryubing worse for Mac users than Ryujinx 1.1.1403. I think what I'm gonna do is revert Metal, and reopen it as a PR. That way, you can still take advantage of the Metal backend as is, but without making other games worse with no solution. ``` For what it's worth, the shader translation part could at least be "fixed" by always applying a 30ms delay for shader translation to Metal. That being said, that solution sucks ass. The MoltenVK regressions are even worse. I hope this is not a let down to the Mac users. I hope you realize I'm reverting this because you're actively getting a worse experience with it in the emulator. --- Directory.Packages.props | 1 - Ryujinx.sln | 24 +- .../Configuration/GraphicsBackend.cs | 2 - src/Ryujinx.Common/TitleIDs.cs | 48 - src/Ryujinx.Graphics.GAL/ComputeSize.cs | 18 - src/Ryujinx.Graphics.GAL/Format.cs | 78 - src/Ryujinx.Graphics.GAL/ShaderInfo.cs | 17 +- .../Threed/ComputeDraw/VtgAsComputeContext.cs | 19 +- .../Threed/ComputeDraw/VtgAsComputeState.cs | 18 + .../Shader/DiskCache/DiskCacheHostStorage.cs | 13 +- .../DiskCache/ParallelDiskCacheLoader.cs | 11 +- .../Shader/GpuAccessor.cs | 8 +- .../Shader/GpuAccessorBase.cs | 8 +- .../Shader/GpuChannelComputeState.cs | 11 - .../Shader/ShaderCache.cs | 54 +- .../Shader/ShaderInfoBuilder.cs | 51 +- src/Ryujinx.Graphics.Metal/Auto.cs | 146 -- .../BackgroundResources.cs | 107 - src/Ryujinx.Graphics.Metal/BitMap.cs | 157 -- src/Ryujinx.Graphics.Metal/BufferHolder.cs | 385 ---- src/Ryujinx.Graphics.Metal/BufferManager.cs | 237 --- .../BufferUsageBitmap.cs | 85 - src/Ryujinx.Graphics.Metal/CacheByRange.cs | 294 --- .../CommandBufferEncoder.cs | 170 -- .../CommandBufferPool.cs | 289 --- .../CommandBufferScoped.cs | 43 - src/Ryujinx.Graphics.Metal/Constants.cs | 41 - src/Ryujinx.Graphics.Metal/CounterEvent.cs | 22 - .../DepthStencilCache.cs | 68 - .../DisposableBuffer.cs | 26 - .../DisposableSampler.cs | 22 - .../Effects/IPostProcessingEffect.cs | 10 - .../Effects/IScalingFilter.cs | 18 - .../EncoderResources.cs | 63 - src/Ryujinx.Graphics.Metal/EncoderState.cs | 206 -- .../EncoderStateManager.cs | 1790 ----------------- src/Ryujinx.Graphics.Metal/EnumConversion.cs | 292 --- src/Ryujinx.Graphics.Metal/FenceHolder.cs | 77 - src/Ryujinx.Graphics.Metal/FormatConverter.cs | 49 - src/Ryujinx.Graphics.Metal/FormatTable.cs | 196 -- src/Ryujinx.Graphics.Metal/HardwareInfo.cs | 82 - src/Ryujinx.Graphics.Metal/HashTableSlim.cs | 143 -- src/Ryujinx.Graphics.Metal/HelperShader.cs | 868 -------- src/Ryujinx.Graphics.Metal/IdList.cs | 121 -- src/Ryujinx.Graphics.Metal/ImageArray.cs | 74 - .../IndexBufferPattern.cs | 118 -- .../IndexBufferState.cs | 103 - src/Ryujinx.Graphics.Metal/MetalRenderer.cs | 312 --- .../MultiFenceHolder.cs | 262 --- .../PersistentFlushBuffer.cs | 100 - src/Ryujinx.Graphics.Metal/Pipeline.cs | 877 -------- src/Ryujinx.Graphics.Metal/Program.cs | 286 --- .../ResourceBindingSegment.cs | 22 - .../ResourceLayoutBuilder.cs | 59 - .../Ryujinx.Graphics.Metal.csproj | 27 - src/Ryujinx.Graphics.Metal/SamplerHolder.cs | 90 - src/Ryujinx.Graphics.Metal/Shaders/Blit.metal | 43 - .../Shaders/BlitMs.metal | 45 - .../Shaders/ChangeBufferStride.metal | 72 - .../Shaders/ColorClear.metal | 38 - .../Shaders/ConvertD32S8ToD24S8.metal | 66 - .../Shaders/ConvertIndexBuffer.metal | 59 - .../Shaders/DepthBlit.metal | 27 - .../Shaders/DepthBlitMs.metal | 29 - .../Shaders/DepthStencilClear.metal | 42 - .../Shaders/StencilBlit.metal | 27 - .../Shaders/StencilBlitMs.metal | 29 - src/Ryujinx.Graphics.Metal/StagingBuffer.cs | 288 --- .../State/DepthStencilUid.cs | 110 - .../State/PipelineState.cs | 341 ---- .../State/PipelineUid.cs | 208 -- src/Ryujinx.Graphics.Metal/StateCache.cs | 42 - src/Ryujinx.Graphics.Metal/StringHelper.cs | 30 - src/Ryujinx.Graphics.Metal/SyncManager.cs | 214 -- src/Ryujinx.Graphics.Metal/Texture.cs | 654 ------ src/Ryujinx.Graphics.Metal/TextureArray.cs | 93 - src/Ryujinx.Graphics.Metal/TextureBase.cs | 67 - src/Ryujinx.Graphics.Metal/TextureBuffer.cs | 132 -- src/Ryujinx.Graphics.Metal/TextureCopy.cs | 265 --- .../VertexBufferState.cs | 60 - src/Ryujinx.Graphics.Metal/Window.cs | 234 --- .../CodeGen/Msl/CodeGenContext.cs | 108 - .../CodeGen/Msl/Declarations.cs | 577 ------ .../CodeGen/Msl/Defaults.cs | 34 - .../CodeGen/Msl/HelperFunctions/FindLSB.metal | 5 - .../Msl/HelperFunctions/FindMSBS32.metal | 5 - .../Msl/HelperFunctions/FindMSBU32.metal | 6 - .../HelperFunctions/HelperFunctionNames.cs | 10 - .../CodeGen/Msl/HelperFunctions/Precise.metal | 14 - .../Msl/HelperFunctions/SwizzleAdd.metal | 7 - .../CodeGen/Msl/Instructions/InstGen.cs | 186 -- .../CodeGen/Msl/Instructions/InstGenBallot.cs | 30 - .../Msl/Instructions/InstGenBarrier.cs | 15 - .../CodeGen/Msl/Instructions/InstGenCall.cs | 60 - .../CodeGen/Msl/Instructions/InstGenHelper.cs | 222 -- .../CodeGen/Msl/Instructions/InstGenMemory.cs | 672 ------- .../CodeGen/Msl/Instructions/InstGenVector.cs | 32 - .../CodeGen/Msl/Instructions/InstInfo.cs | 18 - .../CodeGen/Msl/Instructions/InstType.cs | 35 - .../CodeGen/Msl/Instructions/IoMap.cs | 83 - .../CodeGen/Msl/MslGenerator.cs | 286 --- .../CodeGen/Msl/NumberFormatter.cs | 94 - .../CodeGen/Msl/OperandManager.cs | 176 -- .../CodeGen/Msl/TypeConversion.cs | 93 - .../Ryujinx.Graphics.Shader.csproj | 7 - src/Ryujinx.Graphics.Shader/SamplerType.cs | 46 - .../StructuredIr/HelperFunctionsMask.cs | 7 - .../StructuredIr/StructuredProgram.cs | 17 +- .../StructuredIr/StructuredProgramContext.cs | 3 +- .../StructuredIr/StructuredProgramInfo.cs | 11 +- .../Translation/FeatureFlags.cs | 1 - .../Translation/ResourceManager.cs | 153 +- .../Translation/TargetApi.cs | 1 - .../Translation/TargetLanguage.cs | 2 +- .../Transforms/ForcePreciseEnable.cs | 2 - .../Translation/TranslatorContext.cs | 114 +- .../Ryujinx.Headless.SDL2.csproj | 72 + src/Ryujinx.ShaderTools/Program.cs | 2 +- src/Ryujinx/AppHost.cs | 8 +- src/Ryujinx/Assets/locales.json | 2 +- src/Ryujinx/Headless/HeadlessRyujinx.Init.cs | 6 - src/Ryujinx/Headless/HeadlessRyujinx.cs | 3 - src/Ryujinx/Headless/Windows/MetalWindow.cs | 47 - src/Ryujinx/Program.cs | 1 - src/Ryujinx/Ryujinx.csproj | 23 +- .../UI/Renderer/EmbeddedWindowMetal.cs | 19 - src/Ryujinx/UI/Renderer/RendererHost.cs | 13 +- .../UI/ViewModels/MainWindowViewModel.cs | 5 +- .../UI/ViewModels/SettingsHacksViewModel.cs | 23 - .../UI/ViewModels/SettingsViewModel.cs | 2 - .../Views/Settings/SettingsGraphicsView.axaml | 17 +- .../UI/Views/Settings/SettingsHacksView.axaml | 33 - .../UI/Windows/SettingsWindow.axaml.cs | 18 +- .../Utilities/AppLibrary/ApplicationData.cs | 1 - .../Configuration/ConfigurationState.cs | 19 +- 135 files changed, 302 insertions(+), 15077 deletions(-) delete mode 100644 src/Ryujinx.Graphics.GAL/ComputeSize.cs delete mode 100644 src/Ryujinx.Graphics.Metal/Auto.cs delete mode 100644 src/Ryujinx.Graphics.Metal/BackgroundResources.cs delete mode 100644 src/Ryujinx.Graphics.Metal/BitMap.cs delete mode 100644 src/Ryujinx.Graphics.Metal/BufferHolder.cs delete mode 100644 src/Ryujinx.Graphics.Metal/BufferManager.cs delete mode 100644 src/Ryujinx.Graphics.Metal/BufferUsageBitmap.cs delete mode 100644 src/Ryujinx.Graphics.Metal/CacheByRange.cs delete mode 100644 src/Ryujinx.Graphics.Metal/CommandBufferEncoder.cs delete mode 100644 src/Ryujinx.Graphics.Metal/CommandBufferPool.cs delete mode 100644 src/Ryujinx.Graphics.Metal/CommandBufferScoped.cs delete mode 100644 src/Ryujinx.Graphics.Metal/Constants.cs delete mode 100644 src/Ryujinx.Graphics.Metal/CounterEvent.cs delete mode 100644 src/Ryujinx.Graphics.Metal/DepthStencilCache.cs delete mode 100644 src/Ryujinx.Graphics.Metal/DisposableBuffer.cs delete mode 100644 src/Ryujinx.Graphics.Metal/DisposableSampler.cs delete mode 100644 src/Ryujinx.Graphics.Metal/Effects/IPostProcessingEffect.cs delete mode 100644 src/Ryujinx.Graphics.Metal/Effects/IScalingFilter.cs delete mode 100644 src/Ryujinx.Graphics.Metal/EncoderResources.cs delete mode 100644 src/Ryujinx.Graphics.Metal/EncoderState.cs delete mode 100644 src/Ryujinx.Graphics.Metal/EncoderStateManager.cs delete mode 100644 src/Ryujinx.Graphics.Metal/EnumConversion.cs delete mode 100644 src/Ryujinx.Graphics.Metal/FenceHolder.cs delete mode 100644 src/Ryujinx.Graphics.Metal/FormatConverter.cs delete mode 100644 src/Ryujinx.Graphics.Metal/FormatTable.cs delete mode 100644 src/Ryujinx.Graphics.Metal/HardwareInfo.cs delete mode 100644 src/Ryujinx.Graphics.Metal/HashTableSlim.cs delete mode 100644 src/Ryujinx.Graphics.Metal/HelperShader.cs delete mode 100644 src/Ryujinx.Graphics.Metal/IdList.cs delete mode 100644 src/Ryujinx.Graphics.Metal/ImageArray.cs delete mode 100644 src/Ryujinx.Graphics.Metal/IndexBufferPattern.cs delete mode 100644 src/Ryujinx.Graphics.Metal/IndexBufferState.cs delete mode 100644 src/Ryujinx.Graphics.Metal/MetalRenderer.cs delete mode 100644 src/Ryujinx.Graphics.Metal/MultiFenceHolder.cs delete mode 100644 src/Ryujinx.Graphics.Metal/PersistentFlushBuffer.cs delete mode 100644 src/Ryujinx.Graphics.Metal/Pipeline.cs delete mode 100644 src/Ryujinx.Graphics.Metal/Program.cs delete mode 100644 src/Ryujinx.Graphics.Metal/ResourceBindingSegment.cs delete mode 100644 src/Ryujinx.Graphics.Metal/ResourceLayoutBuilder.cs delete mode 100644 src/Ryujinx.Graphics.Metal/Ryujinx.Graphics.Metal.csproj delete mode 100644 src/Ryujinx.Graphics.Metal/SamplerHolder.cs delete mode 100644 src/Ryujinx.Graphics.Metal/Shaders/Blit.metal delete mode 100644 src/Ryujinx.Graphics.Metal/Shaders/BlitMs.metal delete mode 100644 src/Ryujinx.Graphics.Metal/Shaders/ChangeBufferStride.metal delete mode 100644 src/Ryujinx.Graphics.Metal/Shaders/ColorClear.metal delete mode 100644 src/Ryujinx.Graphics.Metal/Shaders/ConvertD32S8ToD24S8.metal delete mode 100644 src/Ryujinx.Graphics.Metal/Shaders/ConvertIndexBuffer.metal delete mode 100644 src/Ryujinx.Graphics.Metal/Shaders/DepthBlit.metal delete mode 100644 src/Ryujinx.Graphics.Metal/Shaders/DepthBlitMs.metal delete mode 100644 src/Ryujinx.Graphics.Metal/Shaders/DepthStencilClear.metal delete mode 100644 src/Ryujinx.Graphics.Metal/Shaders/StencilBlit.metal delete mode 100644 src/Ryujinx.Graphics.Metal/Shaders/StencilBlitMs.metal delete mode 100644 src/Ryujinx.Graphics.Metal/StagingBuffer.cs delete mode 100644 src/Ryujinx.Graphics.Metal/State/DepthStencilUid.cs delete mode 100644 src/Ryujinx.Graphics.Metal/State/PipelineState.cs delete mode 100644 src/Ryujinx.Graphics.Metal/State/PipelineUid.cs delete mode 100644 src/Ryujinx.Graphics.Metal/StateCache.cs delete mode 100644 src/Ryujinx.Graphics.Metal/StringHelper.cs delete mode 100644 src/Ryujinx.Graphics.Metal/SyncManager.cs delete mode 100644 src/Ryujinx.Graphics.Metal/Texture.cs delete mode 100644 src/Ryujinx.Graphics.Metal/TextureArray.cs delete mode 100644 src/Ryujinx.Graphics.Metal/TextureBase.cs delete mode 100644 src/Ryujinx.Graphics.Metal/TextureBuffer.cs delete mode 100644 src/Ryujinx.Graphics.Metal/TextureCopy.cs delete mode 100644 src/Ryujinx.Graphics.Metal/VertexBufferState.cs delete mode 100644 src/Ryujinx.Graphics.Metal/Window.cs delete mode 100644 src/Ryujinx.Graphics.Shader/CodeGen/Msl/CodeGenContext.cs delete mode 100644 src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs delete mode 100644 src/Ryujinx.Graphics.Shader/CodeGen/Msl/Defaults.cs delete mode 100644 src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/FindLSB.metal delete mode 100644 src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/FindMSBS32.metal delete mode 100644 src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/FindMSBU32.metal delete mode 100644 src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/HelperFunctionNames.cs delete mode 100644 src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/Precise.metal delete mode 100644 src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/SwizzleAdd.metal delete mode 100644 src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs delete mode 100644 src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenBallot.cs delete mode 100644 src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenBarrier.cs delete mode 100644 src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenCall.cs delete mode 100644 src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenHelper.cs delete mode 100644 src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs delete mode 100644 src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenVector.cs delete mode 100644 src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstInfo.cs delete mode 100644 src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstType.cs delete mode 100644 src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs delete mode 100644 src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs delete mode 100644 src/Ryujinx.Graphics.Shader/CodeGen/Msl/NumberFormatter.cs delete mode 100644 src/Ryujinx.Graphics.Shader/CodeGen/Msl/OperandManager.cs delete mode 100644 src/Ryujinx.Graphics.Shader/CodeGen/Msl/TypeConversion.cs create mode 100644 src/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj delete mode 100644 src/Ryujinx/Headless/Windows/MetalWindow.cs delete mode 100644 src/Ryujinx/UI/Renderer/EmbeddedWindowMetal.cs diff --git a/Directory.Packages.props b/Directory.Packages.props index 64867fc5b..62a642374 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -46,7 +46,6 @@ - diff --git a/Ryujinx.sln b/Ryujinx.sln index 9e197e85f..9ed282d09 100644 --- a/Ryujinx.sln +++ b/Ryujinx.sln @@ -77,15 +77,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon.Kernel.Gene EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.HLE.Generators", "src\Ryujinx.HLE.Generators\Ryujinx.HLE.Generators.csproj", "{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.Graphics.Metal", "src\Ryujinx.Graphics.Metal\Ryujinx.Graphics.Metal.csproj", "{C08931FA-1191-417A-864F-3882D93E683B}" - ProjectSection(ProjectDependencies) = postProject - {A602AE97-91A5-4608-8DF1-EBF4ED7A0B9E} = {A602AE97-91A5-4608-8DF1-EBF4ED7A0B9E} - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.BuildValidationTasks", "src\Ryujinx.BuildValidationTasks\Ryujinx.BuildValidationTasks.csproj", "{4A89A234-4F19-497D-A576-DDE8CDFC5B22}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.Graphics.Metal.SharpMetalExtensions", "src/Ryujinx.Graphics.Metal.SharpMetalExtensions\Ryujinx.Graphics.Metal.SharpMetalExtensions.csproj", "{81EA598C-DBA1-40B0-8DA4-4796B78F2037}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{36F870C1-3E5F-485F-B426-F0645AF78751}" ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig @@ -95,6 +86,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution .github\workflows\release.yml = .github\workflows\release.yml EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.BuildValidationTasks", "src\Ryujinx.BuildValidationTasks\Ryujinx.BuildValidationTasks.csproj", "{4A89A234-4F19-497D-A576-DDE8CDFC5B22}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -217,6 +210,10 @@ Global {7C1B2721-13DA-4B62-B046-C626605ECCE6}.Debug|Any CPU.Build.0 = Debug|Any CPU {7C1B2721-13DA-4B62-B046-C626605ECCE6}.Release|Any CPU.ActiveCfg = Release|Any CPU {7C1B2721-13DA-4B62-B046-C626605ECCE6}.Release|Any CPU.Build.0 = Release|Any CPU + {BA161CA0-CD65-4E6E-B644-51C8D1E542DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BA161CA0-CD65-4E6E-B644-51C8D1E542DC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BA161CA0-CD65-4E6E-B644-51C8D1E542DC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BA161CA0-CD65-4E6E-B644-51C8D1E542DC}.Release|Any CPU.Build.0 = Release|Any CPU {6AE2A5E8-4C5A-48B9-997B-E1455C0355C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {6AE2A5E8-4C5A-48B9-997B-E1455C0355C6}.Debug|Any CPU.Build.0 = Debug|Any CPU {6AE2A5E8-4C5A-48B9-997B-E1455C0355C6}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -249,16 +246,9 @@ Global {B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Debug|Any CPU.Build.0 = Debug|Any CPU {B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Release|Any CPU.ActiveCfg = Release|Any CPU {B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Release|Any CPU.Build.0 = Release|Any CPU - {C08931FA-1191-417A-864F-3882D93E683B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C08931FA-1191-417A-864F-3882D93E683B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C08931FA-1191-417A-864F-3882D93E683B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C08931FA-1191-417A-864F-3882D93E683B}.Release|Any CPU.Build.0 = Release|Any CPU {4A89A234-4F19-497D-A576-DDE8CDFC5B22}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4A89A234-4F19-497D-A576-DDE8CDFC5B22}.Debug|Any CPU.Build.0 = Debug|Any CPU {4A89A234-4F19-497D-A576-DDE8CDFC5B22}.Release|Any CPU.ActiveCfg = Release|Any CPU - {81EA598C-DBA1-40B0-8DA4-4796B78F2037}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {81EA598C-DBA1-40B0-8DA4-4796B78F2037}.Debug|Any CPU.Build.0 = Debug|Any CPU - {81EA598C-DBA1-40B0-8DA4-4796B78F2037}.Release|Any CPU.ActiveCfg = Release|Any CPU - {81EA598C-DBA1-40B0-8DA4-4796B78F2037}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/Ryujinx.Common/Configuration/GraphicsBackend.cs b/src/Ryujinx.Common/Configuration/GraphicsBackend.cs index 9d399d560..e3b4f91b0 100644 --- a/src/Ryujinx.Common/Configuration/GraphicsBackend.cs +++ b/src/Ryujinx.Common/Configuration/GraphicsBackend.cs @@ -6,9 +6,7 @@ namespace Ryujinx.Common.Configuration [JsonConverter(typeof(TypedStringEnumConverter))] public enum GraphicsBackend { - Auto, Vulkan, OpenGl, - Metal } } diff --git a/src/Ryujinx.Common/TitleIDs.cs b/src/Ryujinx.Common/TitleIDs.cs index 1bf788c96..82be1572f 100644 --- a/src/Ryujinx.Common/TitleIDs.cs +++ b/src/Ryujinx.Common/TitleIDs.cs @@ -10,54 +10,6 @@ namespace Ryujinx.Common { public static ReactiveObject> CurrentApplication { get; } = new(); - public static GraphicsBackend SelectGraphicsBackend(string titleId, GraphicsBackend currentBackend) - { - switch (currentBackend) - { - case GraphicsBackend.Metal when !OperatingSystem.IsMacOS(): - case GraphicsBackend.OpenGl when OperatingSystem.IsMacOS(): - return GraphicsBackend.Vulkan; - case GraphicsBackend.Vulkan or GraphicsBackend.OpenGl or GraphicsBackend.Metal: - return currentBackend; - } - - if (!RunningPlatform.IsArmMac) - return GraphicsBackend.Vulkan; - - return GreatMetalTitles.ContainsIgnoreCase(titleId) ? GraphicsBackend.Metal : GraphicsBackend.Vulkan; - } - - public static readonly string[] GreatMetalTitles = - [ - "01009b500007c000", // ARMS - "0100a5c00d162000", // Cuphead - "010023800d64a000", // Deltarune - "01003a30012c0000", // LEGO City Undercover - "010048701995e000", // Luigi's Manion 2 HD - "010028600EBDA000", // Mario 3D World - "0100152000022000", // Mario Kart 8 Deluxe - "010075a016a3a000", // Persona 4 Arena Ultimax - "0100187003A36000", // Pokémon: Let's Go, Eevee! - "010003f003a34000", // Pokémon: Let's Go, Pikachu! - "01008C0016544000", // Sea of Stars - "01006A800016E000", // Smash Ultimate - "01006bb00c6f0000", // The Legend of Zelda: Link's Awakening - - // These ones have small issues, but those happen on Vulkan as well: - "01006f8002326000", // Animal Crossings: New Horizons - "01009bf0072d4000", // Captain Toad: Treasure Tracker - "01009510001ca000", // Fast RMX - "01005CA01580E000", // Persona 5 Royal - "0100b880154fc000", // Persona 5 The Royal (Japan) - "010015100b514000", // Super Mario Bros. Wonder - "0100000000010000", // Super Mario Odyssey - - // Further testing is appreciated, I did not test the entire game: - //"010076f0049a2000", // Bayonetta - //"0100cf5010fec000", // Bayonetta Origins: Cereza and the Lost Demon - //"0100f4300bf2c000", // New Pokemon Snap - ]; - public static string GetDiscordGameAsset(string titleId) => DiscordGameAssetKeys.Contains(titleId) ? titleId : "game"; diff --git a/src/Ryujinx.Graphics.GAL/ComputeSize.cs b/src/Ryujinx.Graphics.GAL/ComputeSize.cs deleted file mode 100644 index c8d89c0fe..000000000 --- a/src/Ryujinx.Graphics.GAL/ComputeSize.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace Ryujinx.Graphics.GAL -{ - public readonly struct ComputeSize - { - public readonly static ComputeSize VtgAsCompute = new(32, 32, 1); - - public readonly int X; - public readonly int Y; - public readonly int Z; - - public ComputeSize(int x, int y, int z) - { - X = x; - Y = y; - Z = z; - } - } -} diff --git a/src/Ryujinx.Graphics.GAL/Format.cs b/src/Ryujinx.Graphics.GAL/Format.cs index b1eb68f72..17c42d2d4 100644 --- a/src/Ryujinx.Graphics.GAL/Format.cs +++ b/src/Ryujinx.Graphics.GAL/Format.cs @@ -339,84 +339,6 @@ namespace Ryujinx.Graphics.GAL return 1; } - /// - /// Get bytes per element for this format. - /// - /// Texture format - /// Byte size for an element of this format (pixel, vertex attribute, etc) - public static int GetBytesPerElement(this Format format) - { - int scalarSize = format.GetScalarSize(); - - switch (format) - { - case Format.R8G8Unorm: - case Format.R8G8Snorm: - case Format.R8G8Uint: - case Format.R8G8Sint: - case Format.R8G8Uscaled: - case Format.R8G8Sscaled: - case Format.R16G16Float: - case Format.R16G16Unorm: - case Format.R16G16Snorm: - case Format.R16G16Uint: - case Format.R16G16Sint: - case Format.R16G16Uscaled: - case Format.R16G16Sscaled: - case Format.R32G32Float: - case Format.R32G32Uint: - case Format.R32G32Sint: - case Format.R32G32Uscaled: - case Format.R32G32Sscaled: - return 2 * scalarSize; - - case Format.R8G8B8Unorm: - case Format.R8G8B8Snorm: - case Format.R8G8B8Uint: - case Format.R8G8B8Sint: - case Format.R8G8B8Uscaled: - case Format.R8G8B8Sscaled: - case Format.R16G16B16Float: - case Format.R16G16B16Unorm: - case Format.R16G16B16Snorm: - case Format.R16G16B16Uint: - case Format.R16G16B16Sint: - case Format.R16G16B16Uscaled: - case Format.R16G16B16Sscaled: - case Format.R32G32B32Float: - case Format.R32G32B32Uint: - case Format.R32G32B32Sint: - case Format.R32G32B32Uscaled: - case Format.R32G32B32Sscaled: - return 3 * scalarSize; - - case Format.R8G8B8A8Unorm: - case Format.R8G8B8A8Snorm: - case Format.R8G8B8A8Uint: - case Format.R8G8B8A8Sint: - case Format.R8G8B8A8Srgb: - case Format.R8G8B8A8Uscaled: - case Format.R8G8B8A8Sscaled: - case Format.B8G8R8A8Unorm: - case Format.B8G8R8A8Srgb: - case Format.R16G16B16A16Float: - case Format.R16G16B16A16Unorm: - case Format.R16G16B16A16Snorm: - case Format.R16G16B16A16Uint: - case Format.R16G16B16A16Sint: - case Format.R16G16B16A16Uscaled: - case Format.R16G16B16A16Sscaled: - case Format.R32G32B32A32Float: - case Format.R32G32B32A32Uint: - case Format.R32G32B32A32Sint: - case Format.R32G32B32A32Uscaled: - case Format.R32G32B32A32Sscaled: - return 4 * scalarSize; - } - - return scalarSize; - } - /// /// Checks if the texture format is a depth or depth-stencil format. /// diff --git a/src/Ryujinx.Graphics.GAL/ShaderInfo.cs b/src/Ryujinx.Graphics.GAL/ShaderInfo.cs index c7965a03d..2fd3227dc 100644 --- a/src/Ryujinx.Graphics.GAL/ShaderInfo.cs +++ b/src/Ryujinx.Graphics.GAL/ShaderInfo.cs @@ -4,22 +4,23 @@ namespace Ryujinx.Graphics.GAL { public int FragmentOutputMap { get; } public ResourceLayout ResourceLayout { get; } - public ComputeSize ComputeLocalSize { get; } public ProgramPipelineState? State { get; } public bool FromCache { get; set; } - public ShaderInfo( - int fragmentOutputMap, - ResourceLayout resourceLayout, - ComputeSize computeLocalSize, - ProgramPipelineState? state, - bool fromCache = false) + public ShaderInfo(int fragmentOutputMap, ResourceLayout resourceLayout, ProgramPipelineState state, bool fromCache = false) { FragmentOutputMap = fragmentOutputMap; ResourceLayout = resourceLayout; - ComputeLocalSize = computeLocalSize; State = state; FromCache = fromCache; } + + public ShaderInfo(int fragmentOutputMap, ResourceLayout resourceLayout, bool fromCache = false) + { + FragmentOutputMap = fragmentOutputMap; + ResourceLayout = resourceLayout; + State = null; + FromCache = fromCache; + } } } diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeContext.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeContext.cs index 15f1a4a33..eba1b6030 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeContext.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeContext.cs @@ -11,6 +11,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw /// class VtgAsComputeContext : IDisposable { + private const int DummyBufferSize = 16; + private readonly GpuContext _context; /// @@ -46,7 +48,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw 1, 1, 1, - format.GetBytesPerElement(), + 1, format, DepthStencilMode.Depth, Target.TextureBuffer, @@ -519,6 +521,21 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw return new BufferRange(_geometryIndexDataBuffer.Handle, offset, size, write); } + /// + /// Gets the range for a dummy 16 bytes buffer, filled with zeros. + /// + /// Dummy buffer range + public BufferRange GetDummyBufferRange() + { + if (_dummyBuffer == BufferHandle.Null) + { + _dummyBuffer = _context.Renderer.CreateBuffer(DummyBufferSize, BufferAccess.DeviceMemory); + _context.Renderer.Pipeline.ClearBuffer(_dummyBuffer, 0, DummyBufferSize, 0); + } + + return new BufferRange(_dummyBuffer, 0, DummyBufferSize); + } + /// /// Gets the range for a sequential index buffer, with ever incrementing index values. /// diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeState.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeState.cs index 16a19debe..0d59275ff 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeState.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeState.cs @@ -147,6 +147,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw { _vacContext.VertexInfoBufferUpdater.SetVertexStride(index, 0, componentsCount); _vacContext.VertexInfoBufferUpdater.SetVertexOffset(index, 0, 0); + SetDummyBufferTexture(_vertexAsCompute.Reservations, index, format); continue; } @@ -162,12 +163,15 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw { _vacContext.VertexInfoBufferUpdater.SetVertexStride(index, 0, componentsCount); _vacContext.VertexInfoBufferUpdater.SetVertexOffset(index, 0, 0); + SetDummyBufferTexture(_vertexAsCompute.Reservations, index, format); continue; } int vbStride = vertexBuffer.UnpackStride(); ulong vbSize = GetVertexBufferSize(address, endAddress.Pack(), vbStride, _indexed, instanced, _firstVertex, _count); + ulong oldVbSize = vbSize; + ulong attributeOffset = (ulong)vertexAttrib.UnpackOffset(); int componentSize = format.GetScalarSize(); @@ -340,6 +344,20 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw return maxOutputVertices / verticesPerPrimitive; } + /// + /// Binds a dummy buffer as vertex buffer into a buffer texture. + /// + /// Shader resource binding reservations + /// Buffer texture index + /// Buffer texture format + private readonly void SetDummyBufferTexture(ResourceReservations reservations, int index, Format format) + { + ITexture bufferTexture = _vacContext.EnsureBufferTexture(index + 2, format); + bufferTexture.SetStorage(_vacContext.GetDummyBufferRange()); + + _context.Renderer.Pipeline.SetTextureAndSampler(ShaderStage.Compute, reservations.GetVertexBufferTextureBinding(index), bufferTexture, null); + } + /// /// Binds a vertex buffer into a buffer texture. /// diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index 4ef0caced..22ffe4812 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -324,11 +324,6 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache bool loadHostCache = header.CodeGenVersion == CodeGenVersion; - if (context.Capabilities.Api == TargetApi.Metal) - { - loadHostCache = false; - } - int programIndex = 0; DataEntry entry = new(); @@ -397,8 +392,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache context, shaders, specState.PipelineState, - specState.TransformFeedbackDescriptors != null, - specState.ComputeState.GetLocalSize()); + specState.TransformFeedbackDescriptors != null); IProgram hostProgram; @@ -635,10 +629,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache return; } - if (context.Capabilities.Api != TargetApi.Metal) - { - WriteHostCode(context, hostCode, program.Shaders, streams, timestamp); - } + WriteHostCode(context, hostCode, program.Shaders, streams, timestamp); } /// diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs index f8cb6c56b..a0d3e8c15 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs @@ -1,4 +1,3 @@ -using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Shader; @@ -367,9 +366,6 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache { try { - if (_context.Capabilities.Api == TargetApi.Metal && _context.DirtyHacks.IsEnabled(DirtyHack.ShaderTranslationDelay)) - Thread.Sleep(_context.DirtyHacks[DirtyHack.ShaderTranslationDelay]); - AsyncProgramTranslation asyncTranslation = new(guestShaders, specState, programIndex, isCompute); _asyncTranslationQueue.Add(asyncTranslation, _cancellationToken); } @@ -494,12 +490,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache { ShaderSource[] shaderSources = new ShaderSource[compilation.TranslatedStages.Length]; - ref GpuChannelComputeState computeState = ref compilation.SpecializationState.ComputeState; - - ShaderInfoBuilder shaderInfoBuilder = new( - _context, - compilation.SpecializationState.TransformFeedbackDescriptors != null, - computeLocalSize: computeState.GetLocalSize()); + ShaderInfoBuilder shaderInfoBuilder = new(_context, compilation.SpecializationState.TransformFeedbackDescriptors != null); for (int index = 0; index < compilation.TranslatedStages.Length; index++) { diff --git a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs index c7d86a47e..393584bd4 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs @@ -17,7 +17,7 @@ namespace Ryujinx.Graphics.Gpu.Shader private readonly GpuAccessorState _state; private readonly int _stageIndex; private readonly bool _compute; - private readonly bool _isOpenGL; + private readonly bool _isVulkan; private readonly bool _hasGeometryShader; private readonly bool _supportsQuads; @@ -39,7 +39,7 @@ namespace Ryujinx.Graphics.Gpu.Shader _channel = channel; _state = state; _stageIndex = stageIndex; - _isOpenGL = context.Capabilities.Api == TargetApi.OpenGL; + _isVulkan = context.Capabilities.Api == TargetApi.Vulkan; _hasGeometryShader = hasGeometryShader; _supportsQuads = context.Capabilities.SupportsQuads; @@ -117,10 +117,10 @@ namespace Ryujinx.Graphics.Gpu.Shader public GpuGraphicsState QueryGraphicsState() { return _state.GraphicsState.CreateShaderGraphicsState( - _isOpenGL, + !_isVulkan, _supportsQuads, _hasGeometryShader, - !_isOpenGL || _state.GraphicsState.YNegateEnabled); + _isVulkan || _state.GraphicsState.YNegateEnabled); } /// diff --git a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs index 701ff764a..d89eebabf 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs @@ -55,7 +55,7 @@ namespace Ryujinx.Graphics.Gpu.Shader { int binding; - if (_context.Capabilities.Api != TargetApi.OpenGL) + if (_context.Capabilities.Api == TargetApi.Vulkan) { binding = GetBindingFromIndex(index, _context.Capabilities.MaximumUniformBuffersPerStage, "Uniform buffer"); } @@ -71,7 +71,7 @@ namespace Ryujinx.Graphics.Gpu.Shader { int binding; - if (_context.Capabilities.Api != TargetApi.OpenGL) + if (_context.Capabilities.Api == TargetApi.Vulkan) { if (count == 1) { @@ -103,7 +103,7 @@ namespace Ryujinx.Graphics.Gpu.Shader { int binding; - if (_context.Capabilities.Api != TargetApi.OpenGL) + if (_context.Capabilities.Api == TargetApi.Vulkan) { binding = GetBindingFromIndex(index, _context.Capabilities.MaximumStorageBuffersPerStage, "Storage buffer"); } @@ -119,7 +119,7 @@ namespace Ryujinx.Graphics.Gpu.Shader { int binding; - if (_context.Capabilities.Api != TargetApi.OpenGL) + if (_context.Capabilities.Api == TargetApi.Vulkan) { if (count == 1) { diff --git a/src/Ryujinx.Graphics.Gpu/Shader/GpuChannelComputeState.cs b/src/Ryujinx.Graphics.Gpu/Shader/GpuChannelComputeState.cs index 720f7e796..d8cdbc348 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/GpuChannelComputeState.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/GpuChannelComputeState.cs @@ -1,5 +1,3 @@ -using Ryujinx.Graphics.GAL; - namespace Ryujinx.Graphics.Gpu.Shader { /// @@ -63,14 +61,5 @@ namespace Ryujinx.Graphics.Gpu.Shader SharedMemorySize = sharedMemorySize; HasUnalignedStorageBuffer = hasUnalignedStorageBuffer; } - - /// - /// Gets the local group size of the shader in a GAL compatible struct. - /// - /// Local group size - public ComputeSize GetLocalSize() - { - return new ComputeSize(LocalSizeX, LocalSizeY, LocalSizeZ); - } } } diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs index 476ae3d82..4fc66c4c0 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs @@ -117,7 +117,7 @@ namespace Ryujinx.Graphics.Gpu.Shader private static string GetDiskCachePath() { return GraphicsConfig.EnableShaderCache && GraphicsConfig.TitleId != null - ? Path.Combine(AppDataManager.GamesDirPath, GraphicsConfig.TitleId.ToLower(), "cache", "shader") + ? Path.Combine(AppDataManager.GamesDirPath, GraphicsConfig.TitleId, "cache", "shader") : null; } @@ -204,7 +204,7 @@ namespace Ryujinx.Graphics.Gpu.Shader GpuChannelComputeState computeState, ulong gpuVa) { - if (_cpPrograms.TryGetValue(gpuVa, out CachedShaderProgram cpShader) && IsShaderEqual(channel, poolState, computeState, cpShader, gpuVa)) + if (_cpPrograms.TryGetValue(gpuVa, out var cpShader) && IsShaderEqual(channel, poolState, computeState, cpShader, gpuVa)) { return cpShader; } @@ -223,11 +223,8 @@ namespace Ryujinx.Graphics.Gpu.Shader TranslatorContext translatorContext = DecodeComputeShader(gpuAccessor, _context.Capabilities.Api, gpuVa); TranslatedShader translatedShader = TranslateShader(_dumper, channel, translatorContext, cachedGuestCode, asCompute: false); - ShaderSource[] shaderSourcesArray = [CreateShaderSource(translatedShader.Program)]; - ShaderInfo info = ShaderInfoBuilder.BuildForCompute( - _context, - translatedShader.Program.Info, - computeState.GetLocalSize()); + ShaderSource[] shaderSourcesArray = new ShaderSource[] { CreateShaderSource(translatedShader.Program) }; + ShaderInfo info = ShaderInfoBuilder.BuildForCompute(_context, translatedShader.Program.Info); IProgram hostProgram = _context.Renderer.CreateProgram(shaderSourcesArray, info); cpShader = new CachedShaderProgram(hostProgram, specState, translatedShader.Shader); @@ -254,8 +251,8 @@ namespace Ryujinx.Graphics.Gpu.Shader { channel.TextureManager.UpdateRenderTargets(); - RtControl rtControl = state.RtControl; - TextureMsaaMode msaaMode = state.RtMsaaMode; + var rtControl = state.RtControl; + var msaaMode = state.RtMsaaMode; pipeline.SamplesCount = msaaMode.SamplesInX() * msaaMode.SamplesInY(); @@ -265,7 +262,7 @@ namespace Ryujinx.Graphics.Gpu.Shader { int rtIndex = rtControl.UnpackPermutationIndex(index); - RtColorState colorState = state.RtColorState[rtIndex]; + var colorState = state.RtColorState[rtIndex]; if (index >= count || colorState.Format == 0 || colorState.WidthOrStride == 0) { @@ -310,12 +307,12 @@ namespace Ryujinx.Graphics.Gpu.Shader ref GpuChannelGraphicsState graphicsState, ShaderAddresses addresses) { - if (_gpPrograms.TryGetValue(addresses, out CachedShaderProgram gpShaders) && IsShaderEqual(channel, ref poolState, ref graphicsState, gpShaders, addresses)) + if (_gpPrograms.TryGetValue(addresses, out var gpShaders) && IsShaderEqual(channel, ref poolState, ref graphicsState, gpShaders, addresses)) { return gpShaders; } - if (_graphicsShaderCache.TryFind(channel, ref poolState, ref graphicsState, addresses, out gpShaders, out CachedGraphicsGuestCode cachedGuestCode)) + if (_graphicsShaderCache.TryFind(channel, ref poolState, ref graphicsState, addresses, out gpShaders, out var cachedGuestCode)) { _gpPrograms[addresses] = gpShaders; return gpShaders; @@ -368,7 +365,7 @@ namespace Ryujinx.Graphics.Gpu.Shader bool geometryToCompute = ShouldConvertGeometryToCompute(_context, geometryHasStore); CachedShaderStage[] shaders = new CachedShaderStage[Constants.ShaderStages + 1]; - List shaderSources = []; + List shaderSources = new(); TranslatorContext previousStage = null; ShaderInfoBuilder infoBuilder = new(_context, transformFeedbackDescriptors != null, vertexToCompute); @@ -428,8 +425,7 @@ namespace Ryujinx.Graphics.Gpu.Shader TranslatorContext lastInVertexPipeline = geometryToCompute ? translatorContexts[4] ?? currentStage : currentStage; - (program, ShaderProgramInfo vacInfo) = lastInVertexPipeline.GenerateVertexPassthroughForCompute(); - infoBuilder.AddStageInfoVac(vacInfo); + program = lastInVertexPipeline.GenerateVertexPassthroughForCompute(); } else { @@ -534,9 +530,9 @@ namespace Ryujinx.Graphics.Gpu.Shader private ShaderAsCompute CreateHostVertexAsComputeProgram(ShaderProgram program, TranslatorContext context, bool tfEnabled) { ShaderSource source = new(program.Code, program.BinaryCode, ShaderStage.Compute, program.Language); - ShaderInfo info = ShaderInfoBuilder.BuildForVertexAsCompute(_context, program.Info, context.GetVertexAsComputeInfo(), tfEnabled); + ShaderInfo info = ShaderInfoBuilder.BuildForVertexAsCompute(_context, program.Info, tfEnabled); - return new(_context.Renderer.CreateProgram([source], info), program.Info, context.GetResourceReservations()); + return new(_context.Renderer.CreateProgram(new[] { source }, info), program.Info, context.GetResourceReservations()); } /// @@ -586,7 +582,7 @@ namespace Ryujinx.Graphics.Gpu.Shader for (int i = 0; i < Constants.TotalTransformFeedbackBuffers; i++) { - TfState tf = state.TfState[i]; + var tf = state.TfState[i]; descs[i] = new TransformFeedbackDescriptor( tf.BufferIndex, @@ -692,7 +688,7 @@ namespace Ryujinx.Graphics.Gpu.Shader /// The generated translator context public static TranslatorContext DecodeComputeShader(IGpuAccessor gpuAccessor, TargetApi api, ulong gpuVa) { - TranslationOptions options = CreateTranslationOptions(api, DefaultFlags | TranslationFlags.Compute); + var options = CreateTranslationOptions(api, DefaultFlags | TranslationFlags.Compute); return Translator.CreateContext(gpuVa, gpuAccessor, options); } @@ -709,7 +705,7 @@ namespace Ryujinx.Graphics.Gpu.Shader /// The generated translator context public static TranslatorContext DecodeGraphicsShader(IGpuAccessor gpuAccessor, TargetApi api, TranslationFlags flags, ulong gpuVa) { - TranslationOptions options = CreateTranslationOptions(api, flags); + var options = CreateTranslationOptions(api, flags); return Translator.CreateContext(gpuVa, gpuAccessor, options); } @@ -735,7 +731,7 @@ namespace Ryujinx.Graphics.Gpu.Shader { ulong cb1DataAddress = channel.BufferManager.GetGraphicsUniformBufferAddress(0, 1); - MemoryManager memoryManager = channel.MemoryManager; + var memoryManager = channel.MemoryManager; codeA ??= memoryManager.GetSpan(vertexA.Address, vertexA.Size).ToArray(); codeB ??= memoryManager.GetSpan(currentStage.Address, currentStage.Size).ToArray(); @@ -773,7 +769,7 @@ namespace Ryujinx.Graphics.Gpu.Shader /// Compiled graphics shader code private static TranslatedShader TranslateShader(ShaderDumper dumper, GpuChannel channel, TranslatorContext context, byte[] code, bool asCompute) { - MemoryManager memoryManager = channel.MemoryManager; + var memoryManager = channel.MemoryManager; ulong cb1DataAddress = context.Stage == ShaderStage.Compute ? channel.BufferManager.GetComputeUniformBufferAddress(1) @@ -801,7 +797,7 @@ namespace Ryujinx.Graphics.Gpu.Shader { if (address == MemoryManager.PteUnmapped || size == 0) { - return []; + return Array.Empty(); } return memoryManager.Physical.GetSpan(address, size).ToArray(); @@ -826,20 +822,16 @@ namespace Ryujinx.Graphics.Gpu.Shader /// /// Creates shader translation options with the requested graphics API and flags. - /// The shader language is chosen based on the current configuration and graphics API. + /// The shader language is choosen based on the current configuration and graphics API. /// /// Target graphics API /// Translation flags /// Translation options private static TranslationOptions CreateTranslationOptions(TargetApi api, TranslationFlags flags) { - TargetLanguage lang = api switch - { - TargetApi.OpenGL => TargetLanguage.Glsl, - TargetApi.Vulkan => GraphicsConfig.EnableSpirvCompilationOnVulkan ? TargetLanguage.Spirv : TargetLanguage.Glsl, - TargetApi.Metal => TargetLanguage.Msl, - _ => throw new NotImplementedException() - }; + TargetLanguage lang = GraphicsConfig.EnableSpirvCompilationOnVulkan && api == TargetApi.Vulkan + ? TargetLanguage.Spirv + : TargetLanguage.Glsl; return new TranslationOptions(lang, api, flags); } diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs index 8aa48e0ce..c74c612a5 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs @@ -22,7 +22,6 @@ namespace Ryujinx.Graphics.Gpu.Shader ResourceStages.Geometry; private readonly GpuContext _context; - private readonly ComputeSize _computeLocalSize; private int _fragmentOutputMap; @@ -40,11 +39,9 @@ namespace Ryujinx.Graphics.Gpu.Shader /// GPU context that owns the shaders that will be added to the builder /// Indicates if the graphics shader is used with transform feedback enabled /// Indicates that the vertex shader will be emulated on a compute shader - /// Indicates the local thread size for a compute shader - public ShaderInfoBuilder(GpuContext context, bool tfEnabled, bool vertexAsCompute = false, ComputeSize computeLocalSize = default) + public ShaderInfoBuilder(GpuContext context, bool tfEnabled, bool vertexAsCompute = false) { _context = context; - _computeLocalSize = computeLocalSize; _fragmentOutputMap = -1; @@ -98,7 +95,7 @@ namespace Ryujinx.Graphics.Gpu.Shader private void PopulateDescriptorAndUsages(ResourceStages stages, ResourceType type, int setIndex, int start, int count, bool write = false) { AddDescriptor(stages, type, setIndex, start, count); - // AddUsage(stages, type, setIndex, start, count, write); + AddUsage(stages, type, setIndex, start, count, write); } /// @@ -162,25 +159,6 @@ namespace Ryujinx.Graphics.Gpu.Shader AddUsage(info.Images, stages, isImage: true); } - public void AddStageInfoVac(ShaderProgramInfo info) - { - ResourceStages stages = info.Stage switch - { - ShaderStage.Compute => ResourceStages.Compute, - ShaderStage.Vertex => ResourceStages.Vertex, - ShaderStage.TessellationControl => ResourceStages.TessellationControl, - ShaderStage.TessellationEvaluation => ResourceStages.TessellationEvaluation, - ShaderStage.Geometry => ResourceStages.Geometry, - ShaderStage.Fragment => ResourceStages.Fragment, - _ => ResourceStages.None, - }; - - AddUsage(info.CBuffers, stages, isStorage: false); - AddUsage(info.SBuffers, stages, isStorage: true); - AddUsage(info.Textures, stages, isImage: false); - AddUsage(info.Images, stages, isImage: true); - } - /// /// Adds a resource descriptor to the list of descriptors. /// @@ -383,7 +361,14 @@ namespace Ryujinx.Graphics.Gpu.Shader ResourceLayout resourceLayout = new(descriptors.AsReadOnly(), usages.AsReadOnly()); - return new ShaderInfo(_fragmentOutputMap, resourceLayout, _computeLocalSize, pipeline, fromCache); + if (pipeline.HasValue) + { + return new ShaderInfo(_fragmentOutputMap, resourceLayout, pipeline.Value, fromCache); + } + else + { + return new ShaderInfo(_fragmentOutputMap, resourceLayout, fromCache); + } } /// @@ -393,16 +378,14 @@ namespace Ryujinx.Graphics.Gpu.Shader /// Shaders from the disk cache /// Optional pipeline for background compilation /// Indicates if the graphics shader is used with transform feedback enabled - /// Compute local thread size /// Shader information public static ShaderInfo BuildForCache( GpuContext context, IEnumerable programs, ProgramPipelineState? pipeline, - bool tfEnabled, - ComputeSize computeLocalSize) + bool tfEnabled) { - ShaderInfoBuilder builder = new(context, tfEnabled, computeLocalSize: computeLocalSize); + ShaderInfoBuilder builder = new(context, tfEnabled); foreach (CachedShaderStage program in programs) { @@ -420,12 +403,11 @@ namespace Ryujinx.Graphics.Gpu.Shader /// /// GPU context that owns the shader /// Compute shader information - /// Compute local thread size /// True if the compute shader comes from a disk cache, false otherwise /// Shader information - public static ShaderInfo BuildForCompute(GpuContext context, ShaderProgramInfo info, ComputeSize computeLocalSize, bool fromCache = false) + public static ShaderInfo BuildForCompute(GpuContext context, ShaderProgramInfo info, bool fromCache = false) { - ShaderInfoBuilder builder = new(context, tfEnabled: false, vertexAsCompute: false, computeLocalSize: computeLocalSize); + ShaderInfoBuilder builder = new(context, tfEnabled: false, vertexAsCompute: false); builder.AddStageInfo(info); @@ -440,11 +422,10 @@ namespace Ryujinx.Graphics.Gpu.Shader /// Indicates if the graphics shader is used with transform feedback enabled /// True if the compute shader comes from a disk cache, false otherwise /// Shader information - public static ShaderInfo BuildForVertexAsCompute(GpuContext context, ShaderProgramInfo info, ShaderProgramInfo info2, bool tfEnabled, bool fromCache = false) + public static ShaderInfo BuildForVertexAsCompute(GpuContext context, ShaderProgramInfo info, bool tfEnabled, bool fromCache = false) { - ShaderInfoBuilder builder = new(context, tfEnabled, vertexAsCompute: true, computeLocalSize: ComputeSize.VtgAsCompute); + ShaderInfoBuilder builder = new(context, tfEnabled, vertexAsCompute: true); - builder.AddStageInfoVac(info2); builder.AddStageInfo(info, vertexAsCompute: true); return builder.Build(null, fromCache); diff --git a/src/Ryujinx.Graphics.Metal/Auto.cs b/src/Ryujinx.Graphics.Metal/Auto.cs deleted file mode 100644 index 7e79ecbc3..000000000 --- a/src/Ryujinx.Graphics.Metal/Auto.cs +++ /dev/null @@ -1,146 +0,0 @@ -using System; -using System.Diagnostics; -using System.Runtime.Versioning; -using System.Threading; - -namespace Ryujinx.Graphics.Metal -{ - interface IAuto - { - bool HasCommandBufferDependency(CommandBufferScoped cbs); - - void IncrementReferenceCount(); - void DecrementReferenceCount(int cbIndex); - void DecrementReferenceCount(); - } - - interface IAutoPrivate : IAuto - { - void AddCommandBufferDependencies(CommandBufferScoped cbs); - } - - [SupportedOSPlatform("macos")] - class Auto : IAutoPrivate, IDisposable where T : IDisposable - { - private int _referenceCount; - private T _value; - - private readonly BitMap _cbOwnership; - private readonly MultiFenceHolder _waitable; - - private bool _disposed; - private bool _destroyed; - - public Auto(T value) - { - _referenceCount = 1; - _value = value; - _cbOwnership = new BitMap(CommandBufferPool.MaxCommandBuffers); - } - - public Auto(T value, MultiFenceHolder waitable) : this(value) - { - _waitable = waitable; - } - - public T Get(CommandBufferScoped cbs, int offset, int size, bool write = false) - { - _waitable?.AddBufferUse(cbs.CommandBufferIndex, offset, size, write); - return Get(cbs); - } - - public T GetUnsafe() - { - return _value; - } - - public T Get(CommandBufferScoped cbs) - { - if (!_destroyed) - { - AddCommandBufferDependencies(cbs); - } - - return _value; - } - - public bool HasCommandBufferDependency(CommandBufferScoped cbs) - { - return _cbOwnership.IsSet(cbs.CommandBufferIndex); - } - - public bool HasRentedCommandBufferDependency(CommandBufferPool cbp) - { - return _cbOwnership.AnySet(); - } - - public void AddCommandBufferDependencies(CommandBufferScoped cbs) - { - // We don't want to add a reference to this object to the command buffer - // more than once, so if we detect that the command buffer already has ownership - // of this object, then we can just return without doing anything else. - if (_cbOwnership.Set(cbs.CommandBufferIndex)) - { - if (_waitable != null) - { - cbs.AddWaitable(_waitable); - } - - cbs.AddDependant(this); - } - } - - public bool TryIncrementReferenceCount() - { - int lastValue; - do - { - lastValue = _referenceCount; - - if (lastValue == 0) - { - return false; - } - } - while (Interlocked.CompareExchange(ref _referenceCount, lastValue + 1, lastValue) != lastValue); - - return true; - } - - public void IncrementReferenceCount() - { - if (Interlocked.Increment(ref _referenceCount) == 1) - { - Interlocked.Decrement(ref _referenceCount); - throw new InvalidOperationException("Attempted to increment the reference count of an object that was already destroyed."); - } - } - - public void DecrementReferenceCount(int cbIndex) - { - _cbOwnership.Clear(cbIndex); - DecrementReferenceCount(); - } - - public void DecrementReferenceCount() - { - if (Interlocked.Decrement(ref _referenceCount) == 0) - { - _value.Dispose(); - _value = default; - _destroyed = true; - } - - Debug.Assert(_referenceCount >= 0); - } - - public void Dispose() - { - if (!_disposed) - { - DecrementReferenceCount(); - _disposed = true; - } - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/BackgroundResources.cs b/src/Ryujinx.Graphics.Metal/BackgroundResources.cs deleted file mode 100644 index e38b877a6..000000000 --- a/src/Ryujinx.Graphics.Metal/BackgroundResources.cs +++ /dev/null @@ -1,107 +0,0 @@ -using SharpMetal.Metal; -using System; -using System.Collections.Generic; -using System.Runtime.Versioning; -using System.Threading; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - class BackgroundResource : IDisposable - { - private readonly MetalRenderer _renderer; - - private CommandBufferPool _pool; - private PersistentFlushBuffer _flushBuffer; - - public BackgroundResource(MetalRenderer renderer) - { - _renderer = renderer; - } - - public CommandBufferPool GetPool() - { - if (_pool == null) - { - MTLCommandQueue queue = _renderer.BackgroundQueue; - _pool = new CommandBufferPool(queue, true); - _pool.Initialize(null); // TODO: Proper encoder factory for background render/compute - } - - return _pool; - } - - public PersistentFlushBuffer GetFlushBuffer() - { - _flushBuffer ??= new PersistentFlushBuffer(_renderer); - - return _flushBuffer; - } - - public void Dispose() - { - _pool?.Dispose(); - _flushBuffer?.Dispose(); - } - } - - [SupportedOSPlatform("macos")] - class BackgroundResources : IDisposable - { - private readonly MetalRenderer _renderer; - - private readonly Dictionary _resources; - - public BackgroundResources(MetalRenderer renderer) - { - _renderer = renderer; - - _resources = new Dictionary(); - } - - private void Cleanup() - { - lock (_resources) - { - foreach (KeyValuePair tuple in _resources) - { - if (!tuple.Key.IsAlive) - { - tuple.Value.Dispose(); - _resources.Remove(tuple.Key); - } - } - } - } - - public BackgroundResource Get() - { - Thread thread = Thread.CurrentThread; - - lock (_resources) - { - if (!_resources.TryGetValue(thread, out BackgroundResource resource)) - { - Cleanup(); - - resource = new BackgroundResource(_renderer); - - _resources[thread] = resource; - } - - return resource; - } - } - - public void Dispose() - { - lock (_resources) - { - foreach (BackgroundResource resource in _resources.Values) - { - resource.Dispose(); - } - } - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/BitMap.cs b/src/Ryujinx.Graphics.Metal/BitMap.cs deleted file mode 100644 index 4ddc438c1..000000000 --- a/src/Ryujinx.Graphics.Metal/BitMap.cs +++ /dev/null @@ -1,157 +0,0 @@ -namespace Ryujinx.Graphics.Metal -{ - readonly struct BitMap - { - public const int IntSize = 64; - - private const int IntShift = 6; - private const int IntMask = IntSize - 1; - - private readonly long[] _masks; - - public BitMap(int count) - { - _masks = new long[(count + IntMask) / IntSize]; - } - - public bool AnySet() - { - for (int i = 0; i < _masks.Length; i++) - { - if (_masks[i] != 0) - { - return true; - } - } - - return false; - } - - public bool IsSet(int bit) - { - int wordIndex = bit >> IntShift; - int wordBit = bit & IntMask; - - long wordMask = 1L << wordBit; - - return (_masks[wordIndex] & wordMask) != 0; - } - - public bool IsSet(int start, int end) - { - if (start == end) - { - return IsSet(start); - } - - int startIndex = start >> IntShift; - int startBit = start & IntMask; - long startMask = -1L << startBit; - - int endIndex = end >> IntShift; - int endBit = end & IntMask; - long endMask = (long)(ulong.MaxValue >> (IntMask - endBit)); - - if (startIndex == endIndex) - { - return (_masks[startIndex] & startMask & endMask) != 0; - } - - if ((_masks[startIndex] & startMask) != 0) - { - return true; - } - - for (int i = startIndex + 1; i < endIndex; i++) - { - if (_masks[i] != 0) - { - return true; - } - } - - if ((_masks[endIndex] & endMask) != 0) - { - return true; - } - - return false; - } - - public bool Set(int bit) - { - int wordIndex = bit >> IntShift; - int wordBit = bit & IntMask; - - long wordMask = 1L << wordBit; - - if ((_masks[wordIndex] & wordMask) != 0) - { - return false; - } - - _masks[wordIndex] |= wordMask; - - return true; - } - - public void SetRange(int start, int end) - { - if (start == end) - { - Set(start); - return; - } - - int startIndex = start >> IntShift; - int startBit = start & IntMask; - long startMask = -1L << startBit; - - int endIndex = end >> IntShift; - int endBit = end & IntMask; - long endMask = (long)(ulong.MaxValue >> (IntMask - endBit)); - - if (startIndex == endIndex) - { - _masks[startIndex] |= startMask & endMask; - } - else - { - _masks[startIndex] |= startMask; - - for (int i = startIndex + 1; i < endIndex; i++) - { - _masks[i] |= -1; - } - - _masks[endIndex] |= endMask; - } - } - - public void Clear(int bit) - { - int wordIndex = bit >> IntShift; - int wordBit = bit & IntMask; - - long wordMask = 1L << wordBit; - - _masks[wordIndex] &= ~wordMask; - } - - public void Clear() - { - for (int i = 0; i < _masks.Length; i++) - { - _masks[i] = 0; - } - } - - public void ClearInt(int start, int end) - { - for (int i = start; i <= end; i++) - { - _masks[i] = 0; - } - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/BufferHolder.cs b/src/Ryujinx.Graphics.Metal/BufferHolder.cs deleted file mode 100644 index 630571658..000000000 --- a/src/Ryujinx.Graphics.Metal/BufferHolder.cs +++ /dev/null @@ -1,385 +0,0 @@ -using Ryujinx.Graphics.GAL; -using SharpMetal.Metal; -using System; -using System.Runtime.InteropServices; -using System.Runtime.Versioning; -using System.Threading; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - class BufferHolder : IDisposable - { - private CacheByRange _cachedConvertedBuffers; - - public int Size { get; } - - private readonly IntPtr _map; - private readonly MetalRenderer _renderer; - private readonly Pipeline _pipeline; - - private readonly MultiFenceHolder _waitable; - private readonly Auto _buffer; - - private readonly ReaderWriterLockSlim _flushLock; - private FenceHolder _flushFence; - private int _flushWaiting; - - public BufferHolder(MetalRenderer renderer, Pipeline pipeline, MTLBuffer buffer, int size) - { - _renderer = renderer; - _pipeline = pipeline; - _map = buffer.Contents; - _waitable = new MultiFenceHolder(size); - _buffer = new Auto(new(buffer), _waitable); - - _flushLock = new ReaderWriterLockSlim(); - - Size = size; - } - - public Auto GetBuffer() - { - return _buffer; - } - - public Auto GetBuffer(bool isWrite) - { - if (isWrite) - { - SignalWrite(0, Size); - } - - return _buffer; - } - - public Auto GetBuffer(int offset, int size, bool isWrite) - { - if (isWrite) - { - SignalWrite(offset, size); - } - - return _buffer; - } - - public void SignalWrite(int offset, int size) - { - if (offset == 0 && size == Size) - { - _cachedConvertedBuffers.Clear(); - } - else - { - _cachedConvertedBuffers.ClearRange(offset, size); - } - } - - private void ClearFlushFence() - { - // Assumes _flushLock is held as writer. - - if (_flushFence != null) - { - if (_flushWaiting == 0) - { - _flushFence.Put(); - } - - _flushFence = null; - } - } - - private void WaitForFlushFence() - { - if (_flushFence == null) - { - return; - } - - // If storage has changed, make sure the fence has been reached so that the data is in place. - _flushLock.ExitReadLock(); - _flushLock.EnterWriteLock(); - - if (_flushFence != null) - { - FenceHolder fence = _flushFence; - Interlocked.Increment(ref _flushWaiting); - - // Don't wait in the lock. - - _flushLock.ExitWriteLock(); - - fence.Wait(); - - _flushLock.EnterWriteLock(); - - if (Interlocked.Decrement(ref _flushWaiting) == 0) - { - fence.Put(); - } - - _flushFence = null; - } - - // Assumes the _flushLock is held as reader, returns in same state. - _flushLock.ExitWriteLock(); - _flushLock.EnterReadLock(); - } - - public PinnedSpan GetData(int offset, int size) - { - _flushLock.EnterReadLock(); - - WaitForFlushFence(); - - Span result; - - if (_map != IntPtr.Zero) - { - result = GetDataStorage(offset, size); - - // Need to be careful here, the buffer can't be unmapped while the data is being used. - _buffer.IncrementReferenceCount(); - - _flushLock.ExitReadLock(); - - return PinnedSpan.UnsafeFromSpan(result, _buffer.DecrementReferenceCount); - } - - throw new InvalidOperationException("The buffer is not mapped"); - } - - public unsafe Span GetDataStorage(int offset, int size) - { - int mappingSize = Math.Min(size, Size - offset); - - if (_map != IntPtr.Zero) - { - return new Span((void*)(_map + offset), mappingSize); - } - - throw new InvalidOperationException("The buffer is not mapped."); - } - - public unsafe void SetData(int offset, ReadOnlySpan data, CommandBufferScoped? cbs = null, bool allowCbsWait = true) - { - int dataSize = Math.Min(data.Length, Size - offset); - if (dataSize == 0) - { - return; - } - - if (_map != IntPtr.Zero) - { - // If persistently mapped, set the data directly if the buffer is not currently in use. - bool isRented = _buffer.HasRentedCommandBufferDependency(_renderer.CommandBufferPool); - - // If the buffer is rented, take a little more time and check if the use overlaps this handle. - bool needsFlush = isRented && _waitable.IsBufferRangeInUse(offset, dataSize, false); - - if (!needsFlush) - { - WaitForFences(offset, dataSize); - - data[..dataSize].CopyTo(new Span((void*)(_map + offset), dataSize)); - - SignalWrite(offset, dataSize); - - return; - } - } - - if (cbs != null && - cbs.Value.Encoders.CurrentEncoderType == EncoderType.Render && - !(_buffer.HasCommandBufferDependency(cbs.Value) && - _waitable.IsBufferRangeInUse(cbs.Value.CommandBufferIndex, offset, dataSize))) - { - // If the buffer hasn't been used on the command buffer yet, try to preload the data. - // This avoids ending and beginning render passes on each buffer data upload. - - cbs = _pipeline.GetPreloadCommandBuffer(); - } - - if (allowCbsWait) - { - _renderer.BufferManager.StagingBuffer.PushData(_renderer.CommandBufferPool, cbs, this, offset, data); - } - else - { - bool rentCbs = cbs == null; - if (rentCbs) - { - cbs = _renderer.CommandBufferPool.Rent(); - } - - if (!_renderer.BufferManager.StagingBuffer.TryPushData(cbs.Value, this, offset, data)) - { - // Need to do a slow upload. - BufferHolder srcHolder = _renderer.BufferManager.Create(dataSize); - srcHolder.SetDataUnchecked(0, data); - - Auto srcBuffer = srcHolder.GetBuffer(); - Auto dstBuffer = this.GetBuffer(true); - - Copy(cbs.Value, srcBuffer, dstBuffer, 0, offset, dataSize); - - srcHolder.Dispose(); - } - - if (rentCbs) - { - cbs.Value.Dispose(); - } - } - } - - public unsafe void SetDataUnchecked(int offset, ReadOnlySpan data) - { - int dataSize = Math.Min(data.Length, Size - offset); - if (dataSize == 0) - { - return; - } - - if (_map != IntPtr.Zero) - { - data[..dataSize].CopyTo(new Span((void*)(_map + offset), dataSize)); - } - } - - public void SetDataUnchecked(int offset, ReadOnlySpan data) where T : unmanaged - { - SetDataUnchecked(offset, MemoryMarshal.AsBytes(data)); - } - - public static void Copy( - CommandBufferScoped cbs, - Auto src, - Auto dst, - int srcOffset, - int dstOffset, - int size, - bool registerSrcUsage = true) - { - MTLBuffer srcBuffer = registerSrcUsage ? src.Get(cbs, srcOffset, size).Value : src.GetUnsafe().Value; - MTLBuffer dstbuffer = dst.Get(cbs, dstOffset, size, true).Value; - - cbs.Encoders.EnsureBlitEncoder().CopyFromBuffer( - srcBuffer, - (ulong)srcOffset, - dstbuffer, - (ulong)dstOffset, - (ulong)size); - } - - public void WaitForFences() - { - _waitable.WaitForFences(); - } - - public void WaitForFences(int offset, int size) - { - _waitable.WaitForFences(offset, size); - } - - private bool BoundToRange(int offset, ref int size) - { - if (offset >= Size) - { - return false; - } - - size = Math.Min(Size - offset, size); - - return true; - } - - public Auto GetBufferI8ToI16(CommandBufferScoped cbs, int offset, int size) - { - if (!BoundToRange(offset, ref size)) - { - return null; - } - - I8ToI16CacheKey key = new(_renderer); - - if (!_cachedConvertedBuffers.TryGetValue(offset, size, key, out BufferHolder holder)) - { - holder = _renderer.BufferManager.Create((size * 2 + 3) & ~3); - - _renderer.HelperShader.ConvertI8ToI16(cbs, this, holder, offset, size); - - key.SetBuffer(holder.GetBuffer()); - - _cachedConvertedBuffers.Add(offset, size, key, holder); - } - - return holder.GetBuffer(); - } - - public Auto GetBufferTopologyConversion(CommandBufferScoped cbs, int offset, int size, IndexBufferPattern pattern, int indexSize) - { - if (!BoundToRange(offset, ref size)) - { - return null; - } - - TopologyConversionCacheKey key = new(_renderer, pattern, indexSize); - - if (!_cachedConvertedBuffers.TryGetValue(offset, size, key, out BufferHolder holder)) - { - // The destination index size is always I32. - - int indexCount = size / indexSize; - - int convertedCount = pattern.GetConvertedCount(indexCount); - - holder = _renderer.BufferManager.Create(convertedCount * 4); - - _renderer.HelperShader.ConvertIndexBuffer(cbs, this, holder, pattern, indexSize, offset, indexCount); - - key.SetBuffer(holder.GetBuffer()); - - _cachedConvertedBuffers.Add(offset, size, key, holder); - } - - return holder.GetBuffer(); - } - - public bool TryGetCachedConvertedBuffer(int offset, int size, ICacheKey key, out BufferHolder holder) - { - return _cachedConvertedBuffers.TryGetValue(offset, size, key, out holder); - } - - public void AddCachedConvertedBuffer(int offset, int size, ICacheKey key, BufferHolder holder) - { - _cachedConvertedBuffers.Add(offset, size, key, holder); - } - - public void AddCachedConvertedBufferDependency(int offset, int size, ICacheKey key, Dependency dependency) - { - _cachedConvertedBuffers.AddDependency(offset, size, key, dependency); - } - - public void RemoveCachedConvertedBuffer(int offset, int size, ICacheKey key) - { - _cachedConvertedBuffers.Remove(offset, size, key); - } - - - public void Dispose() - { - _pipeline.FlushCommandsIfWeightExceeding(_buffer, (ulong)Size); - - _buffer.Dispose(); - _cachedConvertedBuffers.Dispose(); - - _flushLock.EnterWriteLock(); - - ClearFlushFence(); - - _flushLock.ExitWriteLock(); - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/BufferManager.cs b/src/Ryujinx.Graphics.Metal/BufferManager.cs deleted file mode 100644 index 73a8d6fe7..000000000 --- a/src/Ryujinx.Graphics.Metal/BufferManager.cs +++ /dev/null @@ -1,237 +0,0 @@ -using Ryujinx.Common.Logging; -using Ryujinx.Graphics.GAL; -using SharpMetal.Metal; -using System; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - readonly struct ScopedTemporaryBuffer : IDisposable - { - private readonly BufferManager _bufferManager; - private readonly bool _isReserved; - - public readonly BufferRange Range; - public readonly BufferHolder Holder; - - public BufferHandle Handle => Range.Handle; - public int Offset => Range.Offset; - - public ScopedTemporaryBuffer(BufferManager bufferManager, BufferHolder holder, BufferHandle handle, int offset, int size, bool isReserved) - { - _bufferManager = bufferManager; - - Range = new BufferRange(handle, offset, size); - Holder = holder; - - _isReserved = isReserved; - } - - public void Dispose() - { - if (!_isReserved) - { - _bufferManager.Delete(Range.Handle); - } - } - } - - [SupportedOSPlatform("macos")] - class BufferManager : IDisposable - { - private readonly IdList _buffers; - - private readonly MTLDevice _device; - private readonly MetalRenderer _renderer; - private readonly Pipeline _pipeline; - - public int BufferCount { get; private set; } - - public StagingBuffer StagingBuffer { get; } - - public BufferManager(MTLDevice device, MetalRenderer renderer, Pipeline pipeline) - { - _device = device; - _renderer = renderer; - _pipeline = pipeline; - _buffers = new IdList(); - - StagingBuffer = new StagingBuffer(_renderer, this); - } - - public BufferHandle Create(nint pointer, int size) - { - // TODO: This is the wrong Metal method, we need no-copy which SharpMetal isn't giving us. - MTLBuffer buffer = _device.NewBuffer(pointer, (ulong)size, MTLResourceOptions.ResourceStorageModeShared); - - if (buffer == IntPtr.Zero) - { - Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to create buffer with size 0x{size:X}, and pointer 0x{pointer:X}."); - - return BufferHandle.Null; - } - - BufferHolder holder = new(_renderer, _pipeline, buffer, size); - - BufferCount++; - - ulong handle64 = (uint)_buffers.Add(holder); - - return Unsafe.As(ref handle64); - } - - public BufferHandle CreateWithHandle(int size) - { - return CreateWithHandle(size, out _); - } - - public BufferHandle CreateWithHandle(int size, out BufferHolder holder) - { - holder = Create(size); - - if (holder == null) - { - return BufferHandle.Null; - } - - BufferCount++; - - ulong handle64 = (uint)_buffers.Add(holder); - - return Unsafe.As(ref handle64); - } - - public ScopedTemporaryBuffer ReserveOrCreate(CommandBufferScoped cbs, int size) - { - StagingBufferReserved? result = StagingBuffer.TryReserveData(cbs, size); - - if (result.HasValue) - { - return new ScopedTemporaryBuffer(this, result.Value.Buffer, StagingBuffer.Handle, result.Value.Offset, result.Value.Size, true); - } - else - { - // Create a temporary buffer. - BufferHandle handle = CreateWithHandle(size, out BufferHolder holder); - - return new ScopedTemporaryBuffer(this, holder, handle, 0, size, false); - } - } - - public BufferHolder Create(int size) - { - MTLBuffer buffer = _device.NewBuffer((ulong)size, MTLResourceOptions.ResourceStorageModeShared); - - if (buffer != IntPtr.Zero) - { - return new BufferHolder(_renderer, _pipeline, buffer, size); - } - - Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to create buffer with size 0x{size:X}."); - - return null; - } - - public Auto GetBuffer(BufferHandle handle, bool isWrite, out int size) - { - if (TryGetBuffer(handle, out BufferHolder holder)) - { - size = holder.Size; - return holder.GetBuffer(isWrite); - } - - size = 0; - return null; - } - - public Auto GetBuffer(BufferHandle handle, int offset, int size, bool isWrite) - { - if (TryGetBuffer(handle, out BufferHolder holder)) - { - return holder.GetBuffer(offset, size, isWrite); - } - - return null; - } - - public Auto GetBuffer(BufferHandle handle, bool isWrite) - { - if (TryGetBuffer(handle, out BufferHolder holder)) - { - return holder.GetBuffer(isWrite); - } - - return null; - } - - public Auto GetBufferI8ToI16(CommandBufferScoped cbs, BufferHandle handle, int offset, int size) - { - if (TryGetBuffer(handle, out BufferHolder holder)) - { - return holder.GetBufferI8ToI16(cbs, offset, size); - } - - return null; - } - - public Auto GetBufferTopologyConversion(CommandBufferScoped cbs, BufferHandle handle, int offset, int size, IndexBufferPattern pattern, int indexSize) - { - if (TryGetBuffer(handle, out BufferHolder holder)) - { - return holder.GetBufferTopologyConversion(cbs, offset, size, pattern, indexSize); - } - - return null; - } - - public PinnedSpan GetData(BufferHandle handle, int offset, int size) - { - if (TryGetBuffer(handle, out BufferHolder holder)) - { - return holder.GetData(offset, size); - } - - return new PinnedSpan(); - } - - public void SetData(BufferHandle handle, int offset, ReadOnlySpan data) where T : unmanaged - { - SetData(handle, offset, MemoryMarshal.Cast(data), null); - } - - public void SetData(BufferHandle handle, int offset, ReadOnlySpan data, CommandBufferScoped? cbs) - { - if (TryGetBuffer(handle, out BufferHolder holder)) - { - holder.SetData(offset, data, cbs); - } - } - - public void Delete(BufferHandle handle) - { - if (TryGetBuffer(handle, out BufferHolder holder)) - { - holder.Dispose(); - _buffers.Remove((int)Unsafe.As(ref handle)); - } - } - - private bool TryGetBuffer(BufferHandle handle, out BufferHolder holder) - { - return _buffers.TryGetValue((int)Unsafe.As(ref handle), out holder); - } - - public void Dispose() - { - StagingBuffer.Dispose(); - - foreach (BufferHolder buffer in _buffers) - { - buffer.Dispose(); - } - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/BufferUsageBitmap.cs b/src/Ryujinx.Graphics.Metal/BufferUsageBitmap.cs deleted file mode 100644 index 379e27407..000000000 --- a/src/Ryujinx.Graphics.Metal/BufferUsageBitmap.cs +++ /dev/null @@ -1,85 +0,0 @@ -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - internal class BufferUsageBitmap - { - private readonly BitMap _bitmap; - private readonly int _size; - private readonly int _granularity; - private readonly int _bits; - private readonly int _writeBitOffset; - - private readonly int _intsPerCb; - private readonly int _bitsPerCb; - - public BufferUsageBitmap(int size, int granularity) - { - _size = size; - _granularity = granularity; - - // There are two sets of bits - one for read tracking, and the other for write. - int bits = (size + (granularity - 1)) / granularity; - _writeBitOffset = bits; - _bits = bits << 1; - - _intsPerCb = (_bits + (BitMap.IntSize - 1)) / BitMap.IntSize; - _bitsPerCb = _intsPerCb * BitMap.IntSize; - - _bitmap = new BitMap(_bitsPerCb * CommandBufferPool.MaxCommandBuffers); - } - - public void Add(int cbIndex, int offset, int size, bool write) - { - if (size == 0) - { - return; - } - - // Some usages can be out of bounds (vertex buffer on amd), so bound if necessary. - if (offset + size > _size) - { - size = _size - offset; - } - - int cbBase = cbIndex * _bitsPerCb + (write ? _writeBitOffset : 0); - int start = cbBase + offset / _granularity; - int end = cbBase + (offset + size - 1) / _granularity; - - _bitmap.SetRange(start, end); - } - - public bool OverlapsWith(int cbIndex, int offset, int size, bool write = false) - { - if (size == 0) - { - return false; - } - - int cbBase = cbIndex * _bitsPerCb + (write ? _writeBitOffset : 0); - int start = cbBase + offset / _granularity; - int end = cbBase + (offset + size - 1) / _granularity; - - return _bitmap.IsSet(start, end); - } - - public bool OverlapsWith(int offset, int size, bool write) - { - for (int i = 0; i < CommandBufferPool.MaxCommandBuffers; i++) - { - if (OverlapsWith(i, offset, size, write)) - { - return true; - } - } - - return false; - } - - public void Clear(int cbIndex) - { - _bitmap.ClearInt(cbIndex * _intsPerCb, (cbIndex + 1) * _intsPerCb - 1); - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/CacheByRange.cs b/src/Ryujinx.Graphics.Metal/CacheByRange.cs deleted file mode 100644 index 2002eeba4..000000000 --- a/src/Ryujinx.Graphics.Metal/CacheByRange.cs +++ /dev/null @@ -1,294 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - interface ICacheKey : IDisposable - { - bool KeyEqual(ICacheKey other); - } - - [SupportedOSPlatform("macos")] - struct I8ToI16CacheKey : ICacheKey - { - // Used to notify the pipeline that bindings have invalidated on dispose. - // private readonly MetalRenderer _renderer; - // private Auto _buffer; - - public I8ToI16CacheKey(MetalRenderer renderer) - { - // _renderer = renderer; - // _buffer = null; - } - - public readonly bool KeyEqual(ICacheKey other) - { - return other is I8ToI16CacheKey; - } - - public readonly void SetBuffer(Auto buffer) - { - // _buffer = buffer; - } - - public readonly void Dispose() - { - // TODO: Tell pipeline buffer is dirty! - // _renderer.PipelineInternal.DirtyIndexBuffer(_buffer); - } - } - - [SupportedOSPlatform("macos")] - readonly struct TopologyConversionCacheKey : ICacheKey - { - private readonly IndexBufferPattern _pattern; - private readonly int _indexSize; - - // Used to notify the pipeline that bindings have invalidated on dispose. - // private readonly MetalRenderer _renderer; - // private Auto _buffer; - - public TopologyConversionCacheKey(MetalRenderer renderer, IndexBufferPattern pattern, int indexSize) - { - // _renderer = renderer; - // _buffer = null; - _pattern = pattern; - _indexSize = indexSize; - } - - public readonly bool KeyEqual(ICacheKey other) - { - return other is TopologyConversionCacheKey entry && - entry._pattern == _pattern && - entry._indexSize == _indexSize; - } - - public void SetBuffer(Auto buffer) - { - // _buffer = buffer; - } - - public readonly void Dispose() - { - // TODO: Tell pipeline buffer is dirty! - // _renderer.PipelineInternal.DirtyVertexBuffer(_buffer); - } - } - - [SupportedOSPlatform("macos")] - readonly struct Dependency - { - private readonly BufferHolder _buffer; - private readonly int _offset; - private readonly int _size; - private readonly ICacheKey _key; - - public Dependency(BufferHolder buffer, int offset, int size, ICacheKey key) - { - _buffer = buffer; - _offset = offset; - _size = size; - _key = key; - } - - public void RemoveFromOwner() - { - _buffer.RemoveCachedConvertedBuffer(_offset, _size, _key); - } - } - - [SupportedOSPlatform("macos")] - struct CacheByRange where T : IDisposable - { - private struct Entry - { - public readonly ICacheKey Key; - public readonly T Value; - public List DependencyList; - - public Entry(ICacheKey key, T value) - { - Key = key; - Value = value; - DependencyList = null; - } - - public readonly void InvalidateDependencies() - { - if (DependencyList != null) - { - foreach (Dependency dependency in DependencyList) - { - dependency.RemoveFromOwner(); - } - - DependencyList.Clear(); - } - } - } - - private Dictionary> _ranges; - - public void Add(int offset, int size, ICacheKey key, T value) - { - List entries = GetEntries(offset, size); - - entries.Add(new Entry(key, value)); - } - - public void AddDependency(int offset, int size, ICacheKey key, Dependency dependency) - { - List entries = GetEntries(offset, size); - - for (int i = 0; i < entries.Count; i++) - { - Entry entry = entries[i]; - - if (entry.Key.KeyEqual(key)) - { - if (entry.DependencyList == null) - { - entry.DependencyList = []; - entries[i] = entry; - } - - entry.DependencyList.Add(dependency); - - break; - } - } - } - - public void Remove(int offset, int size, ICacheKey key) - { - List entries = GetEntries(offset, size); - - for (int i = 0; i < entries.Count; i++) - { - Entry entry = entries[i]; - - if (entry.Key.KeyEqual(key)) - { - entries.RemoveAt(i--); - - DestroyEntry(entry); - } - } - - if (entries.Count == 0) - { - _ranges.Remove(PackRange(offset, size)); - } - } - - public bool TryGetValue(int offset, int size, ICacheKey key, out T value) - { - List entries = GetEntries(offset, size); - - foreach (Entry entry in entries) - { - if (entry.Key.KeyEqual(key)) - { - value = entry.Value; - - return true; - } - } - - value = default; - return false; - } - - public void Clear() - { - if (_ranges != null) - { - foreach (List entries in _ranges.Values) - { - foreach (Entry entry in entries) - { - DestroyEntry(entry); - } - } - - _ranges.Clear(); - _ranges = null; - } - } - - public readonly void ClearRange(int offset, int size) - { - if (_ranges != null && _ranges.Count > 0) - { - int end = offset + size; - - List toRemove = null; - - foreach (KeyValuePair> range in _ranges) - { - (int rOffset, int rSize) = UnpackRange(range.Key); - - int rEnd = rOffset + rSize; - - if (rEnd > offset && rOffset < end) - { - List entries = range.Value; - - foreach (Entry entry in entries) - { - DestroyEntry(entry); - } - - (toRemove ??= []).Add(range.Key); - } - } - - if (toRemove != null) - { - foreach (ulong range in toRemove) - { - _ranges.Remove(range); - } - } - } - } - - private List GetEntries(int offset, int size) - { - _ranges ??= new Dictionary>(); - - ulong key = PackRange(offset, size); - - if (!_ranges.TryGetValue(key, out List value)) - { - value = []; - _ranges.Add(key, value); - } - - return value; - } - - private static void DestroyEntry(Entry entry) - { - entry.Key.Dispose(); - entry.Value?.Dispose(); - entry.InvalidateDependencies(); - } - - private static ulong PackRange(int offset, int size) - { - return (uint)offset | ((ulong)size << 32); - } - - private static (int offset, int size) UnpackRange(ulong range) - { - return ((int)range, (int)(range >> 32)); - } - - public void Dispose() - { - Clear(); - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/CommandBufferEncoder.cs b/src/Ryujinx.Graphics.Metal/CommandBufferEncoder.cs deleted file mode 100644 index 3bc30e239..000000000 --- a/src/Ryujinx.Graphics.Metal/CommandBufferEncoder.cs +++ /dev/null @@ -1,170 +0,0 @@ -using Ryujinx.Graphics.Metal; -using SharpMetal.Metal; -using System; -using System.Runtime.CompilerServices; -using System.Runtime.Versioning; - -interface IEncoderFactory -{ - MTLRenderCommandEncoder CreateRenderCommandEncoder(); - MTLComputeCommandEncoder CreateComputeCommandEncoder(); -} - -/// -/// Tracks active encoder object for a command buffer. -/// -[SupportedOSPlatform("macos")] -class CommandBufferEncoder -{ - public EncoderType CurrentEncoderType { get; private set; } = EncoderType.None; - - public MTLBlitCommandEncoder BlitEncoder => new(CurrentEncoder.Value); - - public MTLComputeCommandEncoder ComputeEncoder => new(CurrentEncoder.Value); - - public MTLRenderCommandEncoder RenderEncoder => new(CurrentEncoder.Value); - - internal MTLCommandEncoder? CurrentEncoder { get; private set; } - - private MTLCommandBuffer _commandBuffer; - private IEncoderFactory _encoderFactory; - - public void Initialize(MTLCommandBuffer commandBuffer, IEncoderFactory encoderFactory) - { - _commandBuffer = commandBuffer; - _encoderFactory = encoderFactory; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public MTLRenderCommandEncoder EnsureRenderEncoder() - { - if (CurrentEncoderType != EncoderType.Render) - { - return BeginRenderPass(); - } - - return RenderEncoder; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public MTLBlitCommandEncoder EnsureBlitEncoder() - { - if (CurrentEncoderType != EncoderType.Blit) - { - return BeginBlitPass(); - } - - return BlitEncoder; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public MTLComputeCommandEncoder EnsureComputeEncoder() - { - if (CurrentEncoderType != EncoderType.Compute) - { - return BeginComputePass(); - } - - return ComputeEncoder; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool TryGetRenderEncoder(out MTLRenderCommandEncoder encoder) - { - if (CurrentEncoderType != EncoderType.Render) - { - encoder = default; - return false; - } - - encoder = RenderEncoder; - return true; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool TryGetBlitEncoder(out MTLBlitCommandEncoder encoder) - { - if (CurrentEncoderType != EncoderType.Blit) - { - encoder = default; - return false; - } - - encoder = BlitEncoder; - return true; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool TryGetComputeEncoder(out MTLComputeCommandEncoder encoder) - { - if (CurrentEncoderType != EncoderType.Compute) - { - encoder = default; - return false; - } - - encoder = ComputeEncoder; - return true; - } - - public void EndCurrentPass() - { - if (CurrentEncoder != null) - { - switch (CurrentEncoderType) - { - case EncoderType.Blit: - BlitEncoder.EndEncoding(); - CurrentEncoder = null; - break; - case EncoderType.Compute: - ComputeEncoder.EndEncoding(); - CurrentEncoder = null; - break; - case EncoderType.Render: - RenderEncoder.EndEncoding(); - CurrentEncoder = null; - break; - default: - throw new InvalidOperationException(); - } - - CurrentEncoderType = EncoderType.None; - } - } - - private MTLRenderCommandEncoder BeginRenderPass() - { - EndCurrentPass(); - - MTLRenderCommandEncoder renderCommandEncoder = _encoderFactory.CreateRenderCommandEncoder(); - - CurrentEncoder = renderCommandEncoder; - CurrentEncoderType = EncoderType.Render; - - return renderCommandEncoder; - } - - private MTLBlitCommandEncoder BeginBlitPass() - { - EndCurrentPass(); - - using MTLBlitPassDescriptor descriptor = new(); - MTLBlitCommandEncoder blitCommandEncoder = _commandBuffer.BlitCommandEncoder(descriptor); - - CurrentEncoder = blitCommandEncoder; - CurrentEncoderType = EncoderType.Blit; - return blitCommandEncoder; - } - - private MTLComputeCommandEncoder BeginComputePass() - { - EndCurrentPass(); - - MTLComputeCommandEncoder computeCommandEncoder = _encoderFactory.CreateComputeCommandEncoder(); - - CurrentEncoder = computeCommandEncoder; - CurrentEncoderType = EncoderType.Compute; - return computeCommandEncoder; - } -} diff --git a/src/Ryujinx.Graphics.Metal/CommandBufferPool.cs b/src/Ryujinx.Graphics.Metal/CommandBufferPool.cs deleted file mode 100644 index d8c35b757..000000000 --- a/src/Ryujinx.Graphics.Metal/CommandBufferPool.cs +++ /dev/null @@ -1,289 +0,0 @@ -using SharpMetal.Metal; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Runtime.Versioning; -using System.Threading; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - class CommandBufferPool : IDisposable - { - public const int MaxCommandBuffers = 16; - - private readonly int _totalCommandBuffers; - private readonly int _totalCommandBuffersMask; - private readonly MTLCommandQueue _queue; - private readonly Thread _owner; - private IEncoderFactory _defaultEncoderFactory; - - public bool OwnedByCurrentThread => _owner == Thread.CurrentThread; - - [SupportedOSPlatform("macos")] - private struct ReservedCommandBuffer - { - public bool InUse; - public bool InConsumption; - public int SubmissionCount; - public MTLCommandBuffer CommandBuffer; - public CommandBufferEncoder Encoders; - public FenceHolder Fence; - - public List Dependants; - public List Waitables; - - public void Use(MTLCommandQueue queue, IEncoderFactory stateManager) - { - MTLCommandBufferDescriptor descriptor = new(); -#if DEBUG - descriptor.ErrorOptions = MTLCommandBufferErrorOption.EncoderExecutionStatus; -#endif - - CommandBuffer = queue.CommandBuffer(descriptor); - Fence = new FenceHolder(CommandBuffer); - - Encoders.Initialize(CommandBuffer, stateManager); - - InUse = true; - } - - public void Initialize() - { - Dependants = []; - Waitables = []; - Encoders = new CommandBufferEncoder(); - } - } - - private readonly ReservedCommandBuffer[] _commandBuffers; - - private readonly int[] _queuedIndexes; - private int _queuedIndexesPtr; - private int _queuedCount; - private int _inUseCount; - - public CommandBufferPool(MTLCommandQueue queue, bool isLight = false) - { - _queue = queue; - _owner = Thread.CurrentThread; - - _totalCommandBuffers = isLight ? 2 : MaxCommandBuffers; - _totalCommandBuffersMask = _totalCommandBuffers - 1; - - _commandBuffers = new ReservedCommandBuffer[_totalCommandBuffers]; - - _queuedIndexes = new int[_totalCommandBuffers]; - _queuedIndexesPtr = 0; - _queuedCount = 0; - } - - public void Initialize(IEncoderFactory encoderFactory) - { - _defaultEncoderFactory = encoderFactory; - - for (int i = 0; i < _totalCommandBuffers; i++) - { - _commandBuffers[i].Initialize(); - WaitAndDecrementRef(i); - } - } - - public void AddDependant(int cbIndex, IAuto dependant) - { - dependant.IncrementReferenceCount(); - _commandBuffers[cbIndex].Dependants.Add(dependant); - } - - public void AddWaitable(MultiFenceHolder waitable) - { - lock (_commandBuffers) - { - for (int i = 0; i < _totalCommandBuffers; i++) - { - ref ReservedCommandBuffer entry = ref _commandBuffers[i]; - - if (entry.InConsumption) - { - AddWaitable(i, waitable); - } - } - } - } - - public void AddInUseWaitable(MultiFenceHolder waitable) - { - lock (_commandBuffers) - { - for (int i = 0; i < _totalCommandBuffers; i++) - { - ref ReservedCommandBuffer entry = ref _commandBuffers[i]; - - if (entry.InUse) - { - AddWaitable(i, waitable); - } - } - } - } - - public void AddWaitable(int cbIndex, MultiFenceHolder waitable) - { - ref ReservedCommandBuffer entry = ref _commandBuffers[cbIndex]; - if (waitable.AddFence(cbIndex, entry.Fence)) - { - entry.Waitables.Add(waitable); - } - } - - public bool IsFenceOnRentedCommandBuffer(FenceHolder fence) - { - lock (_commandBuffers) - { - for (int i = 0; i < _totalCommandBuffers; i++) - { - ref ReservedCommandBuffer entry = ref _commandBuffers[i]; - - if (entry.InUse && entry.Fence == fence) - { - return true; - } - } - } - - return false; - } - - public FenceHolder GetFence(int cbIndex) - { - return _commandBuffers[cbIndex].Fence; - } - - public int GetSubmissionCount(int cbIndex) - { - return _commandBuffers[cbIndex].SubmissionCount; - } - - private int FreeConsumed(bool wait) - { - int freeEntry = 0; - - while (_queuedCount > 0) - { - int index = _queuedIndexes[_queuedIndexesPtr]; - - ref ReservedCommandBuffer entry = ref _commandBuffers[index]; - - if (wait || !entry.InConsumption || entry.Fence.IsSignaled()) - { - WaitAndDecrementRef(index); - - wait = false; - freeEntry = index; - - _queuedCount--; - _queuedIndexesPtr = (_queuedIndexesPtr + 1) % _totalCommandBuffers; - } - else - { - break; - } - } - - return freeEntry; - } - - public CommandBufferScoped ReturnAndRent(CommandBufferScoped cbs) - { - Return(cbs); - return Rent(); - } - - public CommandBufferScoped Rent() - { - lock (_commandBuffers) - { - int cursor = FreeConsumed(_inUseCount + _queuedCount == _totalCommandBuffers); - - for (int i = 0; i < _totalCommandBuffers; i++) - { - ref ReservedCommandBuffer entry = ref _commandBuffers[cursor]; - - if (!entry.InUse && !entry.InConsumption) - { - entry.Use(_queue, _defaultEncoderFactory); - - _inUseCount++; - - return new CommandBufferScoped(this, entry.CommandBuffer, entry.Encoders, cursor); - } - - cursor = (cursor + 1) & _totalCommandBuffersMask; - } - } - - throw new InvalidOperationException($"Out of command buffers (In use: {_inUseCount}, queued: {_queuedCount}, total: {_totalCommandBuffers})"); - } - - public void Return(CommandBufferScoped cbs) - { - // Ensure the encoder is committed. - cbs.Encoders.EndCurrentPass(); - - lock (_commandBuffers) - { - int cbIndex = cbs.CommandBufferIndex; - - ref ReservedCommandBuffer entry = ref _commandBuffers[cbIndex]; - - Debug.Assert(entry.InUse); - Debug.Assert(entry.CommandBuffer.NativePtr == cbs.CommandBuffer.NativePtr); - entry.InUse = false; - entry.InConsumption = true; - entry.SubmissionCount++; - _inUseCount--; - - MTLCommandBuffer commandBuffer = entry.CommandBuffer; - commandBuffer.Commit(); - - int ptr = (_queuedIndexesPtr + _queuedCount) % _totalCommandBuffers; - _queuedIndexes[ptr] = cbIndex; - _queuedCount++; - } - } - - private void WaitAndDecrementRef(int cbIndex) - { - ref ReservedCommandBuffer entry = ref _commandBuffers[cbIndex]; - - if (entry.InConsumption) - { - entry.Fence.Wait(); - entry.InConsumption = false; - } - - foreach (IAuto dependant in entry.Dependants) - { - dependant.DecrementReferenceCount(cbIndex); - } - - foreach (MultiFenceHolder waitable in entry.Waitables) - { - waitable.RemoveFence(cbIndex); - waitable.RemoveBufferUses(cbIndex); - } - - entry.Dependants.Clear(); - entry.Waitables.Clear(); - entry.Fence?.Dispose(); - } - - public void Dispose() - { - for (int i = 0; i < _totalCommandBuffers; i++) - { - WaitAndDecrementRef(i); - } - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/CommandBufferScoped.cs b/src/Ryujinx.Graphics.Metal/CommandBufferScoped.cs deleted file mode 100644 index 822f69b46..000000000 --- a/src/Ryujinx.Graphics.Metal/CommandBufferScoped.cs +++ /dev/null @@ -1,43 +0,0 @@ -using SharpMetal.Metal; -using System; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - readonly struct CommandBufferScoped : IDisposable - { - private readonly CommandBufferPool _pool; - public MTLCommandBuffer CommandBuffer { get; } - public CommandBufferEncoder Encoders { get; } - public int CommandBufferIndex { get; } - - public CommandBufferScoped(CommandBufferPool pool, MTLCommandBuffer commandBuffer, CommandBufferEncoder encoders, int commandBufferIndex) - { - _pool = pool; - CommandBuffer = commandBuffer; - Encoders = encoders; - CommandBufferIndex = commandBufferIndex; - } - - public void AddDependant(IAuto dependant) - { - _pool.AddDependant(CommandBufferIndex, dependant); - } - - public void AddWaitable(MultiFenceHolder waitable) - { - _pool.AddWaitable(CommandBufferIndex, waitable); - } - - public FenceHolder GetFence() - { - return _pool.GetFence(CommandBufferIndex); - } - - public void Dispose() - { - _pool?.Return(this); - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/Constants.cs b/src/Ryujinx.Graphics.Metal/Constants.cs deleted file mode 100644 index 43baf722a..000000000 --- a/src/Ryujinx.Graphics.Metal/Constants.cs +++ /dev/null @@ -1,41 +0,0 @@ -namespace Ryujinx.Graphics.Metal -{ - static class Constants - { - public const int MaxShaderStages = 5; - public const int MaxVertexBuffers = 16; - public const int MaxUniformBuffersPerStage = 18; - public const int MaxStorageBuffersPerStage = 16; - public const int MaxTexturesPerStage = 64; - public const int MaxImagesPerStage = 16; - - public const int MaxUniformBufferBindings = MaxUniformBuffersPerStage * MaxShaderStages; - public const int MaxStorageBufferBindings = MaxStorageBuffersPerStage * MaxShaderStages; - public const int MaxTextureBindings = MaxTexturesPerStage * MaxShaderStages; - public const int MaxImageBindings = MaxImagesPerStage * MaxShaderStages; - public const int MaxColorAttachments = 8; - public const int MaxViewports = 16; - // TODO: Check this value - public const int MaxVertexAttributes = 31; - - public const int MinResourceAlignment = 16; - - // Must match constants set in shader generation - public const uint ZeroBufferIndex = MaxVertexBuffers; - public const uint BaseSetIndex = MaxVertexBuffers + 1; - - public const uint ConstantBuffersIndex = BaseSetIndex; - public const uint StorageBuffersIndex = BaseSetIndex + 1; - public const uint TexturesIndex = BaseSetIndex + 2; - public const uint ImagesIndex = BaseSetIndex + 3; - - public const uint ConstantBuffersSetIndex = 0; - public const uint StorageBuffersSetIndex = 1; - public const uint TexturesSetIndex = 2; - public const uint ImagesSetIndex = 3; - - public const uint MaximumBufferArgumentTableEntries = 31; - - public const uint MaximumExtraSets = MaximumBufferArgumentTableEntries - ImagesIndex; - } -} diff --git a/src/Ryujinx.Graphics.Metal/CounterEvent.cs b/src/Ryujinx.Graphics.Metal/CounterEvent.cs deleted file mode 100644 index 46b04997e..000000000 --- a/src/Ryujinx.Graphics.Metal/CounterEvent.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Ryujinx.Graphics.GAL; - -namespace Ryujinx.Graphics.Metal -{ - class CounterEvent : ICounterEvent - { - public CounterEvent() - { - Invalid = false; - } - - public bool Invalid { get; set; } - public bool ReserveForHostAccess() - { - return true; - } - - public void Flush() { } - - public void Dispose() { } - } -} diff --git a/src/Ryujinx.Graphics.Metal/DepthStencilCache.cs b/src/Ryujinx.Graphics.Metal/DepthStencilCache.cs deleted file mode 100644 index 47d996010..000000000 --- a/src/Ryujinx.Graphics.Metal/DepthStencilCache.cs +++ /dev/null @@ -1,68 +0,0 @@ -using Ryujinx.Graphics.Metal.State; -using SharpMetal.Metal; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - class DepthStencilCache : StateCache - { - private readonly MTLDevice _device; - - public DepthStencilCache(MTLDevice device) - { - _device = device; - } - - protected override DepthStencilUid GetHash(DepthStencilUid descriptor) - { - return descriptor; - } - - protected override MTLDepthStencilState CreateValue(DepthStencilUid descriptor) - { - // Create descriptors - - ref StencilUid frontUid = ref descriptor.FrontFace; - - using MTLStencilDescriptor frontFaceStencil = new() - { - StencilFailureOperation = frontUid.StencilFailureOperation, - DepthFailureOperation = frontUid.DepthFailureOperation, - DepthStencilPassOperation = frontUid.DepthStencilPassOperation, - StencilCompareFunction = frontUid.StencilCompareFunction, - ReadMask = frontUid.ReadMask, - WriteMask = frontUid.WriteMask - }; - - ref StencilUid backUid = ref descriptor.BackFace; - - using MTLStencilDescriptor backFaceStencil = new() - { - StencilFailureOperation = backUid.StencilFailureOperation, - DepthFailureOperation = backUid.DepthFailureOperation, - DepthStencilPassOperation = backUid.DepthStencilPassOperation, - StencilCompareFunction = backUid.StencilCompareFunction, - ReadMask = backUid.ReadMask, - WriteMask = backUid.WriteMask - }; - - MTLDepthStencilDescriptor mtlDescriptor = new() - { - DepthCompareFunction = descriptor.DepthCompareFunction, - DepthWriteEnabled = descriptor.DepthWriteEnabled - }; - - if (descriptor.StencilTestEnabled) - { - mtlDescriptor.BackFaceStencil = backFaceStencil; - mtlDescriptor.FrontFaceStencil = frontFaceStencil; - } - - using (mtlDescriptor) - { - return _device.NewDepthStencilState(mtlDescriptor); - } - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/DisposableBuffer.cs b/src/Ryujinx.Graphics.Metal/DisposableBuffer.cs deleted file mode 100644 index a2d2247c4..000000000 --- a/src/Ryujinx.Graphics.Metal/DisposableBuffer.cs +++ /dev/null @@ -1,26 +0,0 @@ -using SharpMetal.Metal; -using System; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - readonly struct DisposableBuffer : IDisposable - { - public MTLBuffer Value { get; } - - public DisposableBuffer(MTLBuffer buffer) - { - Value = buffer; - } - - public void Dispose() - { - if (Value != IntPtr.Zero) - { - Value.SetPurgeableState(MTLPurgeableState.Empty); - Value.Dispose(); - } - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/DisposableSampler.cs b/src/Ryujinx.Graphics.Metal/DisposableSampler.cs deleted file mode 100644 index ba041be89..000000000 --- a/src/Ryujinx.Graphics.Metal/DisposableSampler.cs +++ /dev/null @@ -1,22 +0,0 @@ -using SharpMetal.Metal; -using System; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - readonly struct DisposableSampler : IDisposable - { - public MTLSamplerState Value { get; } - - public DisposableSampler(MTLSamplerState sampler) - { - Value = sampler; - } - - public void Dispose() - { - Value.Dispose(); - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/Effects/IPostProcessingEffect.cs b/src/Ryujinx.Graphics.Metal/Effects/IPostProcessingEffect.cs deleted file mode 100644 index d575d521f..000000000 --- a/src/Ryujinx.Graphics.Metal/Effects/IPostProcessingEffect.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; - -namespace Ryujinx.Graphics.Metal.Effects -{ - internal interface IPostProcessingEffect : IDisposable - { - const int LocalGroupSize = 64; - Texture Run(Texture view, int width, int height); - } -} diff --git a/src/Ryujinx.Graphics.Metal/Effects/IScalingFilter.cs b/src/Ryujinx.Graphics.Metal/Effects/IScalingFilter.cs deleted file mode 100644 index 19f1a3c3d..000000000 --- a/src/Ryujinx.Graphics.Metal/Effects/IScalingFilter.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Ryujinx.Graphics.GAL; -using System; - -namespace Ryujinx.Graphics.Metal.Effects -{ - internal interface IScalingFilter : IDisposable - { - float Level { get; set; } - void Run( - Texture view, - Texture destinationTexture, - Format format, - int width, - int height, - Extents2D source, - Extents2D destination); - } -} diff --git a/src/Ryujinx.Graphics.Metal/EncoderResources.cs b/src/Ryujinx.Graphics.Metal/EncoderResources.cs deleted file mode 100644 index 8b856c1ce..000000000 --- a/src/Ryujinx.Graphics.Metal/EncoderResources.cs +++ /dev/null @@ -1,63 +0,0 @@ -using SharpMetal.Metal; -using System.Collections.Generic; - -namespace Ryujinx.Graphics.Metal -{ - public struct RenderEncoderBindings - { - public List Resources = []; - public List VertexBuffers = []; - public List FragmentBuffers = []; - - public RenderEncoderBindings() { } - - public readonly void Clear() - { - Resources.Clear(); - VertexBuffers.Clear(); - FragmentBuffers.Clear(); - } - } - - public struct ComputeEncoderBindings - { - public List Resources = []; - public List Buffers = []; - - public ComputeEncoderBindings() { } - - public readonly void Clear() - { - Resources.Clear(); - Buffers.Clear(); - } - } - - public struct BufferResource - { - public MTLBuffer Buffer; - public ulong Offset; - public ulong Binding; - - public BufferResource(MTLBuffer buffer, ulong offset, ulong binding) - { - Buffer = buffer; - Offset = offset; - Binding = binding; - } - } - - public struct Resource - { - public MTLResource MtlResource; - public MTLResourceUsage ResourceUsage; - public MTLRenderStages Stages; - - public Resource(MTLResource resource, MTLResourceUsage resourceUsage, MTLRenderStages stages) - { - MtlResource = resource; - ResourceUsage = resourceUsage; - Stages = stages; - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/EncoderState.cs b/src/Ryujinx.Graphics.Metal/EncoderState.cs deleted file mode 100644 index 64c50d71b..000000000 --- a/src/Ryujinx.Graphics.Metal/EncoderState.cs +++ /dev/null @@ -1,206 +0,0 @@ -using Ryujinx.Common.Memory; -using Ryujinx.Graphics.GAL; -using Ryujinx.Graphics.Metal.State; -using Ryujinx.Graphics.Shader; -using SharpMetal.Metal; -using System; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - [Flags] - enum DirtyFlags - { - None = 0, - RenderPipeline = 1 << 0, - ComputePipeline = 1 << 1, - DepthStencil = 1 << 2, - DepthClamp = 1 << 3, - DepthBias = 1 << 4, - CullMode = 1 << 5, - FrontFace = 1 << 6, - StencilRef = 1 << 7, - Viewports = 1 << 8, - Scissors = 1 << 9, - Uniforms = 1 << 10, - Storages = 1 << 11, - Textures = 1 << 12, - Images = 1 << 13, - - ArgBuffers = Uniforms | Storages | Textures | Images, - - RenderAll = RenderPipeline | DepthStencil | DepthClamp | DepthBias | CullMode | FrontFace | StencilRef | Viewports | Scissors | ArgBuffers, - ComputeAll = ComputePipeline | ArgBuffers, - All = RenderAll | ComputeAll, - } - - record struct BufferRef - { - public Auto Buffer; - public BufferRange? Range; - - public BufferRef(Auto buffer) - { - Buffer = buffer; - } - - public BufferRef(Auto buffer, ref BufferRange range) - { - Buffer = buffer; - Range = range; - } - } - - record struct TextureRef - { - public ShaderStage Stage; - public TextureBase Storage; - public Auto Sampler; - public Format ImageFormat; - - public TextureRef(ShaderStage stage, TextureBase storage, Auto sampler) - { - Stage = stage; - Storage = storage; - Sampler = sampler; - } - } - - record struct ImageRef - { - public ShaderStage Stage; - public Texture Storage; - - public ImageRef(ShaderStage stage, Texture storage) - { - Stage = stage; - Storage = storage; - } - } - - struct PredrawState - { - public MTLCullMode CullMode; - public DepthStencilUid DepthStencilUid; - public PrimitiveTopology Topology; - public MTLViewport[] Viewports; - } - - struct RenderTargetCopy - { - public MTLScissorRect[] Scissors; - public Texture DepthStencil; - public Texture[] RenderTargets; - } - - [SupportedOSPlatform("macos")] - class EncoderState - { - public Program RenderProgram = null; - public Program ComputeProgram = null; - - public PipelineState Pipeline; - public DepthStencilUid DepthStencilUid; - - public readonly record struct ArrayRef(ShaderStage Stage, T Array); - - public readonly BufferRef[] UniformBufferRefs = new BufferRef[Constants.MaxUniformBufferBindings]; - public readonly BufferRef[] StorageBufferRefs = new BufferRef[Constants.MaxStorageBufferBindings]; - public readonly TextureRef[] TextureRefs = new TextureRef[Constants.MaxTextureBindings * 2]; - public readonly ImageRef[] ImageRefs = new ImageRef[Constants.MaxImageBindings * 2]; - - public ArrayRef[] TextureArrayRefs = []; - public ArrayRef[] ImageArrayRefs = []; - - public ArrayRef[] TextureArrayExtraRefs = []; - public ArrayRef[] ImageArrayExtraRefs = []; - - public IndexBufferState IndexBuffer = default; - - public MTLDepthClipMode DepthClipMode = MTLDepthClipMode.Clip; - - public float DepthBias; - public float SlopeScale; - public float Clamp; - - public int BackRefValue = 0; - public int FrontRefValue = 0; - - public PrimitiveTopology Topology = PrimitiveTopology.Triangles; - public MTLCullMode CullMode = MTLCullMode.None; - public MTLWinding Winding = MTLWinding.CounterClockwise; - public bool CullBoth = false; - - public MTLViewport[] Viewports = new MTLViewport[Constants.MaxViewports]; - public MTLScissorRect[] Scissors = new MTLScissorRect[Constants.MaxViewports]; - - // Changes to attachments take recreation! - public Texture DepthStencil; - public Texture[] RenderTargets = new Texture[Constants.MaxColorAttachments]; - public ITexture PreMaskDepthStencil = default; - public ITexture[] PreMaskRenderTargets; - public bool FramebufferUsingColorWriteMask; - - public Array8 StoredBlend; - public ColorF BlendColor = new(); - - public readonly VertexBufferState[] VertexBuffers = new VertexBufferState[Constants.MaxVertexBuffers]; - public readonly VertexAttribDescriptor[] VertexAttribs = new VertexAttribDescriptor[Constants.MaxVertexAttributes]; - // Dirty flags - public DirtyFlags Dirty = DirtyFlags.None; - - // Only to be used for present - public bool ClearLoadAction = false; - - public RenderEncoderBindings RenderEncoderBindings = new(); - public ComputeEncoderBindings ComputeEncoderBindings = new(); - - public EncoderState() - { - Pipeline.Initialize(); - DepthStencilUid.DepthCompareFunction = MTLCompareFunction.Always; - } - - public RenderTargetCopy InheritForClear(EncoderState other, bool depth, int singleIndex = -1) - { - // Inherit render target related information without causing a render encoder split. - - RenderTargetCopy oldState = new() - { - Scissors = other.Scissors, - RenderTargets = other.RenderTargets, - DepthStencil = other.DepthStencil - }; - - Scissors = other.Scissors; - RenderTargets = other.RenderTargets; - DepthStencil = other.DepthStencil; - - Pipeline.ColorBlendAttachmentStateCount = other.Pipeline.ColorBlendAttachmentStateCount; - Pipeline.Internal.ColorBlendState = other.Pipeline.Internal.ColorBlendState; - Pipeline.DepthStencilFormat = other.Pipeline.DepthStencilFormat; - - ref Array8 blendStates = ref Pipeline.Internal.ColorBlendState; - - // Mask out irrelevant attachments. - for (int i = 0; i < blendStates.Length; i++) - { - if (depth || (singleIndex != -1 && singleIndex != i)) - { - blendStates[i].WriteMask = MTLColorWriteMask.None; - } - } - - return oldState; - } - - public void Restore(RenderTargetCopy copy) - { - Scissors = copy.Scissors; - RenderTargets = copy.RenderTargets; - DepthStencil = copy.DepthStencil; - - Pipeline.Internal.ResetColorState(); - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs deleted file mode 100644 index 7901e5a52..000000000 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ /dev/null @@ -1,1790 +0,0 @@ -using Ryujinx.Common.Logging; -using Ryujinx.Common.Memory; -using Ryujinx.Graphics.GAL; -using Ryujinx.Graphics.Metal.State; -using Ryujinx.Graphics.Shader; -using SharpMetal.Metal; -using System; -using System.Linq; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Runtime.Versioning; -using BufferAssignment = Ryujinx.Graphics.GAL.BufferAssignment; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - struct EncoderStateManager : IDisposable - { - private const int ArrayGrowthSize = 16; - - private readonly MTLDevice _device; - private readonly Pipeline _pipeline; - private readonly BufferManager _bufferManager; - - private readonly DepthStencilCache _depthStencilCache; - private readonly MTLDepthStencilState _defaultState; - - private readonly EncoderState _mainState = new(); - private EncoderState _currentState; - - public readonly IndexBufferState IndexBuffer => _currentState.IndexBuffer; - public readonly PrimitiveTopology Topology => _currentState.Topology; - public readonly Texture[] RenderTargets => _currentState.RenderTargets; - public readonly Texture DepthStencil => _currentState.DepthStencil; - public readonly ComputeSize ComputeLocalSize => _currentState.ComputeProgram.ComputeLocalSize; - - // RGBA32F is the biggest format - private const int ZeroBufferSize = 4 * 4; - private readonly BufferHandle _zeroBuffer; - - public unsafe EncoderStateManager(MTLDevice device, BufferManager bufferManager, Pipeline pipeline) - { - _device = device; - _pipeline = pipeline; - _bufferManager = bufferManager; - - _depthStencilCache = new(device); - _currentState = _mainState; - - _defaultState = _depthStencilCache.GetOrCreate(_currentState.DepthStencilUid); - - // Zero buffer - byte[] zeros = new byte[ZeroBufferSize]; - fixed (byte* ptr = zeros) - { - _zeroBuffer = _bufferManager.Create((IntPtr)ptr, ZeroBufferSize); - } - } - - public readonly void Dispose() - { - _depthStencilCache.Dispose(); - } - - private readonly void SignalDirty(DirtyFlags flags) - { - _currentState.Dirty |= flags; - } - - public readonly void SignalRenderDirty() - { - SignalDirty(DirtyFlags.RenderAll); - } - - public readonly void SignalComputeDirty() - { - SignalDirty(DirtyFlags.ComputeAll); - } - - public EncoderState SwapState(EncoderState state, DirtyFlags flags = DirtyFlags.All) - { - _currentState = state ?? _mainState; - - SignalDirty(flags); - - return _mainState; - } - - public PredrawState SavePredrawState() - { - return new PredrawState - { - CullMode = _currentState.CullMode, - DepthStencilUid = _currentState.DepthStencilUid, - Topology = _currentState.Topology, - Viewports = _currentState.Viewports.ToArray(), - }; - } - - public readonly void RestorePredrawState(PredrawState state) - { - _currentState.CullMode = state.CullMode; - _currentState.DepthStencilUid = state.DepthStencilUid; - _currentState.Topology = state.Topology; - _currentState.Viewports = state.Viewports; - - SignalDirty(DirtyFlags.CullMode | DirtyFlags.DepthStencil | DirtyFlags.Viewports); - } - - public readonly void SetClearLoadAction(bool clear) - { - _currentState.ClearLoadAction = clear; - } - - public readonly void DirtyTextures() - { - SignalDirty(DirtyFlags.Textures); - } - - public readonly void DirtyImages() - { - SignalDirty(DirtyFlags.Images); - } - - public readonly MTLRenderCommandEncoder CreateRenderCommandEncoder() - { - // Initialise Pass & State - using MTLRenderPassDescriptor renderPassDescriptor = new(); - - for (int i = 0; i < Constants.MaxColorAttachments; i++) - { - if (_currentState.RenderTargets[i] is Texture tex) - { - MTLRenderPassColorAttachmentDescriptor passAttachment = renderPassDescriptor.ColorAttachments.Object((ulong)i); - tex.PopulateRenderPassAttachment(passAttachment); - passAttachment.LoadAction = _currentState.ClearLoadAction ? MTLLoadAction.Clear : MTLLoadAction.Load; - passAttachment.StoreAction = MTLStoreAction.Store; - } - } - - MTLRenderPassDepthAttachmentDescriptor depthAttachment = renderPassDescriptor.DepthAttachment; - MTLRenderPassStencilAttachmentDescriptor stencilAttachment = renderPassDescriptor.StencilAttachment; - - if (_currentState.DepthStencil != null) - { - switch (_currentState.DepthStencil.GetHandle().PixelFormat) - { - // Depth Only Attachment - case MTLPixelFormat.Depth16Unorm: - case MTLPixelFormat.Depth32Float: - depthAttachment.Texture = _currentState.DepthStencil.GetHandle(); - depthAttachment.LoadAction = MTLLoadAction.Load; - depthAttachment.StoreAction = MTLStoreAction.Store; - break; - - // Stencil Only Attachment - case MTLPixelFormat.Stencil8: - stencilAttachment.Texture = _currentState.DepthStencil.GetHandle(); - stencilAttachment.LoadAction = MTLLoadAction.Load; - stencilAttachment.StoreAction = MTLStoreAction.Store; - break; - - // Combined Attachment - case MTLPixelFormat.Depth24UnormStencil8: - case MTLPixelFormat.Depth32FloatStencil8: - depthAttachment.Texture = _currentState.DepthStencil.GetHandle(); - depthAttachment.LoadAction = MTLLoadAction.Load; - depthAttachment.StoreAction = MTLStoreAction.Store; - - stencilAttachment.Texture = _currentState.DepthStencil.GetHandle(); - stencilAttachment.LoadAction = MTLLoadAction.Load; - stencilAttachment.StoreAction = MTLStoreAction.Store; - break; - default: - Logger.Error?.PrintMsg(LogClass.Gpu, $"Unsupported Depth/Stencil Format: {_currentState.DepthStencil.GetHandle().PixelFormat}!"); - break; - } - } - - // Initialise Encoder - MTLRenderCommandEncoder renderCommandEncoder = _pipeline.CommandBuffer.RenderCommandEncoder(renderPassDescriptor); - - return renderCommandEncoder; - } - - public readonly MTLComputeCommandEncoder CreateComputeCommandEncoder() - { - using MTLComputePassDescriptor descriptor = new(); - MTLComputeCommandEncoder computeCommandEncoder = _pipeline.CommandBuffer.ComputeCommandEncoder(descriptor); - - return computeCommandEncoder; - } - - public readonly void RenderResourcesPrepass() - { - _currentState.RenderEncoderBindings.Clear(); - - if ((_currentState.Dirty & DirtyFlags.RenderPipeline) != 0) - { - SetVertexBuffers(_currentState.VertexBuffers, ref _currentState.RenderEncoderBindings); - } - - if ((_currentState.Dirty & DirtyFlags.Uniforms) != 0) - { - UpdateAndBind(_currentState.RenderProgram, Constants.ConstantBuffersSetIndex, ref _currentState.RenderEncoderBindings); - } - - if ((_currentState.Dirty & DirtyFlags.Storages) != 0) - { - UpdateAndBind(_currentState.RenderProgram, Constants.StorageBuffersSetIndex, ref _currentState.RenderEncoderBindings); - } - - if ((_currentState.Dirty & DirtyFlags.Textures) != 0) - { - UpdateAndBind(_currentState.RenderProgram, Constants.TexturesSetIndex, ref _currentState.RenderEncoderBindings); - } - - if ((_currentState.Dirty & DirtyFlags.Images) != 0) - { - UpdateAndBind(_currentState.RenderProgram, Constants.ImagesSetIndex, ref _currentState.RenderEncoderBindings); - } - } - - public readonly void ComputeResourcesPrepass() - { - _currentState.ComputeEncoderBindings.Clear(); - - if ((_currentState.Dirty & DirtyFlags.Uniforms) != 0) - { - UpdateAndBind(_currentState.ComputeProgram, Constants.ConstantBuffersSetIndex, ref _currentState.ComputeEncoderBindings); - } - - if ((_currentState.Dirty & DirtyFlags.Storages) != 0) - { - UpdateAndBind(_currentState.ComputeProgram, Constants.StorageBuffersSetIndex, ref _currentState.ComputeEncoderBindings); - } - - if ((_currentState.Dirty & DirtyFlags.Textures) != 0) - { - UpdateAndBind(_currentState.ComputeProgram, Constants.TexturesSetIndex, ref _currentState.ComputeEncoderBindings); - } - - if ((_currentState.Dirty & DirtyFlags.Images) != 0) - { - UpdateAndBind(_currentState.ComputeProgram, Constants.ImagesSetIndex, ref _currentState.ComputeEncoderBindings); - } - } - - public void RebindRenderState(MTLRenderCommandEncoder renderCommandEncoder) - { - if ((_currentState.Dirty & DirtyFlags.RenderPipeline) != 0) - { - SetRenderPipelineState(renderCommandEncoder); - } - - if ((_currentState.Dirty & DirtyFlags.DepthStencil) != 0) - { - SetDepthStencilState(renderCommandEncoder); - } - - if ((_currentState.Dirty & DirtyFlags.DepthClamp) != 0) - { - SetDepthClamp(renderCommandEncoder); - } - - if ((_currentState.Dirty & DirtyFlags.DepthBias) != 0) - { - SetDepthBias(renderCommandEncoder); - } - - if ((_currentState.Dirty & DirtyFlags.CullMode) != 0) - { - SetCullMode(renderCommandEncoder); - } - - if ((_currentState.Dirty & DirtyFlags.FrontFace) != 0) - { - SetFrontFace(renderCommandEncoder); - } - - if ((_currentState.Dirty & DirtyFlags.StencilRef) != 0) - { - SetStencilRefValue(renderCommandEncoder); - } - - if ((_currentState.Dirty & DirtyFlags.Viewports) != 0) - { - SetViewports(renderCommandEncoder); - } - - if ((_currentState.Dirty & DirtyFlags.Scissors) != 0) - { - SetScissors(renderCommandEncoder); - } - - foreach (Resource resource in _currentState.RenderEncoderBindings.Resources) - { - renderCommandEncoder.UseResource(resource.MtlResource, resource.ResourceUsage, resource.Stages); - } - - foreach (BufferResource buffer in _currentState.RenderEncoderBindings.VertexBuffers) - { - renderCommandEncoder.SetVertexBuffer(buffer.Buffer, buffer.Offset, buffer.Binding); - } - - foreach (BufferResource buffer in _currentState.RenderEncoderBindings.FragmentBuffers) - { - renderCommandEncoder.SetFragmentBuffer(buffer.Buffer, buffer.Offset, buffer.Binding); - } - - _currentState.Dirty &= ~DirtyFlags.RenderAll; - } - - public readonly void RebindComputeState(MTLComputeCommandEncoder computeCommandEncoder) - { - if ((_currentState.Dirty & DirtyFlags.ComputePipeline) != 0) - { - SetComputePipelineState(computeCommandEncoder); - } - - foreach (Resource resource in _currentState.ComputeEncoderBindings.Resources) - { - computeCommandEncoder.UseResource(resource.MtlResource, resource.ResourceUsage); - } - - foreach (BufferResource buffer in _currentState.ComputeEncoderBindings.Buffers) - { - computeCommandEncoder.SetBuffer(buffer.Buffer, buffer.Offset, buffer.Binding); - } - - _currentState.Dirty &= ~DirtyFlags.ComputeAll; - } - - private readonly void SetRenderPipelineState(MTLRenderCommandEncoder renderCommandEncoder) - { - MTLRenderPipelineState pipelineState = _currentState.Pipeline.CreateRenderPipeline(_device, _currentState.RenderProgram); - - renderCommandEncoder.SetRenderPipelineState(pipelineState); - - renderCommandEncoder.SetBlendColor( - _currentState.BlendColor.Red, - _currentState.BlendColor.Green, - _currentState.BlendColor.Blue, - _currentState.BlendColor.Alpha); - } - - private readonly void SetComputePipelineState(MTLComputeCommandEncoder computeCommandEncoder) - { - if (_currentState.ComputeProgram == null) - { - return; - } - - MTLComputePipelineState pipelineState = PipelineState.CreateComputePipeline(_device, _currentState.ComputeProgram); - - computeCommandEncoder.SetComputePipelineState(pipelineState); - } - - public readonly void UpdateIndexBuffer(BufferRange buffer, IndexType type) - { - if (buffer.Handle != BufferHandle.Null) - { - _currentState.IndexBuffer = new IndexBufferState(buffer.Handle, buffer.Offset, buffer.Size, type); - } - else - { - _currentState.IndexBuffer = IndexBufferState.Null; - } - } - - public readonly void UpdatePrimitiveTopology(PrimitiveTopology topology) - { - _currentState.Topology = topology; - } - - public readonly void UpdateProgram(IProgram program) - { - Program prg = (Program)program; - - if (prg.VertexFunction == IntPtr.Zero && prg.ComputeFunction == IntPtr.Zero) - { - if (prg.FragmentFunction == IntPtr.Zero) - { - Logger.Error?.PrintMsg(LogClass.Gpu, "No compute function"); - } - else - { - Logger.Error?.PrintMsg(LogClass.Gpu, "No vertex function"); - } - return; - } - - if (prg.VertexFunction != IntPtr.Zero) - { - _currentState.RenderProgram = prg; - - SignalDirty(DirtyFlags.RenderPipeline | DirtyFlags.ArgBuffers); - } - else if (prg.ComputeFunction != IntPtr.Zero) - { - _currentState.ComputeProgram = prg; - - SignalDirty(DirtyFlags.ComputePipeline | DirtyFlags.ArgBuffers); - } - } - - public readonly void UpdateRasterizerDiscard(bool discard) - { - _currentState.Pipeline.RasterizerDiscardEnable = discard; - - SignalDirty(DirtyFlags.RenderPipeline); - } - - public readonly void UpdateRenderTargets(ITexture[] colors, ITexture depthStencil) - { - _currentState.FramebufferUsingColorWriteMask = false; - UpdateRenderTargetsInternal(colors, depthStencil); - } - - public readonly void UpdateRenderTargetColorMasks(ReadOnlySpan componentMask) - { - ref Array8 blendState = ref _currentState.Pipeline.Internal.ColorBlendState; - - for (int i = 0; i < componentMask.Length; i++) - { - bool red = (componentMask[i] & (0x1 << 0)) != 0; - bool green = (componentMask[i] & (0x1 << 1)) != 0; - bool blue = (componentMask[i] & (0x1 << 2)) != 0; - bool alpha = (componentMask[i] & (0x1 << 3)) != 0; - - MTLColorWriteMask mask = MTLColorWriteMask.None; - - mask |= red ? MTLColorWriteMask.Red : 0; - mask |= green ? MTLColorWriteMask.Green : 0; - mask |= blue ? MTLColorWriteMask.Blue : 0; - mask |= alpha ? MTLColorWriteMask.Alpha : 0; - - ref ColorBlendStateUid mtlBlend = ref blendState[i]; - - // When color write mask is 0, remove all blend state to help the pipeline cache. - // Restore it when the mask becomes non-zero. - if (mtlBlend.WriteMask != mask) - { - if (mask == 0) - { - _currentState.StoredBlend[i] = mtlBlend; - - mtlBlend.Swap(new ColorBlendStateUid()); - } - else if (mtlBlend.WriteMask == 0) - { - mtlBlend.Swap(_currentState.StoredBlend[i]); - } - } - - blendState[i].WriteMask = mask; - } - - if (_currentState.FramebufferUsingColorWriteMask) - { - UpdateRenderTargetsInternal(_currentState.PreMaskRenderTargets, _currentState.PreMaskDepthStencil); - } - else - { - // Requires recreating pipeline - if (_pipeline.CurrentEncoderType == EncoderType.Render) - { - _pipeline.EndCurrentPass(); - } - } - } - - private readonly void UpdateRenderTargetsInternal(ITexture[] colors, ITexture depthStencil) - { - // TBDR GPUs don't work properly if the same attachment is bound to multiple targets, - // due to each attachment being a copy of the real attachment, rather than a direct write. - // - // Just try to remove duplicate attachments. - // Save a copy of the array to rebind when mask changes. - - // Look for textures that are masked out. - - ref PipelineState pipeline = ref _currentState.Pipeline; - ref Array8 blendState = ref pipeline.Internal.ColorBlendState; - - pipeline.ColorBlendAttachmentStateCount = (uint)colors.Length; - - for (int i = 0; i < colors.Length; i++) - { - if (colors[i] == null) - { - continue; - } - - MTLColorWriteMask mtlMask = blendState[i].WriteMask; - - for (int j = 0; j < i; j++) - { - // Check each binding for a duplicate binding before it. - - if (colors[i] == colors[j]) - { - // Prefer the binding with no write mask. - - MTLColorWriteMask mtlMask2 = blendState[j].WriteMask; - - if (mtlMask == 0) - { - colors[i] = null; - MaskOut(colors, depthStencil); - } - else if (mtlMask2 == 0) - { - colors[j] = null; - MaskOut(colors, depthStencil); - } - } - } - } - - _currentState.RenderTargets = new Texture[Constants.MaxColorAttachments]; - - for (int i = 0; i < colors.Length; i++) - { - if (colors[i] is not Texture tex) - { - blendState[i].PixelFormat = MTLPixelFormat.Invalid; - - continue; - } - - blendState[i].PixelFormat = tex.GetHandle().PixelFormat; // TODO: cache this - _currentState.RenderTargets[i] = tex; - } - - if (depthStencil is Texture depthTexture) - { - pipeline.DepthStencilFormat = depthTexture.GetHandle().PixelFormat; // TODO: cache this - _currentState.DepthStencil = depthTexture; - } - else if (depthStencil == null) - { - pipeline.DepthStencilFormat = MTLPixelFormat.Invalid; - _currentState.DepthStencil = null; - } - - // Requires recreating pipeline - if (_pipeline.CurrentEncoderType == EncoderType.Render) - { - _pipeline.EndCurrentPass(); - } - } - - private readonly void MaskOut(ITexture[] colors, ITexture depthStencil) - { - if (!_currentState.FramebufferUsingColorWriteMask) - { - _currentState.PreMaskRenderTargets = colors; - _currentState.PreMaskDepthStencil = depthStencil; - } - - // If true, then the framebuffer must be recreated when the mask changes. - _currentState.FramebufferUsingColorWriteMask = true; - } - - public readonly void UpdateVertexAttribs(ReadOnlySpan vertexAttribs) - { - vertexAttribs.CopyTo(_currentState.VertexAttribs); - - // Update the buffers on the pipeline - UpdatePipelineVertexState(_currentState.VertexBuffers, _currentState.VertexAttribs); - - SignalDirty(DirtyFlags.RenderPipeline); - } - - public readonly void UpdateBlendDescriptors(int index, BlendDescriptor blend) - { - ref ColorBlendStateUid blendState = ref _currentState.Pipeline.Internal.ColorBlendState[index]; - - blendState.Enable = blend.Enable; - blendState.AlphaBlendOperation = blend.AlphaOp.Convert(); - blendState.RgbBlendOperation = blend.ColorOp.Convert(); - blendState.SourceAlphaBlendFactor = blend.AlphaSrcFactor.Convert(); - blendState.DestinationAlphaBlendFactor = blend.AlphaDstFactor.Convert(); - blendState.SourceRGBBlendFactor = blend.ColorSrcFactor.Convert(); - blendState.DestinationRGBBlendFactor = blend.ColorDstFactor.Convert(); - - if (blendState.WriteMask == 0) - { - _currentState.StoredBlend[index] = blendState; - - blendState.Swap(new ColorBlendStateUid()); - } - - _currentState.BlendColor = blend.BlendConstant; - - SignalDirty(DirtyFlags.RenderPipeline); - } - - public void UpdateStencilState(StencilTestDescriptor stencilTest) - { - ref DepthStencilUid uid = ref _currentState.DepthStencilUid; - - uid.FrontFace = new StencilUid - { - StencilFailureOperation = stencilTest.FrontSFail.Convert(), - DepthFailureOperation = stencilTest.FrontDpFail.Convert(), - DepthStencilPassOperation = stencilTest.FrontDpPass.Convert(), - StencilCompareFunction = stencilTest.FrontFunc.Convert(), - ReadMask = (uint)stencilTest.FrontFuncMask, - WriteMask = (uint)stencilTest.FrontMask - }; - - uid.BackFace = new StencilUid - { - StencilFailureOperation = stencilTest.BackSFail.Convert(), - DepthFailureOperation = stencilTest.BackDpFail.Convert(), - DepthStencilPassOperation = stencilTest.BackDpPass.Convert(), - StencilCompareFunction = stencilTest.BackFunc.Convert(), - ReadMask = (uint)stencilTest.BackFuncMask, - WriteMask = (uint)stencilTest.BackMask - }; - - uid.StencilTestEnabled = stencilTest.TestEnable; - - UpdateStencilRefValue(stencilTest.FrontFuncRef, stencilTest.BackFuncRef); - - SignalDirty(DirtyFlags.DepthStencil); - } - - public readonly void UpdateDepthState(DepthTestDescriptor depthTest) - { - ref DepthStencilUid uid = ref _currentState.DepthStencilUid; - - uid.DepthCompareFunction = depthTest.TestEnable ? depthTest.Func.Convert() : MTLCompareFunction.Always; - uid.DepthWriteEnabled = depthTest.TestEnable && depthTest.WriteEnable; - - SignalDirty(DirtyFlags.DepthStencil); - } - - public readonly void UpdateDepthClamp(bool clamp) - { - _currentState.DepthClipMode = clamp ? MTLDepthClipMode.Clamp : MTLDepthClipMode.Clip; - - // Inline update - if (_pipeline.Encoders.TryGetRenderEncoder(out MTLRenderCommandEncoder renderCommandEncoder)) - { - SetDepthClamp(renderCommandEncoder); - return; - } - - SignalDirty(DirtyFlags.DepthClamp); - } - - public readonly void UpdateDepthBias(float depthBias, float slopeScale, float clamp) - { - _currentState.DepthBias = depthBias; - _currentState.SlopeScale = slopeScale; - _currentState.Clamp = clamp; - - // Inline update - if (_pipeline.Encoders.TryGetRenderEncoder(out MTLRenderCommandEncoder renderCommandEncoder)) - { - SetDepthBias(renderCommandEncoder); - return; - } - - SignalDirty(DirtyFlags.DepthBias); - } - - public readonly void UpdateLogicOpState(bool enable, LogicalOp op) - { - _currentState.Pipeline.LogicOpEnable = enable; - _currentState.Pipeline.LogicOp = op.Convert(); - - SignalDirty(DirtyFlags.RenderPipeline); - } - - public readonly void UpdateMultisampleState(MultisampleDescriptor multisample) - { - _currentState.Pipeline.AlphaToCoverageEnable = multisample.AlphaToCoverageEnable; - _currentState.Pipeline.AlphaToOneEnable = multisample.AlphaToOneEnable; - - SignalDirty(DirtyFlags.RenderPipeline); - } - - public void UpdateScissors(ReadOnlySpan> regions) - { - for (int i = 0; i < regions.Length; i++) - { - Rectangle region = regions[i]; - - _currentState.Scissors[i] = new MTLScissorRect - { - height = (ulong)region.Height, - width = (ulong)region.Width, - x = (ulong)region.X, - y = (ulong)region.Y - }; - } - - // Inline update - if (_pipeline.Encoders.TryGetRenderEncoder(out MTLRenderCommandEncoder renderCommandEncoder)) - { - SetScissors(renderCommandEncoder); - return; - } - - SignalDirty(DirtyFlags.Scissors); - } - - public void UpdateViewports(ReadOnlySpan viewports) - { - static float Clamp(float value) - { - return Math.Clamp(value, 0f, 1f); - } - - for (int i = 0; i < viewports.Length; i++) - { - Viewport viewport = viewports[i]; - // Y coordinate is inverted - _currentState.Viewports[i] = new MTLViewport - { - originX = viewport.Region.X, - originY = viewport.Region.Y + viewport.Region.Height, - width = viewport.Region.Width, - height = -viewport.Region.Height, - znear = Clamp(viewport.DepthNear), - zfar = Clamp(viewport.DepthFar) - }; - } - - // Inline update - if (_pipeline.Encoders.TryGetRenderEncoder(out MTLRenderCommandEncoder renderCommandEncoder)) - { - SetViewports(renderCommandEncoder); - return; - } - - SignalDirty(DirtyFlags.Viewports); - } - - public readonly void UpdateVertexBuffers(ReadOnlySpan vertexBuffers) - { - for (int i = 0; i < Constants.MaxVertexBuffers; i++) - { - if (i < vertexBuffers.Length) - { - VertexBufferDescriptor vertexBuffer = vertexBuffers[i]; - - _currentState.VertexBuffers[i] = new VertexBufferState( - vertexBuffer.Buffer.Handle, - vertexBuffer.Buffer.Offset, - vertexBuffer.Buffer.Size, - vertexBuffer.Divisor, - vertexBuffer.Stride); - } - else - { - _currentState.VertexBuffers[i] = VertexBufferState.Null; - } - } - - // Update the buffers on the pipeline - UpdatePipelineVertexState(_currentState.VertexBuffers, _currentState.VertexAttribs); - - SignalDirty(DirtyFlags.RenderPipeline); - } - - public readonly void UpdateUniformBuffers(ReadOnlySpan buffers) - { - foreach (BufferAssignment assignment in buffers) - { - BufferRange buffer = assignment.Range; - int index = assignment.Binding; - - Auto mtlBuffer = buffer.Handle == BufferHandle.Null - ? null - : _bufferManager.GetBuffer(buffer.Handle, buffer.Write); - - _currentState.UniformBufferRefs[index] = new BufferRef(mtlBuffer, ref buffer); - } - - SignalDirty(DirtyFlags.Uniforms); - } - - public readonly void UpdateStorageBuffers(ReadOnlySpan buffers) - { - foreach (BufferAssignment assignment in buffers) - { - BufferRange buffer = assignment.Range; - int index = assignment.Binding; - - Auto mtlBuffer = buffer.Handle == BufferHandle.Null - ? null - : _bufferManager.GetBuffer(buffer.Handle, buffer.Write); - - _currentState.StorageBufferRefs[index] = new BufferRef(mtlBuffer, ref buffer); - } - - SignalDirty(DirtyFlags.Storages); - } - - public readonly void UpdateStorageBuffers(int first, ReadOnlySpan> buffers) - { - for (int i = 0; i < buffers.Length; i++) - { - Auto mtlBuffer = buffers[i]; - int index = first + i; - - _currentState.StorageBufferRefs[index] = new BufferRef(mtlBuffer); - } - - SignalDirty(DirtyFlags.Storages); - } - - public void UpdateCullMode(bool enable, Face face) - { - bool dirtyScissor = (face == Face.FrontAndBack) != _currentState.CullBoth; - - _currentState.CullMode = enable ? face.Convert() : MTLCullMode.None; - _currentState.CullBoth = face == Face.FrontAndBack; - - // Inline update - if (_pipeline.Encoders.TryGetRenderEncoder(out MTLRenderCommandEncoder renderCommandEncoder)) - { - SetCullMode(renderCommandEncoder); - SetScissors(renderCommandEncoder); - return; - } - - // Mark dirty - SignalDirty(DirtyFlags.CullMode); - - if (dirtyScissor) - { - SignalDirty(DirtyFlags.Scissors); - } - } - - public readonly void UpdateFrontFace(FrontFace frontFace) - { - _currentState.Winding = frontFace.Convert(); - - // Inline update - if (_pipeline.Encoders.TryGetRenderEncoder(out MTLRenderCommandEncoder renderCommandEncoder)) - { - SetFrontFace(renderCommandEncoder); - return; - } - - SignalDirty(DirtyFlags.FrontFace); - } - - private readonly void UpdateStencilRefValue(int frontRef, int backRef) - { - _currentState.FrontRefValue = frontRef; - _currentState.BackRefValue = backRef; - - // Inline update - if (_pipeline.Encoders.TryGetRenderEncoder(out MTLRenderCommandEncoder renderCommandEncoder)) - { - SetStencilRefValue(renderCommandEncoder); - } - - SignalDirty(DirtyFlags.StencilRef); - } - - public readonly void UpdateTextureAndSampler(ShaderStage stage, int binding, TextureBase texture, SamplerHolder samplerHolder) - { - if (texture != null) - { - _currentState.TextureRefs[binding] = new(stage, texture, samplerHolder?.GetSampler()); - } - else - { - _currentState.TextureRefs[binding] = default; - } - - SignalDirty(DirtyFlags.Textures); - } - - public readonly void UpdateImage(ShaderStage stage, int binding, TextureBase image) - { - if (image is Texture view) - { - _currentState.ImageRefs[binding] = new(stage, view); - } - else - { - _currentState.ImageRefs[binding] = default; - } - - SignalDirty(DirtyFlags.Images); - } - - public readonly void UpdateTextureArray(ShaderStage stage, int binding, TextureArray array) - { - ref EncoderState.ArrayRef arrayRef = ref GetArrayRef(ref _currentState.TextureArrayRefs, binding, ArrayGrowthSize); - - if (arrayRef.Stage != stage || arrayRef.Array != array) - { - arrayRef = new EncoderState.ArrayRef(stage, array); - - SignalDirty(DirtyFlags.Textures); - } - } - - public readonly void UpdateTextureArraySeparate(ShaderStage stage, int setIndex, TextureArray array) - { - ref EncoderState.ArrayRef arrayRef = ref GetArrayRef(ref _currentState.TextureArrayExtraRefs, setIndex - MetalRenderer.TotalSets); - - if (arrayRef.Stage != stage || arrayRef.Array != array) - { - arrayRef = new EncoderState.ArrayRef(stage, array); - - SignalDirty(DirtyFlags.Textures); - } - } - - public readonly void UpdateImageArray(ShaderStage stage, int binding, ImageArray array) - { - ref EncoderState.ArrayRef arrayRef = ref GetArrayRef(ref _currentState.ImageArrayRefs, binding, ArrayGrowthSize); - - if (arrayRef.Stage != stage || arrayRef.Array != array) - { - arrayRef = new EncoderState.ArrayRef(stage, array); - - SignalDirty(DirtyFlags.Images); - } - } - - public readonly void UpdateImageArraySeparate(ShaderStage stage, int setIndex, ImageArray array) - { - ref EncoderState.ArrayRef arrayRef = ref GetArrayRef(ref _currentState.ImageArrayExtraRefs, setIndex - MetalRenderer.TotalSets); - - if (arrayRef.Stage != stage || arrayRef.Array != array) - { - arrayRef = new EncoderState.ArrayRef(stage, array); - - SignalDirty(DirtyFlags.Images); - } - } - - private static ref EncoderState.ArrayRef GetArrayRef(ref EncoderState.ArrayRef[] array, int index, int growthSize = 1) - { - ArgumentOutOfRangeException.ThrowIfNegative(index); - - if (array.Length <= index) - { - Array.Resize(ref array, index + growthSize); - } - - return ref array[index]; - } - - private readonly void SetDepthStencilState(MTLRenderCommandEncoder renderCommandEncoder) - { - if (DepthStencil != null) - { - MTLDepthStencilState state = _depthStencilCache.GetOrCreate(_currentState.DepthStencilUid); - - renderCommandEncoder.SetDepthStencilState(state); - } - else - { - renderCommandEncoder.SetDepthStencilState(_defaultState); - } - } - - private readonly void SetDepthClamp(MTLRenderCommandEncoder renderCommandEncoder) - { - renderCommandEncoder.SetDepthClipMode(_currentState.DepthClipMode); - } - - private readonly void SetDepthBias(MTLRenderCommandEncoder renderCommandEncoder) - { - renderCommandEncoder.SetDepthBias(_currentState.DepthBias, _currentState.SlopeScale, _currentState.Clamp); - } - - private unsafe void SetScissors(MTLRenderCommandEncoder renderCommandEncoder) - { - bool isTriangles = (_currentState.Topology == PrimitiveTopology.Triangles) || - (_currentState.Topology == PrimitiveTopology.TriangleStrip); - - if (_currentState.CullBoth && isTriangles) - { - renderCommandEncoder.SetScissorRect(new MTLScissorRect { x = 0, y = 0, width = 0, height = 0 }); - } - else - { - if (_currentState.Scissors.Length > 0) - { - fixed (MTLScissorRect* pMtlScissors = _currentState.Scissors) - { - renderCommandEncoder.SetScissorRects((IntPtr)pMtlScissors, (ulong)_currentState.Scissors.Length); - } - } - } - } - - private readonly unsafe void SetViewports(MTLRenderCommandEncoder renderCommandEncoder) - { - if (_currentState.Viewports.Length > 0) - { - fixed (MTLViewport* pMtlViewports = _currentState.Viewports) - { - renderCommandEncoder.SetViewports((IntPtr)pMtlViewports, (ulong)_currentState.Viewports.Length); - } - } - } - - private readonly void UpdatePipelineVertexState(VertexBufferState[] bufferDescriptors, VertexAttribDescriptor[] attribDescriptors) - { - ref PipelineState pipeline = ref _currentState.Pipeline; - uint indexMask = 0; - - for (int i = 0; i < attribDescriptors.Length; i++) - { - ref VertexInputAttributeUid attrib = ref pipeline.Internal.VertexAttributes[i]; - - if (attribDescriptors[i].IsZero) - { - attrib.Format = attribDescriptors[i].Format.Convert(); - indexMask |= 1u << (int)Constants.ZeroBufferIndex; - attrib.BufferIndex = Constants.ZeroBufferIndex; - attrib.Offset = 0; - } - else - { - attrib.Format = attribDescriptors[i].Format.Convert(); - indexMask |= 1u << attribDescriptors[i].BufferIndex; - attrib.BufferIndex = (ulong)attribDescriptors[i].BufferIndex; - attrib.Offset = (ulong)attribDescriptors[i].Offset; - } - } - - for (int i = 0; i < bufferDescriptors.Length; i++) - { - ref VertexInputLayoutUid layout = ref pipeline.Internal.VertexBindings[i]; - - if ((indexMask & (1u << i)) != 0) - { - layout.Stride = (uint)bufferDescriptors[i].Stride; - - if (layout.Stride == 0) - { - layout.Stride = 1; - layout.StepFunction = MTLVertexStepFunction.Constant; - layout.StepRate = 0; - } - else - { - if (bufferDescriptors[i].Divisor > 0) - { - layout.StepFunction = MTLVertexStepFunction.PerInstance; - layout.StepRate = (uint)bufferDescriptors[i].Divisor; - } - else - { - layout.StepFunction = MTLVertexStepFunction.PerVertex; - layout.StepRate = 1; - } - } - } - else - { - layout = new(); - } - } - - ref VertexInputLayoutUid zeroBufLayout = ref pipeline.Internal.VertexBindings[(int)Constants.ZeroBufferIndex]; - - // Zero buffer - if ((indexMask & (1u << (int)Constants.ZeroBufferIndex)) != 0) - { - zeroBufLayout.Stride = 1; - zeroBufLayout.StepFunction = MTLVertexStepFunction.Constant; - zeroBufLayout.StepRate = 0; - } - else - { - zeroBufLayout = new(); - } - - pipeline.VertexAttributeDescriptionsCount = (uint)attribDescriptors.Length; - pipeline.VertexBindingDescriptionsCount = Constants.ZeroBufferIndex + 1; // TODO: move this out? - } - - private readonly void SetVertexBuffers(VertexBufferState[] bufferStates, ref readonly RenderEncoderBindings bindings) - { - for (int i = 0; i < bufferStates.Length; i++) - { - (MTLBuffer mtlBuffer, int offset) = bufferStates[i].GetVertexBuffer(_bufferManager, _pipeline.Cbs); - - if (mtlBuffer.NativePtr != IntPtr.Zero) - { - bindings.VertexBuffers.Add(new BufferResource(mtlBuffer, (ulong)offset, (ulong)i)); - } - } - - Auto autoZeroBuffer = _zeroBuffer == BufferHandle.Null - ? null - : _bufferManager.GetBuffer(_zeroBuffer, false); - - if (autoZeroBuffer == null) - { - return; - } - - MTLBuffer zeroMtlBuffer = autoZeroBuffer.Get(_pipeline.Cbs).Value; - bindings.VertexBuffers.Add(new BufferResource(zeroMtlBuffer, 0, Constants.ZeroBufferIndex)); - } - - private readonly (ulong gpuAddress, IntPtr nativePtr) AddressForBuffer(ref BufferRef buffer) - { - ulong gpuAddress = 0; - IntPtr nativePtr = IntPtr.Zero; - - BufferRange? range = buffer.Range; - Auto autoBuffer = buffer.Buffer; - - if (autoBuffer != null) - { - int offset = 0; - MTLBuffer mtlBuffer; - - if (range.HasValue) - { - offset = range.Value.Offset; - mtlBuffer = autoBuffer.Get(_pipeline.Cbs, offset, range.Value.Size, range.Value.Write).Value; - } - else - { - mtlBuffer = autoBuffer.Get(_pipeline.Cbs).Value; - } - - gpuAddress = mtlBuffer.GpuAddress + (ulong)offset; - nativePtr = mtlBuffer.NativePtr; - } - - return (gpuAddress, nativePtr); - } - - private readonly (ulong gpuAddress, IntPtr nativePtr) AddressForTexture(ref TextureRef texture) - { - TextureBase storage = texture.Storage; - - ulong gpuAddress = 0; - IntPtr nativePtr = IntPtr.Zero; - - if (storage != null) - { - if (storage is TextureBuffer textureBuffer) - { - textureBuffer.RebuildStorage(false); - } - - MTLTexture mtlTexture = storage.GetHandle(); - - gpuAddress = mtlTexture.GpuResourceID._impl; - nativePtr = mtlTexture.NativePtr; - } - - return (gpuAddress, nativePtr); - } - - private readonly (ulong gpuAddress, IntPtr nativePtr) AddressForImage(ref ImageRef image) - { - Texture storage = image.Storage; - - ulong gpuAddress = 0; - IntPtr nativePtr = IntPtr.Zero; - - if (storage != null) - { - MTLTexture mtlTexture = storage.GetHandle(); - - gpuAddress = mtlTexture.GpuResourceID._impl; - nativePtr = mtlTexture.NativePtr; - } - - return (gpuAddress, nativePtr); - } - - private readonly (ulong gpuAddress, IntPtr nativePtr) AddressForTextureBuffer(ref TextureBuffer bufferTexture) - { - ulong gpuAddress = 0; - IntPtr nativePtr = IntPtr.Zero; - - if (bufferTexture != null) - { - bufferTexture.RebuildStorage(false); - - MTLTexture mtlTexture = bufferTexture.GetHandle(); - - gpuAddress = mtlTexture.GpuResourceID._impl; - nativePtr = mtlTexture.NativePtr; - } - - return (gpuAddress, nativePtr); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void AddResource(IntPtr resourcePointer, MTLResourceUsage usage, MTLRenderStages stages, ref readonly RenderEncoderBindings bindings) - { - if (resourcePointer != IntPtr.Zero) - { - bindings.Resources.Add(new Resource(new MTLResource(resourcePointer), usage, stages)); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void AddResource(IntPtr resourcePointer, MTLResourceUsage usage, ref readonly ComputeEncoderBindings bindings) - { - if (resourcePointer != IntPtr.Zero) - { - bindings.Resources.Add(new Resource(new MTLResource(resourcePointer), usage, 0)); - } - } - - private readonly void UpdateAndBind(Program program, uint setIndex, ref readonly RenderEncoderBindings bindings) - { - ResourceBindingSegment[] bindingSegments = program.BindingSegments[setIndex]; - - if (bindingSegments.Length == 0) - { - return; - } - - ScopedTemporaryBuffer vertArgBuffer = default; - ScopedTemporaryBuffer fragArgBuffer = default; - - if (program.ArgumentBufferSizes[setIndex] > 0) - { - vertArgBuffer = _bufferManager.ReserveOrCreate(_pipeline.Cbs, program.ArgumentBufferSizes[setIndex] * sizeof(ulong)); - } - - if (program.FragArgumentBufferSizes[setIndex] > 0) - { - fragArgBuffer = _bufferManager.ReserveOrCreate(_pipeline.Cbs, program.FragArgumentBufferSizes[setIndex] * sizeof(ulong)); - } - - Span vertResourceIds = stackalloc ulong[program.ArgumentBufferSizes[setIndex]]; - Span fragResourceIds = stackalloc ulong[program.FragArgumentBufferSizes[setIndex]]; - - int vertResourceIdIndex = 0; - int fragResourceIdIndex = 0; - - foreach (ResourceBindingSegment segment in bindingSegments) - { - int binding = segment.Binding; - int count = segment.Count; - - switch (setIndex) - { - case Constants.ConstantBuffersSetIndex: - for (int i = 0; i < count; i++) - { - int index = binding + i; - - ref BufferRef buffer = ref _currentState.UniformBufferRefs[index]; - (ulong gpuAddress, IntPtr nativePtr) = AddressForBuffer(ref buffer); - - MTLRenderStages renderStages = 0; - - if ((segment.Stages & ResourceStages.Vertex) != 0) - { - vertResourceIds[vertResourceIdIndex] = gpuAddress; - vertResourceIdIndex++; - - renderStages |= MTLRenderStages.RenderStageVertex; - } - - if ((segment.Stages & ResourceStages.Fragment) != 0) - { - fragResourceIds[fragResourceIdIndex] = gpuAddress; - fragResourceIdIndex++; - - renderStages |= MTLRenderStages.RenderStageFragment; - } - - AddResource(nativePtr, MTLResourceUsage.Read, renderStages, in bindings); - } - break; - case Constants.StorageBuffersSetIndex: - for (int i = 0; i < count; i++) - { - int index = binding + i; - - ref BufferRef buffer = ref _currentState.StorageBufferRefs[index]; - (ulong gpuAddress, IntPtr nativePtr) = AddressForBuffer(ref buffer); - - MTLRenderStages renderStages = 0; - - if ((segment.Stages & ResourceStages.Vertex) != 0) - { - vertResourceIds[vertResourceIdIndex] = gpuAddress; - vertResourceIdIndex++; - - renderStages |= MTLRenderStages.RenderStageVertex; - } - - if ((segment.Stages & ResourceStages.Fragment) != 0) - { - fragResourceIds[fragResourceIdIndex] = gpuAddress; - fragResourceIdIndex++; - - renderStages |= MTLRenderStages.RenderStageFragment; - } - - AddResource(nativePtr, MTLResourceUsage.Read, renderStages, in bindings); - } - break; - case Constants.TexturesSetIndex: - if (!segment.IsArray) - { - for (int i = 0; i < count; i++) - { - int index = binding + i; - - ref TextureRef texture = ref _currentState.TextureRefs[index]; - (ulong gpuAddress, IntPtr nativePtr) = AddressForTexture(ref texture); - - MTLRenderStages renderStages = 0; - - if ((segment.Stages & ResourceStages.Vertex) != 0) - { - vertResourceIds[vertResourceIdIndex] = gpuAddress; - vertResourceIdIndex++; - - if (texture.Sampler != null) - { - vertResourceIds[vertResourceIdIndex] = texture.Sampler.Get(_pipeline.Cbs).Value.GpuResourceID._impl; - vertResourceIdIndex++; - } - - renderStages |= MTLRenderStages.RenderStageVertex; - } - - if ((segment.Stages & ResourceStages.Fragment) != 0) - { - fragResourceIds[fragResourceIdIndex] = gpuAddress; - fragResourceIdIndex++; - - if (texture.Sampler != null) - { - fragResourceIds[fragResourceIdIndex] = texture.Sampler.Get(_pipeline.Cbs).Value.GpuResourceID._impl; - fragResourceIdIndex++; - } - - renderStages |= MTLRenderStages.RenderStageFragment; - } - - AddResource(nativePtr, MTLResourceUsage.Read, renderStages, in bindings); - } - } - else - { - TextureArray textureArray = _currentState.TextureArrayRefs[binding].Array; - - if (segment.Type != ResourceType.BufferTexture) - { - TextureRef[] textures = textureArray.GetTextureRefs(); - Auto[] samplers = new Auto[textures.Length]; - - for (int i = 0; i < textures.Length; i++) - { - TextureRef texture = textures[i]; - (ulong gpuAddress, IntPtr nativePtr) = AddressForTexture(ref texture); - - samplers[i] = texture.Sampler; - - MTLRenderStages renderStages = 0; - - if ((segment.Stages & ResourceStages.Vertex) != 0) - { - vertResourceIds[vertResourceIdIndex] = gpuAddress; - vertResourceIdIndex++; - - renderStages |= MTLRenderStages.RenderStageVertex; - } - - if ((segment.Stages & ResourceStages.Fragment) != 0) - { - fragResourceIds[fragResourceIdIndex] = gpuAddress; - fragResourceIdIndex++; - - renderStages |= MTLRenderStages.RenderStageFragment; - } - - AddResource(nativePtr, MTLResourceUsage.Read, renderStages, in bindings); - } - - foreach (Auto sampler in samplers) - { - ulong gpuAddress = 0; - - if (sampler != null) - { - gpuAddress = sampler.Get(_pipeline.Cbs).Value.GpuResourceID._impl; - } - - if ((segment.Stages & ResourceStages.Vertex) != 0) - { - vertResourceIds[vertResourceIdIndex] = gpuAddress; - vertResourceIdIndex++; - } - - if ((segment.Stages & ResourceStages.Fragment) != 0) - { - fragResourceIds[fragResourceIdIndex] = gpuAddress; - fragResourceIdIndex++; - } - } - } - else - { - TextureBuffer[] bufferTextures = textureArray.GetBufferTextureRefs(); - - for (int i = 0; i < bufferTextures.Length; i++) - { - TextureBuffer bufferTexture = bufferTextures[i]; - (ulong gpuAddress, IntPtr nativePtr) = AddressForTextureBuffer(ref bufferTexture); - - MTLRenderStages renderStages = 0; - - if ((segment.Stages & ResourceStages.Vertex) != 0) - { - vertResourceIds[vertResourceIdIndex] = gpuAddress; - vertResourceIdIndex++; - - renderStages |= MTLRenderStages.RenderStageVertex; - } - - if ((segment.Stages & ResourceStages.Fragment) != 0) - { - fragResourceIds[fragResourceIdIndex] = gpuAddress; - fragResourceIdIndex++; - - renderStages |= MTLRenderStages.RenderStageFragment; - } - - AddResource(nativePtr, MTLResourceUsage.Read, renderStages, in bindings); - } - } - } - break; - case Constants.ImagesSetIndex: - if (!segment.IsArray) - { - for (int i = 0; i < count; i++) - { - int index = binding + i; - - ref ImageRef image = ref _currentState.ImageRefs[index]; - (ulong gpuAddress, IntPtr nativePtr) = AddressForImage(ref image); - - MTLRenderStages renderStages = 0; - - if ((segment.Stages & ResourceStages.Vertex) != 0) - { - vertResourceIds[vertResourceIdIndex] = gpuAddress; - vertResourceIdIndex++; - renderStages |= MTLRenderStages.RenderStageVertex; - } - - if ((segment.Stages & ResourceStages.Fragment) != 0) - { - fragResourceIds[fragResourceIdIndex] = gpuAddress; - fragResourceIdIndex++; - renderStages |= MTLRenderStages.RenderStageFragment; - } - - AddResource(nativePtr, MTLResourceUsage.Read | MTLResourceUsage.Write, renderStages, in bindings); - } - } - else - { - ImageArray imageArray = _currentState.ImageArrayRefs[binding].Array; - - if (segment.Type != ResourceType.BufferImage) - { - TextureRef[] images = imageArray.GetTextureRefs(); - - for (int i = 0; i < images.Length; i++) - { - TextureRef image = images[i]; - (ulong gpuAddress, IntPtr nativePtr) = AddressForTexture(ref image); - - MTLRenderStages renderStages = 0; - - if ((segment.Stages & ResourceStages.Vertex) != 0) - { - vertResourceIds[vertResourceIdIndex] = gpuAddress; - vertResourceIdIndex++; - renderStages |= MTLRenderStages.RenderStageVertex; - } - - if ((segment.Stages & ResourceStages.Fragment) != 0) - { - fragResourceIds[fragResourceIdIndex] = gpuAddress; - fragResourceIdIndex++; - renderStages |= MTLRenderStages.RenderStageFragment; - } - - AddResource(nativePtr, MTLResourceUsage.Read | MTLResourceUsage.Write, renderStages, in bindings); - } - } - else - { - TextureBuffer[] bufferImages = imageArray.GetBufferTextureRefs(); - - for (int i = 0; i < bufferImages.Length; i++) - { - TextureBuffer image = bufferImages[i]; - (ulong gpuAddress, IntPtr nativePtr) = AddressForTextureBuffer(ref image); - - MTLRenderStages renderStages = 0; - - if ((segment.Stages & ResourceStages.Vertex) != 0) - { - vertResourceIds[vertResourceIdIndex] = gpuAddress; - vertResourceIdIndex++; - renderStages |= MTLRenderStages.RenderStageVertex; - } - - if ((segment.Stages & ResourceStages.Fragment) != 0) - { - fragResourceIds[fragResourceIdIndex] = gpuAddress; - fragResourceIdIndex++; - renderStages |= MTLRenderStages.RenderStageFragment; - } - - AddResource(nativePtr, MTLResourceUsage.Read | MTLResourceUsage.Write, renderStages, in bindings); - } - } - } - break; - } - } - - if (program.ArgumentBufferSizes[setIndex] > 0) - { - vertArgBuffer.Holder.SetDataUnchecked(vertArgBuffer.Offset, MemoryMarshal.AsBytes(vertResourceIds)); - MTLBuffer mtlVertArgBuffer = _bufferManager.GetBuffer(vertArgBuffer.Handle, false).Get(_pipeline.Cbs).Value; - bindings.VertexBuffers.Add(new BufferResource(mtlVertArgBuffer, (uint)vertArgBuffer.Range.Offset, SetIndexToBindingIndex(setIndex))); - } - - if (program.FragArgumentBufferSizes[setIndex] > 0) - { - fragArgBuffer.Holder.SetDataUnchecked(fragArgBuffer.Offset, MemoryMarshal.AsBytes(fragResourceIds)); - MTLBuffer mtlFragArgBuffer = _bufferManager.GetBuffer(fragArgBuffer.Handle, false).Get(_pipeline.Cbs).Value; - bindings.FragmentBuffers.Add(new BufferResource(mtlFragArgBuffer, (uint)fragArgBuffer.Range.Offset, SetIndexToBindingIndex(setIndex))); - } - } - - private readonly void UpdateAndBind(Program program, uint setIndex, ref readonly ComputeEncoderBindings bindings) - { - ResourceBindingSegment[] bindingSegments = program.BindingSegments[setIndex]; - - if (bindingSegments.Length == 0) - { - return; - } - - ScopedTemporaryBuffer argBuffer = default; - - if (program.ArgumentBufferSizes[setIndex] > 0) - { - argBuffer = _bufferManager.ReserveOrCreate(_pipeline.Cbs, program.ArgumentBufferSizes[setIndex] * sizeof(ulong)); - } - - Span resourceIds = stackalloc ulong[program.ArgumentBufferSizes[setIndex]]; - int resourceIdIndex = 0; - - foreach (ResourceBindingSegment segment in bindingSegments) - { - int binding = segment.Binding; - int count = segment.Count; - - switch (setIndex) - { - case Constants.ConstantBuffersSetIndex: - for (int i = 0; i < count; i++) - { - int index = binding + i; - - ref BufferRef buffer = ref _currentState.UniformBufferRefs[index]; - (ulong gpuAddress, IntPtr nativePtr) = AddressForBuffer(ref buffer); - - if ((segment.Stages & ResourceStages.Compute) != 0) - { - AddResource(nativePtr, MTLResourceUsage.Read, in bindings); - bindings.Resources.Add(new Resource(new MTLResource(nativePtr), MTLResourceUsage.Read, 0)); - resourceIds[resourceIdIndex] = gpuAddress; - resourceIdIndex++; - } - } - break; - case Constants.StorageBuffersSetIndex: - for (int i = 0; i < count; i++) - { - int index = binding + i; - - ref BufferRef buffer = ref _currentState.StorageBufferRefs[index]; - (ulong gpuAddress, IntPtr nativePtr) = AddressForBuffer(ref buffer); - - if ((segment.Stages & ResourceStages.Compute) != 0) - { - AddResource(nativePtr, MTLResourceUsage.Read | MTLResourceUsage.Write, in bindings); - resourceIds[resourceIdIndex] = gpuAddress; - resourceIdIndex++; - } - } - break; - case Constants.TexturesSetIndex: - if (!segment.IsArray) - { - for (int i = 0; i < count; i++) - { - int index = binding + i; - - ref TextureRef texture = ref _currentState.TextureRefs[index]; - (ulong gpuAddress, IntPtr nativePtr) = AddressForTexture(ref texture); - - if ((segment.Stages & ResourceStages.Compute) != 0) - { - AddResource(nativePtr, MTLResourceUsage.Read, in bindings); - resourceIds[resourceIdIndex] = gpuAddress; - resourceIdIndex++; - - if (texture.Sampler != null) - { - resourceIds[resourceIdIndex] = texture.Sampler.Get(_pipeline.Cbs).Value.GpuResourceID._impl; - resourceIdIndex++; - } - } - } - } - else - { - TextureArray textureArray = _currentState.TextureArrayRefs[binding].Array; - - if (segment.Type != ResourceType.BufferTexture) - { - TextureRef[] textures = textureArray.GetTextureRefs(); - Auto[] samplers = new Auto[textures.Length]; - - for (int i = 0; i < textures.Length; i++) - { - TextureRef texture = textures[i]; - (ulong gpuAddress, IntPtr nativePtr) = AddressForTexture(ref texture); - - if ((segment.Stages & ResourceStages.Compute) != 0) - { - AddResource(nativePtr, MTLResourceUsage.Read, in bindings); - resourceIds[resourceIdIndex] = gpuAddress; - resourceIdIndex++; - - samplers[i] = texture.Sampler; - } - } - - foreach (Auto sampler in samplers) - { - if (sampler != null) - { - resourceIds[resourceIdIndex] = sampler.Get(_pipeline.Cbs).Value.GpuResourceID._impl; - resourceIdIndex++; - } - } - } - else - { - TextureBuffer[] bufferTextures = textureArray.GetBufferTextureRefs(); - - for (int i = 0; i < bufferTextures.Length; i++) - { - TextureBuffer bufferTexture = bufferTextures[i]; - (ulong gpuAddress, IntPtr nativePtr) = AddressForTextureBuffer(ref bufferTexture); - - if ((segment.Stages & ResourceStages.Compute) != 0) - { - AddResource(nativePtr, MTLResourceUsage.Read, in bindings); - resourceIds[resourceIdIndex] = gpuAddress; - resourceIdIndex++; - } - } - } - } - break; - case Constants.ImagesSetIndex: - if (!segment.IsArray) - { - for (int i = 0; i < count; i++) - { - int index = binding + i; - - ref ImageRef image = ref _currentState.ImageRefs[index]; - (ulong gpuAddress, IntPtr nativePtr) = AddressForImage(ref image); - - if ((segment.Stages & ResourceStages.Compute) != 0) - { - AddResource(nativePtr, MTLResourceUsage.Read | MTLResourceUsage.Write, in bindings); - resourceIds[resourceIdIndex] = gpuAddress; - resourceIdIndex++; - } - } - } - else - { - ImageArray imageArray = _currentState.ImageArrayRefs[binding].Array; - - if (segment.Type != ResourceType.BufferImage) - { - TextureRef[] images = imageArray.GetTextureRefs(); - - for (int i = 0; i < images.Length; i++) - { - TextureRef image = images[i]; - (ulong gpuAddress, IntPtr nativePtr) = AddressForTexture(ref image); - - if ((segment.Stages & ResourceStages.Compute) != 0) - { - AddResource(nativePtr, MTLResourceUsage.Read | MTLResourceUsage.Write, in bindings); - resourceIds[resourceIdIndex] = gpuAddress; - resourceIdIndex++; - } - } - } - else - { - TextureBuffer[] bufferImages = imageArray.GetBufferTextureRefs(); - - for (int i = 0; i < bufferImages.Length; i++) - { - TextureBuffer image = bufferImages[i]; - (ulong gpuAddress, IntPtr nativePtr) = AddressForTextureBuffer(ref image); - - if ((segment.Stages & ResourceStages.Compute) != 0) - { - AddResource(nativePtr, MTLResourceUsage.Read | MTLResourceUsage.Write, in bindings); - resourceIds[resourceIdIndex] = gpuAddress; - resourceIdIndex++; - } - } - } - } - break; - } - } - - if (program.ArgumentBufferSizes[setIndex] > 0) - { - argBuffer.Holder.SetDataUnchecked(argBuffer.Offset, MemoryMarshal.AsBytes(resourceIds)); - MTLBuffer mtlArgBuffer = _bufferManager.GetBuffer(argBuffer.Handle, false).Get(_pipeline.Cbs).Value; - bindings.Buffers.Add(new BufferResource(mtlArgBuffer, (uint)argBuffer.Range.Offset, SetIndexToBindingIndex(setIndex))); - } - } - - private static uint SetIndexToBindingIndex(uint setIndex) - { - return setIndex switch - { - Constants.ConstantBuffersSetIndex => Constants.ConstantBuffersIndex, - Constants.StorageBuffersSetIndex => Constants.StorageBuffersIndex, - Constants.TexturesSetIndex => Constants.TexturesIndex, - Constants.ImagesSetIndex => Constants.ImagesIndex, - _ => throw new NotImplementedException() - }; - } - - private readonly void SetCullMode(MTLRenderCommandEncoder renderCommandEncoder) - { - renderCommandEncoder.SetCullMode(_currentState.CullMode); - } - - private readonly void SetFrontFace(MTLRenderCommandEncoder renderCommandEncoder) - { - renderCommandEncoder.SetFrontFacingWinding(_currentState.Winding); - } - - private readonly void SetStencilRefValue(MTLRenderCommandEncoder renderCommandEncoder) - { - renderCommandEncoder.SetStencilReferenceValues((uint)_currentState.FrontRefValue, (uint)_currentState.BackRefValue); - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/EnumConversion.cs b/src/Ryujinx.Graphics.Metal/EnumConversion.cs deleted file mode 100644 index 7cfb5cbd4..000000000 --- a/src/Ryujinx.Graphics.Metal/EnumConversion.cs +++ /dev/null @@ -1,292 +0,0 @@ -using Ryujinx.Common.Logging; -using Ryujinx.Graphics.GAL; -using SharpMetal.Metal; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - static class EnumConversion - { - public static MTLSamplerAddressMode Convert(this AddressMode mode) - { - return mode switch - { - AddressMode.Clamp => MTLSamplerAddressMode.ClampToEdge, // TODO: Should be clamp. - AddressMode.Repeat => MTLSamplerAddressMode.Repeat, - AddressMode.MirrorClamp => MTLSamplerAddressMode.MirrorClampToEdge, // TODO: Should be mirror clamp. - AddressMode.MirroredRepeat => MTLSamplerAddressMode.MirrorRepeat, - AddressMode.ClampToBorder => MTLSamplerAddressMode.ClampToBorderColor, - AddressMode.ClampToEdge => MTLSamplerAddressMode.ClampToEdge, - AddressMode.MirrorClampToEdge => MTLSamplerAddressMode.MirrorClampToEdge, - AddressMode.MirrorClampToBorder => MTLSamplerAddressMode.ClampToBorderColor, // TODO: Should be mirror clamp to border. - _ => LogInvalidAndReturn(mode, nameof(AddressMode), MTLSamplerAddressMode.ClampToEdge) // TODO: Should be clamp. - }; - } - - public static MTLBlendFactor Convert(this BlendFactor factor) - { - return factor switch - { - BlendFactor.Zero or BlendFactor.ZeroGl => MTLBlendFactor.Zero, - BlendFactor.One or BlendFactor.OneGl => MTLBlendFactor.One, - BlendFactor.SrcColor or BlendFactor.SrcColorGl => MTLBlendFactor.SourceColor, - BlendFactor.OneMinusSrcColor or BlendFactor.OneMinusSrcColorGl => MTLBlendFactor.OneMinusSourceColor, - BlendFactor.SrcAlpha or BlendFactor.SrcAlphaGl => MTLBlendFactor.SourceAlpha, - BlendFactor.OneMinusSrcAlpha or BlendFactor.OneMinusSrcAlphaGl => MTLBlendFactor.OneMinusSourceAlpha, - BlendFactor.DstAlpha or BlendFactor.DstAlphaGl => MTLBlendFactor.DestinationAlpha, - BlendFactor.OneMinusDstAlpha or BlendFactor.OneMinusDstAlphaGl => MTLBlendFactor.OneMinusDestinationAlpha, - BlendFactor.DstColor or BlendFactor.DstColorGl => MTLBlendFactor.DestinationColor, - BlendFactor.OneMinusDstColor or BlendFactor.OneMinusDstColorGl => MTLBlendFactor.OneMinusDestinationColor, - BlendFactor.SrcAlphaSaturate or BlendFactor.SrcAlphaSaturateGl => MTLBlendFactor.SourceAlphaSaturated, - BlendFactor.Src1Color or BlendFactor.Src1ColorGl => MTLBlendFactor.Source1Color, - BlendFactor.OneMinusSrc1Color or BlendFactor.OneMinusSrc1ColorGl => MTLBlendFactor.OneMinusSource1Color, - BlendFactor.Src1Alpha or BlendFactor.Src1AlphaGl => MTLBlendFactor.Source1Alpha, - BlendFactor.OneMinusSrc1Alpha or BlendFactor.OneMinusSrc1AlphaGl => MTLBlendFactor.OneMinusSource1Alpha, - BlendFactor.ConstantColor => MTLBlendFactor.BlendColor, - BlendFactor.OneMinusConstantColor => MTLBlendFactor.OneMinusBlendColor, - BlendFactor.ConstantAlpha => MTLBlendFactor.BlendAlpha, - BlendFactor.OneMinusConstantAlpha => MTLBlendFactor.OneMinusBlendAlpha, - _ => LogInvalidAndReturn(factor, nameof(BlendFactor), MTLBlendFactor.Zero) - }; - } - - public static MTLBlendOperation Convert(this BlendOp op) - { - return op switch - { - BlendOp.Add or BlendOp.AddGl => MTLBlendOperation.Add, - BlendOp.Subtract or BlendOp.SubtractGl => MTLBlendOperation.Subtract, - BlendOp.ReverseSubtract or BlendOp.ReverseSubtractGl => MTLBlendOperation.ReverseSubtract, - BlendOp.Minimum => MTLBlendOperation.Min, - BlendOp.Maximum => MTLBlendOperation.Max, - _ => LogInvalidAndReturn(op, nameof(BlendOp), MTLBlendOperation.Add) - }; - } - - public static MTLCompareFunction Convert(this CompareOp op) - { - return op switch - { - CompareOp.Never or CompareOp.NeverGl => MTLCompareFunction.Never, - CompareOp.Less or CompareOp.LessGl => MTLCompareFunction.Less, - CompareOp.Equal or CompareOp.EqualGl => MTLCompareFunction.Equal, - CompareOp.LessOrEqual or CompareOp.LessOrEqualGl => MTLCompareFunction.LessEqual, - CompareOp.Greater or CompareOp.GreaterGl => MTLCompareFunction.Greater, - CompareOp.NotEqual or CompareOp.NotEqualGl => MTLCompareFunction.NotEqual, - CompareOp.GreaterOrEqual or CompareOp.GreaterOrEqualGl => MTLCompareFunction.GreaterEqual, - CompareOp.Always or CompareOp.AlwaysGl => MTLCompareFunction.Always, - _ => LogInvalidAndReturn(op, nameof(CompareOp), MTLCompareFunction.Never) - }; - } - - public static MTLCullMode Convert(this Face face) - { - return face switch - { - Face.Back => MTLCullMode.Back, - Face.Front => MTLCullMode.Front, - Face.FrontAndBack => MTLCullMode.None, - _ => LogInvalidAndReturn(face, nameof(Face), MTLCullMode.Back) - }; - } - - public static MTLWinding Convert(this FrontFace frontFace) - { - // The viewport is flipped vertically, therefore we need to switch the winding order as well - return frontFace switch - { - FrontFace.Clockwise => MTLWinding.CounterClockwise, - FrontFace.CounterClockwise => MTLWinding.Clockwise, - _ => LogInvalidAndReturn(frontFace, nameof(FrontFace), MTLWinding.Clockwise) - }; - } - - public static MTLIndexType Convert(this IndexType type) - { - return type switch - { - IndexType.UShort => MTLIndexType.UInt16, - IndexType.UInt => MTLIndexType.UInt32, - _ => LogInvalidAndReturn(type, nameof(IndexType), MTLIndexType.UInt16) - }; - } - - public static MTLLogicOperation Convert(this LogicalOp op) - { - return op switch - { - LogicalOp.Clear => MTLLogicOperation.Clear, - LogicalOp.And => MTLLogicOperation.And, - LogicalOp.AndReverse => MTLLogicOperation.AndReverse, - LogicalOp.Copy => MTLLogicOperation.Copy, - LogicalOp.AndInverted => MTLLogicOperation.AndInverted, - LogicalOp.Noop => MTLLogicOperation.Noop, - LogicalOp.Xor => MTLLogicOperation.Xor, - LogicalOp.Or => MTLLogicOperation.Or, - LogicalOp.Nor => MTLLogicOperation.Nor, - LogicalOp.Equiv => MTLLogicOperation.Equivalence, - LogicalOp.Invert => MTLLogicOperation.Invert, - LogicalOp.OrReverse => MTLLogicOperation.OrReverse, - LogicalOp.CopyInverted => MTLLogicOperation.CopyInverted, - LogicalOp.OrInverted => MTLLogicOperation.OrInverted, - LogicalOp.Nand => MTLLogicOperation.Nand, - LogicalOp.Set => MTLLogicOperation.Set, - _ => LogInvalidAndReturn(op, nameof(LogicalOp), MTLLogicOperation.And) - }; - } - - public static MTLSamplerMinMagFilter Convert(this MagFilter filter) - { - return filter switch - { - MagFilter.Nearest => MTLSamplerMinMagFilter.Nearest, - MagFilter.Linear => MTLSamplerMinMagFilter.Linear, - _ => LogInvalidAndReturn(filter, nameof(MagFilter), MTLSamplerMinMagFilter.Nearest) - }; - } - - public static (MTLSamplerMinMagFilter, MTLSamplerMipFilter) Convert(this MinFilter filter) - { - return filter switch - { - MinFilter.Nearest => (MTLSamplerMinMagFilter.Nearest, MTLSamplerMipFilter.Nearest), - MinFilter.Linear => (MTLSamplerMinMagFilter.Linear, MTLSamplerMipFilter.Linear), - MinFilter.NearestMipmapNearest => (MTLSamplerMinMagFilter.Nearest, MTLSamplerMipFilter.Nearest), - MinFilter.LinearMipmapNearest => (MTLSamplerMinMagFilter.Linear, MTLSamplerMipFilter.Nearest), - MinFilter.NearestMipmapLinear => (MTLSamplerMinMagFilter.Nearest, MTLSamplerMipFilter.Linear), - MinFilter.LinearMipmapLinear => (MTLSamplerMinMagFilter.Linear, MTLSamplerMipFilter.Linear), - _ => LogInvalidAndReturn(filter, nameof(MinFilter), (MTLSamplerMinMagFilter.Nearest, MTLSamplerMipFilter.Nearest)) - - }; - } - - public static MTLPrimitiveType Convert(this PrimitiveTopology topology) - { - return topology switch - { - PrimitiveTopology.Points => MTLPrimitiveType.Point, - PrimitiveTopology.Lines => MTLPrimitiveType.Line, - PrimitiveTopology.LineStrip => MTLPrimitiveType.LineStrip, - PrimitiveTopology.Triangles => MTLPrimitiveType.Triangle, - PrimitiveTopology.TriangleStrip => MTLPrimitiveType.TriangleStrip, - _ => LogInvalidAndReturn(topology, nameof(PrimitiveTopology), MTLPrimitiveType.Triangle) - }; - } - - public static MTLStencilOperation Convert(this StencilOp op) - { - return op switch - { - StencilOp.Keep or StencilOp.KeepGl => MTLStencilOperation.Keep, - StencilOp.Zero or StencilOp.ZeroGl => MTLStencilOperation.Zero, - StencilOp.Replace or StencilOp.ReplaceGl => MTLStencilOperation.Replace, - StencilOp.IncrementAndClamp or StencilOp.IncrementAndClampGl => MTLStencilOperation.IncrementClamp, - StencilOp.DecrementAndClamp or StencilOp.DecrementAndClampGl => MTLStencilOperation.DecrementClamp, - StencilOp.Invert or StencilOp.InvertGl => MTLStencilOperation.Invert, - StencilOp.IncrementAndWrap or StencilOp.IncrementAndWrapGl => MTLStencilOperation.IncrementWrap, - StencilOp.DecrementAndWrap or StencilOp.DecrementAndWrapGl => MTLStencilOperation.DecrementWrap, - _ => LogInvalidAndReturn(op, nameof(StencilOp), MTLStencilOperation.Keep) - }; - } - - public static MTLTextureType Convert(this Target target) - { - return target switch - { - Target.TextureBuffer => MTLTextureType.TextureBuffer, - Target.Texture1D => MTLTextureType.Type1D, - Target.Texture1DArray => MTLTextureType.Type1DArray, - Target.Texture2D => MTLTextureType.Type2D, - Target.Texture2DArray => MTLTextureType.Type2DArray, - Target.Texture2DMultisample => MTLTextureType.Type2DMultisample, - Target.Texture2DMultisampleArray => MTLTextureType.Type2DMultisampleArray, - Target.Texture3D => MTLTextureType.Type3D, - Target.Cubemap => MTLTextureType.Cube, - Target.CubemapArray => MTLTextureType.CubeArray, - _ => LogInvalidAndReturn(target, nameof(Target), MTLTextureType.Type2D) - }; - } - - public static MTLTextureSwizzle Convert(this SwizzleComponent swizzleComponent) - { - return swizzleComponent switch - { - SwizzleComponent.Zero => MTLTextureSwizzle.Zero, - SwizzleComponent.One => MTLTextureSwizzle.One, - SwizzleComponent.Red => MTLTextureSwizzle.Red, - SwizzleComponent.Green => MTLTextureSwizzle.Green, - SwizzleComponent.Blue => MTLTextureSwizzle.Blue, - SwizzleComponent.Alpha => MTLTextureSwizzle.Alpha, - _ => LogInvalidAndReturn(swizzleComponent, nameof(SwizzleComponent), MTLTextureSwizzle.Zero) - }; - } - - public static MTLVertexFormat Convert(this Format format) - { - return format switch - { - Format.R16Float => MTLVertexFormat.Half, - Format.R16G16Float => MTLVertexFormat.Half2, - Format.R16G16B16Float => MTLVertexFormat.Half3, - Format.R16G16B16A16Float => MTLVertexFormat.Half4, - Format.R32Float => MTLVertexFormat.Float, - Format.R32G32Float => MTLVertexFormat.Float2, - Format.R32G32B32Float => MTLVertexFormat.Float3, - Format.R11G11B10Float => MTLVertexFormat.FloatRG11B10, - Format.R32G32B32A32Float => MTLVertexFormat.Float4, - Format.R8Uint => MTLVertexFormat.UChar, - Format.R8G8Uint => MTLVertexFormat.UChar2, - Format.R8G8B8Uint => MTLVertexFormat.UChar3, - Format.R8G8B8A8Uint => MTLVertexFormat.UChar4, - Format.R16Uint => MTLVertexFormat.UShort, - Format.R16G16Uint => MTLVertexFormat.UShort2, - Format.R16G16B16Uint => MTLVertexFormat.UShort3, - Format.R16G16B16A16Uint => MTLVertexFormat.UShort4, - Format.R32Uint => MTLVertexFormat.UInt, - Format.R32G32Uint => MTLVertexFormat.UInt2, - Format.R32G32B32Uint => MTLVertexFormat.UInt3, - Format.R32G32B32A32Uint => MTLVertexFormat.UInt4, - Format.R8Sint => MTLVertexFormat.Char, - Format.R8G8Sint => MTLVertexFormat.Char2, - Format.R8G8B8Sint => MTLVertexFormat.Char3, - Format.R8G8B8A8Sint => MTLVertexFormat.Char4, - Format.R16Sint => MTLVertexFormat.Short, - Format.R16G16Sint => MTLVertexFormat.Short2, - Format.R16G16B16Sint => MTLVertexFormat.Short3, - Format.R16G16B16A16Sint => MTLVertexFormat.Short4, - Format.R32Sint => MTLVertexFormat.Int, - Format.R32G32Sint => MTLVertexFormat.Int2, - Format.R32G32B32Sint => MTLVertexFormat.Int3, - Format.R32G32B32A32Sint => MTLVertexFormat.Int4, - Format.R8Unorm => MTLVertexFormat.UCharNormalized, - Format.R8G8Unorm => MTLVertexFormat.UChar2Normalized, - Format.R8G8B8Unorm => MTLVertexFormat.UChar3Normalized, - Format.R8G8B8A8Unorm => MTLVertexFormat.UChar4Normalized, - Format.R16Unorm => MTLVertexFormat.UShortNormalized, - Format.R16G16Unorm => MTLVertexFormat.UShort2Normalized, - Format.R16G16B16Unorm => MTLVertexFormat.UShort3Normalized, - Format.R16G16B16A16Unorm => MTLVertexFormat.UShort4Normalized, - Format.R10G10B10A2Unorm => MTLVertexFormat.UInt1010102Normalized, - Format.R8Snorm => MTLVertexFormat.CharNormalized, - Format.R8G8Snorm => MTLVertexFormat.Char2Normalized, - Format.R8G8B8Snorm => MTLVertexFormat.Char3Normalized, - Format.R8G8B8A8Snorm => MTLVertexFormat.Char4Normalized, - Format.R16Snorm => MTLVertexFormat.ShortNormalized, - Format.R16G16Snorm => MTLVertexFormat.Short2Normalized, - Format.R16G16B16Snorm => MTLVertexFormat.Short3Normalized, - Format.R16G16B16A16Snorm => MTLVertexFormat.Short4Normalized, - Format.R10G10B10A2Snorm => MTLVertexFormat.Int1010102Normalized, - - _ => LogInvalidAndReturn(format, nameof(Format), MTLVertexFormat.Float4) - }; - } - - private static T2 LogInvalidAndReturn(T1 value, string name, T2 defaultValue = default) - { - Logger.Debug?.Print(LogClass.Gpu, $"Invalid {name} enum value: {value}."); - - return defaultValue; - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/FenceHolder.cs b/src/Ryujinx.Graphics.Metal/FenceHolder.cs deleted file mode 100644 index a8dd28c0d..000000000 --- a/src/Ryujinx.Graphics.Metal/FenceHolder.cs +++ /dev/null @@ -1,77 +0,0 @@ -using SharpMetal.Metal; -using System; -using System.Runtime.Versioning; -using System.Threading; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - class FenceHolder : IDisposable - { - private MTLCommandBuffer _fence; - private int _referenceCount; - private bool _disposed; - - public FenceHolder(MTLCommandBuffer fence) - { - _fence = fence; - _referenceCount = 1; - } - - public MTLCommandBuffer GetUnsafe() - { - return _fence; - } - - public bool TryGet(out MTLCommandBuffer fence) - { - int lastValue; - do - { - lastValue = _referenceCount; - - if (lastValue == 0) - { - fence = default; - return false; - } - } while (Interlocked.CompareExchange(ref _referenceCount, lastValue + 1, lastValue) != lastValue); - - fence = _fence; - return true; - } - - public MTLCommandBuffer Get() - { - Interlocked.Increment(ref _referenceCount); - return _fence; - } - - public void Put() - { - if (Interlocked.Decrement(ref _referenceCount) == 0) - { - _fence = default; - } - } - - public void Wait() - { - _fence.WaitUntilCompleted(); - } - - public bool IsSignaled() - { - return _fence.Status == MTLCommandBufferStatus.Completed; - } - - public void Dispose() - { - if (!_disposed) - { - Put(); - _disposed = true; - } - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/FormatConverter.cs b/src/Ryujinx.Graphics.Metal/FormatConverter.cs deleted file mode 100644 index e099187b8..000000000 --- a/src/Ryujinx.Graphics.Metal/FormatConverter.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System; -using System.Runtime.InteropServices; - -namespace Ryujinx.Graphics.Metal -{ - class FormatConverter - { - public static void ConvertD24S8ToD32FS8(Span output, ReadOnlySpan input) - { - const float UnormToFloat = 1f / 0xffffff; - - Span outputUint = MemoryMarshal.Cast(output); - ReadOnlySpan inputUint = MemoryMarshal.Cast(input); - - int i = 0; - - for (; i < inputUint.Length; i++) - { - uint depthStencil = inputUint[i]; - uint depth = depthStencil >> 8; - uint stencil = depthStencil & 0xff; - - int j = i * 2; - - outputUint[j] = (uint)BitConverter.SingleToInt32Bits(depth * UnormToFloat); - outputUint[j + 1] = stencil; - } - } - - public static void ConvertD32FS8ToD24S8(Span output, ReadOnlySpan input) - { - Span outputUint = MemoryMarshal.Cast(output); - ReadOnlySpan inputUint = MemoryMarshal.Cast(input); - - int i = 0; - - for (; i < inputUint.Length; i += 2) - { - float depth = BitConverter.Int32BitsToSingle((int)inputUint[i]); - uint stencil = inputUint[i + 1]; - uint depthStencil = (Math.Clamp((uint)(depth * 0xffffff), 0, 0xffffff) << 8) | (stencil & 0xff); - - int j = i >> 1; - - outputUint[j] = depthStencil; - } - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/FormatTable.cs b/src/Ryujinx.Graphics.Metal/FormatTable.cs deleted file mode 100644 index 10c8b435c..000000000 --- a/src/Ryujinx.Graphics.Metal/FormatTable.cs +++ /dev/null @@ -1,196 +0,0 @@ -using Ryujinx.Common.Logging; -using Ryujinx.Graphics.GAL; -using SharpMetal.Metal; -using System; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - static class FormatTable - { - private static readonly MTLPixelFormat[] _table; - - static FormatTable() - { - _table = new MTLPixelFormat[Enum.GetNames(typeof(Format)).Length]; - - Add(Format.R8Unorm, MTLPixelFormat.R8Unorm); - Add(Format.R8Snorm, MTLPixelFormat.R8Snorm); - Add(Format.R8Uint, MTLPixelFormat.R8Uint); - Add(Format.R8Sint, MTLPixelFormat.R8Sint); - Add(Format.R16Float, MTLPixelFormat.R16Float); - Add(Format.R16Unorm, MTLPixelFormat.R16Unorm); - Add(Format.R16Snorm, MTLPixelFormat.R16Snorm); - Add(Format.R16Uint, MTLPixelFormat.R16Uint); - Add(Format.R16Sint, MTLPixelFormat.R16Sint); - Add(Format.R32Float, MTLPixelFormat.R32Float); - Add(Format.R32Uint, MTLPixelFormat.R32Uint); - Add(Format.R32Sint, MTLPixelFormat.R32Sint); - Add(Format.R8G8Unorm, MTLPixelFormat.RG8Unorm); - Add(Format.R8G8Snorm, MTLPixelFormat.RG8Snorm); - Add(Format.R8G8Uint, MTLPixelFormat.RG8Uint); - Add(Format.R8G8Sint, MTLPixelFormat.RG8Sint); - Add(Format.R16G16Float, MTLPixelFormat.RG16Float); - Add(Format.R16G16Unorm, MTLPixelFormat.RG16Unorm); - Add(Format.R16G16Snorm, MTLPixelFormat.RG16Snorm); - Add(Format.R16G16Uint, MTLPixelFormat.RG16Uint); - Add(Format.R16G16Sint, MTLPixelFormat.RG16Sint); - Add(Format.R32G32Float, MTLPixelFormat.RG32Float); - Add(Format.R32G32Uint, MTLPixelFormat.RG32Uint); - Add(Format.R32G32Sint, MTLPixelFormat.RG32Sint); - // Add(Format.R8G8B8Unorm, MTLPixelFormat.R8G8B8Unorm); - // Add(Format.R8G8B8Snorm, MTLPixelFormat.R8G8B8Snorm); - // Add(Format.R8G8B8Uint, MTLPixelFormat.R8G8B8Uint); - // Add(Format.R8G8B8Sint, MTLPixelFormat.R8G8B8Sint); - // Add(Format.R16G16B16Float, MTLPixelFormat.R16G16B16Float); - // Add(Format.R16G16B16Unorm, MTLPixelFormat.R16G16B16Unorm); - // Add(Format.R16G16B16Snorm, MTLPixelFormat.R16G16B16SNorm); - // Add(Format.R16G16B16Uint, MTLPixelFormat.R16G16B16Uint); - // Add(Format.R16G16B16Sint, MTLPixelFormat.R16G16B16Sint); - // Add(Format.R32G32B32Float, MTLPixelFormat.R32G32B32Sfloat); - // Add(Format.R32G32B32Uint, MTLPixelFormat.R32G32B32Uint); - // Add(Format.R32G32B32Sint, MTLPixelFormat.R32G32B32Sint); - Add(Format.R8G8B8A8Unorm, MTLPixelFormat.RGBA8Unorm); - Add(Format.R8G8B8A8Snorm, MTLPixelFormat.RGBA8Snorm); - Add(Format.R8G8B8A8Uint, MTLPixelFormat.RGBA8Uint); - Add(Format.R8G8B8A8Sint, MTLPixelFormat.RGBA8Sint); - Add(Format.R16G16B16A16Float, MTLPixelFormat.RGBA16Float); - Add(Format.R16G16B16A16Unorm, MTLPixelFormat.RGBA16Unorm); - Add(Format.R16G16B16A16Snorm, MTLPixelFormat.RGBA16Snorm); - Add(Format.R16G16B16A16Uint, MTLPixelFormat.RGBA16Uint); - Add(Format.R16G16B16A16Sint, MTLPixelFormat.RGBA16Sint); - Add(Format.R32G32B32A32Float, MTLPixelFormat.RGBA32Float); - Add(Format.R32G32B32A32Uint, MTLPixelFormat.RGBA32Uint); - Add(Format.R32G32B32A32Sint, MTLPixelFormat.RGBA32Sint); - Add(Format.S8Uint, MTLPixelFormat.Stencil8); - Add(Format.D16Unorm, MTLPixelFormat.Depth16Unorm); - Add(Format.S8UintD24Unorm, MTLPixelFormat.Depth24UnormStencil8); - Add(Format.X8UintD24Unorm, MTLPixelFormat.Depth24UnormStencil8); - Add(Format.D32Float, MTLPixelFormat.Depth32Float); - Add(Format.D24UnormS8Uint, MTLPixelFormat.Depth24UnormStencil8); - Add(Format.D32FloatS8Uint, MTLPixelFormat.Depth32FloatStencil8); - Add(Format.R8G8B8A8Srgb, MTLPixelFormat.RGBA8UnormsRGB); - // Add(Format.R4G4Unorm, MTLPixelFormat.R4G4Unorm); - Add(Format.R4G4B4A4Unorm, MTLPixelFormat.RGBA8Unorm); - // Add(Format.R5G5B5X1Unorm, MTLPixelFormat.R5G5B5X1Unorm); - Add(Format.R5G5B5A1Unorm, MTLPixelFormat.BGR5A1Unorm); - Add(Format.R5G6B5Unorm, MTLPixelFormat.B5G6R5Unorm); - Add(Format.R10G10B10A2Unorm, MTLPixelFormat.RGB10A2Unorm); - Add(Format.R10G10B10A2Uint, MTLPixelFormat.RGB10A2Uint); - Add(Format.R11G11B10Float, MTLPixelFormat.RG11B10Float); - Add(Format.R9G9B9E5Float, MTLPixelFormat.RGB9E5Float); - Add(Format.Bc1RgbaUnorm, MTLPixelFormat.BC1RGBA); - Add(Format.Bc2Unorm, MTLPixelFormat.BC2RGBA); - Add(Format.Bc3Unorm, MTLPixelFormat.BC3RGBA); - Add(Format.Bc1RgbaSrgb, MTLPixelFormat.BC1RGBAsRGB); - Add(Format.Bc2Srgb, MTLPixelFormat.BC2RGBAsRGB); - Add(Format.Bc3Srgb, MTLPixelFormat.BC3RGBAsRGB); - Add(Format.Bc4Unorm, MTLPixelFormat.BC4RUnorm); - Add(Format.Bc4Snorm, MTLPixelFormat.BC4RSnorm); - Add(Format.Bc5Unorm, MTLPixelFormat.BC5RGUnorm); - Add(Format.Bc5Snorm, MTLPixelFormat.BC5RGSnorm); - Add(Format.Bc7Unorm, MTLPixelFormat.BC7RGBAUnorm); - Add(Format.Bc7Srgb, MTLPixelFormat.BC7RGBAUnormsRGB); - Add(Format.Bc6HSfloat, MTLPixelFormat.BC6HRGBFloat); - Add(Format.Bc6HUfloat, MTLPixelFormat.BC6HRGBUfloat); - Add(Format.Etc2RgbUnorm, MTLPixelFormat.ETC2RGB8); - // Add(Format.Etc2RgbaUnorm, MTLPixelFormat.ETC2RGBA8); - Add(Format.Etc2RgbPtaUnorm, MTLPixelFormat.ETC2RGB8A1); - Add(Format.Etc2RgbSrgb, MTLPixelFormat.ETC2RGB8sRGB); - // Add(Format.Etc2RgbaSrgb, MTLPixelFormat.ETC2RGBA8sRGB); - Add(Format.Etc2RgbPtaSrgb, MTLPixelFormat.ETC2RGB8A1sRGB); - // Add(Format.R8Uscaled, MTLPixelFormat.R8Uscaled); - // Add(Format.R8Sscaled, MTLPixelFormat.R8Sscaled); - // Add(Format.R16Uscaled, MTLPixelFormat.R16Uscaled); - // Add(Format.R16Sscaled, MTLPixelFormat.R16Sscaled); - // Add(Format.R32Uscaled, MTLPixelFormat.R32Uscaled); - // Add(Format.R32Sscaled, MTLPixelFormat.R32Sscaled); - // Add(Format.R8G8Uscaled, MTLPixelFormat.R8G8Uscaled); - // Add(Format.R8G8Sscaled, MTLPixelFormat.R8G8Sscaled); - // Add(Format.R16G16Uscaled, MTLPixelFormat.R16G16Uscaled); - // Add(Format.R16G16Sscaled, MTLPixelFormat.R16G16Sscaled); - // Add(Format.R32G32Uscaled, MTLPixelFormat.R32G32Uscaled); - // Add(Format.R32G32Sscaled, MTLPixelFormat.R32G32Sscaled); - // Add(Format.R8G8B8Uscaled, MTLPixelFormat.R8G8B8Uscaled); - // Add(Format.R8G8B8Sscaled, MTLPixelFormat.R8G8B8Sscaled); - // Add(Format.R16G16B16Uscaled, MTLPixelFormat.R16G16B16Uscaled); - // Add(Format.R16G16B16Sscaled, MTLPixelFormat.R16G16B16Sscaled); - // Add(Format.R32G32B32Uscaled, MTLPixelFormat.R32G32B32Uscaled); - // Add(Format.R32G32B32Sscaled, MTLPixelFormat.R32G32B32Sscaled); - // Add(Format.R8G8B8A8Uscaled, MTLPixelFormat.R8G8B8A8Uscaled); - // Add(Format.R8G8B8A8Sscaled, MTLPixelFormat.R8G8B8A8Sscaled); - // Add(Format.R16G16B16A16Uscaled, MTLPixelFormat.R16G16B16A16Uscaled); - // Add(Format.R16G16B16A16Sscaled, MTLPixelFormat.R16G16B16A16Sscaled); - // Add(Format.R32G32B32A32Uscaled, MTLPixelFormat.R32G32B32A32Uscaled); - // Add(Format.R32G32B32A32Sscaled, MTLPixelFormat.R32G32B32A32Sscaled); - // Add(Format.R10G10B10A2Snorm, MTLPixelFormat.A2B10G10R10SNormPack32); - // Add(Format.R10G10B10A2Sint, MTLPixelFormat.A2B10G10R10SintPack32); - // Add(Format.R10G10B10A2Uscaled, MTLPixelFormat.A2B10G10R10UscaledPack32); - // Add(Format.R10G10B10A2Sscaled, MTLPixelFormat.A2B10G10R10SscaledPack32); - Add(Format.Astc4x4Unorm, MTLPixelFormat.ASTC4x4LDR); - Add(Format.Astc5x4Unorm, MTLPixelFormat.ASTC5x4LDR); - Add(Format.Astc5x5Unorm, MTLPixelFormat.ASTC5x5LDR); - Add(Format.Astc6x5Unorm, MTLPixelFormat.ASTC6x5LDR); - Add(Format.Astc6x6Unorm, MTLPixelFormat.ASTC6x6LDR); - Add(Format.Astc8x5Unorm, MTLPixelFormat.ASTC8x5LDR); - Add(Format.Astc8x6Unorm, MTLPixelFormat.ASTC8x6LDR); - Add(Format.Astc8x8Unorm, MTLPixelFormat.ASTC8x8LDR); - Add(Format.Astc10x5Unorm, MTLPixelFormat.ASTC10x5LDR); - Add(Format.Astc10x6Unorm, MTLPixelFormat.ASTC10x6LDR); - Add(Format.Astc10x8Unorm, MTLPixelFormat.ASTC10x8LDR); - Add(Format.Astc10x10Unorm, MTLPixelFormat.ASTC10x10LDR); - Add(Format.Astc12x10Unorm, MTLPixelFormat.ASTC12x10LDR); - Add(Format.Astc12x12Unorm, MTLPixelFormat.ASTC12x12LDR); - Add(Format.Astc4x4Srgb, MTLPixelFormat.ASTC4x4sRGB); - Add(Format.Astc5x4Srgb, MTLPixelFormat.ASTC5x4sRGB); - Add(Format.Astc5x5Srgb, MTLPixelFormat.ASTC5x5sRGB); - Add(Format.Astc6x5Srgb, MTLPixelFormat.ASTC6x5sRGB); - Add(Format.Astc6x6Srgb, MTLPixelFormat.ASTC6x6sRGB); - Add(Format.Astc8x5Srgb, MTLPixelFormat.ASTC8x5sRGB); - Add(Format.Astc8x6Srgb, MTLPixelFormat.ASTC8x6sRGB); - Add(Format.Astc8x8Srgb, MTLPixelFormat.ASTC8x8sRGB); - Add(Format.Astc10x5Srgb, MTLPixelFormat.ASTC10x5sRGB); - Add(Format.Astc10x6Srgb, MTLPixelFormat.ASTC10x6sRGB); - Add(Format.Astc10x8Srgb, MTLPixelFormat.ASTC10x8sRGB); - Add(Format.Astc10x10Srgb, MTLPixelFormat.ASTC10x10sRGB); - Add(Format.Astc12x10Srgb, MTLPixelFormat.ASTC12x10sRGB); - Add(Format.Astc12x12Srgb, MTLPixelFormat.ASTC12x12sRGB); - Add(Format.B5G6R5Unorm, MTLPixelFormat.B5G6R5Unorm); - Add(Format.B5G5R5A1Unorm, MTLPixelFormat.BGR5A1Unorm); - Add(Format.A1B5G5R5Unorm, MTLPixelFormat.A1BGR5Unorm); - Add(Format.B8G8R8A8Unorm, MTLPixelFormat.BGRA8Unorm); - Add(Format.B8G8R8A8Srgb, MTLPixelFormat.BGRA8UnormsRGB); - } - - private static void Add(Format format, MTLPixelFormat mtlFormat) - { - _table[(int)format] = mtlFormat; - } - - public static MTLPixelFormat GetFormat(Format format) - { - MTLPixelFormat mtlFormat = _table[(int)format]; - - if (IsD24S8(format)) - { - if (!MTLDevice.CreateSystemDefaultDevice().Depth24Stencil8PixelFormatSupported) - { - mtlFormat = MTLPixelFormat.Depth32FloatStencil8; - } - } - - if (mtlFormat == MTLPixelFormat.Invalid) - { - Logger.Error?.PrintMsg(LogClass.Gpu, $"Format {format} is not supported by the host."); - } - - return mtlFormat; - } - - public static bool IsD24S8(Format format) - { - return format == Format.D24UnormS8Uint || format == Format.S8UintD24Unorm || format == Format.X8UintD24Unorm; - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/HardwareInfo.cs b/src/Ryujinx.Graphics.Metal/HardwareInfo.cs deleted file mode 100644 index f6a132f09..000000000 --- a/src/Ryujinx.Graphics.Metal/HardwareInfo.cs +++ /dev/null @@ -1,82 +0,0 @@ -using System; -using System.Runtime.InteropServices; - -namespace Ryujinx.Graphics.Metal -{ - static partial class HardwareInfoTools - { - - private readonly static IntPtr _kCFAllocatorDefault = IntPtr.Zero; - private readonly static UInt32 _kCFStringEncodingASCII = 0x0600; - private const string IOKit = "/System/Library/Frameworks/IOKit.framework/IOKit"; - private const string CoreFoundation = "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation"; - - [LibraryImport(IOKit, StringMarshalling = StringMarshalling.Utf8)] - private static partial IntPtr IOServiceMatching(string name); - - [LibraryImport(IOKit)] - private static partial IntPtr IOServiceGetMatchingService(IntPtr mainPort, IntPtr matching); - - [LibraryImport(IOKit)] - private static partial IntPtr IORegistryEntryCreateCFProperty(IntPtr entry, IntPtr key, IntPtr allocator, UInt32 options); - - [LibraryImport(CoreFoundation, StringMarshalling = StringMarshalling.Utf8)] - private static partial IntPtr CFStringCreateWithCString(IntPtr allocator, string cString, UInt32 encoding); - - [LibraryImport(CoreFoundation)] - [return: MarshalAs(UnmanagedType.U1)] - public static partial bool CFStringGetCString(IntPtr theString, IntPtr buffer, long bufferSizes, UInt32 encoding); - - [LibraryImport(CoreFoundation)] - public static partial IntPtr CFDataGetBytePtr(IntPtr theData); - - static string GetNameFromId(uint id) - { - return id switch - { - 0x1002 => "AMD", - 0x106B => "Apple", - 0x10DE => "NVIDIA", - 0x13B5 => "ARM", - 0x8086 => "Intel", - _ => $"0x{id:X}" - }; - } - - public static string GetVendor() - { - IntPtr serviceDict = IOServiceMatching("IOGPU"); - IntPtr service = IOServiceGetMatchingService(IntPtr.Zero, serviceDict); - IntPtr cfString = CFStringCreateWithCString(_kCFAllocatorDefault, "vendor-id", _kCFStringEncodingASCII); - IntPtr cfProperty = IORegistryEntryCreateCFProperty(service, cfString, _kCFAllocatorDefault, 0); - - byte[] buffer = new byte[4]; - IntPtr bufferPtr = CFDataGetBytePtr(cfProperty); - Marshal.Copy(bufferPtr, buffer, 0, buffer.Length); - - uint vendorId = BitConverter.ToUInt32(buffer); - - return GetNameFromId(vendorId); - } - - public static string GetModel() - { - IntPtr serviceDict = IOServiceMatching("IOGPU"); - IntPtr service = IOServiceGetMatchingService(IntPtr.Zero, serviceDict); - IntPtr cfString = CFStringCreateWithCString(_kCFAllocatorDefault, "model", _kCFStringEncodingASCII); - IntPtr cfProperty = IORegistryEntryCreateCFProperty(service, cfString, _kCFAllocatorDefault, 0); - - char[] buffer = new char[64]; - IntPtr bufferPtr = Marshal.AllocHGlobal(buffer.Length); - - if (CFStringGetCString(cfProperty, bufferPtr, buffer.Length, _kCFStringEncodingASCII)) - { - string model = Marshal.PtrToStringUTF8(bufferPtr); - Marshal.FreeHGlobal(bufferPtr); - return model; - } - - return string.Empty; - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/HashTableSlim.cs b/src/Ryujinx.Graphics.Metal/HashTableSlim.cs deleted file mode 100644 index 267acc6f4..000000000 --- a/src/Ryujinx.Graphics.Metal/HashTableSlim.cs +++ /dev/null @@ -1,143 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Runtime.CompilerServices; - -namespace Ryujinx.Graphics.Metal -{ - interface IRefEquatable - { - bool Equals(ref T other); - } - - class HashTableSlim where TKey : IRefEquatable - { - private const int TotalBuckets = 16; // Must be power of 2 - private const int TotalBucketsMask = TotalBuckets - 1; - - private struct Entry - { - public int Hash; - public TKey Key; - public TValue Value; - } - - private struct Bucket - { - public int Length; - public Entry[] Entries; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly Span AsSpan() - { - return Entries == null ? Span.Empty : Entries.AsSpan(0, Length); - } - } - - private readonly Bucket[] _hashTable = new Bucket[TotalBuckets]; - - public IEnumerable Keys - { - get - { - foreach (Bucket bucket in _hashTable) - { - for (int i = 0; i < bucket.Length; i++) - { - yield return bucket.Entries[i].Key; - } - } - } - } - - public IEnumerable Values - { - get - { - foreach (Bucket bucket in _hashTable) - { - for (int i = 0; i < bucket.Length; i++) - { - yield return bucket.Entries[i].Value; - } - } - } - } - - public void Add(ref TKey key, TValue value) - { - Entry entry = new() - { - Hash = key.GetHashCode(), - Key = key, - Value = value, - }; - - int hashCode = key.GetHashCode(); - int bucketIndex = hashCode & TotalBucketsMask; - - ref Bucket bucket = ref _hashTable[bucketIndex]; - if (bucket.Entries != null) - { - int index = bucket.Length; - - if (index >= bucket.Entries.Length) - { - Array.Resize(ref bucket.Entries, index + 1); - } - - bucket.Entries[index] = entry; - } - else - { - bucket.Entries = - [ - entry - ]; - } - - bucket.Length++; - } - - public bool Remove(ref TKey key) - { - int hashCode = key.GetHashCode(); - - ref Bucket bucket = ref _hashTable[hashCode & TotalBucketsMask]; - Span entries = bucket.AsSpan(); - for (int i = 0; i < entries.Length; i++) - { - ref Entry entry = ref entries[i]; - - if (entry.Hash == hashCode && entry.Key.Equals(ref key)) - { - entries[(i + 1)..].CopyTo(entries[i..]); - bucket.Length--; - - return true; - } - } - - return false; - } - - public bool TryGetValue(ref TKey key, out TValue value) - { - int hashCode = key.GetHashCode(); - - Span entries = _hashTable[hashCode & TotalBucketsMask].AsSpan(); - for (int i = 0; i < entries.Length; i++) - { - ref Entry entry = ref entries[i]; - - if (entry.Hash == hashCode && entry.Key.Equals(ref key)) - { - value = entry.Value; - return true; - } - } - - value = default; - return false; - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/HelperShader.cs b/src/Ryujinx.Graphics.Metal/HelperShader.cs deleted file mode 100644 index 2638a6eac..000000000 --- a/src/Ryujinx.Graphics.Metal/HelperShader.cs +++ /dev/null @@ -1,868 +0,0 @@ -using Ryujinx.Common; -using Ryujinx.Common.Logging; -using Ryujinx.Graphics.GAL; -using Ryujinx.Graphics.Shader; -using Ryujinx.Graphics.Shader.Translation; -using SharpMetal.Metal; -using System; -using System.Collections.Generic; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - class HelperShader : IDisposable - { - private const int ConvertElementsPerWorkgroup = 32 * 100; // Work group size of 32 times 100 elements. - private const string ShadersSourcePath = "/Ryujinx.Graphics.Metal/Shaders"; - private readonly MetalRenderer _renderer; - private readonly Pipeline _pipeline; - private MTLDevice _device; - - private readonly ISampler _samplerLinear; - private readonly ISampler _samplerNearest; - private readonly IProgram _programColorBlitF; - private readonly IProgram _programColorBlitI; - private readonly IProgram _programColorBlitU; - private readonly IProgram _programColorBlitMsF; - private readonly IProgram _programColorBlitMsI; - private readonly IProgram _programColorBlitMsU; - private readonly List _programsColorClearF = []; - private readonly List _programsColorClearI = []; - private readonly List _programsColorClearU = []; - private readonly IProgram _programDepthStencilClear; - private readonly IProgram _programStrideChange; - private readonly IProgram _programConvertD32S8ToD24S8; - private readonly IProgram _programConvertIndexBuffer; - private readonly IProgram _programDepthBlit; - private readonly IProgram _programDepthBlitMs; - private readonly IProgram _programStencilBlit; - private readonly IProgram _programStencilBlitMs; - - private readonly EncoderState _helperShaderState = new(); - - public HelperShader(MTLDevice device, MetalRenderer renderer, Pipeline pipeline) - { - _device = device; - _renderer = renderer; - _pipeline = pipeline; - - _samplerNearest = new SamplerHolder(renderer, _device, SamplerCreateInfo.Create(MinFilter.Nearest, MagFilter.Nearest)); - _samplerLinear = new SamplerHolder(renderer, _device, SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear)); - - ResourceLayout blitResourceLayout = new ResourceLayoutBuilder() - .Add(ResourceStages.Vertex, ResourceType.UniformBuffer, 0) - .Add(ResourceStages.Fragment, ResourceType.TextureAndSampler, 0).Build(); - - string blitSource = ReadMsl("Blit.metal"); - - string blitSourceF = blitSource.Replace("FORMAT", "float", StringComparison.Ordinal); - _programColorBlitF = new Program(renderer, device, [ - new ShaderSource(blitSourceF, ShaderStage.Fragment, TargetLanguage.Msl), - new ShaderSource(blitSourceF, ShaderStage.Vertex, TargetLanguage.Msl) - ], blitResourceLayout); - - string blitSourceI = blitSource.Replace("FORMAT", "int"); - _programColorBlitI = new Program(renderer, device, [ - new ShaderSource(blitSourceI, ShaderStage.Fragment, TargetLanguage.Msl), - new ShaderSource(blitSourceI, ShaderStage.Vertex, TargetLanguage.Msl) - ], blitResourceLayout); - - string blitSourceU = blitSource.Replace("FORMAT", "uint"); - _programColorBlitU = new Program(renderer, device, [ - new ShaderSource(blitSourceU, ShaderStage.Fragment, TargetLanguage.Msl), - new ShaderSource(blitSourceU, ShaderStage.Vertex, TargetLanguage.Msl) - ], blitResourceLayout); - - string blitMsSource = ReadMsl("BlitMs.metal"); - - string blitMsSourceF = blitMsSource.Replace("FORMAT", "float"); - _programColorBlitMsF = new Program(renderer, device, [ - new ShaderSource(blitMsSourceF, ShaderStage.Fragment, TargetLanguage.Msl), - new ShaderSource(blitMsSourceF, ShaderStage.Vertex, TargetLanguage.Msl) - ], blitResourceLayout); - - string blitMsSourceI = blitMsSource.Replace("FORMAT", "int"); - _programColorBlitMsI = new Program(renderer, device, [ - new ShaderSource(blitMsSourceI, ShaderStage.Fragment, TargetLanguage.Msl), - new ShaderSource(blitMsSourceI, ShaderStage.Vertex, TargetLanguage.Msl) - ], blitResourceLayout); - - string blitMsSourceU = blitMsSource.Replace("FORMAT", "uint"); - _programColorBlitMsU = new Program(renderer, device, [ - new ShaderSource(blitMsSourceU, ShaderStage.Fragment, TargetLanguage.Msl), - new ShaderSource(blitMsSourceU, ShaderStage.Vertex, TargetLanguage.Msl) - ], blitResourceLayout); - - ResourceLayout colorClearResourceLayout = new ResourceLayoutBuilder() - .Add(ResourceStages.Fragment, ResourceType.UniformBuffer, 0).Build(); - - string colorClearSource = ReadMsl("ColorClear.metal"); - - for (int i = 0; i < Constants.MaxColorAttachments; i++) - { - string crntSource = colorClearSource.Replace("COLOR_ATTACHMENT_INDEX", i.ToString()).Replace("FORMAT", "float"); - _programsColorClearF.Add(new Program(renderer, device, [ - new ShaderSource(crntSource, ShaderStage.Fragment, TargetLanguage.Msl), - new ShaderSource(crntSource, ShaderStage.Vertex, TargetLanguage.Msl) - ], colorClearResourceLayout)); - } - - for (int i = 0; i < Constants.MaxColorAttachments; i++) - { - string crntSource = colorClearSource.Replace("COLOR_ATTACHMENT_INDEX", i.ToString()).Replace("FORMAT", "int"); - _programsColorClearI.Add(new Program(renderer, device, [ - new ShaderSource(crntSource, ShaderStage.Fragment, TargetLanguage.Msl), - new ShaderSource(crntSource, ShaderStage.Vertex, TargetLanguage.Msl) - ], colorClearResourceLayout)); - } - - for (int i = 0; i < Constants.MaxColorAttachments; i++) - { - string crntSource = colorClearSource.Replace("COLOR_ATTACHMENT_INDEX", i.ToString()).Replace("FORMAT", "uint"); - _programsColorClearU.Add(new Program(renderer, device, [ - new ShaderSource(crntSource, ShaderStage.Fragment, TargetLanguage.Msl), - new ShaderSource(crntSource, ShaderStage.Vertex, TargetLanguage.Msl) - ], colorClearResourceLayout)); - } - - string depthStencilClearSource = ReadMsl("DepthStencilClear.metal"); - _programDepthStencilClear = new Program(renderer, device, [ - new ShaderSource(depthStencilClearSource, ShaderStage.Fragment, TargetLanguage.Msl), - new ShaderSource(depthStencilClearSource, ShaderStage.Vertex, TargetLanguage.Msl) - ], colorClearResourceLayout); - - ResourceLayout strideChangeResourceLayout = new ResourceLayoutBuilder() - .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0) - .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1) - .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2, true).Build(); - - string strideChangeSource = ReadMsl("ChangeBufferStride.metal"); - _programStrideChange = new Program(renderer, device, [ - new ShaderSource(strideChangeSource, ShaderStage.Compute, TargetLanguage.Msl) - ], strideChangeResourceLayout, new ComputeSize(64, 1, 1)); - - ResourceLayout convertD32S8ToD24S8ResourceLayout = new ResourceLayoutBuilder() - .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0) - .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1) - .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2, true).Build(); - - string convertD32S8ToD24S8Source = ReadMsl("ConvertD32S8ToD24S8.metal"); - _programConvertD32S8ToD24S8 = new Program(renderer, device, [ - new ShaderSource(convertD32S8ToD24S8Source, ShaderStage.Compute, TargetLanguage.Msl) - ], convertD32S8ToD24S8ResourceLayout, new ComputeSize(64, 1, 1)); - - ResourceLayout convertIndexBufferLayout = new ResourceLayoutBuilder() - .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1) - .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2, true) - .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 3).Build(); - - string convertIndexBufferSource = ReadMsl("ConvertIndexBuffer.metal"); - _programConvertIndexBuffer = new Program(renderer, device, [ - new ShaderSource(convertIndexBufferSource, ShaderStage.Compute, TargetLanguage.Msl) - ], convertIndexBufferLayout, new ComputeSize(16, 1, 1)); - - string depthBlitSource = ReadMsl("DepthBlit.metal"); - _programDepthBlit = new Program(renderer, device, [ - new ShaderSource(depthBlitSource, ShaderStage.Fragment, TargetLanguage.Msl), - new ShaderSource(blitSourceF, ShaderStage.Vertex, TargetLanguage.Msl) - ], blitResourceLayout); - - string depthBlitMsSource = ReadMsl("DepthBlitMs.metal"); - _programDepthBlitMs = new Program(renderer, device, [ - new ShaderSource(depthBlitMsSource, ShaderStage.Fragment, TargetLanguage.Msl), - new ShaderSource(blitSourceF, ShaderStage.Vertex, TargetLanguage.Msl) - ], blitResourceLayout); - - string stencilBlitSource = ReadMsl("StencilBlit.metal"); - _programStencilBlit = new Program(renderer, device, [ - new ShaderSource(stencilBlitSource, ShaderStage.Fragment, TargetLanguage.Msl), - new ShaderSource(blitSourceF, ShaderStage.Vertex, TargetLanguage.Msl) - ], blitResourceLayout); - - string stencilBlitMsSource = ReadMsl("StencilBlitMs.metal"); - _programStencilBlitMs = new Program(renderer, device, [ - new ShaderSource(stencilBlitMsSource, ShaderStage.Fragment, TargetLanguage.Msl), - new ShaderSource(blitSourceF, ShaderStage.Vertex, TargetLanguage.Msl) - ], blitResourceLayout); - } - - private static string ReadMsl(string fileName) - { - string msl = EmbeddedResources.ReadAllText(string.Join('/', ShadersSourcePath, fileName)); - -#pragma warning disable IDE0055 // Disable formatting - msl = msl.Replace("CONSTANT_BUFFERS_INDEX", $"{Constants.ConstantBuffersIndex}") - .Replace("STORAGE_BUFFERS_INDEX", $"{Constants.StorageBuffersIndex}") - .Replace("TEXTURES_INDEX", $"{Constants.TexturesIndex}") - .Replace("IMAGES_INDEX", $"{Constants.ImagesIndex}"); -#pragma warning restore IDE0055 - - return msl; - } - - public unsafe void BlitColor( - CommandBufferScoped cbs, - Texture src, - Texture dst, - Extents2D srcRegion, - Extents2D dstRegion, - bool linearFilter, - bool clear = false) - { - _pipeline.SwapState(_helperShaderState); - - const int RegionBufferSize = 16; - - ISampler sampler = linearFilter ? _samplerLinear : _samplerNearest; - - _pipeline.SetTextureAndSampler(ShaderStage.Fragment, 0, src, sampler); - - Span region = stackalloc float[RegionBufferSize / sizeof(float)]; - - region[0] = srcRegion.X1 / (float)src.Width; - region[1] = srcRegion.X2 / (float)src.Width; - region[2] = srcRegion.Y1 / (float)src.Height; - region[3] = srcRegion.Y2 / (float)src.Height; - - if (dstRegion.X1 > dstRegion.X2) - { - (region[0], region[1]) = (region[1], region[0]); - } - - if (dstRegion.Y1 > dstRegion.Y2) - { - (region[2], region[3]) = (region[3], region[2]); - } - - using ScopedTemporaryBuffer buffer = _renderer.BufferManager.ReserveOrCreate(cbs, RegionBufferSize); - buffer.Holder.SetDataUnchecked(buffer.Offset, region); - _pipeline.SetUniformBuffers([new BufferAssignment(0, buffer.Range)]); - - Rectangle rect = new( - MathF.Min(dstRegion.X1, dstRegion.X2), - MathF.Min(dstRegion.Y1, dstRegion.Y2), - MathF.Abs(dstRegion.X2 - dstRegion.X1), - MathF.Abs(dstRegion.Y2 - dstRegion.Y1)); - - Span viewports = stackalloc Viewport[16]; - - viewports[0] = new Viewport( - rect, - ViewportSwizzle.PositiveX, - ViewportSwizzle.PositiveY, - ViewportSwizzle.PositiveZ, - ViewportSwizzle.PositiveW, - 0f, - 1f); - - bool dstIsDepthOrStencil = dst.Info.Format.IsDepthOrStencil(); - - if (dstIsDepthOrStencil) - { - // TODO: Depth & stencil blit! - Logger.Warning?.PrintMsg(LogClass.Gpu, "Requested a depth or stencil blit!"); - _pipeline.SwapState(null); - return; - } - - string debugGroupName = "Blit Color "; - - if (src.Info.Target.IsMultisample()) - { - if (dst.Info.Format.IsSint()) - { - debugGroupName += "MS Int"; - _pipeline.SetProgram(_programColorBlitMsI); - } - else if (dst.Info.Format.IsUint()) - { - debugGroupName += "MS UInt"; - _pipeline.SetProgram(_programColorBlitMsU); - } - else - { - debugGroupName += "MS Float"; - _pipeline.SetProgram(_programColorBlitMsF); - } - } - else - { - if (dst.Info.Format.IsSint()) - { - debugGroupName += "Int"; - _pipeline.SetProgram(_programColorBlitI); - } - else if (dst.Info.Format.IsUint()) - { - debugGroupName += "UInt"; - _pipeline.SetProgram(_programColorBlitU); - } - else - { - debugGroupName += "Float"; - _pipeline.SetProgram(_programColorBlitF); - } - } - - int dstWidth = dst.Width; - int dstHeight = dst.Height; - - Span> scissors = stackalloc Rectangle[16]; - - scissors[0] = new Rectangle(0, 0, dstWidth, dstHeight); - - _pipeline.SetRenderTargets([dst], null); - _pipeline.SetScissors(scissors); - - _pipeline.SetClearLoadAction(clear); - - _pipeline.SetViewports(viewports); - _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); - _pipeline.Draw(4, 1, 0, 0, debugGroupName); - - // Cleanup - if (clear) - { - _pipeline.SetClearLoadAction(false); - } - - // Restore previous state - _pipeline.SwapState(null); - } - - public unsafe void BlitDepthStencil( - CommandBufferScoped cbs, - Texture src, - Texture dst, - Extents2D srcRegion, - Extents2D dstRegion) - { - _pipeline.SwapState(_helperShaderState); - - const int RegionBufferSize = 16; - - Span region = stackalloc float[RegionBufferSize / sizeof(float)]; - - region[0] = srcRegion.X1 / (float)src.Width; - region[1] = srcRegion.X2 / (float)src.Width; - region[2] = srcRegion.Y1 / (float)src.Height; - region[3] = srcRegion.Y2 / (float)src.Height; - - if (dstRegion.X1 > dstRegion.X2) - { - (region[0], region[1]) = (region[1], region[0]); - } - - if (dstRegion.Y1 > dstRegion.Y2) - { - (region[2], region[3]) = (region[3], region[2]); - } - - using ScopedTemporaryBuffer buffer = _renderer.BufferManager.ReserveOrCreate(cbs, RegionBufferSize); - buffer.Holder.SetDataUnchecked(buffer.Offset, region); - _pipeline.SetUniformBuffers([new BufferAssignment(0, buffer.Range)]); - - Span viewports = stackalloc Viewport[16]; - - Rectangle rect = new( - MathF.Min(dstRegion.X1, dstRegion.X2), - MathF.Min(dstRegion.Y1, dstRegion.Y2), - MathF.Abs(dstRegion.X2 - dstRegion.X1), - MathF.Abs(dstRegion.Y2 - dstRegion.Y1)); - - viewports[0] = new Viewport( - rect, - ViewportSwizzle.PositiveX, - ViewportSwizzle.PositiveY, - ViewportSwizzle.PositiveZ, - ViewportSwizzle.PositiveW, - 0f, - 1f); - - int dstWidth = dst.Width; - int dstHeight = dst.Height; - - Span> scissors = stackalloc Rectangle[16]; - - scissors[0] = new Rectangle(0, 0, dstWidth, dstHeight); - - _pipeline.SetRenderTargets([], dst); - _pipeline.SetScissors(scissors); - _pipeline.SetViewports(viewports); - _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); - - if (src.Info.Format is - Format.D16Unorm or - Format.D32Float or - Format.X8UintD24Unorm or - Format.D24UnormS8Uint or - Format.D32FloatS8Uint or - Format.S8UintD24Unorm) - { - Texture depthTexture = CreateDepthOrStencilView(src, DepthStencilMode.Depth); - - BlitDepthStencilDraw(depthTexture, isDepth: true); - - if (depthTexture != src) - { - depthTexture.Release(); - } - } - - if (src.Info.Format is - Format.S8Uint or - Format.D24UnormS8Uint or - Format.D32FloatS8Uint or - Format.S8UintD24Unorm) - { - Texture stencilTexture = CreateDepthOrStencilView(src, DepthStencilMode.Stencil); - - BlitDepthStencilDraw(stencilTexture, isDepth: false); - - if (stencilTexture != src) - { - stencilTexture.Release(); - } - } - - // Restore previous state - _pipeline.SwapState(null); - } - - private static Texture CreateDepthOrStencilView(Texture depthStencilTexture, DepthStencilMode depthStencilMode) - { - if (depthStencilTexture.Info.DepthStencilMode == depthStencilMode) - { - return depthStencilTexture; - } - - return (Texture)depthStencilTexture.CreateView(new TextureCreateInfo( - depthStencilTexture.Info.Width, - depthStencilTexture.Info.Height, - depthStencilTexture.Info.Depth, - depthStencilTexture.Info.Levels, - depthStencilTexture.Info.Samples, - depthStencilTexture.Info.BlockWidth, - depthStencilTexture.Info.BlockHeight, - depthStencilTexture.Info.BytesPerPixel, - depthStencilTexture.Info.Format, - depthStencilMode, - depthStencilTexture.Info.Target, - SwizzleComponent.Red, - SwizzleComponent.Green, - SwizzleComponent.Blue, - SwizzleComponent.Alpha), 0, 0); - } - - private void BlitDepthStencilDraw(Texture src, bool isDepth) - { - // TODO: Check this https://github.com/Ryujinx/Ryujinx/pull/5003/ - _pipeline.SetTextureAndSampler(ShaderStage.Fragment, 0, src, _samplerNearest); - - string debugGroupName; - - if (isDepth) - { - debugGroupName = "Depth Blit"; - _pipeline.SetProgram(src.Info.Target.IsMultisample() ? _programDepthBlitMs : _programDepthBlit); - _pipeline.SetDepthTest(new DepthTestDescriptor(true, true, CompareOp.Always)); - } - else - { - debugGroupName = "Stencil Blit"; - _pipeline.SetProgram(src.Info.Target.IsMultisample() ? _programStencilBlitMs : _programStencilBlit); - _pipeline.SetStencilTest(CreateStencilTestDescriptor(true)); - } - - _pipeline.Draw(4, 1, 0, 0, debugGroupName); - - if (isDepth) - { - _pipeline.SetDepthTest(new DepthTestDescriptor(false, false, CompareOp.Always)); - } - else - { - _pipeline.SetStencilTest(CreateStencilTestDescriptor(false)); - } - } - - public unsafe void DrawTexture( - ITexture src, - ISampler srcSampler, - Extents2DF srcRegion, - Extents2DF dstRegion) - { - // Save current state - PredrawState state = _pipeline.SavePredrawState(); - - _pipeline.SetFaceCulling(false, Face.Front); - _pipeline.SetStencilTest(new StencilTestDescriptor()); - _pipeline.SetDepthTest(new DepthTestDescriptor()); - - const int RegionBufferSize = 16; - - _pipeline.SetTextureAndSampler(ShaderStage.Fragment, 0, src, srcSampler); - - Span region = stackalloc float[RegionBufferSize / sizeof(float)]; - - region[0] = srcRegion.X1 / src.Width; - region[1] = srcRegion.X2 / src.Width; - region[2] = srcRegion.Y1 / src.Height; - region[3] = srcRegion.Y2 / src.Height; - - if (dstRegion.X1 > dstRegion.X2) - { - (region[0], region[1]) = (region[1], region[0]); - } - - if (dstRegion.Y1 > dstRegion.Y2) - { - (region[2], region[3]) = (region[3], region[2]); - } - - BufferHandle bufferHandle = _renderer.BufferManager.CreateWithHandle(RegionBufferSize); - _renderer.BufferManager.SetData(bufferHandle, 0, region); - _pipeline.SetUniformBuffers([new BufferAssignment(0, new BufferRange(bufferHandle, 0, RegionBufferSize))]); - - Span viewports = stackalloc Viewport[16]; - - Rectangle rect = new( - MathF.Min(dstRegion.X1, dstRegion.X2), - MathF.Min(dstRegion.Y1, dstRegion.Y2), - MathF.Abs(dstRegion.X2 - dstRegion.X1), - MathF.Abs(dstRegion.Y2 - dstRegion.Y1)); - - viewports[0] = new Viewport( - rect, - ViewportSwizzle.PositiveX, - ViewportSwizzle.PositiveY, - ViewportSwizzle.PositiveZ, - ViewportSwizzle.PositiveW, - 0f, - 1f); - - _pipeline.SetProgram(_programColorBlitF); - _pipeline.SetViewports(viewports); - _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); - _pipeline.Draw(4, 1, 0, 0, "Draw Texture"); - - _renderer.BufferManager.Delete(bufferHandle); - - // Restore previous state - _pipeline.RestorePredrawState(state); - } - - public void ConvertI8ToI16(CommandBufferScoped cbs, BufferHolder src, BufferHolder dst, int srcOffset, int size) - { - ChangeStride(cbs, src, dst, srcOffset, size, 1, 2); - } - - public unsafe void ChangeStride( - CommandBufferScoped cbs, - BufferHolder src, - BufferHolder dst, - int srcOffset, - int size, - int stride, - int newStride) - { - int elems = size / stride; - - Auto srcBuffer = src.GetBuffer(); - Auto dstBuffer = dst.GetBuffer(); - - const int ParamsBufferSize = 4 * sizeof(int); - - // Save current state - _pipeline.SwapState(_helperShaderState); - - Span shaderParams = stackalloc int[ParamsBufferSize / sizeof(int)]; - - shaderParams[0] = stride; - shaderParams[1] = newStride; - shaderParams[2] = size; - shaderParams[3] = srcOffset; - - using ScopedTemporaryBuffer buffer = _renderer.BufferManager.ReserveOrCreate(cbs, ParamsBufferSize); - buffer.Holder.SetDataUnchecked(buffer.Offset, shaderParams); - _pipeline.SetUniformBuffers([new BufferAssignment(0, buffer.Range)]); - - Span> sbRanges = new Auto[2]; - - sbRanges[0] = srcBuffer; - sbRanges[1] = dstBuffer; - _pipeline.SetStorageBuffers(1, sbRanges); - - _pipeline.SetProgram(_programStrideChange); - _pipeline.DispatchCompute(1 + elems / ConvertElementsPerWorkgroup, 1, 1, "Change Stride"); - - // Restore previous state - _pipeline.SwapState(null); - } - - public unsafe void ConvertD32S8ToD24S8(CommandBufferScoped cbs, BufferHolder src, Auto dstBuffer, int pixelCount, int dstOffset) - { - int inSize = pixelCount * 2 * sizeof(int); - - Auto srcBuffer = src.GetBuffer(); - - const int ParamsBufferSize = sizeof(int) * 2; - - // Save current state - _pipeline.SwapState(_helperShaderState); - - Span shaderParams = stackalloc int[2]; - - shaderParams[0] = pixelCount; - shaderParams[1] = dstOffset; - - using ScopedTemporaryBuffer buffer = _renderer.BufferManager.ReserveOrCreate(cbs, ParamsBufferSize); - buffer.Holder.SetDataUnchecked(buffer.Offset, shaderParams); - _pipeline.SetUniformBuffers([new BufferAssignment(0, buffer.Range)]); - - Span> sbRanges = new Auto[2]; - - sbRanges[0] = srcBuffer; - sbRanges[1] = dstBuffer; - _pipeline.SetStorageBuffers(1, sbRanges); - - _pipeline.SetProgram(_programConvertD32S8ToD24S8); - _pipeline.DispatchCompute(1 + inSize / ConvertElementsPerWorkgroup, 1, 1, "D32S8 to D24S8 Conversion"); - - // Restore previous state - _pipeline.SwapState(null); - } - - public void ConvertIndexBuffer( - CommandBufferScoped cbs, - BufferHolder src, - BufferHolder dst, - IndexBufferPattern pattern, - int indexSize, - int srcOffset, - int indexCount) - { - // TODO: Support conversion with primitive restart enabled. - - int primitiveCount = pattern.GetPrimitiveCount(indexCount); - int outputIndexSize = 4; - - Auto srcBuffer = src.GetBuffer(); - Auto dstBuffer = dst.GetBuffer(); - - const int ParamsBufferSize = 16 * sizeof(int); - - // Save current state - _pipeline.SwapState(_helperShaderState); - - Span shaderParams = stackalloc int[ParamsBufferSize / sizeof(int)]; - - shaderParams[8] = pattern.PrimitiveVertices; - shaderParams[9] = pattern.PrimitiveVerticesOut; - shaderParams[10] = indexSize; - shaderParams[11] = outputIndexSize; - shaderParams[12] = pattern.BaseIndex; - shaderParams[13] = pattern.IndexStride; - shaderParams[14] = srcOffset; - shaderParams[15] = primitiveCount; - - pattern.OffsetIndex.CopyTo(shaderParams[..pattern.OffsetIndex.Length]); - - using ScopedTemporaryBuffer patternScoped = _renderer.BufferManager.ReserveOrCreate(cbs, ParamsBufferSize); - patternScoped.Holder.SetDataUnchecked(patternScoped.Offset, shaderParams); - - Span> sbRanges = new Auto[2]; - - sbRanges[0] = srcBuffer; - sbRanges[1] = dstBuffer; - _pipeline.SetStorageBuffers(1, sbRanges); - _pipeline.SetStorageBuffers([new BufferAssignment(3, patternScoped.Range)]); - - _pipeline.SetProgram(_programConvertIndexBuffer); - _pipeline.DispatchCompute(BitUtils.DivRoundUp(primitiveCount, 16), 1, 1, "Convert Index Buffer"); - - // Restore previous state - _pipeline.SwapState(null); - } - - public unsafe void ClearColor( - int index, - ReadOnlySpan clearColor, - uint componentMask, - int dstWidth, - int dstHeight, - Format format) - { - // Keep original scissor - DirtyFlags clearFlags = DirtyFlags.All & (~DirtyFlags.Scissors); - - // Save current state - EncoderState originalState = _pipeline.SwapState(_helperShaderState, clearFlags, false); - - // Inherit some state without fully recreating render pipeline. - RenderTargetCopy save = _helperShaderState.InheritForClear(originalState, false, index); - - const int ClearColorBufferSize = 16; - - // TODO: Flush - - using ScopedTemporaryBuffer buffer = _renderer.BufferManager.ReserveOrCreate(_pipeline.Cbs, ClearColorBufferSize); - buffer.Holder.SetDataUnchecked(buffer.Offset, clearColor); - _pipeline.SetUniformBuffers([new BufferAssignment(0, buffer.Range)]); - - Span viewports = stackalloc Viewport[16]; - - // TODO: Set exact viewport! - viewports[0] = new Viewport( - new Rectangle(0, 0, dstWidth, dstHeight), - ViewportSwizzle.PositiveX, - ViewportSwizzle.PositiveY, - ViewportSwizzle.PositiveZ, - ViewportSwizzle.PositiveW, - 0f, - 1f); - - Span componentMasks = stackalloc uint[index + 1]; - componentMasks[index] = componentMask; - - string debugGroupName = "Clear Color "; - - if (format.IsSint()) - { - debugGroupName += "Int"; - _pipeline.SetProgram(_programsColorClearI[index]); - } - else if (format.IsUint()) - { - debugGroupName += "UInt"; - _pipeline.SetProgram(_programsColorClearU[index]); - } - else - { - debugGroupName += "Float"; - _pipeline.SetProgram(_programsColorClearF[index]); - } - - _pipeline.SetBlendState(index, new BlendDescriptor()); - _pipeline.SetFaceCulling(false, Face.Front); - _pipeline.SetDepthTest(new DepthTestDescriptor(false, false, CompareOp.Always)); - _pipeline.SetRenderTargetColorMasks(componentMasks); - _pipeline.SetViewports(viewports); - _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); - _pipeline.Draw(4, 1, 0, 0, debugGroupName); - - // Restore previous state - _pipeline.SwapState(null, clearFlags, false); - - _helperShaderState.Restore(save); - } - - public unsafe void ClearDepthStencil( - float depthValue, - bool depthMask, - int stencilValue, - int stencilMask, - int dstWidth, - int dstHeight) - { - // Keep original scissor - DirtyFlags clearFlags = DirtyFlags.All & (~DirtyFlags.Scissors); - MTLScissorRect[] helperScissors = _helperShaderState.Scissors; - - // Save current state - EncoderState originalState = _pipeline.SwapState(_helperShaderState, clearFlags, false); - - // Inherit some state without fully recreating render pipeline. - RenderTargetCopy save = _helperShaderState.InheritForClear(originalState, true); - - const int ClearDepthBufferSize = 16; - - using ScopedTemporaryBuffer buffer = _renderer.BufferManager.ReserveOrCreate(_pipeline.Cbs, ClearDepthBufferSize); - buffer.Holder.SetDataUnchecked(buffer.Offset, new ReadOnlySpan(ref depthValue)); - _pipeline.SetUniformBuffers([new BufferAssignment(0, buffer.Range)]); - - Span viewports = stackalloc Viewport[1]; - - viewports[0] = new Viewport( - new Rectangle(0, 0, dstWidth, dstHeight), - ViewportSwizzle.PositiveX, - ViewportSwizzle.PositiveY, - ViewportSwizzle.PositiveZ, - ViewportSwizzle.PositiveW, - 0f, - 1f); - - _pipeline.SetProgram(_programDepthStencilClear); - _pipeline.SetFaceCulling(false, Face.Front); - _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); - _pipeline.SetViewports(viewports); - _pipeline.SetDepthTest(new DepthTestDescriptor(true, depthMask, CompareOp.Always)); - _pipeline.SetStencilTest(CreateStencilTestDescriptor(stencilMask != 0, stencilValue, 0xFF, stencilMask)); - _pipeline.Draw(4, 1, 0, 0, "Clear Depth Stencil"); - - // Cleanup - _pipeline.SetDepthTest(new DepthTestDescriptor(false, false, CompareOp.Always)); - _pipeline.SetStencilTest(CreateStencilTestDescriptor(false)); - - // Restore previous state - _pipeline.SwapState(null, clearFlags, false); - - _helperShaderState.Restore(save); - } - - private static StencilTestDescriptor CreateStencilTestDescriptor( - bool enabled, - int refValue = 0, - int compareMask = 0xff, - int writeMask = 0xff) - { - return new StencilTestDescriptor( - enabled, - CompareOp.Always, - StencilOp.Replace, - StencilOp.Replace, - StencilOp.Replace, - refValue, - compareMask, - writeMask, - CompareOp.Always, - StencilOp.Replace, - StencilOp.Replace, - StencilOp.Replace, - refValue, - compareMask, - writeMask); - } - - public void Dispose() - { - _programColorBlitF.Dispose(); - _programColorBlitI.Dispose(); - _programColorBlitU.Dispose(); - _programColorBlitMsF.Dispose(); - _programColorBlitMsI.Dispose(); - _programColorBlitMsU.Dispose(); - - foreach (IProgram programColorClear in _programsColorClearF) - { - programColorClear.Dispose(); - } - - foreach (IProgram programColorClear in _programsColorClearU) - { - programColorClear.Dispose(); - } - - foreach (IProgram programColorClear in _programsColorClearI) - { - programColorClear.Dispose(); - } - - _programDepthStencilClear.Dispose(); - _pipeline.Dispose(); - _samplerLinear.Dispose(); - _samplerNearest.Dispose(); - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/IdList.cs b/src/Ryujinx.Graphics.Metal/IdList.cs deleted file mode 100644 index 72c9d5fcc..000000000 --- a/src/Ryujinx.Graphics.Metal/IdList.cs +++ /dev/null @@ -1,121 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Ryujinx.Graphics.Metal -{ - class IdList where T : class - { - private readonly List _list; - private int _freeMin; - - public IdList() - { - _list = []; - _freeMin = 0; - } - - public int Add(T value) - { - int id; - int count = _list.Count; - id = _list.IndexOf(null, _freeMin); - - if ((uint)id < (uint)count) - { - _list[id] = value; - } - else - { - id = count; - _freeMin = id + 1; - - _list.Add(value); - } - - return id + 1; - } - - public void Remove(int id) - { - id--; - - int count = _list.Count; - - if ((uint)id >= (uint)count) - { - return; - } - - if (id + 1 == count) - { - // Trim unused items. - int removeIndex = id; - - while (removeIndex > 0 && _list[removeIndex - 1] == null) - { - removeIndex--; - } - - _list.RemoveRange(removeIndex, count - removeIndex); - - if (_freeMin > removeIndex) - { - _freeMin = removeIndex; - } - } - else - { - _list[id] = null; - - if (_freeMin > id) - { - _freeMin = id; - } - } - } - - public bool TryGetValue(int id, out T value) - { - id--; - - try - { - if ((uint)id < (uint)_list.Count) - { - value = _list[id]; - return value != null; - } - - value = null; - return false; - } - catch (ArgumentOutOfRangeException) - { - value = null; - return false; - } - catch (IndexOutOfRangeException) - { - value = null; - return false; - } - } - - public void Clear() - { - _list.Clear(); - _freeMin = 0; - } - - public IEnumerator GetEnumerator() - { - for (int i = 0; i < _list.Count; i++) - { - if (_list[i] != null) - { - yield return _list[i]; - } - } - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/ImageArray.cs b/src/Ryujinx.Graphics.Metal/ImageArray.cs deleted file mode 100644 index 9fa0df09d..000000000 --- a/src/Ryujinx.Graphics.Metal/ImageArray.cs +++ /dev/null @@ -1,74 +0,0 @@ -using Ryujinx.Graphics.GAL; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - internal class ImageArray : IImageArray - { - private readonly TextureRef[] _textureRefs; - private readonly TextureBuffer[] _bufferTextureRefs; - - private readonly bool _isBuffer; - private readonly Pipeline _pipeline; - - public ImageArray(int size, bool isBuffer, Pipeline pipeline) - { - if (isBuffer) - { - _bufferTextureRefs = new TextureBuffer[size]; - } - else - { - _textureRefs = new TextureRef[size]; - } - - _isBuffer = isBuffer; - _pipeline = pipeline; - } - - public void SetImages(int index, ITexture[] images) - { - for (int i = 0; i < images.Length; i++) - { - ITexture image = images[i]; - - if (image is TextureBuffer textureBuffer) - { - _bufferTextureRefs[index + i] = textureBuffer; - } - else if (image is Texture texture) - { - _textureRefs[index + i].Storage = texture; - } - else if (!_isBuffer) - { - _textureRefs[index + i].Storage = null; - } - else - { - _bufferTextureRefs[index + i] = null; - } - } - - SetDirty(); - } - - public TextureRef[] GetTextureRefs() - { - return _textureRefs; - } - - public TextureBuffer[] GetBufferTextureRefs() - { - return _bufferTextureRefs; - } - - private void SetDirty() - { - _pipeline.DirtyImages(); - } - - public void Dispose() { } - } -} diff --git a/src/Ryujinx.Graphics.Metal/IndexBufferPattern.cs b/src/Ryujinx.Graphics.Metal/IndexBufferPattern.cs deleted file mode 100644 index 24e3222fe..000000000 --- a/src/Ryujinx.Graphics.Metal/IndexBufferPattern.cs +++ /dev/null @@ -1,118 +0,0 @@ -using Ryujinx.Graphics.GAL; -using System; -using System.Runtime.InteropServices; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - internal class IndexBufferPattern : IDisposable - { - public int PrimitiveVertices { get; } - public int PrimitiveVerticesOut { get; } - public int BaseIndex { get; } - public int[] OffsetIndex { get; } - public int IndexStride { get; } - public bool RepeatStart { get; } - - private readonly MetalRenderer _renderer; - private int _currentSize; - private BufferHandle _repeatingBuffer; - - public IndexBufferPattern(MetalRenderer renderer, - int primitiveVertices, - int primitiveVerticesOut, - int baseIndex, - int[] offsetIndex, - int indexStride, - bool repeatStart) - { - PrimitiveVertices = primitiveVertices; - PrimitiveVerticesOut = primitiveVerticesOut; - BaseIndex = baseIndex; - OffsetIndex = offsetIndex; - IndexStride = indexStride; - RepeatStart = repeatStart; - - _renderer = renderer; - } - - public int GetPrimitiveCount(int vertexCount) - { - return Math.Max(0, (vertexCount - BaseIndex) / IndexStride); - } - - public int GetConvertedCount(int indexCount) - { - int primitiveCount = GetPrimitiveCount(indexCount); - return primitiveCount * OffsetIndex.Length; - } - - public BufferHandle GetRepeatingBuffer(int vertexCount, out int indexCount) - { - int primitiveCount = GetPrimitiveCount(vertexCount); - indexCount = primitiveCount * PrimitiveVerticesOut; - - int expectedSize = primitiveCount * OffsetIndex.Length; - - if (expectedSize <= _currentSize && _repeatingBuffer != BufferHandle.Null) - { - return _repeatingBuffer; - } - - // Expand the repeating pattern to the number of requested primitives. - BufferHandle newBuffer = _renderer.BufferManager.CreateWithHandle(expectedSize * sizeof(int)); - - // Copy the old data to the new one. - if (_repeatingBuffer != BufferHandle.Null) - { - _renderer.Pipeline.CopyBuffer(_repeatingBuffer, newBuffer, 0, 0, _currentSize * sizeof(int)); - _renderer.BufferManager.Delete(_repeatingBuffer); - } - - _repeatingBuffer = newBuffer; - - // Add the additional repeats on top. - int newPrimitives = primitiveCount; - int oldPrimitives = (_currentSize) / OffsetIndex.Length; - - int[] newData; - - newPrimitives -= oldPrimitives; - newData = new int[expectedSize - _currentSize]; - - int outOffset = 0; - int index = oldPrimitives * IndexStride + BaseIndex; - - for (int i = 0; i < newPrimitives; i++) - { - if (RepeatStart) - { - // Used for triangle fan - newData[outOffset++] = 0; - } - - for (int j = RepeatStart ? 1 : 0; j < OffsetIndex.Length; j++) - { - newData[outOffset++] = index + OffsetIndex[j]; - } - - index += IndexStride; - } - - _renderer.SetBufferData(newBuffer, _currentSize * sizeof(int), MemoryMarshal.Cast(newData)); - _currentSize = expectedSize; - - return newBuffer; - } - - public void Dispose() - { - if (_repeatingBuffer != BufferHandle.Null) - { - _renderer.BufferManager.Delete(_repeatingBuffer); - _repeatingBuffer = BufferHandle.Null; - } - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/IndexBufferState.cs b/src/Ryujinx.Graphics.Metal/IndexBufferState.cs deleted file mode 100644 index 02c9ff9ef..000000000 --- a/src/Ryujinx.Graphics.Metal/IndexBufferState.cs +++ /dev/null @@ -1,103 +0,0 @@ -using Ryujinx.Graphics.GAL; -using SharpMetal.Metal; -using System; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - readonly internal struct IndexBufferState - { - public static IndexBufferState Null => new(BufferHandle.Null, 0, 0); - - private readonly int _offset; - private readonly int _size; - private readonly IndexType _type; - - private readonly BufferHandle _handle; - - public IndexBufferState(BufferHandle handle, int offset, int size, IndexType type = IndexType.UInt) - { - _handle = handle; - _offset = offset; - _size = size; - _type = type; - } - - public (MTLBuffer, int, MTLIndexType) GetIndexBuffer(MetalRenderer renderer, CommandBufferScoped cbs) - { - Auto autoBuffer; - int offset, size; - MTLIndexType type; - - if (_type == IndexType.UByte) - { - // Index type is not supported. Convert to I16. - autoBuffer = renderer.BufferManager.GetBufferI8ToI16(cbs, _handle, _offset, _size); - - type = MTLIndexType.UInt16; - offset = 0; - size = _size * 2; - } - else - { - autoBuffer = renderer.BufferManager.GetBuffer(_handle, false, out int bufferSize); - - if (_offset >= bufferSize) - { - autoBuffer = null; - } - - type = _type.Convert(); - offset = _offset; - size = _size; - } - - if (autoBuffer != null) - { - DisposableBuffer buffer = autoBuffer.Get(cbs, offset, size); - - return (buffer.Value, offset, type); - } - - return (new MTLBuffer(IntPtr.Zero), 0, MTLIndexType.UInt16); - } - - public (MTLBuffer, int, MTLIndexType) GetConvertedIndexBuffer( - MetalRenderer renderer, - CommandBufferScoped cbs, - int firstIndex, - int indexCount, - int convertedCount, - IndexBufferPattern pattern) - { - // Convert the index buffer using the given pattern. - int indexSize = GetIndexSize(); - - int firstIndexOffset = firstIndex * indexSize; - - Auto autoBuffer = renderer.BufferManager.GetBufferTopologyConversion(cbs, _handle, _offset + firstIndexOffset, indexCount * indexSize, pattern, indexSize); - - int size = convertedCount * 4; - - if (autoBuffer != null) - { - DisposableBuffer buffer = autoBuffer.Get(cbs, 0, size); - - return (buffer.Value, 0, MTLIndexType.UInt32); - } - - return (new MTLBuffer(IntPtr.Zero), 0, MTLIndexType.UInt32); - } - - private int GetIndexSize() - { - return _type switch - { - IndexType.UInt => 4, - IndexType.UShort => 2, - _ => 1, - }; - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs deleted file mode 100644 index 86e2451b3..000000000 --- a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs +++ /dev/null @@ -1,312 +0,0 @@ -using Ryujinx.Common.Configuration; -using Ryujinx.Graphics.GAL; -using Ryujinx.Graphics.Shader.Translation; -using SharpMetal.Metal; -using SharpMetal.QuartzCore; -using System; -using System.Collections.Generic; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - public sealed class MetalRenderer : IRenderer - { - public const int TotalSets = 4; - - private readonly MTLDevice _device; - private readonly MTLCommandQueue _queue; - private readonly Func _getMetalLayer; - - private Pipeline _pipeline; - private Window _window; - - public uint ProgramCount { get; set; } - -#pragma warning disable CS0067 // The event is never used - public event EventHandler ScreenCaptured; -#pragma warning restore CS0067 - - public bool PreferThreading => true; - public IPipeline Pipeline => _pipeline; - public IWindow Window => _window; - - internal MTLCommandQueue BackgroundQueue { get; private set; } - internal HelperShader HelperShader { get; private set; } - internal BufferManager BufferManager { get; private set; } - internal CommandBufferPool CommandBufferPool { get; private set; } - internal BackgroundResources BackgroundResources { get; private set; } - internal Action InterruptAction { get; private set; } - internal SyncManager SyncManager { get; private set; } - - internal HashSet Programs { get; } - internal HashSet Samplers { get; } - - public MetalRenderer(Func metalLayer) - { - _device = MTLDevice.CreateSystemDefaultDevice(); - Programs = []; - Samplers = []; - - if (_device.ArgumentBuffersSupport != MTLArgumentBuffersTier.Tier2) - { - throw new NotSupportedException("Metal backend requires Tier 2 Argument Buffer support."); - } - - _queue = _device.NewCommandQueue(CommandBufferPool.MaxCommandBuffers + 1); - BackgroundQueue = _device.NewCommandQueue(CommandBufferPool.MaxCommandBuffers); - - _getMetalLayer = metalLayer; - } - - public void Initialize(GraphicsDebugLevel logLevel) - { - CAMetalLayer layer = _getMetalLayer(); - layer.Device = _device; - layer.FramebufferOnly = false; - - CommandBufferPool = new CommandBufferPool(_queue); - _window = new Window(this, layer); - _pipeline = new Pipeline(_device, this); - BufferManager = new BufferManager(_device, this, _pipeline); - - _pipeline.InitEncoderStateManager(BufferManager); - - BackgroundResources = new BackgroundResources(this); - HelperShader = new HelperShader(_device, this, _pipeline); - SyncManager = new SyncManager(this); - } - - public void BackgroundContextAction(Action action, bool alwaysBackground = false) - { - // GetData methods should be thread safe, so we can call this directly. - // Texture copy (scaled) may also happen in here, so that should also be thread safe. - - action(); - } - - public BufferHandle CreateBuffer(int size, BufferAccess access) - { - return BufferManager.CreateWithHandle(size); - } - - public BufferHandle CreateBuffer(IntPtr pointer, int size) - { - return BufferManager.Create(pointer, size); - } - - public BufferHandle CreateBufferSparse(ReadOnlySpan storageBuffers) - { - throw new NotImplementedException(); - } - - public IImageArray CreateImageArray(int size, bool isBuffer) - { - return new ImageArray(size, isBuffer, _pipeline); - } - - public IProgram CreateProgram(ShaderSource[] shaders, ShaderInfo info) - { - ProgramCount++; - return new Program(this, _device, shaders, info.ResourceLayout, info.ComputeLocalSize); - } - - public ISampler CreateSampler(SamplerCreateInfo info) - { - return new SamplerHolder(this, _device, info); - } - - public ITexture CreateTexture(TextureCreateInfo info) - { - if (info.Target == Target.TextureBuffer) - { - return new TextureBuffer(_device, this, _pipeline, info); - } - - return new Texture(_device, this, _pipeline, info); - } - - public ITextureArray CreateTextureArray(int size, bool isBuffer) - { - return new TextureArray(size, isBuffer, _pipeline); - } - - public bool PrepareHostMapping(IntPtr address, ulong size) - { - // TODO: Metal Host Mapping - return false; - } - - public void CreateSync(ulong id, bool strict) - { - SyncManager.Create(id, strict); - } - - public void DeleteBuffer(BufferHandle buffer) - { - BufferManager.Delete(buffer); - } - - public PinnedSpan GetBufferData(BufferHandle buffer, int offset, int size) - { - return BufferManager.GetData(buffer, offset, size); - } - - public Capabilities GetCapabilities() - { - // TODO: Finalize these values - return new Capabilities( - api: TargetApi.Metal, - vendorName: HardwareInfoTools.GetVendor(), - SystemMemoryType.UnifiedMemory, - hasFrontFacingBug: false, - hasVectorIndexingBug: false, - needsFragmentOutputSpecialization: true, - reduceShaderPrecision: true, - supportsAstcCompression: true, - supportsBc123Compression: true, - supportsBc45Compression: true, - supportsBc67Compression: true, - supportsEtc2Compression: true, - supports3DTextureCompression: true, - supportsBgraFormat: true, - supportsR4G4Format: false, - supportsR4G4B4A4Format: true, - supportsScaledVertexFormats: false, - supportsSnormBufferTextureFormat: true, - supportsSparseBuffer: false, - supports5BitComponentFormat: true, - supportsBlendEquationAdvanced: false, - supportsFragmentShaderInterlock: true, - supportsFragmentShaderOrderingIntel: false, - supportsGeometryShader: false, - supportsGeometryShaderPassthrough: false, - supportsTransformFeedback: false, - supportsImageLoadFormatted: false, - supportsLayerVertexTessellation: false, - supportsMismatchingViewFormat: true, - supportsCubemapView: true, - supportsNonConstantTextureOffset: false, - supportsQuads: false, - supportsSeparateSampler: true, - supportsShaderBallot: false, - supportsShaderBarrierDivergence: false, - supportsShaderFloat64: false, - supportsTextureGatherOffsets: false, - supportsTextureShadowLod: false, - supportsVertexStoreAndAtomics: false, - supportsViewportIndexVertexTessellation: false, - supportsViewportMask: false, - supportsViewportSwizzle: false, - supportsIndirectParameters: true, - supportsDepthClipControl: false, - uniformBufferSetIndex: (int)Constants.ConstantBuffersSetIndex, - storageBufferSetIndex: (int)Constants.StorageBuffersSetIndex, - textureSetIndex: (int)Constants.TexturesSetIndex, - imageSetIndex: (int)Constants.ImagesSetIndex, - extraSetBaseIndex: TotalSets, - maximumExtraSets: (int)Constants.MaximumExtraSets, - maximumUniformBuffersPerStage: Constants.MaxUniformBuffersPerStage, - maximumStorageBuffersPerStage: Constants.MaxStorageBuffersPerStage, - maximumTexturesPerStage: Constants.MaxTexturesPerStage, - maximumImagesPerStage: Constants.MaxImagesPerStage, - maximumComputeSharedMemorySize: (int)_device.MaxThreadgroupMemoryLength, - maximumSupportedAnisotropy: 16, - shaderSubgroupSize: 256, - storageBufferOffsetAlignment: 16, - textureBufferOffsetAlignment: 16, - gatherBiasPrecision: 0, - maximumGpuMemory: 0 - ); - } - - public ulong GetCurrentSync() - { - return SyncManager.GetCurrent(); - } - - public HardwareInfo GetHardwareInfo() - { - return new HardwareInfo(HardwareInfoTools.GetVendor(), HardwareInfoTools.GetModel(), "Apple"); - } - - public IProgram LoadProgramBinary(byte[] programBinary, bool hasFragmentShader, ShaderInfo info) - { - throw new NotImplementedException(); - } - - public void SetBufferData(BufferHandle buffer, int offset, ReadOnlySpan data) - { - BufferManager.SetData(buffer, offset, data, _pipeline.Cbs); - } - - public void UpdateCounters() - { - // https://developer.apple.com/documentation/metal/gpu_counters_and_counter_sample_buffers/creating_a_counter_sample_buffer_to_store_a_gpu_s_counter_data_during_a_pass?language=objc - } - - public void PreFrame() - { - SyncManager.Cleanup(); - } - - public ICounterEvent ReportCounter(CounterType type, EventHandler resultHandler, float divisor, bool hostReserved) - { - // https://developer.apple.com/documentation/metal/gpu_counters_and_counter_sample_buffers/creating_a_counter_sample_buffer_to_store_a_gpu_s_counter_data_during_a_pass?language=objc - CounterEvent counterEvent = new(); - resultHandler?.Invoke(counterEvent, type == CounterType.SamplesPassed ? (ulong)1 : 0); - return counterEvent; - } - - public void ResetCounter(CounterType type) - { - // https://developer.apple.com/documentation/metal/gpu_counters_and_counter_sample_buffers/creating_a_counter_sample_buffer_to_store_a_gpu_s_counter_data_during_a_pass?language=objc - } - - public void WaitSync(ulong id) - { - SyncManager.Wait(id); - } - - public void FlushAllCommands() - { - _pipeline.FlushCommandsImpl(); - } - - public void RegisterFlush() - { - SyncManager.RegisterFlush(); - - // Periodically free unused regions of the staging buffer to avoid doing it all at once. - BufferManager.StagingBuffer.FreeCompleted(); - } - - public void SetInterruptAction(Action interruptAction) - { - InterruptAction = interruptAction; - } - - public void Screenshot() - { - // TODO: Screenshots - } - - public void Dispose() - { - BackgroundResources.Dispose(); - - foreach (Program program in Programs) - { - program.Dispose(); - } - - foreach (SamplerHolder sampler in Samplers) - { - sampler.Dispose(); - } - - _pipeline.Dispose(); - _window.Dispose(); - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/MultiFenceHolder.cs b/src/Ryujinx.Graphics.Metal/MultiFenceHolder.cs deleted file mode 100644 index 89ae1fa77..000000000 --- a/src/Ryujinx.Graphics.Metal/MultiFenceHolder.cs +++ /dev/null @@ -1,262 +0,0 @@ -using SharpMetal.Metal; -using System; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - /// - /// Holder for multiple host GPU fences. - /// - [SupportedOSPlatform("macos")] - class MultiFenceHolder - { - private const int BufferUsageTrackingGranularity = 4096; - - private readonly FenceHolder[] _fences; - private readonly BufferUsageBitmap _bufferUsageBitmap; - - /// - /// Creates a new instance of the multiple fence holder. - /// - public MultiFenceHolder() - { - _fences = new FenceHolder[CommandBufferPool.MaxCommandBuffers]; - } - - /// - /// Creates a new instance of the multiple fence holder, with a given buffer size in mind. - /// - /// Size of the buffer - public MultiFenceHolder(int size) - { - _fences = new FenceHolder[CommandBufferPool.MaxCommandBuffers]; - _bufferUsageBitmap = new BufferUsageBitmap(size, BufferUsageTrackingGranularity); - } - - /// - /// Adds read/write buffer usage information to the uses list. - /// - /// Index of the command buffer where the buffer is used - /// Offset of the buffer being used - /// Size of the buffer region being used, in bytes - /// Whether the access is a write or not - public void AddBufferUse(int cbIndex, int offset, int size, bool write) - { - _bufferUsageBitmap.Add(cbIndex, offset, size, false); - - if (write) - { - _bufferUsageBitmap.Add(cbIndex, offset, size, true); - } - } - - /// - /// Removes all buffer usage information for a given command buffer. - /// - /// Index of the command buffer where the buffer is used - public void RemoveBufferUses(int cbIndex) - { - _bufferUsageBitmap?.Clear(cbIndex); - } - - /// - /// Checks if a given range of a buffer is being used by a command buffer still being processed by the GPU. - /// - /// Index of the command buffer where the buffer is used - /// Offset of the buffer being used - /// Size of the buffer region being used, in bytes - /// True if in use, false otherwise - public bool IsBufferRangeInUse(int cbIndex, int offset, int size) - { - return _bufferUsageBitmap.OverlapsWith(cbIndex, offset, size); - } - - /// - /// Checks if a given range of a buffer is being used by any command buffer still being processed by the GPU. - /// - /// Offset of the buffer being used - /// Size of the buffer region being used, in bytes - /// True if only write usages should count - /// True if in use, false otherwise - public bool IsBufferRangeInUse(int offset, int size, bool write) - { - return _bufferUsageBitmap.OverlapsWith(offset, size, write); - } - - /// - /// Adds a fence to the holder. - /// - /// Command buffer index of the command buffer that owns the fence - /// Fence to be added - /// True if the command buffer's previous fence value was null - public bool AddFence(int cbIndex, FenceHolder fence) - { - ref FenceHolder fenceRef = ref _fences[cbIndex]; - - if (fenceRef == null) - { - fenceRef = fence; - return true; - } - - return false; - } - - /// - /// Removes a fence from the holder. - /// - /// Command buffer index of the command buffer that owns the fence - public void RemoveFence(int cbIndex) - { - _fences[cbIndex] = null; - } - - /// - /// Determines if a fence referenced on the given command buffer. - /// - /// Index of the command buffer to check if it's used - /// True if referenced, false otherwise - public bool HasFence(int cbIndex) - { - return _fences[cbIndex] != null; - } - - /// - /// Wait until all the fences on the holder are signaled. - /// - public void WaitForFences() - { - WaitForFencesImpl(0, 0, true); - } - - /// - /// Wait until all the fences on the holder with buffer uses overlapping the specified range are signaled. - /// - /// Start offset of the buffer range - /// Size of the buffer range in bytes - public void WaitForFences(int offset, int size) - { - WaitForFencesImpl(offset, size, true); - } - - /// - /// Wait until all the fences on the holder with buffer uses overlapping the specified range are signaled. - /// - - // TODO: Add a proper timeout! - public bool WaitForFences(bool indefinite) - { - return WaitForFencesImpl(0, 0, indefinite); - } - - /// - /// Wait until all the fences on the holder with buffer uses overlapping the specified range are signaled. - /// - /// Start offset of the buffer range - /// Size of the buffer range in bytes - /// Indicates if this should wait indefinitely - /// True if all fences were signaled before the timeout expired, false otherwise - private bool WaitForFencesImpl(int offset, int size, bool indefinite) - { - Span fenceHolders = new FenceHolder[CommandBufferPool.MaxCommandBuffers]; - - int count = size != 0 ? GetOverlappingFences(fenceHolders, offset, size) : GetFences(fenceHolders); - Span fences = stackalloc MTLCommandBuffer[count]; - - int fenceCount = 0; - - for (int i = 0; i < count; i++) - { - if (fenceHolders[i].TryGet(out MTLCommandBuffer fence)) - { - fences[fenceCount] = fence; - - if (fenceCount < i) - { - fenceHolders[fenceCount] = fenceHolders[i]; - } - - fenceCount++; - } - } - - if (fenceCount == 0) - { - return true; - } - - bool signaled = true; - - if (indefinite) - { - foreach (MTLCommandBuffer fence in fences) - { - fence.WaitUntilCompleted(); - } - } - else - { - foreach (MTLCommandBuffer fence in fences) - { - if (fence.Status != MTLCommandBufferStatus.Completed) - { - signaled = false; - } - } - } - - for (int i = 0; i < fenceCount; i++) - { - fenceHolders[i].Put(); - } - - return signaled; - } - - /// - /// Gets fences to wait for. - /// - /// Span to store fences in - /// Number of fences placed in storage - private int GetFences(Span storage) - { - int count = 0; - - for (int i = 0; i < _fences.Length; i++) - { - FenceHolder fence = _fences[i]; - - if (fence != null) - { - storage[count++] = fence; - } - } - - return count; - } - - /// - /// Gets fences to wait for use of a given buffer region. - /// - /// Span to store overlapping fences in - /// Offset of the range - /// Size of the range in bytes - /// Number of fences for the specified region placed in storage - private int GetOverlappingFences(Span storage, int offset, int size) - { - int count = 0; - - for (int i = 0; i < _fences.Length; i++) - { - FenceHolder fence = _fences[i]; - - if (fence != null && _bufferUsageBitmap.OverlapsWith(i, offset, size)) - { - storage[count++] = fence; - } - } - - return count; - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/PersistentFlushBuffer.cs b/src/Ryujinx.Graphics.Metal/PersistentFlushBuffer.cs deleted file mode 100644 index fc79a40a9..000000000 --- a/src/Ryujinx.Graphics.Metal/PersistentFlushBuffer.cs +++ /dev/null @@ -1,100 +0,0 @@ -using Ryujinx.Graphics.GAL; -using SharpMetal.Metal; -using System; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - internal class PersistentFlushBuffer : IDisposable - { - private readonly MetalRenderer _renderer; - - private BufferHolder _flushStorage; - - public PersistentFlushBuffer(MetalRenderer renderer) - { - _renderer = renderer; - } - - private BufferHolder ResizeIfNeeded(int size) - { - BufferHolder flushStorage = _flushStorage; - - if (flushStorage == null || size > _flushStorage.Size) - { - flushStorage?.Dispose(); - - flushStorage = _renderer.BufferManager.Create(size); - _flushStorage = flushStorage; - } - - return flushStorage; - } - - public Span GetBufferData(CommandBufferPool cbp, BufferHolder buffer, int offset, int size) - { - BufferHolder flushStorage = ResizeIfNeeded(size); - Auto srcBuffer; - - using (CommandBufferScoped cbs = cbp.Rent()) - { - srcBuffer = buffer.GetBuffer(); - Auto dstBuffer = flushStorage.GetBuffer(); - - if (srcBuffer.TryIncrementReferenceCount()) - { - BufferHolder.Copy(cbs, srcBuffer, dstBuffer, offset, 0, size, registerSrcUsage: false); - } - else - { - // Source buffer is no longer alive, don't copy anything to flush storage. - srcBuffer = null; - } - } - - flushStorage.WaitForFences(); - srcBuffer?.DecrementReferenceCount(); - return flushStorage.GetDataStorage(0, size); - } - - public Span GetTextureData(CommandBufferPool cbp, Texture view, int size) - { - TextureCreateInfo info = view.Info; - - BufferHolder flushStorage = ResizeIfNeeded(size); - - using (CommandBufferScoped cbs = cbp.Rent()) - { - MTLBuffer buffer = flushStorage.GetBuffer().Get(cbs).Value; - MTLTexture image = view.GetHandle(); - - view.CopyFromOrToBuffer(cbs, buffer, image, size, true, 0, 0, info.GetLayers(), info.Levels, singleSlice: false); - } - - flushStorage.WaitForFences(); - return flushStorage.GetDataStorage(0, size); - } - - public Span GetTextureData(CommandBufferPool cbp, Texture view, int size, int layer, int level) - { - BufferHolder flushStorage = ResizeIfNeeded(size); - - using (CommandBufferScoped cbs = cbp.Rent()) - { - MTLBuffer buffer = flushStorage.GetBuffer().Get(cbs).Value; - MTLTexture image = view.GetHandle(); - - view.CopyFromOrToBuffer(cbs, buffer, image, size, true, layer, level, 1, 1, singleSlice: true); - } - - flushStorage.WaitForFences(); - return flushStorage.GetDataStorage(0, size); - } - - public void Dispose() - { - _flushStorage.Dispose(); - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs deleted file mode 100644 index aebcb5493..000000000 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ /dev/null @@ -1,877 +0,0 @@ -using Ryujinx.Common.Logging; -using Ryujinx.Graphics.GAL; -using Ryujinx.Graphics.Shader; -using SharpMetal.Foundation; -using SharpMetal.Metal; -using SharpMetal.QuartzCore; -using System; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - public enum EncoderType - { - Blit, - Compute, - Render, - None - } - - [SupportedOSPlatform("macos")] - class Pipeline : IPipeline, IEncoderFactory, IDisposable - { - private const ulong MinByteWeightForFlush = 256 * 1024 * 1024; // MiB - - private readonly MTLDevice _device; - private readonly MetalRenderer _renderer; - private EncoderStateManager _encoderStateManager; - private ulong _byteWeight; - - public MTLCommandBuffer CommandBuffer; - - public IndexBufferPattern QuadsToTrisPattern; - public IndexBufferPattern TriFanToTrisPattern; - - internal CommandBufferScoped? PreloadCbs { get; private set; } - internal CommandBufferScoped Cbs { get; private set; } - internal CommandBufferEncoder Encoders => Cbs.Encoders; - internal EncoderType CurrentEncoderType => Encoders.CurrentEncoderType; - - public Pipeline(MTLDevice device, MetalRenderer renderer) - { - _device = device; - _renderer = renderer; - - renderer.CommandBufferPool.Initialize(this); - - CommandBuffer = (Cbs = _renderer.CommandBufferPool.Rent()).CommandBuffer; - } - - internal void InitEncoderStateManager(BufferManager bufferManager) - { - _encoderStateManager = new EncoderStateManager(_device, bufferManager, this); - - QuadsToTrisPattern = new IndexBufferPattern(_renderer, 4, 6, 0, [0, 1, 2, 0, 2, 3], 4, false); - TriFanToTrisPattern = new IndexBufferPattern(_renderer, 3, 3, 2, [int.MinValue, -1, 0], 1, true); - } - - public EncoderState SwapState(EncoderState state, DirtyFlags flags = DirtyFlags.All, bool endRenderPass = true) - { - if (endRenderPass && CurrentEncoderType == EncoderType.Render) - { - EndCurrentPass(); - } - - return _encoderStateManager.SwapState(state, flags); - } - - public PredrawState SavePredrawState() - { - return _encoderStateManager.SavePredrawState(); - } - - public void RestorePredrawState(PredrawState state) - { - _encoderStateManager.RestorePredrawState(state); - } - - public void SetClearLoadAction(bool clear) - { - _encoderStateManager.SetClearLoadAction(clear); - } - - public MTLRenderCommandEncoder GetOrCreateRenderEncoder(bool forDraw = false) - { - // Mark all state as dirty to ensure it is set on the new encoder - if (Cbs.Encoders.CurrentEncoderType != EncoderType.Render) - { - _encoderStateManager.SignalRenderDirty(); - } - - if (forDraw) - { - _encoderStateManager.RenderResourcesPrepass(); - } - - MTLRenderCommandEncoder renderCommandEncoder = Cbs.Encoders.EnsureRenderEncoder(); - - if (forDraw) - { - _encoderStateManager.RebindRenderState(renderCommandEncoder); - } - - return renderCommandEncoder; - } - - public MTLBlitCommandEncoder GetOrCreateBlitEncoder() - { - return Cbs.Encoders.EnsureBlitEncoder(); - } - - public MTLComputeCommandEncoder GetOrCreateComputeEncoder(bool forDispatch = false) - { - // Mark all state as dirty to ensure it is set on the new encoder - if (Cbs.Encoders.CurrentEncoderType != EncoderType.Compute) - { - _encoderStateManager.SignalComputeDirty(); - } - - if (forDispatch) - { - _encoderStateManager.ComputeResourcesPrepass(); - } - - MTLComputeCommandEncoder computeCommandEncoder = Cbs.Encoders.EnsureComputeEncoder(); - - if (forDispatch) - { - _encoderStateManager.RebindComputeState(computeCommandEncoder); - } - - return computeCommandEncoder; - } - - public void EndCurrentPass() - { - Cbs.Encoders.EndCurrentPass(); - } - - public MTLRenderCommandEncoder CreateRenderCommandEncoder() - { - return _encoderStateManager.CreateRenderCommandEncoder(); - } - - public MTLComputeCommandEncoder CreateComputeCommandEncoder() - { - return _encoderStateManager.CreateComputeCommandEncoder(); - } - - public void Present(CAMetalDrawable drawable, Texture src, Extents2D srcRegion, Extents2D dstRegion, bool isLinear) - { - // TODO: Clean this up - TextureCreateInfo textureInfo = new((int)drawable.Texture.Width, (int)drawable.Texture.Height, (int)drawable.Texture.Depth, (int)drawable.Texture.MipmapLevelCount, (int)drawable.Texture.SampleCount, 0, 0, 0, Format.B8G8R8A8Unorm, 0, Target.Texture2D, SwizzleComponent.Red, SwizzleComponent.Green, SwizzleComponent.Blue, SwizzleComponent.Alpha); - Texture dst = new(_device, _renderer, this, textureInfo, drawable.Texture, 0, 0); - - _renderer.HelperShader.BlitColor(Cbs, src, dst, srcRegion, dstRegion, isLinear, true); - - EndCurrentPass(); - - Cbs.CommandBuffer.PresentDrawable(drawable); - - FlushCommandsImpl(); - - // TODO: Auto flush counting - _renderer.SyncManager.GetAndResetWaitTicks(); - - // Cleanup - dst.Dispose(); - } - - public CommandBufferScoped GetPreloadCommandBuffer() - { - PreloadCbs ??= _renderer.CommandBufferPool.Rent(); - - return PreloadCbs.Value; - } - - public void FlushCommandsIfWeightExceeding(IAuto disposedResource, ulong byteWeight) - { - bool usedByCurrentCb = disposedResource.HasCommandBufferDependency(Cbs); - - if (PreloadCbs != null && !usedByCurrentCb) - { - usedByCurrentCb = disposedResource.HasCommandBufferDependency(PreloadCbs.Value); - } - - if (usedByCurrentCb) - { - // Since we can only free memory after the command buffer that uses a given resource was executed, - // keeping the command buffer might cause a high amount of memory to be in use. - // To prevent that, we force submit command buffers if the memory usage by resources - // in use by the current command buffer is above a given limit, and those resources were disposed. - _byteWeight += byteWeight; - - if (_byteWeight >= MinByteWeightForFlush) - { - FlushCommandsImpl(); - } - } - } - - public void FlushCommandsImpl() - { - EndCurrentPass(); - - _byteWeight = 0; - - if (PreloadCbs != null) - { - PreloadCbs.Value.Dispose(); - PreloadCbs = null; - } - - CommandBuffer = (Cbs = _renderer.CommandBufferPool.ReturnAndRent(Cbs)).CommandBuffer; - _renderer.RegisterFlush(); - } - - public void DirtyTextures() - { - _encoderStateManager.DirtyTextures(); - } - - public void DirtyImages() - { - _encoderStateManager.DirtyImages(); - } - - public void Blit( - Texture src, - Texture dst, - Extents2D srcRegion, - Extents2D dstRegion, - bool isDepthOrStencil, - bool linearFilter) - { - if (isDepthOrStencil) - { - _renderer.HelperShader.BlitDepthStencil(Cbs, src, dst, srcRegion, dstRegion); - } - else - { - _renderer.HelperShader.BlitColor(Cbs, src, dst, srcRegion, dstRegion, linearFilter); - } - } - - public void Barrier() - { - switch (CurrentEncoderType) - { - case EncoderType.Render: - { - MTLBarrierScope scope = MTLBarrierScope.Buffers | MTLBarrierScope.Textures | MTLBarrierScope.RenderTargets; - MTLRenderStages stages = MTLRenderStages.RenderStageVertex | MTLRenderStages.RenderStageFragment; - Encoders.RenderEncoder.MemoryBarrier(scope, stages, stages); - break; - } - case EncoderType.Compute: - { - MTLBarrierScope scope = MTLBarrierScope.Buffers | MTLBarrierScope.Textures | MTLBarrierScope.RenderTargets; - Encoders.ComputeEncoder.MemoryBarrier(scope); - break; - } - } - } - - public void ClearBuffer(BufferHandle destination, int offset, int size, uint value) - { - MTLBlitCommandEncoder blitCommandEncoder = GetOrCreateBlitEncoder(); - - MTLBuffer mtlBuffer = _renderer.BufferManager.GetBuffer(destination, offset, size, true).Get(Cbs, offset, size, true).Value; - - // Might need a closer look, range's count, lower, and upper bound - // must be a multiple of 4 - blitCommandEncoder.FillBuffer(mtlBuffer, - new NSRange - { - location = (ulong)offset, - length = (ulong)size - }, - (byte)value); - } - - public void ClearRenderTargetColor(int index, int layer, int layerCount, uint componentMask, ColorF color) - { - float[] colors = [color.Red, color.Green, color.Blue, color.Alpha]; - Texture dst = _encoderStateManager.RenderTargets[index]; - - // TODO: Remove workaround for Wonder which has an invalid texture due to unsupported format - if (dst == null) - { - Logger.Warning?.PrintMsg(LogClass.Gpu, "Attempted to clear invalid render target!"); - return; - } - - _renderer.HelperShader.ClearColor(index, colors, componentMask, dst.Width, dst.Height, dst.Info.Format); - } - - public void ClearRenderTargetDepthStencil(int layer, int layerCount, float depthValue, bool depthMask, int stencilValue, int stencilMask) - { - Texture depthStencil = _encoderStateManager.DepthStencil; - - if (depthStencil == null) - { - return; - } - - _renderer.HelperShader.ClearDepthStencil(depthValue, depthMask, stencilValue, stencilMask, depthStencil.Width, depthStencil.Height); - } - - public void CommandBufferBarrier() - { - Barrier(); - } - - public void CopyBuffer(BufferHandle src, BufferHandle dst, int srcOffset, int dstOffset, int size) - { - Auto srcBuffer = _renderer.BufferManager.GetBuffer(src, srcOffset, size, false); - Auto dstBuffer = _renderer.BufferManager.GetBuffer(dst, dstOffset, size, true); - - BufferHolder.Copy(Cbs, srcBuffer, dstBuffer, srcOffset, dstOffset, size); - } - - public void PushDebugGroup(string name) - { - MTLCommandEncoder? encoder = Encoders.CurrentEncoder; - NSString debugGroupName = StringHelper.NSString(name); - - if (encoder == null) - { - return; - } - - switch (Encoders.CurrentEncoderType) - { - case EncoderType.Render: - encoder.Value.PushDebugGroup(debugGroupName); - break; - case EncoderType.Blit: - encoder.Value.PushDebugGroup(debugGroupName); - break; - case EncoderType.Compute: - encoder.Value.PushDebugGroup(debugGroupName); - break; - } - } - - public void PopDebugGroup() - { - MTLCommandEncoder? encoder = Encoders.CurrentEncoder; - - if (encoder == null) - { - return; - } - - switch (Encoders.CurrentEncoderType) - { - case EncoderType.Render: - encoder.Value.PopDebugGroup(); - break; - case EncoderType.Blit: - encoder.Value.PopDebugGroup(); - break; - case EncoderType.Compute: - encoder.Value.PopDebugGroup(); - break; - } - } - - public void DispatchCompute(int groupsX, int groupsY, int groupsZ) - { - DispatchCompute(groupsX, groupsY, groupsZ, String.Empty); - } - - public void DispatchCompute(int groupsX, int groupsY, int groupsZ, string debugGroupName) - { - MTLComputeCommandEncoder computeCommandEncoder = GetOrCreateComputeEncoder(true); - - ComputeSize localSize = _encoderStateManager.ComputeLocalSize; - - if (debugGroupName != String.Empty) - { - PushDebugGroup(debugGroupName); - } - - computeCommandEncoder.DispatchThreadgroups( - new MTLSize { width = (ulong)groupsX, height = (ulong)groupsY, depth = (ulong)groupsZ }, - new MTLSize { width = (ulong)localSize.X, height = (ulong)localSize.Y, depth = (ulong)localSize.Z }); - - if (debugGroupName != String.Empty) - { - PopDebugGroup(); - } - } - - public void Draw(int vertexCount, int instanceCount, int firstVertex, int firstInstance) - { - Draw(vertexCount, instanceCount, firstVertex, firstInstance, String.Empty); - } - - public void Draw(int vertexCount, int instanceCount, int firstVertex, int firstInstance, string debugGroupName) - { - if (vertexCount == 0) - { - return; - } - - MTLPrimitiveType primitiveType = TopologyRemap(_encoderStateManager.Topology).Convert(); - - if (TopologyUnsupported(_encoderStateManager.Topology)) - { - IndexBufferPattern pattern = GetIndexBufferPattern(); - - BufferHandle handle = pattern.GetRepeatingBuffer(vertexCount, out int indexCount); - Auto buffer = _renderer.BufferManager.GetBuffer(handle, false); - MTLBuffer mtlBuffer = buffer.Get(Cbs, 0, indexCount * sizeof(int)).Value; - - MTLRenderCommandEncoder renderCommandEncoder = GetOrCreateRenderEncoder(true); - - renderCommandEncoder.DrawIndexedPrimitives( - primitiveType, - (ulong)indexCount, - MTLIndexType.UInt32, - mtlBuffer, - 0); - } - else - { - MTLRenderCommandEncoder renderCommandEncoder = GetOrCreateRenderEncoder(true); - - if (debugGroupName != String.Empty) - { - PushDebugGroup(debugGroupName); - } - - renderCommandEncoder.DrawPrimitives( - primitiveType, - (ulong)firstVertex, - (ulong)vertexCount, - (ulong)instanceCount, - (ulong)firstInstance); - - if (debugGroupName != String.Empty) - { - PopDebugGroup(); - } - } - } - - private IndexBufferPattern GetIndexBufferPattern() - { - return _encoderStateManager.Topology switch - { - PrimitiveTopology.Quads => QuadsToTrisPattern, - PrimitiveTopology.TriangleFan or PrimitiveTopology.Polygon => TriFanToTrisPattern, - _ => throw new NotSupportedException($"Unsupported topology: {_encoderStateManager.Topology}"), - }; - } - - private PrimitiveTopology TopologyRemap(PrimitiveTopology topology) - { - return topology switch - { - PrimitiveTopology.Quads => PrimitiveTopology.Triangles, - PrimitiveTopology.QuadStrip => PrimitiveTopology.TriangleStrip, - PrimitiveTopology.TriangleFan or PrimitiveTopology.Polygon => PrimitiveTopology.Triangles, - _ => topology, - }; - } - - private bool TopologyUnsupported(PrimitiveTopology topology) - { - return topology switch - { - PrimitiveTopology.Quads or PrimitiveTopology.TriangleFan or PrimitiveTopology.Polygon => true, - _ => false, - }; - } - - public void DrawIndexed(int indexCount, int instanceCount, int firstIndex, int firstVertex, int firstInstance) - { - if (indexCount == 0) - { - return; - } - - MTLBuffer mtlBuffer; - int offset; - MTLIndexType type; - int finalIndexCount = indexCount; - - MTLPrimitiveType primitiveType = TopologyRemap(_encoderStateManager.Topology).Convert(); - - if (TopologyUnsupported(_encoderStateManager.Topology)) - { - IndexBufferPattern pattern = GetIndexBufferPattern(); - int convertedCount = pattern.GetConvertedCount(indexCount); - - finalIndexCount = convertedCount; - - (mtlBuffer, offset, type) = _encoderStateManager.IndexBuffer.GetConvertedIndexBuffer(_renderer, Cbs, firstIndex, indexCount, convertedCount, pattern); - } - else - { - (mtlBuffer, offset, type) = _encoderStateManager.IndexBuffer.GetIndexBuffer(_renderer, Cbs); - } - - if (mtlBuffer.NativePtr != IntPtr.Zero) - { - MTLRenderCommandEncoder renderCommandEncoder = GetOrCreateRenderEncoder(true); - - renderCommandEncoder.DrawIndexedPrimitives( - primitiveType, - (ulong)finalIndexCount, - type, - mtlBuffer, - (ulong)offset, - (ulong)instanceCount, - firstVertex, - (ulong)firstInstance); - } - } - - public void DrawIndexedIndirect(BufferRange indirectBuffer) - { - DrawIndexedIndirectOffset(indirectBuffer); - } - - public void DrawIndexedIndirectOffset(BufferRange indirectBuffer, int offset = 0) - { - // TODO: Reindex unsupported topologies - if (TopologyUnsupported(_encoderStateManager.Topology)) - { - Logger.Warning?.Print(LogClass.Gpu, $"Drawing indexed with unsupported topology: {_encoderStateManager.Topology}"); - } - - MTLBuffer buffer = _renderer.BufferManager - .GetBuffer(indirectBuffer.Handle, indirectBuffer.Offset, indirectBuffer.Size, false) - .Get(Cbs, indirectBuffer.Offset, indirectBuffer.Size).Value; - - MTLPrimitiveType primitiveType = TopologyRemap(_encoderStateManager.Topology).Convert(); - - (MTLBuffer indexBuffer, int indexOffset, MTLIndexType type) = _encoderStateManager.IndexBuffer.GetIndexBuffer(_renderer, Cbs); - - if (indexBuffer.NativePtr != IntPtr.Zero && buffer.NativePtr != IntPtr.Zero) - { - MTLRenderCommandEncoder renderCommandEncoder = GetOrCreateRenderEncoder(true); - - renderCommandEncoder.DrawIndexedPrimitives( - primitiveType, - type, - indexBuffer, - (ulong)indexOffset, - buffer, - (ulong)(indirectBuffer.Offset + offset)); - } - } - - public void DrawIndexedIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride) - { - for (int i = 0; i < maxDrawCount; i++) - { - DrawIndexedIndirectOffset(indirectBuffer, stride * i); - } - } - - public void DrawIndirect(BufferRange indirectBuffer) - { - DrawIndirectOffset(indirectBuffer); - } - - public void DrawIndirectOffset(BufferRange indirectBuffer, int offset = 0) - { - if (TopologyUnsupported(_encoderStateManager.Topology)) - { - // TODO: Reindex unsupported topologies - Logger.Warning?.Print(LogClass.Gpu, $"Drawing indirect with unsupported topology: {_encoderStateManager.Topology}"); - } - - MTLBuffer buffer = _renderer.BufferManager - .GetBuffer(indirectBuffer.Handle, indirectBuffer.Offset, indirectBuffer.Size, false) - .Get(Cbs, indirectBuffer.Offset, indirectBuffer.Size).Value; - - MTLPrimitiveType primitiveType = TopologyRemap(_encoderStateManager.Topology).Convert(); - MTLRenderCommandEncoder renderCommandEncoder = GetOrCreateRenderEncoder(true); - - renderCommandEncoder.DrawPrimitives( - primitiveType, - buffer, - (ulong)(indirectBuffer.Offset + offset)); - } - - public void DrawIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride) - { - for (int i = 0; i < maxDrawCount; i++) - { - DrawIndirectOffset(indirectBuffer, stride * i); - } - } - - public void DrawTexture(ITexture texture, ISampler sampler, Extents2DF srcRegion, Extents2DF dstRegion) - { - _renderer.HelperShader.DrawTexture(texture, sampler, srcRegion, dstRegion); - } - - public void SetAlphaTest(bool enable, float reference, CompareOp op) - { - // This is currently handled using shader specialization, as Metal does not support alpha test. - // In the future, we may want to use this to write the reference value into the support buffer, - // to avoid creating one version of the shader per reference value used. - } - - public void SetBlendState(AdvancedBlendDescriptor blend) - { - // Metal does not support advanced blend. - } - - public void SetBlendState(int index, BlendDescriptor blend) - { - _encoderStateManager.UpdateBlendDescriptors(index, blend); - } - - public void SetDepthBias(PolygonModeMask enables, float factor, float units, float clamp) - { - if (enables == 0) - { - _encoderStateManager.UpdateDepthBias(0, 0, 0); - } - else - { - _encoderStateManager.UpdateDepthBias(units, factor, clamp); - } - } - - public void SetDepthClamp(bool clamp) - { - _encoderStateManager.UpdateDepthClamp(clamp); - } - - public void SetDepthMode(DepthMode mode) - { - // Metal does not support depth clip control. - } - - public void SetDepthTest(DepthTestDescriptor depthTest) - { - _encoderStateManager.UpdateDepthState(depthTest); - } - - public void SetFaceCulling(bool enable, Face face) - { - _encoderStateManager.UpdateCullMode(enable, face); - } - - public void SetFrontFace(FrontFace frontFace) - { - _encoderStateManager.UpdateFrontFace(frontFace); - } - - public void SetIndexBuffer(BufferRange buffer, IndexType type) - { - _encoderStateManager.UpdateIndexBuffer(buffer, type); - } - - public void SetImage(ShaderStage stage, int binding, ITexture image) - { - if (image is TextureBase img) - { - _encoderStateManager.UpdateImage(stage, binding, img); - } - } - - public void SetImageArray(ShaderStage stage, int binding, IImageArray array) - { - if (array is ImageArray imageArray) - { - _encoderStateManager.UpdateImageArray(stage, binding, imageArray); - } - } - - public void SetImageArraySeparate(ShaderStage stage, int setIndex, IImageArray array) - { - if (array is ImageArray imageArray) - { - _encoderStateManager.UpdateImageArraySeparate(stage, setIndex, imageArray); - } - } - - public void SetLineParameters(float width, bool smooth) - { - // Metal does not support wide-lines. - } - - public void SetLogicOpState(bool enable, LogicalOp op) - { - _encoderStateManager.UpdateLogicOpState(enable, op); - } - - public void SetMultisampleState(MultisampleDescriptor multisample) - { - _encoderStateManager.UpdateMultisampleState(multisample); - } - - public void SetPatchParameters(int vertices, ReadOnlySpan defaultOuterLevel, ReadOnlySpan defaultInnerLevel) - { - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); - } - - public void SetPointParameters(float size, bool isProgramPointSize, bool enablePointSprite, Origin origin) - { - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); - } - - public void SetPolygonMode(PolygonMode frontMode, PolygonMode backMode) - { - // Metal does not support polygon mode. - } - - public void SetPrimitiveRestart(bool enable, int index) - { - // Always active for LineStrip and TriangleStrip - // https://github.com/gpuweb/gpuweb/issues/1220#issuecomment-732483263 - // https://developer.apple.com/documentation/metal/mtlrendercommandencoder/1515520-drawindexedprimitives - // https://stackoverflow.com/questions/70813665/how-to-render-multiple-trianglestrips-using-metal - - // Emulating disabling this is very difficult. It's unlikely for an index buffer to use the largest possible index, - // so it's fine nearly all of the time. - } - - public void SetPrimitiveTopology(PrimitiveTopology topology) - { - _encoderStateManager.UpdatePrimitiveTopology(topology); - } - - public void SetProgram(IProgram program) - { - _encoderStateManager.UpdateProgram(program); - } - - public void SetRasterizerDiscard(bool discard) - { - _encoderStateManager.UpdateRasterizerDiscard(discard); - } - - public void SetRenderTargetColorMasks(ReadOnlySpan componentMask) - { - _encoderStateManager.UpdateRenderTargetColorMasks(componentMask); - } - - public void SetRenderTargets(ITexture[] colors, ITexture depthStencil) - { - _encoderStateManager.UpdateRenderTargets(colors, depthStencil); - } - - public void SetScissors(ReadOnlySpan> regions) - { - _encoderStateManager.UpdateScissors(regions); - } - - public void SetStencilTest(StencilTestDescriptor stencilTest) - { - _encoderStateManager.UpdateStencilState(stencilTest); - } - - public void SetUniformBuffers(ReadOnlySpan buffers) - { - _encoderStateManager.UpdateUniformBuffers(buffers); - } - - public void SetStorageBuffers(ReadOnlySpan buffers) - { - _encoderStateManager.UpdateStorageBuffers(buffers); - } - - internal void SetStorageBuffers(int first, ReadOnlySpan> buffers) - { - _encoderStateManager.UpdateStorageBuffers(first, buffers); - } - - public void SetTextureAndSampler(ShaderStage stage, int binding, ITexture texture, ISampler sampler) - { - if (texture is TextureBase tex) - { - if (sampler == null || sampler is SamplerHolder) - { - _encoderStateManager.UpdateTextureAndSampler(stage, binding, tex, (SamplerHolder)sampler); - } - } - } - - public void SetTextureArray(ShaderStage stage, int binding, ITextureArray array) - { - if (array is TextureArray textureArray) - { - _encoderStateManager.UpdateTextureArray(stage, binding, textureArray); - } - } - - public void SetTextureArraySeparate(ShaderStage stage, int setIndex, ITextureArray array) - { - if (array is TextureArray textureArray) - { - _encoderStateManager.UpdateTextureArraySeparate(stage, setIndex, textureArray); - } - } - - public void SetUserClipDistance(int index, bool enableClip) - { - // TODO. Same as Vulkan - } - - public void SetVertexAttribs(ReadOnlySpan vertexAttribs) - { - _encoderStateManager.UpdateVertexAttribs(vertexAttribs); - } - - public void SetVertexBuffers(ReadOnlySpan vertexBuffers) - { - _encoderStateManager.UpdateVertexBuffers(vertexBuffers); - } - - public void SetViewports(ReadOnlySpan viewports) - { - _encoderStateManager.UpdateViewports(viewports); - } - - public void TextureBarrier() - { - if (CurrentEncoderType == EncoderType.Render) - { - Encoders.RenderEncoder.MemoryBarrier(MTLBarrierScope.Textures, MTLRenderStages.RenderStageFragment, MTLRenderStages.RenderStageFragment); - } - } - - public void TextureBarrierTiled() - { - TextureBarrier(); - } - - public bool TryHostConditionalRendering(ICounterEvent value, ulong compare, bool isEqual) - { - // TODO: Implementable via indirect draw commands - return false; - } - - public bool TryHostConditionalRendering(ICounterEvent value, ICounterEvent compare, bool isEqual) - { - // TODO: Implementable via indirect draw commands - return false; - } - - public void EndHostConditionalRendering() - { - // TODO: Implementable via indirect draw commands - } - - public void BeginTransformFeedback(PrimitiveTopology topology) - { - // Metal does not support transform feedback. - } - - public void EndTransformFeedback() - { - // Metal does not support transform feedback. - } - - public void SetTransformFeedbackBuffers(ReadOnlySpan buffers) - { - // Metal does not support transform feedback. - } - - public void Dispose() - { - EndCurrentPass(); - _encoderStateManager.Dispose(); - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/Program.cs b/src/Ryujinx.Graphics.Metal/Program.cs deleted file mode 100644 index 721ee56a7..000000000 --- a/src/Ryujinx.Graphics.Metal/Program.cs +++ /dev/null @@ -1,286 +0,0 @@ -using Ryujinx.Common.Logging; -using Ryujinx.Graphics.GAL; -using Ryujinx.Graphics.Shader; -using SharpMetal.Foundation; -using SharpMetal.Metal; -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Runtime.InteropServices; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - class Program : IProgram - { - private ProgramLinkStatus _status; - private readonly ShaderSource[] _shaders; - private readonly GCHandle[] _handles; - private int _successCount; - - private readonly MetalRenderer _renderer; - - public MTLFunction VertexFunction; - public MTLFunction FragmentFunction; - public MTLFunction ComputeFunction; - public ComputeSize ComputeLocalSize { get; } - - private HashTableSlim _graphicsPipelineCache; - private MTLComputePipelineState? _computePipelineCache; - private bool _firstBackgroundUse; - - public ResourceBindingSegment[][] BindingSegments { get; } - // Argument buffer sizes for Vertex or Compute stages - public int[] ArgumentBufferSizes { get; } - // Argument buffer sizes for Fragment stage - public int[] FragArgumentBufferSizes { get; } - - public Program( - MetalRenderer renderer, - MTLDevice device, - ShaderSource[] shaders, - ResourceLayout resourceLayout, - ComputeSize computeLocalSize = default) - { - _renderer = renderer; - renderer.Programs.Add(this); - - ComputeLocalSize = computeLocalSize; - _shaders = shaders; - _handles = new GCHandle[_shaders.Length]; - - _status = ProgramLinkStatus.Incomplete; - - for (int i = 0; i < _shaders.Length; i++) - { - ShaderSource shader = _shaders[i]; - - using MTLCompileOptions compileOptions = new() - { - PreserveInvariance = true, - LanguageVersion = MTLLanguageVersion.Version31, - }; - int index = i; - - _handles[i] = device.NewLibrary(StringHelper.NSString(shader.Code), compileOptions, (library, error) => CompilationResultHandler(library, error, index)); - } - - (BindingSegments, ArgumentBufferSizes, FragArgumentBufferSizes) = BuildBindingSegments(resourceLayout.SetUsages); - } - - public void CompilationResultHandler(MTLLibrary library, NSError error, int index) - { - ShaderSource shader = _shaders[index]; - - if (_handles[index].IsAllocated) - { - _handles[index].Free(); - } - - if (error != IntPtr.Zero) - { - Logger.Warning?.PrintMsg(LogClass.Gpu, shader.Code); - Logger.Warning?.Print(LogClass.Gpu, $"{shader.Stage} shader linking failed: \n{StringHelper.String(error.LocalizedDescription)}"); - _status = ProgramLinkStatus.Failure; - return; - } - - switch (shader.Stage) - { - case ShaderStage.Compute: - ComputeFunction = library.NewFunction(StringHelper.NSString("kernelMain")); - break; - case ShaderStage.Vertex: - VertexFunction = library.NewFunction(StringHelper.NSString("vertexMain")); - break; - case ShaderStage.Fragment: - FragmentFunction = library.NewFunction(StringHelper.NSString("fragmentMain")); - break; - default: - Logger.Warning?.Print(LogClass.Gpu, $"Cannot handle stage {shader.Stage}!"); - break; - } - - _successCount++; - - if (_successCount >= _shaders.Length && _status != ProgramLinkStatus.Failure) - { - _status = ProgramLinkStatus.Success; - } - } - - private static (ResourceBindingSegment[][], int[], int[]) BuildBindingSegments(ReadOnlyCollection setUsages) - { - ResourceBindingSegment[][] segments = new ResourceBindingSegment[setUsages.Count][]; - int[] argBufferSizes = new int[setUsages.Count]; - int[] fragArgBufferSizes = new int[setUsages.Count]; - - for (int setIndex = 0; setIndex < setUsages.Count; setIndex++) - { - List currentSegments = []; - - ResourceUsage currentUsage = default; - int currentCount = 0; - - for (int index = 0; index < setUsages[setIndex].Usages.Count; index++) - { - ResourceUsage usage = setUsages[setIndex].Usages[index]; - - if (currentUsage.Binding + currentCount != usage.Binding || - currentUsage.Type != usage.Type || - currentUsage.Stages != usage.Stages || - currentUsage.ArrayLength > 1 || - usage.ArrayLength > 1) - { - if (currentCount != 0) - { - currentSegments.Add(new ResourceBindingSegment( - currentUsage.Binding, - currentCount, - currentUsage.Type, - currentUsage.Stages, - currentUsage.ArrayLength > 1)); - - int size = currentCount * ResourcePointerSize(currentUsage.Type); - if (currentUsage.Stages.HasFlag(ResourceStages.Fragment)) - { - fragArgBufferSizes[setIndex] += size; - } - - if (currentUsage.Stages.HasFlag(ResourceStages.Vertex) || - currentUsage.Stages.HasFlag(ResourceStages.Compute)) - { - argBufferSizes[setIndex] += size; - } - } - - currentUsage = usage; - currentCount = usage.ArrayLength; - } - else - { - currentCount++; - } - } - - if (currentCount != 0) - { - currentSegments.Add(new ResourceBindingSegment( - currentUsage.Binding, - currentCount, - currentUsage.Type, - currentUsage.Stages, - currentUsage.ArrayLength > 1)); - - int size = currentCount * ResourcePointerSize(currentUsage.Type); - if (currentUsage.Stages.HasFlag(ResourceStages.Fragment)) - { - fragArgBufferSizes[setIndex] += size; - } - - if (currentUsage.Stages.HasFlag(ResourceStages.Vertex) || - currentUsage.Stages.HasFlag(ResourceStages.Compute)) - { - argBufferSizes[setIndex] += size; - } - } - - segments[setIndex] = currentSegments.ToArray(); - } - - return (segments, argBufferSizes, fragArgBufferSizes); - } - - private static int ResourcePointerSize(ResourceType type) - { - return (type == ResourceType.TextureAndSampler ? 2 : 1); - } - - public ProgramLinkStatus CheckProgramLink(bool blocking) - { - if (blocking) - { - while (_status == ProgramLinkStatus.Incomplete) - { } - - return _status; - } - - return _status; - } - - public byte[] GetBinary() - { - return []; - } - - public void AddGraphicsPipeline(ref PipelineUid key, MTLRenderPipelineState pipeline) - { - (_graphicsPipelineCache ??= new()).Add(ref key, pipeline); - } - - public void AddComputePipeline(MTLComputePipelineState pipeline) - { - _computePipelineCache = pipeline; - } - - public bool TryGetGraphicsPipeline(ref PipelineUid key, out MTLRenderPipelineState pipeline) - { - if (_graphicsPipelineCache == null) - { - pipeline = default; - return false; - } - - if (!_graphicsPipelineCache.TryGetValue(ref key, out pipeline)) - { - if (_firstBackgroundUse) - { - Logger.Warning?.Print(LogClass.Gpu, "Background pipeline compile missed on draw - incorrect pipeline state?"); - _firstBackgroundUse = false; - } - - return false; - } - - _firstBackgroundUse = false; - - return true; - } - - public bool TryGetComputePipeline(out MTLComputePipelineState pipeline) - { - if (_computePipelineCache.HasValue) - { - pipeline = _computePipelineCache.Value; - return true; - } - - pipeline = default; - return false; - } - - public void Dispose() - { - if (!_renderer.Programs.Remove(this)) - { - return; - } - - if (_graphicsPipelineCache != null) - { - foreach (MTLRenderPipelineState pipeline in _graphicsPipelineCache.Values) - { - pipeline.Dispose(); - } - } - - _computePipelineCache?.Dispose(); - - VertexFunction.Dispose(); - FragmentFunction.Dispose(); - ComputeFunction.Dispose(); - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/ResourceBindingSegment.cs b/src/Ryujinx.Graphics.Metal/ResourceBindingSegment.cs deleted file mode 100644 index 8e6d88c4b..000000000 --- a/src/Ryujinx.Graphics.Metal/ResourceBindingSegment.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Ryujinx.Graphics.GAL; - -namespace Ryujinx.Graphics.Metal -{ - readonly struct ResourceBindingSegment - { - public readonly int Binding; - public readonly int Count; - public readonly ResourceType Type; - public readonly ResourceStages Stages; - public readonly bool IsArray; - - public ResourceBindingSegment(int binding, int count, ResourceType type, ResourceStages stages, bool isArray) - { - Binding = binding; - Count = count; - Type = type; - Stages = stages; - IsArray = isArray; - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/ResourceLayoutBuilder.cs b/src/Ryujinx.Graphics.Metal/ResourceLayoutBuilder.cs deleted file mode 100644 index 623f91612..000000000 --- a/src/Ryujinx.Graphics.Metal/ResourceLayoutBuilder.cs +++ /dev/null @@ -1,59 +0,0 @@ -using Ryujinx.Graphics.GAL; -using System; -using System.Collections.Generic; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - class ResourceLayoutBuilder - { - private const int TotalSets = MetalRenderer.TotalSets; - - private readonly List[] _resourceDescriptors; - private readonly List[] _resourceUsages; - - public ResourceLayoutBuilder() - { - _resourceDescriptors = new List[TotalSets]; - _resourceUsages = new List[TotalSets]; - - for (int index = 0; index < TotalSets; index++) - { - _resourceDescriptors[index] = []; - _resourceUsages[index] = []; - } - } - - public ResourceLayoutBuilder Add(ResourceStages stages, ResourceType type, int binding, bool write = false) - { - uint setIndex = type switch - { - ResourceType.UniformBuffer => Constants.ConstantBuffersSetIndex, - ResourceType.StorageBuffer => Constants.StorageBuffersSetIndex, - ResourceType.TextureAndSampler or ResourceType.BufferTexture => Constants.TexturesSetIndex, - ResourceType.Image or ResourceType.BufferImage => Constants.ImagesSetIndex, - _ => throw new ArgumentException($"Invalid resource type \"{type}\"."), - }; - - _resourceDescriptors[setIndex].Add(new ResourceDescriptor(binding, 1, type, stages)); - _resourceUsages[setIndex].Add(new ResourceUsage(binding, 1, type, stages, write)); - - return this; - } - - public ResourceLayout Build() - { - ResourceDescriptorCollection[] descriptors = new ResourceDescriptorCollection[TotalSets]; - ResourceUsageCollection[] usages = new ResourceUsageCollection[TotalSets]; - - for (int index = 0; index < TotalSets; index++) - { - descriptors[index] = new ResourceDescriptorCollection(_resourceDescriptors[index].ToArray().AsReadOnly()); - usages[index] = new ResourceUsageCollection(_resourceUsages[index].ToArray().AsReadOnly()); - } - - return new ResourceLayout(descriptors.AsReadOnly(), usages.AsReadOnly()); - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/Ryujinx.Graphics.Metal.csproj b/src/Ryujinx.Graphics.Metal/Ryujinx.Graphics.Metal.csproj deleted file mode 100644 index 364aa5a8b..000000000 --- a/src/Ryujinx.Graphics.Metal/Ryujinx.Graphics.Metal.csproj +++ /dev/null @@ -1,27 +0,0 @@ - - - - true - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Ryujinx.Graphics.Metal/SamplerHolder.cs b/src/Ryujinx.Graphics.Metal/SamplerHolder.cs deleted file mode 100644 index a448b26fe..000000000 --- a/src/Ryujinx.Graphics.Metal/SamplerHolder.cs +++ /dev/null @@ -1,90 +0,0 @@ -using Ryujinx.Graphics.GAL; -using SharpMetal.Metal; -using System; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - class SamplerHolder : ISampler - { - private readonly MetalRenderer _renderer; - private readonly Auto _sampler; - - public SamplerHolder(MetalRenderer renderer, MTLDevice device, SamplerCreateInfo info) - { - _renderer = renderer; - - renderer.Samplers.Add(this); - - (MTLSamplerMinMagFilter minFilter, MTLSamplerMipFilter mipFilter) = info.MinFilter.Convert(); - - MTLSamplerBorderColor borderColor = GetConstrainedBorderColor(info.BorderColor, out _); - - using MTLSamplerDescriptor descriptor = new() - { - BorderColor = borderColor, - MinFilter = minFilter, - MagFilter = info.MagFilter.Convert(), - MipFilter = mipFilter, - CompareFunction = info.CompareOp.Convert(), - LodMinClamp = info.MinLod, - LodMaxClamp = info.MaxLod, - LodAverage = false, - MaxAnisotropy = Math.Max((uint)info.MaxAnisotropy, 1), - SAddressMode = info.AddressU.Convert(), - TAddressMode = info.AddressV.Convert(), - RAddressMode = info.AddressP.Convert(), - SupportArgumentBuffers = true - }; - - MTLSamplerState sampler = device.NewSamplerState(descriptor); - - _sampler = new Auto(new DisposableSampler(sampler)); - } - - private static MTLSamplerBorderColor GetConstrainedBorderColor(ColorF arbitraryBorderColor, out bool cantConstrain) - { - float r = arbitraryBorderColor.Red; - float g = arbitraryBorderColor.Green; - float b = arbitraryBorderColor.Blue; - float a = arbitraryBorderColor.Alpha; - - if (r == 0f && g == 0f && b == 0f) - { - if (a == 1f) - { - cantConstrain = false; - return MTLSamplerBorderColor.OpaqueBlack; - } - - if (a == 0f) - { - cantConstrain = false; - return MTLSamplerBorderColor.TransparentBlack; - } - } - else if (r == 1f && g == 1f && b == 1f && a == 1f) - { - cantConstrain = false; - return MTLSamplerBorderColor.OpaqueWhite; - } - - cantConstrain = true; - return MTLSamplerBorderColor.OpaqueBlack; - } - - public Auto GetSampler() - { - return _sampler; - } - - public void Dispose() - { - if (_renderer.Samplers.Remove(this)) - { - _sampler.Dispose(); - } - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/Shaders/Blit.metal b/src/Ryujinx.Graphics.Metal/Shaders/Blit.metal deleted file mode 100644 index 887878499..000000000 --- a/src/Ryujinx.Graphics.Metal/Shaders/Blit.metal +++ /dev/null @@ -1,43 +0,0 @@ -#include - -using namespace metal; - -struct CopyVertexOut { - float4 position [[position]]; - float2 uv; -}; - -struct TexCoords { - float data[4]; -}; - -struct ConstantBuffers { - constant TexCoords* tex_coord; -}; - -struct Textures -{ - texture2d texture; - sampler sampler; -}; - -vertex CopyVertexOut vertexMain(uint vid [[vertex_id]], - constant ConstantBuffers &constant_buffers [[buffer(CONSTANT_BUFFERS_INDEX)]]) { - CopyVertexOut out; - - int low = vid & 1; - int high = vid >> 1; - out.uv.x = constant_buffers.tex_coord->data[low]; - out.uv.y = constant_buffers.tex_coord->data[2 + high]; - out.position.x = (float(low) - 0.5f) * 2.0f; - out.position.y = (float(high) - 0.5f) * 2.0f; - out.position.z = 0.0f; - out.position.w = 1.0f; - - return out; -} - -fragment FORMAT4 fragmentMain(CopyVertexOut in [[stage_in]], - constant Textures &textures [[buffer(TEXTURES_INDEX)]]) { - return textures.texture.sample(textures.sampler, in.uv); -} diff --git a/src/Ryujinx.Graphics.Metal/Shaders/BlitMs.metal b/src/Ryujinx.Graphics.Metal/Shaders/BlitMs.metal deleted file mode 100644 index 1077b6cea..000000000 --- a/src/Ryujinx.Graphics.Metal/Shaders/BlitMs.metal +++ /dev/null @@ -1,45 +0,0 @@ -#include - -using namespace metal; - -struct CopyVertexOut { - float4 position [[position]]; - float2 uv; -}; - -struct TexCoords { - float data[4]; -}; - -struct ConstantBuffers { - constant TexCoords* tex_coord; -}; - -struct Textures -{ - texture2d_ms texture; -}; - -vertex CopyVertexOut vertexMain(uint vid [[vertex_id]], - constant ConstantBuffers &constant_buffers [[buffer(CONSTANT_BUFFERS_INDEX)]]) { - CopyVertexOut out; - - int low = vid & 1; - int high = vid >> 1; - out.uv.x = constant_buffers.tex_coord->data[low]; - out.uv.y = constant_buffers.tex_coord->data[2 + high]; - out.position.x = (float(low) - 0.5f) * 2.0f; - out.position.y = (float(high) - 0.5f) * 2.0f; - out.position.z = 0.0f; - out.position.w = 1.0f; - - return out; -} - -fragment FORMAT4 fragmentMain(CopyVertexOut in [[stage_in]], - constant Textures &textures [[buffer(TEXTURES_INDEX)]], - uint sample_id [[sample_id]]) { - uint2 tex_size = uint2(textures.texture.get_width(), textures.texture.get_height()); - uint2 tex_coord = uint2(in.uv * float2(tex_size)); - return textures.texture.read(tex_coord, sample_id); -} diff --git a/src/Ryujinx.Graphics.Metal/Shaders/ChangeBufferStride.metal b/src/Ryujinx.Graphics.Metal/Shaders/ChangeBufferStride.metal deleted file mode 100644 index 1a7d2c574..000000000 --- a/src/Ryujinx.Graphics.Metal/Shaders/ChangeBufferStride.metal +++ /dev/null @@ -1,72 +0,0 @@ -#include - -using namespace metal; - -struct StrideArguments { - int4 data; -}; - -struct InData { - uint8_t data[1]; -}; - -struct OutData { - uint8_t data[1]; -}; - -struct ConstantBuffers { - constant StrideArguments* stride_arguments; -}; - -struct StorageBuffers { - device InData* in_data; - device OutData* out_data; -}; - -kernel void kernelMain(constant ConstantBuffers &constant_buffers [[buffer(CONSTANT_BUFFERS_INDEX)]], - device StorageBuffers &storage_buffers [[buffer(STORAGE_BUFFERS_INDEX)]], - uint3 thread_position_in_grid [[thread_position_in_grid]], - uint3 threads_per_threadgroup [[threads_per_threadgroup]], - uint3 threadgroups_per_grid [[threadgroups_per_grid]]) -{ - // Determine what slice of the stride copies this invocation will perform. - - int sourceStride = constant_buffers.stride_arguments->data.x; - int targetStride = constant_buffers.stride_arguments->data.y; - int bufferSize = constant_buffers.stride_arguments->data.z; - int sourceOffset = constant_buffers.stride_arguments->data.w; - - int strideRemainder = targetStride - sourceStride; - int invocations = int(threads_per_threadgroup.x * threadgroups_per_grid.x); - - int copiesRequired = bufferSize / sourceStride; - - // Find the copies that this invocation should perform. - - // - Copies that all invocations perform. - int allInvocationCopies = copiesRequired / invocations; - - // - Extra remainder copy that this invocation performs. - int index = int(thread_position_in_grid.x); - int extra = (index < (copiesRequired % invocations)) ? 1 : 0; - - int copyCount = allInvocationCopies + extra; - - // Finally, get the starting offset. Make sure to count extra copies. - - int startCopy = allInvocationCopies * index + min(copiesRequired % invocations, index); - - int srcOffset = sourceOffset + startCopy * sourceStride; - int dstOffset = startCopy * targetStride; - - // Perform the copies for this region - for (int i = 0; i < copyCount; i++) { - for (int j = 0; j < sourceStride; j++) { - storage_buffers.out_data->data[dstOffset++] = storage_buffers.in_data->data[srcOffset++]; - } - - for (int j = 0; j < strideRemainder; j++) { - storage_buffers.out_data->data[dstOffset++] = uint8_t(0); - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/Shaders/ColorClear.metal b/src/Ryujinx.Graphics.Metal/Shaders/ColorClear.metal deleted file mode 100644 index 46a57e035..000000000 --- a/src/Ryujinx.Graphics.Metal/Shaders/ColorClear.metal +++ /dev/null @@ -1,38 +0,0 @@ -#include - -using namespace metal; - -struct VertexOut { - float4 position [[position]]; -}; - -struct ClearColor { - FORMAT4 data; -}; - -struct ConstantBuffers { - constant ClearColor* clear_color; -}; - -vertex VertexOut vertexMain(ushort vid [[vertex_id]]) { - int low = vid & 1; - int high = vid >> 1; - - VertexOut out; - - out.position.x = (float(low) - 0.5f) * 2.0f; - out.position.y = (float(high) - 0.5f) * 2.0f; - out.position.z = 0.0f; - out.position.w = 1.0f; - - return out; -} - -struct FragmentOut { - FORMAT4 color [[color(COLOR_ATTACHMENT_INDEX)]]; -}; - -fragment FragmentOut fragmentMain(VertexOut in [[stage_in]], - constant ConstantBuffers &constant_buffers [[buffer(CONSTANT_BUFFERS_INDEX)]]) { - return {constant_buffers.clear_color->data}; -} diff --git a/src/Ryujinx.Graphics.Metal/Shaders/ConvertD32S8ToD24S8.metal b/src/Ryujinx.Graphics.Metal/Shaders/ConvertD32S8ToD24S8.metal deleted file mode 100644 index 870ac3d78..000000000 --- a/src/Ryujinx.Graphics.Metal/Shaders/ConvertD32S8ToD24S8.metal +++ /dev/null @@ -1,66 +0,0 @@ -#include - -using namespace metal; - -struct StrideArguments { - int pixelCount; - int dstStartOffset; -}; - -struct InData { - uint data[1]; -}; - -struct OutData { - uint data[1]; -}; - -struct ConstantBuffers { - constant StrideArguments* stride_arguments; -}; - -struct StorageBuffers { - device InData* in_data; - device OutData* out_data; -}; - -kernel void kernelMain(constant ConstantBuffers &constant_buffers [[buffer(CONSTANT_BUFFERS_INDEX)]], - device StorageBuffers &storage_buffers [[buffer(STORAGE_BUFFERS_INDEX)]], - uint3 thread_position_in_grid [[thread_position_in_grid]], - uint3 threads_per_threadgroup [[threads_per_threadgroup]], - uint3 threadgroups_per_grid [[threadgroups_per_grid]]) -{ - // Determine what slice of the stride copies this invocation will perform. - int invocations = int(threads_per_threadgroup.x * threadgroups_per_grid.x); - - int copiesRequired = constant_buffers.stride_arguments->pixelCount; - - // Find the copies that this invocation should perform. - - // - Copies that all invocations perform. - int allInvocationCopies = copiesRequired / invocations; - - // - Extra remainder copy that this invocation performs. - int index = int(thread_position_in_grid.x); - int extra = (index < (copiesRequired % invocations)) ? 1 : 0; - - int copyCount = allInvocationCopies + extra; - - // Finally, get the starting offset. Make sure to count extra copies. - - int startCopy = allInvocationCopies * index + min(copiesRequired % invocations, index); - - int srcOffset = startCopy * 2; - int dstOffset = constant_buffers.stride_arguments->dstStartOffset + startCopy; - - // Perform the conversion for this region. - for (int i = 0; i < copyCount; i++) - { - float depth = as_type(storage_buffers.in_data->data[srcOffset++]); - uint stencil = storage_buffers.in_data->data[srcOffset++]; - - uint rescaledDepth = uint(clamp(depth, 0.0, 1.0) * 16777215.0); - - storage_buffers.out_data->data[dstOffset++] = (rescaledDepth << 8) | (stencil & 0xff); - } -} diff --git a/src/Ryujinx.Graphics.Metal/Shaders/ConvertIndexBuffer.metal b/src/Ryujinx.Graphics.Metal/Shaders/ConvertIndexBuffer.metal deleted file mode 100644 index c8fee5818..000000000 --- a/src/Ryujinx.Graphics.Metal/Shaders/ConvertIndexBuffer.metal +++ /dev/null @@ -1,59 +0,0 @@ -#include - -using namespace metal; - -struct IndexBufferPattern { - int pattern[8]; - int primitiveVertices; - int primitiveVerticesOut; - int indexSize; - int indexSizeOut; - int baseIndex; - int indexStride; - int srcOffset; - int totalPrimitives; -}; - -struct InData { - uint8_t data[1]; -}; - -struct OutData { - uint8_t data[1]; -}; - -struct StorageBuffers { - device InData* in_data; - device OutData* out_data; - constant IndexBufferPattern* index_buffer_pattern; -}; - -kernel void kernelMain(device StorageBuffers &storage_buffers [[buffer(STORAGE_BUFFERS_INDEX)]], - uint3 thread_position_in_grid [[thread_position_in_grid]]) -{ - int primitiveIndex = int(thread_position_in_grid.x); - if (primitiveIndex >= storage_buffers.index_buffer_pattern->totalPrimitives) - { - return; - } - - int inOffset = primitiveIndex * storage_buffers.index_buffer_pattern->indexStride; - int outOffset = primitiveIndex * storage_buffers.index_buffer_pattern->primitiveVerticesOut; - - for (int i = 0; i < storage_buffers.index_buffer_pattern->primitiveVerticesOut; i++) - { - int j; - int io = max(0, inOffset + storage_buffers.index_buffer_pattern->baseIndex + storage_buffers.index_buffer_pattern->pattern[i]) * storage_buffers.index_buffer_pattern->indexSize; - int oo = (outOffset + i) * storage_buffers.index_buffer_pattern->indexSizeOut; - - for (j = 0; j < storage_buffers.index_buffer_pattern->indexSize; j++) - { - storage_buffers.out_data->data[oo + j] = storage_buffers.in_data->data[storage_buffers.index_buffer_pattern->srcOffset + io + j]; - } - - for(; j < storage_buffers.index_buffer_pattern->indexSizeOut; j++) - { - storage_buffers.out_data->data[oo + j] = uint8_t(0); - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/Shaders/DepthBlit.metal b/src/Ryujinx.Graphics.Metal/Shaders/DepthBlit.metal deleted file mode 100644 index 8b8467c2f..000000000 --- a/src/Ryujinx.Graphics.Metal/Shaders/DepthBlit.metal +++ /dev/null @@ -1,27 +0,0 @@ -#include - -using namespace metal; - -struct CopyVertexOut { - float4 position [[position]]; - float2 uv; -}; - -struct Textures -{ - texture2d texture; - sampler sampler; -}; - -struct FragmentOut { - float depth [[depth(any)]]; -}; - -fragment FragmentOut fragmentMain(CopyVertexOut in [[stage_in]], - constant Textures &textures [[buffer(TEXTURES_INDEX)]]) { - FragmentOut out; - - out.depth = textures.texture.sample(textures.sampler, in.uv).r; - - return out; -} diff --git a/src/Ryujinx.Graphics.Metal/Shaders/DepthBlitMs.metal b/src/Ryujinx.Graphics.Metal/Shaders/DepthBlitMs.metal deleted file mode 100644 index 10791f636..000000000 --- a/src/Ryujinx.Graphics.Metal/Shaders/DepthBlitMs.metal +++ /dev/null @@ -1,29 +0,0 @@ -#include - -using namespace metal; - -struct CopyVertexOut { - float4 position [[position]]; - float2 uv; -}; - -struct Textures -{ - texture2d_ms texture; -}; - -struct FragmentOut { - float depth [[depth(any)]]; -}; - -fragment FragmentOut fragmentMain(CopyVertexOut in [[stage_in]], - constant Textures &textures [[buffer(TEXTURES_INDEX)]], - uint sample_id [[sample_id]]) { - FragmentOut out; - - uint2 tex_size = uint2(textures.texture.get_width(), textures.texture.get_height()); - uint2 tex_coord = uint2(in.uv * float2(tex_size)); - out.depth = textures.texture.read(tex_coord, sample_id).r; - - return out; -} diff --git a/src/Ryujinx.Graphics.Metal/Shaders/DepthStencilClear.metal b/src/Ryujinx.Graphics.Metal/Shaders/DepthStencilClear.metal deleted file mode 100644 index 7e50f2ce7..000000000 --- a/src/Ryujinx.Graphics.Metal/Shaders/DepthStencilClear.metal +++ /dev/null @@ -1,42 +0,0 @@ -#include - -using namespace metal; - -struct VertexOut { - float4 position [[position]]; -}; - -struct FragmentOut { - float depth [[depth(any)]]; -}; - -struct ClearDepth { - float data; -}; - -struct ConstantBuffers { - constant ClearDepth* clear_depth; -}; - -vertex VertexOut vertexMain(ushort vid [[vertex_id]]) { - int low = vid & 1; - int high = vid >> 1; - - VertexOut out; - - out.position.x = (float(low) - 0.5f) * 2.0f; - out.position.y = (float(high) - 0.5f) * 2.0f; - out.position.z = 0.0f; - out.position.w = 1.0f; - - return out; -} - -fragment FragmentOut fragmentMain(VertexOut in [[stage_in]], - constant ConstantBuffers &constant_buffers [[buffer(CONSTANT_BUFFERS_INDEX)]]) { - FragmentOut out; - - out.depth = constant_buffers.clear_depth->data; - - return out; -} diff --git a/src/Ryujinx.Graphics.Metal/Shaders/StencilBlit.metal b/src/Ryujinx.Graphics.Metal/Shaders/StencilBlit.metal deleted file mode 100644 index 0b25f322d..000000000 --- a/src/Ryujinx.Graphics.Metal/Shaders/StencilBlit.metal +++ /dev/null @@ -1,27 +0,0 @@ -#include - -using namespace metal; - -struct CopyVertexOut { - float4 position [[position]]; - float2 uv; -}; - -struct Textures -{ - texture2d texture; - sampler sampler; -}; - -struct FragmentOut { - uint stencil [[stencil]]; -}; - -fragment FragmentOut fragmentMain(CopyVertexOut in [[stage_in]], - constant Textures &textures [[buffer(TEXTURES_INDEX)]]) { - FragmentOut out; - - out.stencil = textures.texture.sample(textures.sampler, in.uv).r; - - return out; -} diff --git a/src/Ryujinx.Graphics.Metal/Shaders/StencilBlitMs.metal b/src/Ryujinx.Graphics.Metal/Shaders/StencilBlitMs.metal deleted file mode 100644 index e7f2d20b7..000000000 --- a/src/Ryujinx.Graphics.Metal/Shaders/StencilBlitMs.metal +++ /dev/null @@ -1,29 +0,0 @@ -#include - -using namespace metal; - -struct CopyVertexOut { - float4 position [[position]]; - float2 uv; -}; - -struct Textures -{ - texture2d_ms texture; -}; - -struct FragmentOut { - uint stencil [[stencil]]; -}; - -fragment FragmentOut fragmentMain(CopyVertexOut in [[stage_in]], - constant Textures &textures [[buffer(TEXTURES_INDEX)]], - uint sample_id [[sample_id]]) { - FragmentOut out; - - uint2 tex_size = uint2(textures.texture.get_width(), textures.texture.get_height()); - uint2 tex_coord = uint2(in.uv * float2(tex_size)); - out.stencil = textures.texture.read(tex_coord, sample_id).r; - - return out; -} diff --git a/src/Ryujinx.Graphics.Metal/StagingBuffer.cs b/src/Ryujinx.Graphics.Metal/StagingBuffer.cs deleted file mode 100644 index b4838ee33..000000000 --- a/src/Ryujinx.Graphics.Metal/StagingBuffer.cs +++ /dev/null @@ -1,288 +0,0 @@ -using Ryujinx.Common; -using Ryujinx.Common.Logging; -using Ryujinx.Graphics.GAL; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - readonly struct StagingBufferReserved - { - public readonly BufferHolder Buffer; - public readonly int Offset; - public readonly int Size; - - public StagingBufferReserved(BufferHolder buffer, int offset, int size) - { - Buffer = buffer; - Offset = offset; - Size = size; - } - } - - [SupportedOSPlatform("macos")] - class StagingBuffer : IDisposable - { - private const int BufferSize = 32 * 1024 * 1024; - - private int _freeOffset; - private int _freeSize; - - private readonly MetalRenderer _renderer; - private readonly BufferHolder _buffer; - private readonly int _resourceAlignment; - - public readonly BufferHandle Handle; - - private readonly struct PendingCopy - { - public FenceHolder Fence { get; } - public int Size { get; } - - public PendingCopy(FenceHolder fence, int size) - { - Fence = fence; - Size = size; - fence.Get(); - } - } - - private readonly Queue _pendingCopies; - - public StagingBuffer(MetalRenderer renderer, BufferManager bufferManager) - { - _renderer = renderer; - - Handle = bufferManager.CreateWithHandle(BufferSize, out _buffer); - _pendingCopies = new Queue(); - _freeSize = BufferSize; - _resourceAlignment = Constants.MinResourceAlignment; - } - - public void PushData(CommandBufferPool cbp, CommandBufferScoped? cbs, BufferHolder dst, int dstOffset, ReadOnlySpan data) - { - bool isRender = cbs != null; - CommandBufferScoped scoped = cbs ?? cbp.Rent(); - - // Must push all data to the buffer. If it can't fit, split it up. - - while (data.Length > 0) - { - if (_freeSize < data.Length) - { - FreeCompleted(); - } - - while (_freeSize == 0) - { - if (!WaitFreeCompleted(cbp)) - { - if (isRender) - { - _renderer.FlushAllCommands(); - scoped = cbp.Rent(); - isRender = false; - } - else - { - scoped = cbp.ReturnAndRent(scoped); - } - } - } - - int chunkSize = Math.Min(_freeSize, data.Length); - - PushDataImpl(scoped, dst, dstOffset, data[..chunkSize]); - - dstOffset += chunkSize; - data = data[chunkSize..]; - } - - if (!isRender) - { - scoped.Dispose(); - } - } - - private void PushDataImpl(CommandBufferScoped cbs, BufferHolder dst, int dstOffset, ReadOnlySpan data) - { - Auto srcBuffer = _buffer.GetBuffer(); - Auto dstBuffer = dst.GetBuffer(dstOffset, data.Length, true); - - int offset = _freeOffset; - int capacity = BufferSize - offset; - if (capacity < data.Length) - { - _buffer.SetDataUnchecked(offset, data[..capacity]); - _buffer.SetDataUnchecked(0, data[capacity..]); - - BufferHolder.Copy(cbs, srcBuffer, dstBuffer, offset, dstOffset, capacity); - BufferHolder.Copy(cbs, srcBuffer, dstBuffer, 0, dstOffset + capacity, data.Length - capacity); - } - else - { - _buffer.SetDataUnchecked(offset, data); - - BufferHolder.Copy(cbs, srcBuffer, dstBuffer, offset, dstOffset, data.Length); - } - - _freeOffset = (offset + data.Length) & (BufferSize - 1); - _freeSize -= data.Length; - Debug.Assert(_freeSize >= 0); - - _pendingCopies.Enqueue(new PendingCopy(cbs.GetFence(), data.Length)); - } - - public bool TryPushData(CommandBufferScoped cbs, BufferHolder dst, int dstOffset, ReadOnlySpan data) - { - if (data.Length > BufferSize) - { - return false; - } - - if (_freeSize < data.Length) - { - FreeCompleted(); - - if (_freeSize < data.Length) - { - return false; - } - } - - PushDataImpl(cbs, dst, dstOffset, data); - - return true; - } - - private StagingBufferReserved ReserveDataImpl(CommandBufferScoped cbs, int size, int alignment) - { - // Assumes the caller has already determined that there is enough space. - int offset = BitUtils.AlignUp(_freeOffset, alignment); - int padding = offset - _freeOffset; - - int capacity = Math.Min(_freeSize, BufferSize - offset); - int reservedLength = size + padding; - if (capacity < size) - { - offset = 0; // Place at start. - reservedLength += capacity; - } - - _freeOffset = (_freeOffset + reservedLength) & (BufferSize - 1); - _freeSize -= reservedLength; - Debug.Assert(_freeSize >= 0); - - _pendingCopies.Enqueue(new PendingCopy(cbs.GetFence(), reservedLength)); - - return new StagingBufferReserved(_buffer, offset, size); - } - - private int GetContiguousFreeSize(int alignment) - { - int alignedFreeOffset = BitUtils.AlignUp(_freeOffset, alignment); - int padding = alignedFreeOffset - _freeOffset; - - // Free regions: - // - Aligned free offset to end (minimum free size - padding) - // - 0 to _freeOffset + freeSize wrapped (only if free area contains 0) - - int endOffset = (_freeOffset + _freeSize) & (BufferSize - 1); - - return Math.Max( - Math.Min(_freeSize - padding, BufferSize - alignedFreeOffset), - endOffset <= _freeOffset ? Math.Min(_freeSize, endOffset) : 0 - ); - } - - /// - /// Reserve a range on the staging buffer for the current command buffer and upload data to it. - /// - /// Command buffer to reserve the data on - /// The minimum size the reserved data requires - /// The required alignment for the buffer offset - /// The reserved range of the staging buffer - public StagingBufferReserved? TryReserveData(CommandBufferScoped cbs, int size, int alignment) - { - if (size > BufferSize) - { - return null; - } - - // Temporary reserved data cannot be fragmented. - - if (GetContiguousFreeSize(alignment) < size) - { - FreeCompleted(); - - if (GetContiguousFreeSize(alignment) < size) - { - Logger.Debug?.PrintMsg(LogClass.Gpu, $"Staging buffer out of space to reserve data of size {size}."); - return null; - } - } - - return ReserveDataImpl(cbs, size, alignment); - } - - /// - /// Reserve a range on the staging buffer for the current command buffer and upload data to it. - /// Uses the most permissive byte alignment. - /// - /// Command buffer to reserve the data on - /// The minimum size the reserved data requires - /// The reserved range of the staging buffer - public StagingBufferReserved? TryReserveData(CommandBufferScoped cbs, int size) - { - return TryReserveData(cbs, size, _resourceAlignment); - } - - private bool WaitFreeCompleted(CommandBufferPool cbp) - { - if (_pendingCopies.TryPeek(out PendingCopy pc)) - { - if (!pc.Fence.IsSignaled()) - { - if (cbp.IsFenceOnRentedCommandBuffer(pc.Fence)) - { - return false; - } - - pc.Fence.Wait(); - } - - PendingCopy dequeued = _pendingCopies.Dequeue(); - Debug.Assert(dequeued.Fence == pc.Fence); - _freeSize += pc.Size; - pc.Fence.Put(); - } - - return true; - } - - public void FreeCompleted() - { - FenceHolder signalledFence = null; - while (_pendingCopies.TryPeek(out PendingCopy pc) && (pc.Fence == signalledFence || pc.Fence.IsSignaled())) - { - signalledFence = pc.Fence; // Already checked - don't need to do it again. - PendingCopy dequeued = _pendingCopies.Dequeue(); - Debug.Assert(dequeued.Fence == pc.Fence); - _freeSize += pc.Size; - pc.Fence.Put(); - } - } - - public void Dispose() - { - _renderer.BufferManager.Delete(Handle); - - while (_pendingCopies.TryDequeue(out PendingCopy pc)) - { - pc.Fence.Put(); - } - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/State/DepthStencilUid.cs b/src/Ryujinx.Graphics.Metal/State/DepthStencilUid.cs deleted file mode 100644 index 63b1d8ef4..000000000 --- a/src/Ryujinx.Graphics.Metal/State/DepthStencilUid.cs +++ /dev/null @@ -1,110 +0,0 @@ -using SharpMetal.Metal; -using System; -using System.Runtime.InteropServices; -using System.Runtime.Intrinsics; - -namespace Ryujinx.Graphics.Metal.State -{ - [StructLayout(LayoutKind.Sequential, Pack = 1)] - public struct StencilUid - { - public uint ReadMask; - public uint WriteMask; - public ushort Operations; - - public MTLStencilOperation StencilFailureOperation - { - readonly get => (MTLStencilOperation)((Operations >> 0) & 0xF); - set => Operations = (ushort)((Operations & 0xFFF0) | ((int)value << 0)); - } - - public MTLStencilOperation DepthFailureOperation - { - readonly get => (MTLStencilOperation)((Operations >> 4) & 0xF); - set => Operations = (ushort)((Operations & 0xFF0F) | ((int)value << 4)); - } - - public MTLStencilOperation DepthStencilPassOperation - { - readonly get => (MTLStencilOperation)((Operations >> 8) & 0xF); - set => Operations = (ushort)((Operations & 0xF0FF) | ((int)value << 8)); - } - - public MTLCompareFunction StencilCompareFunction - { - readonly get => (MTLCompareFunction)((Operations >> 12) & 0xF); - set => Operations = (ushort)((Operations & 0x0FFF) | ((int)value << 12)); - } - } - - - [StructLayout(LayoutKind.Explicit, Size = 24)] - internal struct DepthStencilUid : IEquatable - { - [FieldOffset(0)] - public StencilUid FrontFace; - - [FieldOffset(10)] - public ushort DepthState; - - [FieldOffset(12)] - public StencilUid BackFace; - - [FieldOffset(22)] - private readonly ushort _padding; - - // Quick access aliases -#pragma warning disable IDE0044 // Add readonly modifier - [FieldOffset(0)] - private ulong _id0; - [FieldOffset(8)] - private ulong _id1; - [FieldOffset(0)] - private Vector128 _id01; - [FieldOffset(16)] - private ulong _id2; -#pragma warning restore IDE0044 // Add readonly modifier - - public MTLCompareFunction DepthCompareFunction - { - readonly get => (MTLCompareFunction)((DepthState >> 0) & 0xF); - set => DepthState = (ushort)((DepthState & 0xFFF0) | ((int)value << 0)); - } - - public bool StencilTestEnabled - { - readonly get => ((DepthState >> 4) & 0x1) != 0; - set => DepthState = (ushort)((DepthState & 0xFFEF) | ((value ? 1 : 0) << 4)); - } - - public bool DepthWriteEnabled - { - readonly get => ((DepthState >> 15) & 0x1) != 0; - set => DepthState = (ushort)((DepthState & 0x7FFF) | ((value ? 1 : 0) << 15)); - } - - public readonly override bool Equals(object obj) - { - return obj is DepthStencilUid other && EqualsRef(ref other); - } - - public readonly bool EqualsRef(ref DepthStencilUid other) - { - return _id01.Equals(other._id01) && _id2 == other._id2; - } - - public readonly bool Equals(DepthStencilUid other) - { - return EqualsRef(ref other); - } - - public readonly override int GetHashCode() - { - ulong hash64 = _id0 * 23 ^ - _id1 * 23 ^ - _id2 * 23; - - return (int)hash64 ^ ((int)(hash64 >> 32) * 17); - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/State/PipelineState.cs b/src/Ryujinx.Graphics.Metal/State/PipelineState.cs deleted file mode 100644 index 14073dbe1..000000000 --- a/src/Ryujinx.Graphics.Metal/State/PipelineState.cs +++ /dev/null @@ -1,341 +0,0 @@ -using Ryujinx.Common.Logging; -using Ryujinx.Graphics.GAL; -using SharpMetal.Foundation; -using SharpMetal.Metal; -using System; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - struct PipelineState - { - public PipelineUid Internal; - - public uint StagesCount - { - readonly get => (byte)((Internal.Id0 >> 0) & 0xFF); - set => Internal.Id0 = (Internal.Id0 & 0xFFFFFFFFFFFFFF00) | ((ulong)value << 0); - } - - public uint VertexAttributeDescriptionsCount - { - readonly get => (byte)((Internal.Id0 >> 8) & 0xFF); - set => Internal.Id0 = (Internal.Id0 & 0xFFFFFFFFFFFF00FF) | ((ulong)value << 8); - } - - public uint VertexBindingDescriptionsCount - { - readonly get => (byte)((Internal.Id0 >> 16) & 0xFF); - set => Internal.Id0 = (Internal.Id0 & 0xFFFFFFFFFF00FFFF) | ((ulong)value << 16); - } - - public uint ColorBlendAttachmentStateCount - { - readonly get => (byte)((Internal.Id0 >> 24) & 0xFF); - set => Internal.Id0 = (Internal.Id0 & 0xFFFFFFFF00FFFFFF) | ((ulong)value << 24); - } - - /* - * Can be an input to a pipeline, but not sure what the situation for that is. - public PrimitiveTopology Topology - { - readonly get => (PrimitiveTopology)((Internal.Id6 >> 16) & 0xF); - set => Internal.Id6 = (Internal.Id6 & 0xFFFFFFFFFFF0FFFF) | ((ulong)value << 16); - } - */ - - public MTLLogicOperation LogicOp - { - readonly get => (MTLLogicOperation)((Internal.Id0 >> 32) & 0xF); - set => Internal.Id0 = (Internal.Id0 & 0xFFFFFFF0FFFFFFFF) | ((ulong)value << 32); - } - - //? - public bool PrimitiveRestartEnable - { - readonly get => ((Internal.Id0 >> 36) & 0x1) != 0UL; - set => Internal.Id0 = (Internal.Id0 & 0xFFFFFFEFFFFFFFFF) | ((value ? 1UL : 0UL) << 36); - } - - public bool RasterizerDiscardEnable - { - readonly get => ((Internal.Id0 >> 37) & 0x1) != 0UL; - set => Internal.Id0 = (Internal.Id0 & 0xFFFFFFDFFFFFFFFF) | ((value ? 1UL : 0UL) << 37); - } - - public bool LogicOpEnable - { - readonly get => ((Internal.Id0 >> 38) & 0x1) != 0UL; - set => Internal.Id0 = (Internal.Id0 & 0xFFFFFFBFFFFFFFFF) | ((value ? 1UL : 0UL) << 38); - } - - public bool AlphaToCoverageEnable - { - readonly get => ((Internal.Id0 >> 40) & 0x1) != 0UL; - set => Internal.Id0 = (Internal.Id0 & 0xFFFFFEFFFFFFFFFF) | ((value ? 1UL : 0UL) << 40); - } - - public bool AlphaToOneEnable - { - readonly get => ((Internal.Id0 >> 41) & 0x1) != 0UL; - set => Internal.Id0 = (Internal.Id0 & 0xFFFFFDFFFFFFFFFF) | ((value ? 1UL : 0UL) << 41); - } - - public MTLPixelFormat DepthStencilFormat - { - readonly get => (MTLPixelFormat)(Internal.Id0 >> 48); - set => Internal.Id0 = (Internal.Id0 & 0x0000FFFFFFFFFFFF) | ((ulong)value << 48); - } - - // Not sure how to appropriately use this, but it does need to be passed for tess. - public uint PatchControlPoints - { - readonly get => (uint)((Internal.Id1 >> 0) & 0xFFFFFFFF); - set => Internal.Id1 = (Internal.Id1 & 0xFFFFFFFF00000000) | ((ulong)value << 0); - } - - public uint SamplesCount - { - readonly get => (uint)((Internal.Id1 >> 32) & 0xFFFFFFFF); - set => Internal.Id1 = (Internal.Id1 & 0xFFFFFFFF) | ((ulong)value << 32); - } - - // Advanced blend not supported - - private readonly void BuildColorAttachment(MTLRenderPipelineColorAttachmentDescriptor descriptor, ColorBlendStateUid blendState) - { - descriptor.PixelFormat = blendState.PixelFormat; - descriptor.SetBlendingEnabled(blendState.Enable); - descriptor.AlphaBlendOperation = blendState.AlphaBlendOperation; - descriptor.RgbBlendOperation = blendState.RgbBlendOperation; - descriptor.SourceAlphaBlendFactor = blendState.SourceAlphaBlendFactor; - descriptor.DestinationAlphaBlendFactor = blendState.DestinationAlphaBlendFactor; - descriptor.SourceRGBBlendFactor = blendState.SourceRGBBlendFactor; - descriptor.DestinationRGBBlendFactor = blendState.DestinationRGBBlendFactor; - descriptor.WriteMask = blendState.WriteMask; - } - - private readonly MTLVertexDescriptor BuildVertexDescriptor() - { - MTLVertexDescriptor vertexDescriptor = new(); - - for (int i = 0; i < VertexAttributeDescriptionsCount; i++) - { - VertexInputAttributeUid uid = Internal.VertexAttributes[i]; - - MTLVertexAttributeDescriptor attrib = vertexDescriptor.Attributes.Object((ulong)i); - attrib.Format = uid.Format; - attrib.Offset = uid.Offset; - attrib.BufferIndex = uid.BufferIndex; - } - - for (int i = 0; i < VertexBindingDescriptionsCount; i++) - { - VertexInputLayoutUid uid = Internal.VertexBindings[i]; - - MTLVertexBufferLayoutDescriptor layout = vertexDescriptor.Layouts.Object((ulong)i); - - layout.StepFunction = uid.StepFunction; - layout.StepRate = uid.StepRate; - layout.Stride = uid.Stride; - } - - return vertexDescriptor; - } - - private MTLRenderPipelineDescriptor CreateRenderDescriptor(Program program) - { - MTLRenderPipelineDescriptor renderPipelineDescriptor = new(); - - for (int i = 0; i < Constants.MaxColorAttachments; i++) - { - ColorBlendStateUid blendState = Internal.ColorBlendState[i]; - - if (blendState.PixelFormat != MTLPixelFormat.Invalid) - { - MTLRenderPipelineColorAttachmentDescriptor pipelineAttachment = renderPipelineDescriptor.ColorAttachments.Object((ulong)i); - - BuildColorAttachment(pipelineAttachment, blendState); - } - } - - MTLPixelFormat dsFormat = DepthStencilFormat; - if (dsFormat != MTLPixelFormat.Invalid) - { - switch (dsFormat) - { - // Depth Only Attachment - case MTLPixelFormat.Depth16Unorm: - case MTLPixelFormat.Depth32Float: - renderPipelineDescriptor.DepthAttachmentPixelFormat = dsFormat; - break; - - // Stencil Only Attachment - case MTLPixelFormat.Stencil8: - renderPipelineDescriptor.StencilAttachmentPixelFormat = dsFormat; - break; - - // Combined Attachment - case MTLPixelFormat.Depth24UnormStencil8: - case MTLPixelFormat.Depth32FloatStencil8: - renderPipelineDescriptor.DepthAttachmentPixelFormat = dsFormat; - renderPipelineDescriptor.StencilAttachmentPixelFormat = dsFormat; - break; - default: - Logger.Error?.PrintMsg(LogClass.Gpu, $"Unsupported Depth/Stencil Format: {dsFormat}!"); - break; - } - } - - renderPipelineDescriptor.LogicOperationEnabled = LogicOpEnable; - renderPipelineDescriptor.LogicOperation = LogicOp; - renderPipelineDescriptor.AlphaToCoverageEnabled = AlphaToCoverageEnable; - renderPipelineDescriptor.AlphaToOneEnabled = AlphaToOneEnable; - renderPipelineDescriptor.RasterizationEnabled = !RasterizerDiscardEnable; - renderPipelineDescriptor.SampleCount = Math.Max(1, SamplesCount); - - MTLVertexDescriptor vertexDescriptor = BuildVertexDescriptor(); - renderPipelineDescriptor.VertexDescriptor = vertexDescriptor; - - renderPipelineDescriptor.VertexFunction = program.VertexFunction; - - if (program.FragmentFunction.NativePtr != 0) - { - renderPipelineDescriptor.FragmentFunction = program.FragmentFunction; - } - - return renderPipelineDescriptor; - } - - public MTLRenderPipelineState CreateRenderPipeline(MTLDevice device, Program program) - { - if (program.TryGetGraphicsPipeline(ref Internal, out MTLRenderPipelineState pipelineState)) - { - return pipelineState; - } - - using MTLRenderPipelineDescriptor descriptor = CreateRenderDescriptor(program); - - NSError error = new(IntPtr.Zero); - pipelineState = device.NewRenderPipelineState(descriptor, ref error); - if (error != IntPtr.Zero) - { - Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to create Render Pipeline State: {StringHelper.String(error.LocalizedDescription)}"); - } - - program.AddGraphicsPipeline(ref Internal, pipelineState); - - return pipelineState; - } - - public static MTLComputePipelineDescriptor CreateComputeDescriptor(Program program) - { - ComputeSize localSize = program.ComputeLocalSize; - - uint maxThreads = (uint)(localSize.X * localSize.Y * localSize.Z); - - if (maxThreads == 0) - { - throw new InvalidOperationException($"Local thread size for compute cannot be 0 in any dimension."); - } - - MTLComputePipelineDescriptor descriptor = new() - { - ComputeFunction = program.ComputeFunction, - MaxTotalThreadsPerThreadgroup = maxThreads, - ThreadGroupSizeIsMultipleOfThreadExecutionWidth = true, - }; - - return descriptor; - } - - public static MTLComputePipelineState CreateComputePipeline(MTLDevice device, Program program) - { - if (program.TryGetComputePipeline(out MTLComputePipelineState pipelineState)) - { - return pipelineState; - } - - using MTLComputePipelineDescriptor descriptor = CreateComputeDescriptor(program); - - NSError error = new(IntPtr.Zero); - pipelineState = device.NewComputePipelineState(descriptor, MTLPipelineOption.None, 0, ref error); - if (error != IntPtr.Zero) - { - Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to create Compute Pipeline State: {StringHelper.String(error.LocalizedDescription)}"); - } - - program.AddComputePipeline(pipelineState); - - return pipelineState; - } - - public void Initialize() - { - SamplesCount = 1; - - Internal.ResetColorState(); - } - - /* - * TODO, this is from vulkan. - - private void UpdateVertexAttributeDescriptions(VulkanRenderer gd) - { - // Vertex attributes exceeding the stride are invalid. - // In metal, they cause glitches with the vertex shader fetching incorrect values. - // To work around this, we reduce the format to something that doesn't exceed the stride if possible. - // The assumption is that the exceeding components are not actually accessed on the shader. - - for (int index = 0; index < VertexAttributeDescriptionsCount; index++) - { - var attribute = Internal.VertexAttributeDescriptions[index]; - int vbIndex = GetVertexBufferIndex(attribute.Binding); - - if (vbIndex >= 0) - { - ref var vb = ref Internal.VertexBindingDescriptions[vbIndex]; - - Format format = attribute.Format; - - while (vb.Stride != 0 && attribute.Offset + FormatTable.GetAttributeFormatSize(format) > vb.Stride) - { - Format newFormat = FormatTable.DropLastComponent(format); - - if (newFormat == format) - { - // That case means we failed to find a format that fits within the stride, - // so just restore the original format and give up. - format = attribute.Format; - break; - } - - format = newFormat; - } - - if (attribute.Format != format && gd.FormatCapabilities.BufferFormatSupports(FormatFeatureFlags.VertexBufferBit, format)) - { - attribute.Format = format; - } - } - - _vertexAttributeDescriptions2[index] = attribute; - } - } - - private int GetVertexBufferIndex(uint binding) - { - for (int index = 0; index < VertexBindingDescriptionsCount; index++) - { - if (Internal.VertexBindingDescriptions[index].Binding == binding) - { - return index; - } - } - - return -1; - } - */ - } -} diff --git a/src/Ryujinx.Graphics.Metal/State/PipelineUid.cs b/src/Ryujinx.Graphics.Metal/State/PipelineUid.cs deleted file mode 100644 index 5b514c2c9..000000000 --- a/src/Ryujinx.Graphics.Metal/State/PipelineUid.cs +++ /dev/null @@ -1,208 +0,0 @@ -using Ryujinx.Common.Memory; -using SharpMetal.Metal; -using System; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Runtime.Intrinsics; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - struct VertexInputAttributeUid - { - public ulong Id0; - - public ulong Offset - { - readonly get => (uint)((Id0 >> 0) & 0xFFFFFFFF); - set => Id0 = (Id0 & 0xFFFFFFFF00000000) | ((ulong)value << 0); - } - - public MTLVertexFormat Format - { - readonly get => (MTLVertexFormat)((Id0 >> 32) & 0xFFFF); - set => Id0 = (Id0 & 0xFFFF0000FFFFFFFF) | ((ulong)value << 32); - } - - public ulong BufferIndex - { - readonly get => ((Id0 >> 48) & 0xFFFF); - set => Id0 = (Id0 & 0x0000FFFFFFFFFFFF) | ((ulong)value << 48); - } - } - - struct VertexInputLayoutUid - { - public ulong Id0; - - public uint Stride - { - readonly get => (uint)((Id0 >> 0) & 0xFFFFFFFF); - set => Id0 = (Id0 & 0xFFFFFFFF00000000) | ((ulong)value << 0); - } - - public uint StepRate - { - readonly get => (uint)((Id0 >> 32) & 0x1FFFFFFF); - set => Id0 = (Id0 & 0xE0000000FFFFFFFF) | ((ulong)value << 32); - } - - public MTLVertexStepFunction StepFunction - { - readonly get => (MTLVertexStepFunction)((Id0 >> 61) & 0x7); - set => Id0 = (Id0 & 0x1FFFFFFFFFFFFFFF) | ((ulong)value << 61); - } - } - - struct ColorBlendStateUid - { - public ulong Id0; - - public MTLPixelFormat PixelFormat - { - readonly get => (MTLPixelFormat)((Id0 >> 0) & 0xFFFF); - set => Id0 = (Id0 & 0xFFFFFFFFFFFF0000) | ((ulong)value << 0); - } - - public MTLBlendFactor SourceRGBBlendFactor - { - readonly get => (MTLBlendFactor)((Id0 >> 16) & 0xFF); - set => Id0 = (Id0 & 0xFFFFFFFFFF00FFFF) | ((ulong)value << 16); - } - - public MTLBlendFactor DestinationRGBBlendFactor - { - readonly get => (MTLBlendFactor)((Id0 >> 24) & 0xFF); - set => Id0 = (Id0 & 0xFFFFFFFF00FFFFFF) | ((ulong)value << 24); - } - - public MTLBlendOperation RgbBlendOperation - { - readonly get => (MTLBlendOperation)((Id0 >> 32) & 0xF); - set => Id0 = (Id0 & 0xFFFFFFF0FFFFFFFF) | ((ulong)value << 32); - } - - public MTLBlendOperation AlphaBlendOperation - { - readonly get => (MTLBlendOperation)((Id0 >> 36) & 0xF); - set => Id0 = (Id0 & 0xFFFFFF0FFFFFFFFF) | ((ulong)value << 36); - } - - public MTLBlendFactor SourceAlphaBlendFactor - { - readonly get => (MTLBlendFactor)((Id0 >> 40) & 0xFF); - set => Id0 = (Id0 & 0xFFFF00FFFFFFFFFF) | ((ulong)value << 40); - } - - public MTLBlendFactor DestinationAlphaBlendFactor - { - readonly get => (MTLBlendFactor)((Id0 >> 48) & 0xFF); - set => Id0 = (Id0 & 0xFF00FFFFFFFFFFFF) | ((ulong)value << 48); - } - - public MTLColorWriteMask WriteMask - { - readonly get => (MTLColorWriteMask)((Id0 >> 56) & 0xF); - set => Id0 = (Id0 & 0xF0FFFFFFFFFFFFFF) | ((ulong)value << 56); - } - - public bool Enable - { - readonly get => ((Id0 >> 63) & 0x1) != 0UL; - set => Id0 = (Id0 & 0x7FFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 63); - } - - public void Swap(ColorBlendStateUid uid) - { - MTLPixelFormat format = PixelFormat; - - this = uid; - PixelFormat = format; - } - } - - [SupportedOSPlatform("macos")] - struct PipelineUid : IRefEquatable - { - public ulong Id0; - public ulong Id1; - - private readonly uint VertexAttributeDescriptionsCount => (byte)((Id0 >> 8) & 0xFF); - private readonly uint VertexBindingDescriptionsCount => (byte)((Id0 >> 16) & 0xFF); - private readonly uint ColorBlendAttachmentStateCount => (byte)((Id0 >> 24) & 0xFF); - - public Array32 VertexAttributes; - public Array33 VertexBindings; - public Array8 ColorBlendState; - public uint AttachmentIntegerFormatMask; - public bool LogicOpsAllowed; - - public void ResetColorState() - { - ColorBlendState = new(); - - for (int i = 0; i < ColorBlendState.Length; i++) - { - ColorBlendState[i].WriteMask = MTLColorWriteMask.All; - } - } - - public readonly override bool Equals(object obj) - { - return obj is PipelineUid other && Equals(other); - } - - public bool Equals(ref PipelineUid other) - { - if (!Unsafe.As>(ref Id0).Equals(Unsafe.As>(ref other.Id0))) - { - return false; - } - - if (!SequenceEqual(VertexAttributes.AsSpan(), other.VertexAttributes.AsSpan(), VertexAttributeDescriptionsCount)) - { - return false; - } - - if (!SequenceEqual(VertexBindings.AsSpan(), other.VertexBindings.AsSpan(), VertexBindingDescriptionsCount)) - { - return false; - } - - if (!SequenceEqual(ColorBlendState.AsSpan(), other.ColorBlendState.AsSpan(), ColorBlendAttachmentStateCount)) - { - return false; - } - - return true; - } - - private static bool SequenceEqual(ReadOnlySpan x, ReadOnlySpan y, uint count) where T : unmanaged - { - return MemoryMarshal.Cast(x[..(int)count]).SequenceEqual(MemoryMarshal.Cast(y[..(int)count])); - } - - public override int GetHashCode() - { - ulong hash64 = Id0 * 23 ^ - Id1 * 23; - - for (int i = 0; i < (int)VertexAttributeDescriptionsCount; i++) - { - hash64 ^= VertexAttributes[i].Id0 * 23; - } - - for (int i = 0; i < (int)VertexBindingDescriptionsCount; i++) - { - hash64 ^= VertexBindings[i].Id0 * 23; - } - - for (int i = 0; i < (int)ColorBlendAttachmentStateCount; i++) - { - hash64 ^= ColorBlendState[i].Id0 * 23; - } - - return (int)hash64 ^ ((int)(hash64 >> 32) * 17); - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/StateCache.cs b/src/Ryujinx.Graphics.Metal/StateCache.cs deleted file mode 100644 index 8a9d175f1..000000000 --- a/src/Ryujinx.Graphics.Metal/StateCache.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - abstract class StateCache : IDisposable where T : IDisposable - { - private readonly Dictionary _cache = new(); - - protected abstract THash GetHash(TDescriptor descriptor); - - protected abstract T CreateValue(TDescriptor descriptor); - - public void Dispose() - { - foreach (T value in _cache.Values) - { - value.Dispose(); - } - - GC.SuppressFinalize(this); - } - - public T GetOrCreate(TDescriptor descriptor) - { - THash hash = GetHash(descriptor); - if (_cache.TryGetValue(hash, out T value)) - { - return value; - } - else - { - T newValue = CreateValue(descriptor); - _cache.Add(hash, newValue); - - return newValue; - } - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/StringHelper.cs b/src/Ryujinx.Graphics.Metal/StringHelper.cs deleted file mode 100644 index 46e8ad2e9..000000000 --- a/src/Ryujinx.Graphics.Metal/StringHelper.cs +++ /dev/null @@ -1,30 +0,0 @@ -using SharpMetal.Foundation; -using SharpMetal.ObjectiveCCore; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - class StringHelper - { - public static NSString NSString(string source) - { - return new(ObjectiveC.IntPtr_objc_msgSend(new ObjectiveCClass("NSString"), "stringWithUTF8String:", source)); - } - - public static unsafe string String(NSString source) - { - char[] sourceBuffer = new char[source.Length]; - fixed (char* pSourceBuffer = sourceBuffer) - { - ObjectiveC.bool_objc_msgSend(source, - "getCString:maxLength:encoding:", - pSourceBuffer, - source.MaximumLengthOfBytes(NSStringEncoding.UTF16) + 1, - (ulong)NSStringEncoding.UTF16); - } - - return new string(sourceBuffer); - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/SyncManager.cs b/src/Ryujinx.Graphics.Metal/SyncManager.cs deleted file mode 100644 index f2f26fd91..000000000 --- a/src/Ryujinx.Graphics.Metal/SyncManager.cs +++ /dev/null @@ -1,214 +0,0 @@ -using Ryujinx.Common.Logging; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - class SyncManager - { - private class SyncHandle - { - public ulong ID; - public MultiFenceHolder Waitable; - public ulong FlushId; - public bool Signalled; - - public bool NeedsFlush(ulong currentFlushId) - { - return (long)(FlushId - currentFlushId) >= 0; - } - } - - private ulong _firstHandle; - - private readonly MetalRenderer _renderer; - private readonly List _handles; - private ulong _flushId; - private long _waitTicks; - - public SyncManager(MetalRenderer renderer) - { - _renderer = renderer; - _handles = []; - } - - public void RegisterFlush() - { - _flushId++; - } - - public void Create(ulong id, bool strict) - { - ulong flushId = _flushId; - MultiFenceHolder waitable = new(); - if (strict || _renderer.InterruptAction == null) - { - _renderer.FlushAllCommands(); - _renderer.CommandBufferPool.AddWaitable(waitable); - } - else - { - // Don't flush commands, instead wait for the current command buffer to finish. - // If this sync is waited on before the command buffer is submitted, interrupt the gpu thread and flush it manually. - - _renderer.CommandBufferPool.AddInUseWaitable(waitable); - } - - SyncHandle handle = new() - { - ID = id, - Waitable = waitable, - FlushId = flushId, - }; - - lock (_handles) - { - _handles.Add(handle); - } - } - - public ulong GetCurrent() - { - lock (_handles) - { - ulong lastHandle = _firstHandle; - - foreach (SyncHandle handle in _handles) - { - lock (handle) - { - if (handle.Waitable == null) - { - continue; - } - - if (handle.ID > lastHandle) - { - bool signaled = handle.Signalled || handle.Waitable.WaitForFences(false); - if (signaled) - { - lastHandle = handle.ID; - handle.Signalled = true; - } - } - } - } - - return lastHandle; - } - } - - public void Wait(ulong id) - { - SyncHandle result = null; - - lock (_handles) - { - if ((long)(_firstHandle - id) > 0) - { - return; // The handle has already been signalled or deleted. - } - - foreach (SyncHandle handle in _handles) - { - if (handle.ID == id) - { - result = handle; - break; - } - } - } - - if (result != null) - { - if (result.Waitable == null) - { - return; - } - - long beforeTicks = Stopwatch.GetTimestamp(); - - if (result.NeedsFlush(_flushId)) - { - _renderer.InterruptAction(() => - { - if (result.NeedsFlush(_flushId)) - { - _renderer.FlushAllCommands(); - } - }); - } - - lock (result) - { - if (result.Waitable == null) - { - return; - } - - bool signaled = result.Signalled || result.Waitable.WaitForFences(true); - - if (!signaled) - { - Logger.Error?.PrintMsg(LogClass.Gpu, $"Metal Sync Object {result.ID} failed to signal within 1000ms. Continuing..."); - } - else - { - _waitTicks += Stopwatch.GetTimestamp() - beforeTicks; - result.Signalled = true; - } - } - } - } - - public void Cleanup() - { - // Iterate through handles and remove any that have already been signalled. - - while (true) - { - SyncHandle first = null; - lock (_handles) - { - first = _handles.FirstOrDefault(); - } - - if (first == null || first.NeedsFlush(_flushId)) - { - break; - } - - bool signaled = first.Waitable.WaitForFences(false); - if (signaled) - { - // Delete the sync object. - lock (_handles) - { - lock (first) - { - _firstHandle = first.ID + 1; - _handles.RemoveAt(0); - first.Waitable = null; - } - } - } - else - { - // This sync handle and any following have not been reached yet. - break; - } - } - } - - public long GetAndResetWaitTicks() - { - long result = _waitTicks; - _waitTicks = 0; - - return result; - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs deleted file mode 100644 index 754bf1742..000000000 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ /dev/null @@ -1,654 +0,0 @@ -using Ryujinx.Common.Logging; -using Ryujinx.Common.Memory; -using Ryujinx.Graphics.GAL; -using SharpMetal.Foundation; -using SharpMetal.Metal; -using System; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - class Texture : TextureBase, ITexture - { - private MTLTexture _identitySwizzleHandle; - private readonly bool _identityIsDifferent; - - public Texture(MTLDevice device, MetalRenderer renderer, Pipeline pipeline, TextureCreateInfo info) : base(device, renderer, pipeline, info) - { - MTLPixelFormat pixelFormat = FormatTable.GetFormat(Info.Format); - - MTLTextureDescriptor descriptor = new() - { - PixelFormat = pixelFormat, - Usage = MTLTextureUsage.Unknown, - SampleCount = (ulong)Info.Samples, - TextureType = Info.Target.Convert(), - Width = (ulong)Info.Width, - Height = (ulong)Info.Height, - MipmapLevelCount = (ulong)Info.Levels - }; - - if (info.Target == Target.Texture3D) - { - descriptor.Depth = (ulong)Info.Depth; - } - else if (info.Target != Target.Cubemap) - { - if (info.Target == Target.CubemapArray) - { - descriptor.ArrayLength = (ulong)(Info.Depth / 6); - } - else - { - descriptor.ArrayLength = (ulong)Info.Depth; - } - } - - MTLTextureSwizzleChannels swizzle = GetSwizzle(info, descriptor.PixelFormat); - - _identitySwizzleHandle = Device.NewTexture(descriptor); - - if (SwizzleIsIdentity(swizzle)) - { - MtlTexture = _identitySwizzleHandle; - } - else - { - MtlTexture = CreateDefaultView(_identitySwizzleHandle, swizzle, descriptor); - _identityIsDifferent = true; - } - - MtlFormat = pixelFormat; - descriptor.Dispose(); - } - - public Texture(MTLDevice device, MetalRenderer renderer, Pipeline pipeline, TextureCreateInfo info, MTLTexture sourceTexture, int firstLayer, int firstLevel) : base(device, renderer, pipeline, info) - { - MTLPixelFormat pixelFormat = FormatTable.GetFormat(Info.Format); - - if (info.DepthStencilMode == DepthStencilMode.Stencil) - { - pixelFormat = pixelFormat switch - { - MTLPixelFormat.Depth32FloatStencil8 => MTLPixelFormat.X32Stencil8, - MTLPixelFormat.Depth24UnormStencil8 => MTLPixelFormat.X24Stencil8, - _ => pixelFormat - }; - } - - MTLTextureType textureType = Info.Target.Convert(); - NSRange levels; - levels.location = (ulong)firstLevel; - levels.length = (ulong)Info.Levels; - NSRange slices; - slices.location = (ulong)firstLayer; - slices.length = textureType == MTLTextureType.Type3D ? 1 : (ulong)info.GetDepthOrLayers(); - - MTLTextureSwizzleChannels swizzle = GetSwizzle(info, pixelFormat); - - _identitySwizzleHandle = sourceTexture.NewTextureView(pixelFormat, textureType, levels, slices); - - if (SwizzleIsIdentity(swizzle)) - { - MtlTexture = _identitySwizzleHandle; - } - else - { - MtlTexture = sourceTexture.NewTextureView(pixelFormat, textureType, levels, slices, swizzle); - _identityIsDifferent = true; - } - - MtlFormat = pixelFormat; - FirstLayer = firstLayer; - FirstLevel = firstLevel; - } - - public void PopulateRenderPassAttachment(MTLRenderPassColorAttachmentDescriptor descriptor) - { - descriptor.Texture = _identitySwizzleHandle; - } - - private MTLTexture CreateDefaultView(MTLTexture texture, MTLTextureSwizzleChannels swizzle, MTLTextureDescriptor descriptor) - { - NSRange levels; - levels.location = 0; - levels.length = (ulong)Info.Levels; - NSRange slices; - slices.location = 0; - slices.length = Info.Target == Target.Texture3D ? 1 : (ulong)Info.GetDepthOrLayers(); - - return texture.NewTextureView(descriptor.PixelFormat, descriptor.TextureType, levels, slices, swizzle); - } - - private bool SwizzleIsIdentity(MTLTextureSwizzleChannels swizzle) - { - return swizzle.red == MTLTextureSwizzle.Red && - swizzle.green == MTLTextureSwizzle.Green && - swizzle.blue == MTLTextureSwizzle.Blue && - swizzle.alpha == MTLTextureSwizzle.Alpha; - } - - private MTLTextureSwizzleChannels GetSwizzle(TextureCreateInfo info, MTLPixelFormat pixelFormat) - { - MTLTextureSwizzle swizzleR = Info.SwizzleR.Convert(); - MTLTextureSwizzle swizzleG = Info.SwizzleG.Convert(); - MTLTextureSwizzle swizzleB = Info.SwizzleB.Convert(); - MTLTextureSwizzle swizzleA = Info.SwizzleA.Convert(); - - if (info.Format == Format.R5G5B5A1Unorm || - info.Format == Format.R5G5B5X1Unorm || - info.Format == Format.R5G6B5Unorm) - { - (swizzleB, swizzleR) = (swizzleR, swizzleB); - } - else if (pixelFormat == MTLPixelFormat.ABGR4Unorm || info.Format == Format.A1B5G5R5Unorm) - { - MTLTextureSwizzle tempB = swizzleB; - MTLTextureSwizzle tempA = swizzleA; - - swizzleB = swizzleG; - swizzleA = swizzleR; - swizzleR = tempA; - swizzleG = tempB; - } - - return new MTLTextureSwizzleChannels - { - red = swizzleR, - green = swizzleG, - blue = swizzleB, - alpha = swizzleA - }; - } - - public void CopyTo(ITexture destination, int firstLayer, int firstLevel) - { - CommandBufferScoped cbs = Pipeline.Cbs; - - TextureBase src = this; - TextureBase dst = (TextureBase)destination; - - if (!Valid || !dst.Valid) - { - return; - } - - MTLTexture srcImage = GetHandle(); - MTLTexture dstImage = dst.GetHandle(); - - if (!dst.Info.Target.IsMultisample() && Info.Target.IsMultisample()) - { - // int layers = Math.Min(Info.GetLayers(), dst.Info.GetLayers() - firstLayer); - - // _gd.HelperShader.CopyMSToNonMS(_gd, cbs, src, dst, 0, firstLayer, layers); - } - else if (dst.Info.Target.IsMultisample() && !Info.Target.IsMultisample()) - { - // int layers = Math.Min(Info.GetLayers(), dst.Info.GetLayers() - firstLayer); - - // _gd.HelperShader.CopyNonMSToMS(_gd, cbs, src, dst, 0, firstLayer, layers); - } - else if (dst.Info.BytesPerPixel != Info.BytesPerPixel) - { - // int layers = Math.Min(Info.GetLayers(), dst.Info.GetLayers() - firstLayer); - // int levels = Math.Min(Info.Levels, dst.Info.Levels - firstLevel); - - // _gd.HelperShader.CopyIncompatibleFormats(_gd, cbs, src, dst, 0, firstLayer, 0, firstLevel, layers, levels); - } - else if (src.Info.Format.IsDepthOrStencil() != dst.Info.Format.IsDepthOrStencil()) - { - // int layers = Math.Min(Info.GetLayers(), dst.Info.GetLayers() - firstLayer); - // int levels = Math.Min(Info.Levels, dst.Info.Levels - firstLevel); - - // TODO: depth copy? - // _gd.HelperShader.CopyColor(_gd, cbs, src, dst, 0, firstLayer, 0, FirstLevel, layers, levels); - } - else - { - TextureCopy.Copy( - cbs, - srcImage, - dstImage, - src.Info, - dst.Info, - 0, - firstLayer, - 0, - firstLevel); - } - } - - public void CopyTo(ITexture destination, int srcLayer, int dstLayer, int srcLevel, int dstLevel) - { - CommandBufferScoped cbs = Pipeline.Cbs; - - TextureBase src = this; - TextureBase dst = (TextureBase)destination; - - if (!Valid || !dst.Valid) - { - return; - } - - MTLTexture srcImage = GetHandle(); - MTLTexture dstImage = dst.GetHandle(); - - if (!dst.Info.Target.IsMultisample() && Info.Target.IsMultisample()) - { - // _gd.HelperShader.CopyMSToNonMS(_gd, cbs, src, dst, srcLayer, dstLayer, 1); - } - else if (dst.Info.Target.IsMultisample() && !Info.Target.IsMultisample()) - { - // _gd.HelperShader.CopyNonMSToMS(_gd, cbs, src, dst, srcLayer, dstLayer, 1); - } - else if (dst.Info.BytesPerPixel != Info.BytesPerPixel) - { - // _gd.HelperShader.CopyIncompatibleFormats(_gd, cbs, src, dst, srcLayer, dstLayer, srcLevel, dstLevel, 1, 1); - } - else if (src.Info.Format.IsDepthOrStencil() != dst.Info.Format.IsDepthOrStencil()) - { - // _gd.HelperShader.CopyColor(_gd, cbs, src, dst, srcLayer, dstLayer, srcLevel, dstLevel, 1, 1); - } - else - { - TextureCopy.Copy( - cbs, - srcImage, - dstImage, - src.Info, - dst.Info, - srcLayer, - dstLayer, - srcLevel, - dstLevel, - 1, - 1); - } - } - - public void CopyTo(ITexture destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter) - { - if (!Renderer.CommandBufferPool.OwnedByCurrentThread) - { - Logger.Warning?.PrintMsg(LogClass.Gpu, "Metal doesn't currently support scaled blit on background thread."); - - return; - } - - Texture dst = (Texture)destination; - - bool isDepthOrStencil = dst.Info.Format.IsDepthOrStencil(); - - Pipeline.Blit(this, dst, srcRegion, dstRegion, isDepthOrStencil, linearFilter); - } - - public void CopyTo(BufferRange range, int layer, int level, int stride) - { - CommandBufferScoped cbs = Pipeline.Cbs; - - int outSize = Info.GetMipSize(level); - int hostSize = GetBufferDataLength(outSize); - - int offset = range.Offset; - - Auto autoBuffer = Renderer.BufferManager.GetBuffer(range.Handle, true); - MTLBuffer mtlBuffer = autoBuffer.Get(cbs, range.Offset, outSize).Value; - - if (PrepareOutputBuffer(cbs, hostSize, mtlBuffer, out MTLBuffer copyToBuffer, out BufferHolder tempCopyHolder)) - { - offset = 0; - } - - CopyFromOrToBuffer(cbs, copyToBuffer, MtlTexture, hostSize, true, layer, level, 1, 1, singleSlice: true, offset, stride); - - if (tempCopyHolder != null) - { - CopyDataToOutputBuffer(cbs, tempCopyHolder, autoBuffer, hostSize, range.Offset); - tempCopyHolder.Dispose(); - } - } - - public ITexture CreateView(TextureCreateInfo info, int firstLayer, int firstLevel) - { - return new Texture(Device, Renderer, Pipeline, info, _identitySwizzleHandle, firstLayer, firstLevel); - } - - private void CopyDataToBuffer(Span storage, ReadOnlySpan input) - { - if (NeedsD24S8Conversion()) - { - FormatConverter.ConvertD24S8ToD32FS8(storage, input); - return; - } - - input.CopyTo(storage); - } - - private ReadOnlySpan GetDataFromBuffer(ReadOnlySpan storage, int size, Span output) - { - if (NeedsD24S8Conversion()) - { - if (output.IsEmpty) - { - output = new byte[GetBufferDataLength(size)]; - } - - FormatConverter.ConvertD32FS8ToD24S8(output, storage); - return output; - } - - return storage; - } - - private bool PrepareOutputBuffer(CommandBufferScoped cbs, int hostSize, MTLBuffer target, out MTLBuffer copyTarget, out BufferHolder copyTargetHolder) - { - if (NeedsD24S8Conversion()) - { - copyTargetHolder = Renderer.BufferManager.Create(hostSize); - copyTarget = copyTargetHolder.GetBuffer().Get(cbs, 0, hostSize).Value; - - return true; - } - - copyTarget = target; - copyTargetHolder = null; - - return false; - } - - private void CopyDataToOutputBuffer(CommandBufferScoped cbs, BufferHolder hostData, Auto copyTarget, int hostSize, int dstOffset) - { - if (NeedsD24S8Conversion()) - { - Renderer.HelperShader.ConvertD32S8ToD24S8(cbs, hostData, copyTarget, hostSize / (2 * sizeof(int)), dstOffset); - } - } - - private bool NeedsD24S8Conversion() - { - return FormatTable.IsD24S8(Info.Format) && MtlFormat == MTLPixelFormat.Depth32FloatStencil8; - } - - public void CopyFromOrToBuffer( - CommandBufferScoped cbs, - MTLBuffer buffer, - MTLTexture image, - int size, - bool to, - int dstLayer, - int dstLevel, - int dstLayers, - int dstLevels, - bool singleSlice, - int offset = 0, - int stride = 0) - { - MTLBlitCommandEncoder blitCommandEncoder = cbs.Encoders.EnsureBlitEncoder(); - - bool is3D = Info.Target == Target.Texture3D; - int width = Math.Max(1, Info.Width >> dstLevel); - int height = Math.Max(1, Info.Height >> dstLevel); - int depth = is3D && !singleSlice ? Math.Max(1, Info.Depth >> dstLevel) : 1; - int layers = dstLayers; - int levels = dstLevels; - - for (int oLevel = 0; oLevel < levels; oLevel++) - { - int level = oLevel + dstLevel; - int mipSize = Info.GetMipSize2D(level); - - int mipSizeLevel = GetBufferDataLength(is3D && !singleSlice - ? Info.GetMipSize(level) - : mipSize * dstLayers); - - int endOffset = offset + mipSizeLevel; - - if ((uint)endOffset > (uint)size) - { - break; - } - - for (int oLayer = 0; oLayer < layers; oLayer++) - { - int layer = !is3D ? dstLayer + oLayer : 0; - int z = is3D ? dstLayer + oLayer : 0; - - if (to) - { - blitCommandEncoder.CopyFromTexture( - image, - (ulong)layer, - (ulong)level, - new MTLOrigin { z = (ulong)z }, - new MTLSize { width = (ulong)width, height = (ulong)height, depth = 1 }, - buffer, - (ulong)offset, - (ulong)Info.GetMipStride(level), - (ulong)mipSize - ); - } - else - { - blitCommandEncoder.CopyFromBuffer( - buffer, - (ulong)offset, - (ulong)Info.GetMipStride(level), - (ulong)mipSize, - new MTLSize { width = (ulong)width, height = (ulong)height, depth = 1 }, - image, - (ulong)(layer + oLayer), - (ulong)level, - new MTLOrigin { z = (ulong)z } - ); - } - - offset += mipSize; - } - - width = Math.Max(1, width >> 1); - height = Math.Max(1, height >> 1); - - if (Info.Target == Target.Texture3D) - { - depth = Math.Max(1, depth >> 1); - } - } - } - - private ReadOnlySpan GetData(CommandBufferPool cbp, PersistentFlushBuffer flushBuffer) - { - int size = 0; - - for (int level = 0; level < Info.Levels; level++) - { - size += Info.GetMipSize(level); - } - - size = GetBufferDataLength(size); - - Span result = flushBuffer.GetTextureData(cbp, this, size); - - return GetDataFromBuffer(result, size, result); - } - - private ReadOnlySpan GetData(CommandBufferPool cbp, PersistentFlushBuffer flushBuffer, int layer, int level) - { - int size = GetBufferDataLength(Info.GetMipSize(level)); - - Span result = flushBuffer.GetTextureData(cbp, this, size, layer, level); - - return GetDataFromBuffer(result, size, result); - } - - public PinnedSpan GetData() - { - BackgroundResource resources = Renderer.BackgroundResources.Get(); - - if (Renderer.CommandBufferPool.OwnedByCurrentThread) - { - Renderer.FlushAllCommands(); - - return PinnedSpan.UnsafeFromSpan(GetData(Renderer.CommandBufferPool, resources.GetFlushBuffer())); - } - - return PinnedSpan.UnsafeFromSpan(GetData(resources.GetPool(), resources.GetFlushBuffer())); - } - - public PinnedSpan GetData(int layer, int level) - { - BackgroundResource resources = Renderer.BackgroundResources.Get(); - - if (Renderer.CommandBufferPool.OwnedByCurrentThread) - { - Renderer.FlushAllCommands(); - - return PinnedSpan.UnsafeFromSpan(GetData(Renderer.CommandBufferPool, resources.GetFlushBuffer(), layer, level)); - } - - return PinnedSpan.UnsafeFromSpan(GetData(resources.GetPool(), resources.GetFlushBuffer(), layer, level)); - } - - public void SetData(MemoryOwner data) - { - MTLBlitCommandEncoder blitCommandEncoder = Pipeline.GetOrCreateBlitEncoder(); - - Span dataSpan = data.Memory.Span; - - BufferHolder buffer = Renderer.BufferManager.Create(dataSpan.Length); - buffer.SetDataUnchecked(0, dataSpan); - MTLBuffer mtlBuffer = buffer.GetBuffer(false).Get(Pipeline.Cbs).Value; - - int width = Info.Width; - int height = Info.Height; - int depth = Info.Depth; - int levels = Info.Levels; - int layers = Info.GetLayers(); - bool is3D = Info.Target == Target.Texture3D; - - int offset = 0; - - for (int level = 0; level < levels; level++) - { - int mipSize = Info.GetMipSize2D(level); - int endOffset = offset + mipSize; - - if ((uint)endOffset > (uint)dataSpan.Length) - { - return; - } - - for (int layer = 0; layer < layers; layer++) - { - blitCommandEncoder.CopyFromBuffer( - mtlBuffer, - (ulong)offset, - (ulong)Info.GetMipStride(level), - (ulong)mipSize, - new MTLSize { width = (ulong)width, height = (ulong)height, depth = is3D ? (ulong)depth : 1 }, - MtlTexture, - (ulong)layer, - (ulong)level, - new MTLOrigin() - ); - - offset += mipSize; - } - - width = Math.Max(1, width >> 1); - height = Math.Max(1, height >> 1); - - if (is3D) - { - depth = Math.Max(1, depth >> 1); - } - } - - // Cleanup - buffer.Dispose(); - } - - private void SetData(ReadOnlySpan data, int layer, int level, int layers, int levels, bool singleSlice) - { - int bufferDataLength = GetBufferDataLength(data.Length); - - using BufferHolder bufferHolder = Renderer.BufferManager.Create(bufferDataLength); - - // TODO: loadInline logic - - CommandBufferScoped cbs = Pipeline.Cbs; - - CopyDataToBuffer(bufferHolder.GetDataStorage(0, bufferDataLength), data); - - MTLBuffer buffer = bufferHolder.GetBuffer().Get(cbs).Value; - MTLTexture image = GetHandle(); - - CopyFromOrToBuffer(cbs, buffer, image, bufferDataLength, false, layer, level, layers, levels, singleSlice); - } - - public void SetData(MemoryOwner data, int layer, int level) - { - SetData(data.Memory.Span, layer, level, 1, 1, singleSlice: true); - - data.Dispose(); - } - - public void SetData(MemoryOwner data, int layer, int level, Rectangle region) - { - MTLBlitCommandEncoder blitCommandEncoder = Pipeline.GetOrCreateBlitEncoder(); - - ulong bytesPerRow = (ulong)Info.GetMipStride(level); - ulong bytesPerImage = 0; - if (MtlTexture.TextureType == MTLTextureType.Type3D) - { - bytesPerImage = bytesPerRow * (ulong)Info.Height; - } - - Span dataSpan = data.Memory.Span; - - BufferHolder buffer = Renderer.BufferManager.Create(dataSpan.Length); - buffer.SetDataUnchecked(0, dataSpan); - MTLBuffer mtlBuffer = buffer.GetBuffer(false).Get(Pipeline.Cbs).Value; - - blitCommandEncoder.CopyFromBuffer( - mtlBuffer, - 0, - bytesPerRow, - bytesPerImage, - new MTLSize { width = (ulong)region.Width, height = (ulong)region.Height, depth = 1 }, - MtlTexture, - (ulong)layer, - (ulong)level, - new MTLOrigin { x = (ulong)region.X, y = (ulong)region.Y } - ); - - // Cleanup - buffer.Dispose(); - } - - private int GetBufferDataLength(int length) - { - if (NeedsD24S8Conversion()) - { - return length * 2; - } - - return length; - } - - public void SetStorage(BufferRange buffer) - { - throw new NotImplementedException(); - } - - public override void Release() - { - if (_identityIsDifferent) - { - _identitySwizzleHandle.Dispose(); - } - - base.Release(); - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/TextureArray.cs b/src/Ryujinx.Graphics.Metal/TextureArray.cs deleted file mode 100644 index ea2c74420..000000000 --- a/src/Ryujinx.Graphics.Metal/TextureArray.cs +++ /dev/null @@ -1,93 +0,0 @@ -using Ryujinx.Graphics.GAL; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - internal class TextureArray : ITextureArray - { - private readonly TextureRef[] _textureRefs; - private readonly TextureBuffer[] _bufferTextureRefs; - - private readonly bool _isBuffer; - private readonly Pipeline _pipeline; - - public TextureArray(int size, bool isBuffer, Pipeline pipeline) - { - if (isBuffer) - { - _bufferTextureRefs = new TextureBuffer[size]; - } - else - { - _textureRefs = new TextureRef[size]; - } - - _isBuffer = isBuffer; - _pipeline = pipeline; - } - - public void SetSamplers(int index, ISampler[] samplers) - { - for (int i = 0; i < samplers.Length; i++) - { - ISampler sampler = samplers[i]; - - if (sampler is SamplerHolder samp) - { - _textureRefs[index + i].Sampler = samp.GetSampler(); - } - else - { - _textureRefs[index + i].Sampler = default; - } - } - - SetDirty(); - } - - public void SetTextures(int index, ITexture[] textures) - { - for (int i = 0; i < textures.Length; i++) - { - ITexture texture = textures[i]; - - if (texture is TextureBuffer textureBuffer) - { - _bufferTextureRefs[index + i] = textureBuffer; - } - else if (texture is Texture tex) - { - _textureRefs[index + i].Storage = tex; - } - else if (!_isBuffer) - { - _textureRefs[index + i].Storage = null; - } - else - { - _bufferTextureRefs[index + i] = null; - } - } - - SetDirty(); - } - - public TextureRef[] GetTextureRefs() - { - return _textureRefs; - } - - public TextureBuffer[] GetBufferTextureRefs() - { - return _bufferTextureRefs; - } - - private void SetDirty() - { - _pipeline.DirtyTextures(); - } - - public void Dispose() { } - } -} diff --git a/src/Ryujinx.Graphics.Metal/TextureBase.cs b/src/Ryujinx.Graphics.Metal/TextureBase.cs deleted file mode 100644 index fecd64958..000000000 --- a/src/Ryujinx.Graphics.Metal/TextureBase.cs +++ /dev/null @@ -1,67 +0,0 @@ -using Ryujinx.Graphics.GAL; -using SharpMetal.Metal; -using System; -using System.Runtime.Versioning; -using System.Threading; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - abstract class TextureBase : IDisposable - { - private int _isValid = 1; - - public bool Valid => Volatile.Read(ref _isValid) != 0; - - protected readonly Pipeline Pipeline; - protected readonly MTLDevice Device; - protected readonly MetalRenderer Renderer; - - protected MTLTexture MtlTexture; - - public readonly TextureCreateInfo Info; - public int Width => Info.Width; - public int Height => Info.Height; - public int Depth => Info.Depth; - - public MTLPixelFormat MtlFormat { get; protected set; } - public int FirstLayer { get; protected set; } - public int FirstLevel { get; protected set; } - - public TextureBase(MTLDevice device, MetalRenderer renderer, Pipeline pipeline, TextureCreateInfo info) - { - Device = device; - Renderer = renderer; - Pipeline = pipeline; - Info = info; - } - - public MTLTexture GetHandle() - { - if (_isValid == 0) - { - return new MTLTexture(IntPtr.Zero); - } - - return MtlTexture; - } - - public virtual void Release() - { - Dispose(); - } - - public void Dispose() - { - bool wasValid = Interlocked.Exchange(ref _isValid, 0) != 0; - - if (wasValid) - { - if (MtlTexture != IntPtr.Zero) - { - MtlTexture.Dispose(); - } - } - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/TextureBuffer.cs b/src/Ryujinx.Graphics.Metal/TextureBuffer.cs deleted file mode 100644 index 483cef28b..000000000 --- a/src/Ryujinx.Graphics.Metal/TextureBuffer.cs +++ /dev/null @@ -1,132 +0,0 @@ -using Ryujinx.Common.Memory; -using Ryujinx.Graphics.GAL; -using SharpMetal.Metal; -using System; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - class TextureBuffer : TextureBase, ITexture - { - private MTLTextureDescriptor _descriptor; - private BufferHandle _bufferHandle; - private int _offset; - private int _size; - - private int _bufferCount; - private Auto _buffer; - - public TextureBuffer(MTLDevice device, MetalRenderer renderer, Pipeline pipeline, TextureCreateInfo info) : base(device, renderer, pipeline, info) - { - MTLPixelFormat pixelFormat = FormatTable.GetFormat(Info.Format); - - _descriptor = new MTLTextureDescriptor - { - PixelFormat = pixelFormat, - Usage = MTLTextureUsage.Unknown, - TextureType = MTLTextureType.TextureBuffer, - Width = (ulong)Info.Width, - Height = (ulong)Info.Height, - }; - - MtlFormat = pixelFormat; - } - - public void RebuildStorage(bool write) - { - if (MtlTexture != IntPtr.Zero) - { - MtlTexture.Dispose(); - } - - if (_buffer == null) - { - MtlTexture = default; - } - else - { - DisposableBuffer buffer = _buffer.Get(Pipeline.Cbs, _offset, _size, write); - - _descriptor.Width = (uint)(_size / Info.BytesPerPixel); - MtlTexture = buffer.Value.NewTexture(_descriptor, (ulong)_offset, (ulong)_size); - } - } - - public void CopyTo(ITexture destination, int firstLayer, int firstLevel) - { - throw new NotSupportedException(); - } - - public void CopyTo(ITexture destination, int srcLayer, int dstLayer, int srcLevel, int dstLevel) - { - throw new NotSupportedException(); - } - - public void CopyTo(ITexture destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter) - { - throw new NotSupportedException(); - } - - public ITexture CreateView(TextureCreateInfo info, int firstLayer, int firstLevel) - { - throw new NotSupportedException(); - } - - public PinnedSpan GetData() - { - return Renderer.GetBufferData(_bufferHandle, _offset, _size); - } - - public PinnedSpan GetData(int layer, int level) - { - return GetData(); - } - - public void CopyTo(BufferRange range, int layer, int level, int stride) - { - throw new NotImplementedException(); - } - - public void SetData(MemoryOwner data) - { - Renderer.SetBufferData(_bufferHandle, _offset, data.Memory.Span); - data.Dispose(); - } - - public void SetData(MemoryOwner data, int layer, int level) - { - throw new NotSupportedException(); - } - - public void SetData(MemoryOwner data, int layer, int level, Rectangle region) - { - throw new NotSupportedException(); - } - - public void SetStorage(BufferRange buffer) - { - if (_bufferHandle == buffer.Handle && - _offset == buffer.Offset && - _size == buffer.Size && - _bufferCount == Renderer.BufferManager.BufferCount) - { - return; - } - - _bufferHandle = buffer.Handle; - _offset = buffer.Offset; - _size = buffer.Size; - _bufferCount = Renderer.BufferManager.BufferCount; - - _buffer = Renderer.BufferManager.GetBuffer(_bufferHandle, false); - } - - public override void Release() - { - _descriptor.Dispose(); - - base.Release(); - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/TextureCopy.cs b/src/Ryujinx.Graphics.Metal/TextureCopy.cs deleted file mode 100644 index afd3e961f..000000000 --- a/src/Ryujinx.Graphics.Metal/TextureCopy.cs +++ /dev/null @@ -1,265 +0,0 @@ -using Ryujinx.Common; -using Ryujinx.Common.Logging; -using Ryujinx.Graphics.GAL; -using SharpMetal.Metal; -using System; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - static class TextureCopy - { - public static ulong CopyFromOrToBuffer( - CommandBufferScoped cbs, - MTLBuffer buffer, - MTLTexture image, - TextureCreateInfo info, - bool to, - int dstLayer, - int dstLevel, - int x, - int y, - int width, - int height, - ulong offset = 0) - { - MTLBlitCommandEncoder blitCommandEncoder = cbs.Encoders.EnsureBlitEncoder(); - - bool is3D = info.Target == Target.Texture3D; - - int blockWidth = BitUtils.DivRoundUp(width, info.BlockWidth); - int blockHeight = BitUtils.DivRoundUp(height, info.BlockHeight); - ulong bytesPerRow = (ulong)BitUtils.AlignUp(blockWidth * info.BytesPerPixel, 4); - ulong bytesPerImage = bytesPerRow * (ulong)blockHeight; - - MTLOrigin origin = new() { x = (ulong)x, y = (ulong)y, z = is3D ? (ulong)dstLayer : 0 }; - MTLSize region = new() { width = (ulong)width, height = (ulong)height, depth = 1 }; - - uint layer = is3D ? 0 : (uint)dstLayer; - - if (to) - { - blitCommandEncoder.CopyFromTexture( - image, - layer, - (ulong)dstLevel, - origin, - region, - buffer, - offset, - bytesPerRow, - bytesPerImage); - } - else - { - blitCommandEncoder.CopyFromBuffer(buffer, offset, bytesPerRow, bytesPerImage, region, image, layer, (ulong)dstLevel, origin); - } - - return offset + bytesPerImage; - } - - public static void Copy( - CommandBufferScoped cbs, - MTLTexture srcImage, - MTLTexture dstImage, - TextureCreateInfo srcInfo, - TextureCreateInfo dstInfo, - int srcLayer, - int dstLayer, - int srcLevel, - int dstLevel) - { - int srcDepth = srcInfo.GetDepthOrLayers(); - int srcLevels = srcInfo.Levels; - - int dstDepth = dstInfo.GetDepthOrLayers(); - int dstLevels = dstInfo.Levels; - - if (dstInfo.Target == Target.Texture3D) - { - dstDepth = Math.Max(1, dstDepth >> dstLevel); - } - - int depth = Math.Min(srcDepth, dstDepth); - int levels = Math.Min(srcLevels, dstLevels); - - Copy( - cbs, - srcImage, - dstImage, - srcInfo, - dstInfo, - srcLayer, - dstLayer, - srcLevel, - dstLevel, - depth, - levels); - } - - public static void Copy( - CommandBufferScoped cbs, - MTLTexture srcImage, - MTLTexture dstImage, - TextureCreateInfo srcInfo, - TextureCreateInfo dstInfo, - int srcDepthOrLayer, - int dstDepthOrLayer, - int srcLevel, - int dstLevel, - int depthOrLayers, - int levels) - { - MTLBlitCommandEncoder blitCommandEncoder = cbs.Encoders.EnsureBlitEncoder(); - - int srcZ; - int srcLayer; - int srcDepth; - int srcLayers; - - if (srcInfo.Target == Target.Texture3D) - { - srcZ = srcDepthOrLayer; - srcLayer = 0; - srcDepth = depthOrLayers; - srcLayers = 1; - } - else - { - srcZ = 0; - srcLayer = srcDepthOrLayer; - srcDepth = 1; - srcLayers = depthOrLayers; - } - - int dstZ; - int dstLayer; - int dstLayers; - - if (dstInfo.Target == Target.Texture3D) - { - dstZ = dstDepthOrLayer; - dstLayer = 0; - dstLayers = 1; - } - else - { - dstZ = 0; - dstLayer = dstDepthOrLayer; - dstLayers = depthOrLayers; - } - - int srcWidth = srcInfo.Width; - int srcHeight = srcInfo.Height; - - int dstWidth = dstInfo.Width; - int dstHeight = dstInfo.Height; - - srcWidth = Math.Max(1, srcWidth >> srcLevel); - srcHeight = Math.Max(1, srcHeight >> srcLevel); - - dstWidth = Math.Max(1, dstWidth >> dstLevel); - dstHeight = Math.Max(1, dstHeight >> dstLevel); - - int blockWidth = 1; - int blockHeight = 1; - bool sizeInBlocks = false; - - MTLBuffer tempBuffer = default; - - if (srcInfo.Format != dstInfo.Format && (srcInfo.IsCompressed || dstInfo.IsCompressed)) - { - // Compressed alias copies need to happen through a temporary buffer. - // The data is copied from the source to the buffer, then the buffer to the destination. - // The length of the buffer should be the maximum slice size for the destination. - - tempBuffer = blitCommandEncoder.Device.NewBuffer((ulong)dstInfo.GetMipSize2D(0), MTLResourceOptions.ResourceStorageModePrivate); - } - - // When copying from a compressed to a non-compressed format, - // the non-compressed texture will have the size of the texture - // in blocks (not in texels), so we must adjust that size to - // match the size in texels of the compressed texture. - if (!srcInfo.IsCompressed && dstInfo.IsCompressed) - { - srcWidth *= dstInfo.BlockWidth; - srcHeight *= dstInfo.BlockHeight; - blockWidth = dstInfo.BlockWidth; - blockHeight = dstInfo.BlockHeight; - - sizeInBlocks = true; - } - else if (srcInfo.IsCompressed && !dstInfo.IsCompressed) - { - dstWidth *= srcInfo.BlockWidth; - dstHeight *= srcInfo.BlockHeight; - blockWidth = srcInfo.BlockWidth; - blockHeight = srcInfo.BlockHeight; - } - - int width = Math.Min(srcWidth, dstWidth); - int height = Math.Min(srcHeight, dstHeight); - - for (int level = 0; level < levels; level++) - { - // Stop copy if we are already out of the levels range. - if (level >= srcInfo.Levels || dstLevel + level >= dstInfo.Levels) - { - break; - } - - int copyWidth = sizeInBlocks ? BitUtils.DivRoundUp(width, blockWidth) : width; - int copyHeight = sizeInBlocks ? BitUtils.DivRoundUp(height, blockHeight) : height; - - int layers = Math.Max(dstLayers - dstLayer, srcLayers); - - for (int layer = 0; layer < layers; layer++) - { - if (tempBuffer.NativePtr != 0) - { - // Copy through the temp buffer - CopyFromOrToBuffer(cbs, tempBuffer, srcImage, srcInfo, true, srcLayer + layer, srcLevel + level, 0, 0, copyWidth, copyHeight); - - int dstBufferWidth = sizeInBlocks ? copyWidth * blockWidth : BitUtils.DivRoundUp(copyWidth, blockWidth); - int dstBufferHeight = sizeInBlocks ? copyHeight * blockHeight : BitUtils.DivRoundUp(copyHeight, blockHeight); - - CopyFromOrToBuffer(cbs, tempBuffer, dstImage, dstInfo, false, dstLayer + layer, dstLevel + level, 0, 0, dstBufferWidth, dstBufferHeight); - } - else if (srcInfo.Samples > 1 && srcInfo.Samples != dstInfo.Samples) - { - // TODO - - Logger.Warning?.PrintMsg(LogClass.Gpu, "Unsupported mismatching sample count copy"); - } - else - { - blitCommandEncoder.CopyFromTexture( - srcImage, - (ulong)(srcLayer + layer), - (ulong)(srcLevel + level), - new MTLOrigin { z = (ulong)srcZ }, - new MTLSize { width = (ulong)copyWidth, height = (ulong)copyHeight, depth = (ulong)srcDepth }, - dstImage, - (ulong)(dstLayer + layer), - (ulong)(dstLevel + level), - new MTLOrigin { z = (ulong)dstZ }); - } - } - - width = Math.Max(1, width >> 1); - height = Math.Max(1, height >> 1); - - if (srcInfo.Target == Target.Texture3D) - { - srcDepth = Math.Max(1, srcDepth >> 1); - } - } - - if (tempBuffer.NativePtr != 0) - { - tempBuffer.Dispose(); - } - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/VertexBufferState.cs b/src/Ryujinx.Graphics.Metal/VertexBufferState.cs deleted file mode 100644 index 8fb48ef79..000000000 --- a/src/Ryujinx.Graphics.Metal/VertexBufferState.cs +++ /dev/null @@ -1,60 +0,0 @@ -using Ryujinx.Graphics.GAL; -using SharpMetal.Metal; -using System; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - readonly internal struct VertexBufferState - { - public static VertexBufferState Null => new(BufferHandle.Null, 0, 0, 0); - - private readonly BufferHandle _handle; - private readonly int _offset; - private readonly int _size; - - public readonly int Stride; - public readonly int Divisor; - - public VertexBufferState(BufferHandle handle, int offset, int size, int divisor, int stride = 0) - { - _handle = handle; - _offset = offset; - _size = size; - - Stride = stride; - Divisor = divisor; - } - - public (MTLBuffer, int) GetVertexBuffer(BufferManager bufferManager, CommandBufferScoped cbs) - { - Auto autoBuffer = null; - - if (_handle != BufferHandle.Null) - { - // TODO: Handle restride if necessary - - autoBuffer = bufferManager.GetBuffer(_handle, false, out int size); - - // The original stride must be reapplied in case it was rewritten. - // TODO: Handle restride if necessary - - if (_offset >= size) - { - autoBuffer = null; - } - } - - if (autoBuffer != null) - { - int offset = _offset; - MTLBuffer buffer = autoBuffer.Get(cbs, offset, _size).Value; - - return (buffer, offset); - } - - return (new MTLBuffer(IntPtr.Zero), 0); - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/Window.cs b/src/Ryujinx.Graphics.Metal/Window.cs deleted file mode 100644 index 0b6c6c4d2..000000000 --- a/src/Ryujinx.Graphics.Metal/Window.cs +++ /dev/null @@ -1,234 +0,0 @@ -using Ryujinx.Common.Configuration; -using Ryujinx.Common.Logging; -using Ryujinx.Graphics.GAL; -using Ryujinx.Graphics.Metal.Effects; -using SharpMetal.ObjectiveCCore; -using SharpMetal.QuartzCore; -using System; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - class Window : IWindow, IDisposable - { - public bool ScreenCaptureRequested { get; set; } - - private readonly MetalRenderer _renderer; - private CAMetalLayer _metalLayer; - - private int _width; - private int _height; - - private int _requestedWidth; - private int _requestedHeight; - - private AntiAliasing _currentAntiAliasing; - private bool _updateEffect; - private IPostProcessingEffect _effect; - private IScalingFilter _scalingFilter; - private bool _isLinear; - - public bool IsVSyncEnabled => _metalLayer.DisplaySyncEnabled; - - // private float _scalingFilterLevel; - private bool _updateScalingFilter; - private ScalingFilter _currentScalingFilter; - // private bool _colorSpacePassthroughEnabled; - - public Window(MetalRenderer renderer, CAMetalLayer metalLayer) - { - _renderer = renderer; - _metalLayer = metalLayer; - } - - private void ResizeIfNeeded() - { - if (_requestedWidth != 0 && _requestedHeight != 0) - { - // TODO: This is actually a CGSize, but there is no overload for that, so fill the first two fields of rect with the size. - NSRect rect = new(_requestedWidth, _requestedHeight, 0, 0); - - ObjectiveC.objc_msgSend(_metalLayer, "setDrawableSize:", rect); - - _requestedWidth = 0; - _requestedHeight = 0; - } - } - - public void Present(ITexture texture, ImageCrop crop, Action swapBuffersCallback) - { - if (_renderer.Pipeline is Pipeline pipeline && texture is Texture tex) - { - ResizeIfNeeded(); - - CAMetalDrawable drawable = new(ObjectiveC.IntPtr_objc_msgSend(_metalLayer, "nextDrawable")); - - _width = (int)drawable.Texture.Width; - _height = (int)drawable.Texture.Height; - - UpdateEffect(); - - if (_effect != null) - { - // TODO: Run Effects - // view = _effect.Run() - } - - int srcX0, srcX1, srcY0, srcY1; - - if (crop.Left == 0 && crop.Right == 0) - { - srcX0 = 0; - srcX1 = tex.Width; - } - else - { - srcX0 = crop.Left; - srcX1 = crop.Right; - } - - if (crop.Top == 0 && crop.Bottom == 0) - { - srcY0 = 0; - srcY1 = tex.Height; - } - else - { - srcY0 = crop.Top; - srcY1 = crop.Bottom; - } - - if (ScreenCaptureRequested) - { - // TODO: Support screen captures - - ScreenCaptureRequested = false; - } - - float ratioX = crop.IsStretched ? 1.0f : MathF.Min(1.0f, _height * crop.AspectRatioX / (_width * crop.AspectRatioY)); - float ratioY = crop.IsStretched ? 1.0f : MathF.Min(1.0f, _width * crop.AspectRatioY / (_height * crop.AspectRatioX)); - - int dstWidth = (int)(_width * ratioX); - int dstHeight = (int)(_height * ratioY); - - int dstPaddingX = (_width - dstWidth) / 2; - int dstPaddingY = (_height - dstHeight) / 2; - - int dstX0 = crop.FlipX ? _width - dstPaddingX : dstPaddingX; - int dstX1 = crop.FlipX ? dstPaddingX : _width - dstPaddingX; - - int dstY0 = crop.FlipY ? _height - dstPaddingY : dstPaddingY; - int dstY1 = crop.FlipY ? dstPaddingY : _height - dstPaddingY; - - if (_scalingFilter != null) - { - // TODO: Run scaling filter - } - - pipeline.Present( - drawable, - tex, - new Extents2D(srcX0, srcY0, srcX1, srcY1), - new Extents2D(dstX0, dstY0, dstX1, dstY1), - _isLinear); - } - } - - public void SetSize(int width, int height) - { - _requestedWidth = width; - _requestedHeight = height; - } - - public void ChangeVSyncMode(VSyncMode vSyncMode) - { - _metalLayer.DisplaySyncEnabled = vSyncMode is VSyncMode.Switch; - } - - public void SetAntiAliasing(AntiAliasing effect) - { - if (_currentAntiAliasing == effect && _effect != null) - { - return; - } - - _currentAntiAliasing = effect; - - _updateEffect = true; - } - - public void SetScalingFilter(ScalingFilter type) - { - if (_currentScalingFilter == type && _effect != null) - { - return; - } - - _currentScalingFilter = type; - - _updateScalingFilter = true; - } - - public void SetScalingFilterLevel(float level) - { - // _scalingFilterLevel = level; - _updateScalingFilter = true; - } - - public void SetColorSpacePassthrough(bool colorSpacePassThroughEnabled) - { - // _colorSpacePassthroughEnabled = colorSpacePassThroughEnabled; - } - - private void UpdateEffect() - { - if (_updateEffect) - { - _updateEffect = false; - - switch (_currentAntiAliasing) - { - case AntiAliasing.Fxaa: - _effect?.Dispose(); - Logger.Warning?.PrintMsg(LogClass.Gpu, "FXAA not implemented for Metal backend!"); - break; - case AntiAliasing.None: - _effect?.Dispose(); - _effect = null; - break; - case AntiAliasing.SmaaLow: - case AntiAliasing.SmaaMedium: - case AntiAliasing.SmaaHigh: - case AntiAliasing.SmaaUltra: - // var quality = _currentAntiAliasing - AntiAliasing.SmaaLow; - Logger.Warning?.PrintMsg(LogClass.Gpu, "SMAA not implemented for Metal backend!"); - break; - } - } - - if (_updateScalingFilter) - { - _updateScalingFilter = false; - - switch (_currentScalingFilter) - { - case ScalingFilter.Bilinear: - case ScalingFilter.Nearest: - _scalingFilter?.Dispose(); - _scalingFilter = null; - _isLinear = _currentScalingFilter == ScalingFilter.Bilinear; - break; - case ScalingFilter.Fsr: - Logger.Warning?.PrintMsg(LogClass.Gpu, "FSR not implemented for Metal backend!"); - break; - } - } - } - - public void Dispose() - { - _metalLayer.Dispose(); - } - } -} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/CodeGenContext.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/CodeGenContext.cs deleted file mode 100644 index 2fa188ea9..000000000 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/CodeGenContext.cs +++ /dev/null @@ -1,108 +0,0 @@ -using Ryujinx.Graphics.Shader.StructuredIr; -using Ryujinx.Graphics.Shader.Translation; -using System.Text; - -namespace Ryujinx.Graphics.Shader.CodeGen.Msl -{ - class CodeGenContext - { - public const string Tab = " "; - - // The number of additional arguments that every function (except for the main one) must have (for instance support_buffer) - public const int AdditionalArgCount = 2; - - public StructuredFunction CurrentFunction { get; set; } - - public StructuredProgramInfo Info { get; } - - public AttributeUsage AttributeUsage { get; } - public ShaderDefinitions Definitions { get; } - public ShaderProperties Properties { get; } - public HostCapabilities HostCapabilities { get; } - public ILogger Logger { get; } - public TargetApi TargetApi { get; } - - public OperandManager OperandManager { get; } - - private readonly StringBuilder _sb; - - private int _level; - - private string _indentation; - - public CodeGenContext(StructuredProgramInfo info, CodeGenParameters parameters) - { - Info = info; - AttributeUsage = parameters.AttributeUsage; - Definitions = parameters.Definitions; - Properties = parameters.Properties; - HostCapabilities = parameters.HostCapabilities; - Logger = parameters.Logger; - TargetApi = parameters.TargetApi; - - OperandManager = new OperandManager(); - - _sb = new StringBuilder(); - } - - public void AppendLine() - { - _sb.AppendLine(); - } - - public void AppendLine(string str) - { - _sb.AppendLine(_indentation + str); - } - - public string GetCode() - { - return _sb.ToString(); - } - - public void EnterScope(string prefix = "") - { - AppendLine(prefix + "{"); - - _level++; - - UpdateIndentation(); - } - - public void LeaveScope(string suffix = "") - { - if (_level == 0) - { - return; - } - - _level--; - - UpdateIndentation(); - - AppendLine("}" + suffix); - } - - public StructuredFunction GetFunction(int id) - { - return Info.Functions[id]; - } - - private void UpdateIndentation() - { - _indentation = GetIndentation(_level); - } - - private static string GetIndentation(int level) - { - string indentation = string.Empty; - - for (int index = 0; index < level; index++) - { - indentation += Tab; - } - - return indentation; - } - } -} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs deleted file mode 100644 index a9464834a..000000000 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs +++ /dev/null @@ -1,577 +0,0 @@ -using Ryujinx.Common; -using Ryujinx.Graphics.Shader.IntermediateRepresentation; -using Ryujinx.Graphics.Shader.StructuredIr; -using Ryujinx.Graphics.Shader.Translation; -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Ryujinx.Graphics.Shader.CodeGen.Msl -{ - static class Declarations - { - /* - * Description of MSL Binding Model - * - * There are a few fundamental differences between how GLSL and MSL handle I/O. - * This comment will set out to describe the reasons why things are done certain ways - * and to describe the overall binding model that we're striving for here. - * - * Main I/O Structs - * - * Each stage has a main input and output struct (if applicable) labeled as [Stage][In/Out], i.e VertexIn. - * Every field within these structs is labeled with an [[attribute(n)]] property, - * and the overall struct is labeled with [[stage_in]] for input structs, and defined as the - * output type of the main shader function for the output struct. This struct also contains special - * attribute-based properties like [[position]] that would be "built-ins" in a GLSL context. - * - * These structs are passed as inputs to all inline functions due to containing "built-ins" - * that inline functions assume access to. - * - * Vertex & Zero Buffers - * - * Binding indices 0-16 are reserved for vertex buffers, and binding 18 is reserved for the zero buffer. - * - * Uniforms & Storage Buffers - * - * Uniforms and storage buffers are tightly packed into their respective argument buffers - * (effectively ignoring binding indices at shader level), with each pointer to the corresponding - * struct that defines the layout and fields of these buffers (usually just a single data array), laid - * out one after the other in ascending order of their binding index. - * - * The uniforms argument buffer is always bound at a fixed index of 20. - * The storage buffers argument buffer is always bound at a fixed index of 21. - * - * These structs are passed as inputs to all inline functions as in GLSL or SPIRV, - * uniforms and storage buffers would be globals, and inline functions assume access to these buffers. - * - * Samplers & Textures - * - * Metal does not have a combined image sampler like sampler2D in GLSL, as a result we need to bind - * an individual texture and a sampler object for each instance of a combined image sampler. - * Samplers and textures are bound in a shared argument buffer. This argument buffer is tightly packed - * (effectively ignoring binding indices at shader level), with texture and their samplers (if present) - * laid out one after the other in ascending order of their binding index. - * - * The samplers and textures argument buffer is always bound at a fixed index of 22. - * - */ - - public static int[] Declare(CodeGenContext context, StructuredProgramInfo info) - { - // TODO: Re-enable this warning - context.AppendLine("#pragma clang diagnostic ignored \"-Wunused-variable\""); - context.AppendLine(); - context.AppendLine("#include "); - context.AppendLine("#include "); - context.AppendLine(); - context.AppendLine("using namespace metal;"); - context.AppendLine(); - - bool fsi = (info.HelperFunctionsMask & HelperFunctionsMask.FSI) != 0; - - DeclareInputAttributes(context, info.IoDefinitions.Where(x => IsUserDefined(x, StorageKind.Input))); - context.AppendLine(); - DeclareOutputAttributes(context, info.IoDefinitions.Where(x => x.StorageKind == StorageKind.Output)); - context.AppendLine(); - DeclareBufferStructures(context, context.Properties.ConstantBuffers.Values.OrderBy(x => x.Binding).ToArray(), true, fsi); - DeclareBufferStructures(context, context.Properties.StorageBuffers.Values.OrderBy(x => x.Binding).ToArray(), false, fsi); - - // We need to declare each set as a new struct - Dictionary textureDefinitions = context.Properties.Textures.Values - .GroupBy(x => x.Set) - .ToDictionary(x => x.Key, x => x.OrderBy(y => y.Binding).ToArray()); - - Dictionary imageDefinitions = context.Properties.Images.Values - .GroupBy(x => x.Set) - .ToDictionary(x => x.Key, x => x.OrderBy(y => y.Binding).ToArray()); - - int[] textureSets = textureDefinitions.Keys.ToArray(); - int[] imageSets = imageDefinitions.Keys.ToArray(); - - int[] sets = textureSets.Union(imageSets).ToArray(); - - foreach (KeyValuePair set in textureDefinitions) - { - DeclareTextures(context, set.Value, set.Key); - } - - foreach (KeyValuePair set in imageDefinitions) - { - DeclareImages(context, set.Value, set.Key, fsi); - } - - if ((info.HelperFunctionsMask & HelperFunctionsMask.FindLSB) != 0) - { - AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/FindLSB.metal"); - } - - if ((info.HelperFunctionsMask & HelperFunctionsMask.FindMSBS32) != 0) - { - AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/FindMSBS32.metal"); - } - - if ((info.HelperFunctionsMask & HelperFunctionsMask.FindMSBU32) != 0) - { - AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/FindMSBU32.metal"); - } - - if ((info.HelperFunctionsMask & HelperFunctionsMask.SwizzleAdd) != 0) - { - AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/SwizzleAdd.metal"); - } - - if ((info.HelperFunctionsMask & HelperFunctionsMask.Precise) != 0) - { - AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/Precise.metal"); - } - - return sets; - } - - static bool IsUserDefined(IoDefinition ioDefinition, StorageKind storageKind) - { - return ioDefinition.StorageKind == storageKind && ioDefinition.IoVariable == IoVariable.UserDefined; - } - - public static void DeclareLocals(CodeGenContext context, StructuredFunction function, ShaderStage stage, bool isMainFunc = false) - { - if (isMainFunc) - { - // TODO: Support OaIndexing - if (context.Definitions.IaIndexing) - { - context.EnterScope($"array {Defaults.IAttributePrefix} = "); - - for (int i = 0; i < Constants.MaxAttributes; i++) - { - context.AppendLine($"in.{Defaults.IAttributePrefix}{i},"); - } - - context.LeaveScope(";"); - } - - DeclareMemories(context, context.Properties.LocalMemories.Values, isShared: false); - DeclareMemories(context, context.Properties.SharedMemories.Values, isShared: true); - - switch (stage) - { - case ShaderStage.Vertex: - context.AppendLine("VertexOut out = {};"); - // TODO: Only add if necessary - context.AppendLine("uint instance_index = instance_id + base_instance;"); - break; - case ShaderStage.Fragment: - context.AppendLine("FragmentOut out = {};"); - break; - } - - // TODO: Only add if necessary - if (stage != ShaderStage.Compute) - { - // MSL does not give us access to [[thread_index_in_simdgroup]] - // outside compute. But we may still need to provide this value in frag/vert. - context.AppendLine("uint thread_index_in_simdgroup = simd_prefix_exclusive_sum(1);"); - } - } - - foreach (AstOperand decl in function.Locals) - { - string name = context.OperandManager.DeclareLocal(decl); - - context.AppendLine(GetVarTypeName(decl.VarType) + " " + name + ";"); - } - } - - public static string GetVarTypeName(AggregateType type, bool atomic = false) - { - string s32 = atomic ? "atomic_int" : "int"; - string u32 = atomic ? "atomic_uint" : "uint"; - - return type switch - { - AggregateType.Void => "void", - AggregateType.Bool => "bool", - AggregateType.FP32 => "float", - AggregateType.S32 => s32, - AggregateType.U32 => u32, - AggregateType.Vector2 | AggregateType.Bool => "bool2", - AggregateType.Vector2 | AggregateType.FP32 => "float2", - AggregateType.Vector2 | AggregateType.S32 => "int2", - AggregateType.Vector2 | AggregateType.U32 => "uint2", - AggregateType.Vector3 | AggregateType.Bool => "bool3", - AggregateType.Vector3 | AggregateType.FP32 => "float3", - AggregateType.Vector3 | AggregateType.S32 => "int3", - AggregateType.Vector3 | AggregateType.U32 => "uint3", - AggregateType.Vector4 | AggregateType.Bool => "bool4", - AggregateType.Vector4 | AggregateType.FP32 => "float4", - AggregateType.Vector4 | AggregateType.S32 => "int4", - AggregateType.Vector4 | AggregateType.U32 => "uint4", - _ => throw new ArgumentException($"Invalid variable type \"{type}\"."), - }; - } - - private static void DeclareMemories(CodeGenContext context, IEnumerable memories, bool isShared) - { - string prefix = isShared ? "threadgroup " : string.Empty; - - foreach (MemoryDefinition memory in memories) - { - string arraySize = string.Empty; - if ((memory.Type & AggregateType.Array) != 0) - { - arraySize = $"[{memory.ArrayLength}]"; - } - string typeName = GetVarTypeName(memory.Type & ~AggregateType.Array); - context.AppendLine($"{prefix}{typeName} {memory.Name}{arraySize};"); - } - } - - private static void DeclareBufferStructures(CodeGenContext context, BufferDefinition[] buffers, bool constant, bool fsi) - { - string name = constant ? "ConstantBuffers" : "StorageBuffers"; - string addressSpace = constant ? "constant" : "device"; - - string[] bufferDec = new string[buffers.Length]; - - for (int i = 0; i < buffers.Length; i++) - { - BufferDefinition buffer = buffers[i]; - - bool needsPadding = buffer.Layout == BufferLayout.Std140; - string fsiSuffix = !constant && fsi ? " [[raster_order_group(0)]]" : string.Empty; - - bufferDec[i] = $"{addressSpace} {Defaults.StructPrefix}_{buffer.Name}* {buffer.Name}{fsiSuffix};"; - - context.AppendLine($"struct {Defaults.StructPrefix}_{buffer.Name}"); - context.EnterScope(); - - foreach (StructureField field in buffer.Type.Fields) - { - AggregateType type = field.Type; - type |= (needsPadding && (field.Type & AggregateType.Array) != 0) - ? AggregateType.Vector4 - : AggregateType.Invalid; - - type &= ~AggregateType.Array; - - string typeName = GetVarTypeName(type); - string arraySuffix = string.Empty; - - if (field.Type.HasFlag(AggregateType.Array)) - { - if (field.ArrayLength > 0) - { - arraySuffix = $"[{field.ArrayLength}]"; - } - else - { - // Probably UB, but this is the approach that MVK takes - arraySuffix = "[1]"; - } - } - - context.AppendLine($"{typeName} {field.Name}{arraySuffix};"); - } - - context.LeaveScope(";"); - context.AppendLine(); - } - - context.AppendLine($"struct {name}"); - context.EnterScope(); - - foreach (string declaration in bufferDec) - { - context.AppendLine(declaration); - } - - context.LeaveScope(";"); - context.AppendLine(); - } - - private static void DeclareTextures(CodeGenContext context, TextureDefinition[] textures, int set) - { - string setName = GetNameForSet(set); - context.AppendLine($"struct {setName}"); - context.EnterScope(); - - List textureDec = []; - - foreach (TextureDefinition texture in textures) - { - if (texture.Type != SamplerType.None) - { - string textureTypeName = texture.Type.ToMslTextureType(texture.Format.GetComponentType()); - - if (texture.ArrayLength > 1) - { - textureTypeName = $"array<{textureTypeName}, {texture.ArrayLength}>"; - } - - textureDec.Add($"{textureTypeName} tex_{texture.Name};"); - } - - if (!texture.Separate && texture.Type != SamplerType.TextureBuffer) - { - string samplerType = "sampler"; - - if (texture.ArrayLength > 1) - { - samplerType = $"array<{samplerType}, {texture.ArrayLength}>"; - } - - textureDec.Add($"{samplerType} samp_{texture.Name};"); - } - } - - foreach (string declaration in textureDec) - { - context.AppendLine(declaration); - } - - context.LeaveScope(";"); - context.AppendLine(); - } - - private static void DeclareImages(CodeGenContext context, TextureDefinition[] images, int set, bool fsi) - { - string setName = GetNameForSet(set); - context.AppendLine($"struct {setName}"); - context.EnterScope(); - - string[] imageDec = new string[images.Length]; - - for (int i = 0; i < images.Length; i++) - { - TextureDefinition image = images[i]; - - string imageTypeName = image.Type.ToMslTextureType(image.Format.GetComponentType(), true); - if (image.ArrayLength > 1) - { - imageTypeName = $"array<{imageTypeName}, {image.ArrayLength}>"; - } - - string fsiSuffix = fsi ? " [[raster_order_group(0)]]" : string.Empty; - - imageDec[i] = $"{imageTypeName} {image.Name}{fsiSuffix};"; - } - - foreach (string declaration in imageDec) - { - context.AppendLine(declaration); - } - - context.LeaveScope(";"); - context.AppendLine(); - } - - private static void DeclareInputAttributes(CodeGenContext context, IEnumerable inputs) - { - if (context.Definitions.Stage == ShaderStage.Compute) - { - return; - } - - switch (context.Definitions.Stage) - { - case ShaderStage.Vertex: - context.AppendLine("struct VertexIn"); - break; - case ShaderStage.Fragment: - context.AppendLine("struct FragmentIn"); - break; - } - - context.EnterScope(); - - if (context.Definitions.Stage == ShaderStage.Fragment) - { - // TODO: check if it's needed - context.AppendLine("float4 position [[position, invariant]];"); - context.AppendLine("bool front_facing [[front_facing]];"); - context.AppendLine("float2 point_coord [[point_coord]];"); - context.AppendLine("uint primitive_id [[primitive_id]];"); - } - - if (context.Definitions.IaIndexing) - { - // MSL does not support arrays in stage I/O - // We need to use the SPIRV-Cross workaround - for (int i = 0; i < Constants.MaxAttributes; i++) - { - string suffix = context.Definitions.Stage == ShaderStage.Fragment ? $"[[user(loc{i})]]" : $"[[attribute({i})]]"; - context.AppendLine($"float4 {Defaults.IAttributePrefix}{i} {suffix};"); - } - } - - if (inputs.Any()) - { - foreach (IoDefinition ioDefinition in inputs.OrderBy(x => x.Location)) - { - if (context.Definitions.IaIndexing && ioDefinition.IoVariable == IoVariable.UserDefined) - { - continue; - } - - string iq = string.Empty; - - if (context.Definitions.Stage == ShaderStage.Fragment) - { - iq = context.Definitions.ImapTypes[ioDefinition.Location].GetFirstUsedType() switch - { - PixelImap.Constant => "[[flat]] ", - PixelImap.ScreenLinear => "[[center_no_perspective]] ", - _ => string.Empty, - }; - } - - string type = ioDefinition.IoVariable switch - { - // IoVariable.Position => "float4", - IoVariable.GlobalId => "uint3", - IoVariable.VertexId => "uint", - IoVariable.VertexIndex => "uint", - // IoVariable.PointCoord => "float2", - _ => GetVarTypeName(context.Definitions.GetUserDefinedType(ioDefinition.Location, isOutput: false)) - }; - string name = ioDefinition.IoVariable switch - { - // IoVariable.Position => "position", - IoVariable.GlobalId => "global_id", - IoVariable.VertexId => "vertex_id", - IoVariable.VertexIndex => "vertex_index", - // IoVariable.PointCoord => "point_coord", - _ => $"{Defaults.IAttributePrefix}{ioDefinition.Location}" - }; - string suffix = ioDefinition.IoVariable switch - { - // IoVariable.Position => "[[position, invariant]]", - IoVariable.GlobalId => "[[thread_position_in_grid]]", - IoVariable.VertexId => "[[vertex_id]]", - // TODO: Avoid potential redeclaration - IoVariable.VertexIndex => "[[vertex_id]]", - // IoVariable.PointCoord => "[[point_coord]]", - IoVariable.UserDefined => context.Definitions.Stage == ShaderStage.Fragment ? $"[[user(loc{ioDefinition.Location})]]" : $"[[attribute({ioDefinition.Location})]]", - _ => string.Empty - }; - - context.AppendLine($"{type} {name} {iq}{suffix};"); - } - } - - context.LeaveScope(";"); - } - - private static void DeclareOutputAttributes(CodeGenContext context, IEnumerable outputs) - { - switch (context.Definitions.Stage) - { - case ShaderStage.Vertex: - context.AppendLine("struct VertexOut"); - break; - case ShaderStage.Fragment: - context.AppendLine("struct FragmentOut"); - break; - case ShaderStage.Compute: - context.AppendLine("struct KernelOut"); - break; - } - - context.EnterScope(); - - if (context.Definitions.OaIndexing) - { - // MSL does not support arrays in stage I/O - // We need to use the SPIRV-Cross workaround - for (int i = 0; i < Constants.MaxAttributes; i++) - { - context.AppendLine($"float4 {Defaults.OAttributePrefix}{i} [[user(loc{i})]];"); - } - } - - if (outputs.Any()) - { - outputs = outputs.OrderBy(x => x.Location); - - if (context.Definitions.Stage == ShaderStage.Fragment && context.Definitions.DualSourceBlend) - { - IoDefinition firstOutput = outputs.ElementAtOrDefault(0); - IoDefinition secondOutput = outputs.ElementAtOrDefault(1); - - string type1 = GetVarTypeName(context.Definitions.GetFragmentOutputColorType(firstOutput.Location)); - string type2 = GetVarTypeName(context.Definitions.GetFragmentOutputColorType(secondOutput.Location)); - - string name1 = $"color{firstOutput.Location}"; - string name2 = $"color{firstOutput.Location + 1}"; - - context.AppendLine($"{type1} {name1} [[color({firstOutput.Location}), index(0)]];"); - context.AppendLine($"{type2} {name2} [[color({firstOutput.Location}), index(1)]];"); - - outputs = outputs.Skip(2); - } - - foreach (IoDefinition ioDefinition in outputs) - { - if (context.Definitions.OaIndexing && ioDefinition.IoVariable == IoVariable.UserDefined) - { - continue; - } - - string type = ioDefinition.IoVariable switch - { - IoVariable.Position => "float4", - IoVariable.PointSize => "float", - IoVariable.FragmentOutputColor => GetVarTypeName(context.Definitions.GetFragmentOutputColorType(ioDefinition.Location)), - IoVariable.FragmentOutputDepth => "float", - IoVariable.ClipDistance => "float", - _ => GetVarTypeName(context.Definitions.GetUserDefinedType(ioDefinition.Location, isOutput: true)) - }; - string name = ioDefinition.IoVariable switch - { - IoVariable.Position => "position", - IoVariable.PointSize => "point_size", - IoVariable.FragmentOutputColor => $"color{ioDefinition.Location}", - IoVariable.FragmentOutputDepth => "depth", - IoVariable.ClipDistance => "clip_distance", - _ => $"{Defaults.OAttributePrefix}{ioDefinition.Location}" - }; - string suffix = ioDefinition.IoVariable switch - { - IoVariable.Position => "[[position, invariant]]", - IoVariable.PointSize => "[[point_size]]", - IoVariable.UserDefined => $"[[user(loc{ioDefinition.Location})]]", - IoVariable.FragmentOutputColor => $"[[color({ioDefinition.Location})]]", - IoVariable.FragmentOutputDepth => "[[depth(any)]]", - IoVariable.ClipDistance => $"[[clip_distance]][{Defaults.TotalClipDistances}]", - _ => string.Empty - }; - - context.AppendLine($"{type} {name} {suffix};"); - } - } - - context.LeaveScope(";"); - } - - private static void AppendHelperFunction(CodeGenContext context, string filename) - { - string code = EmbeddedResources.ReadAllText(filename); - - code = code.Replace("\t", CodeGenContext.Tab); - - context.AppendLine(code); - context.AppendLine(); - } - - public static string GetNameForSet(int set, bool forVar = false) - { - return (uint)set switch - { - Defaults.TexturesSetIndex => forVar ? "textures" : "Textures", - Defaults.ImagesSetIndex => forVar ? "images" : "Images", - _ => $"{(forVar ? "set" : "Set")}{set}" - }; - } - } -} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Defaults.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Defaults.cs deleted file mode 100644 index 511a2f606..000000000 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Defaults.cs +++ /dev/null @@ -1,34 +0,0 @@ -namespace Ryujinx.Graphics.Shader.CodeGen.Msl -{ - static class Defaults - { - public const string LocalNamePrefix = "temp"; - - public const string PerPatchAttributePrefix = "patchAttr"; - public const string IAttributePrefix = "inAttr"; - public const string OAttributePrefix = "outAttr"; - - public const string StructPrefix = "struct"; - - public const string ArgumentNamePrefix = "a"; - - public const string UndefinedName = "0"; - - public const int MaxVertexBuffers = 16; - - public const uint ZeroBufferIndex = MaxVertexBuffers; - public const uint BaseSetIndex = MaxVertexBuffers + 1; - - public const uint ConstantBuffersIndex = BaseSetIndex; - public const uint StorageBuffersIndex = BaseSetIndex + 1; - public const uint TexturesIndex = BaseSetIndex + 2; - public const uint ImagesIndex = BaseSetIndex + 3; - - public const uint ConstantBuffersSetIndex = 0; - public const uint StorageBuffersSetIndex = 1; - public const uint TexturesSetIndex = 2; - public const uint ImagesSetIndex = 3; - - public const int TotalClipDistances = 8; - } -} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/FindLSB.metal b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/FindLSB.metal deleted file mode 100644 index ad786adb3..000000000 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/FindLSB.metal +++ /dev/null @@ -1,5 +0,0 @@ -template -inline T findLSB(T x) -{ - return select(ctz(x), T(-1), x == T(0)); -} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/FindMSBS32.metal b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/FindMSBS32.metal deleted file mode 100644 index af4eb6cbd..000000000 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/FindMSBS32.metal +++ /dev/null @@ -1,5 +0,0 @@ -template -inline T findMSBS32(T x) -{ - return select(clz(T(0)) - (clz(x) + T(1)), T(-1), x == T(0)); -} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/FindMSBU32.metal b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/FindMSBU32.metal deleted file mode 100644 index 6d97c41a9..000000000 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/FindMSBU32.metal +++ /dev/null @@ -1,6 +0,0 @@ -template -inline T findMSBU32(T x) -{ - T v = select(x, T(-1) - x, x < T(0)); - return select(clz(T(0)) - (clz(v) + T(1)), T(-1), v == T(0)); -} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/HelperFunctionNames.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/HelperFunctionNames.cs deleted file mode 100644 index 370159a0e..000000000 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/HelperFunctionNames.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Ryujinx.Graphics.Shader.CodeGen.Msl -{ - static class HelperFunctionNames - { - public static string FindLSB = "findLSB"; - public static string FindMSBS32 = "findMSBS32"; - public static string FindMSBU32 = "findMSBU32"; - public static string SwizzleAdd = "swizzleAdd"; - } -} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/Precise.metal b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/Precise.metal deleted file mode 100644 index 366bea1ac..000000000 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/Precise.metal +++ /dev/null @@ -1,14 +0,0 @@ -template -[[clang::optnone]] T PreciseFAdd(T l, T r) { - return fma(T(1), l, r); -} - -template -[[clang::optnone]] T PreciseFSub(T l, T r) { - return fma(T(-1), r, l); -} - -template -[[clang::optnone]] T PreciseFMul(T l, T r) { - return fma(l, r, T(0)); -} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/SwizzleAdd.metal b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/SwizzleAdd.metal deleted file mode 100644 index 22a079b01..000000000 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/SwizzleAdd.metal +++ /dev/null @@ -1,7 +0,0 @@ -float swizzleAdd(float x, float y, int mask, uint thread_index_in_simdgroup) -{ - float4 xLut = float4(1.0, -1.0, 1.0, 0.0); - float4 yLut = float4(1.0, 1.0, -1.0, 1.0); - int lutIdx = (mask >> (int(thread_index_in_simdgroup & 3u) * 2)) & 3; - return x * xLut[lutIdx] + y * yLut[lutIdx]; -} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs deleted file mode 100644 index 0be6035b6..000000000 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs +++ /dev/null @@ -1,186 +0,0 @@ -using Ryujinx.Graphics.Shader.IntermediateRepresentation; -using Ryujinx.Graphics.Shader.StructuredIr; -using Ryujinx.Graphics.Shader.Translation; -using System; -using System.Text; -using static Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions.InstGenBallot; -using static Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions.InstGenBarrier; -using static Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions.InstGenCall; -using static Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions.InstGenHelper; -using static Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions.InstGenMemory; -using static Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions.InstGenVector; -using static Ryujinx.Graphics.Shader.StructuredIr.InstructionInfo; - -namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions -{ - static class InstGen - { - public static string GetExpression(CodeGenContext context, IAstNode node) - { - if (node is AstOperation operation) - { - return GetExpression(context, operation); - } - else if (node is AstOperand operand) - { - return context.OperandManager.GetExpression(context, operand); - } - - throw new ArgumentException($"Invalid node type \"{node?.GetType().Name ?? "null"}\"."); - } - - private static string GetExpression(CodeGenContext context, AstOperation operation) - { - Instruction inst = operation.Inst; - - InstInfo info = GetInstructionInfo(inst); - - if ((info.Type & InstType.Call) != 0) - { - bool atomic = (info.Type & InstType.Atomic) != 0; - - int arity = (int)(info.Type & InstType.ArityMask); - - StringBuilder builder = new(); - - if (atomic && (operation.StorageKind == StorageKind.StorageBuffer || operation.StorageKind == StorageKind.SharedMemory)) - { - AggregateType dstType = operation.Inst == Instruction.AtomicMaxS32 || operation.Inst == Instruction.AtomicMinS32 - ? AggregateType.S32 - : AggregateType.U32; - - bool shared = operation.StorageKind == StorageKind.SharedMemory; - - builder.Append($"({(shared ? "threadgroup" : "device")} {Declarations.GetVarTypeName(dstType, true)}*)&{GenerateLoadOrStore(context, operation, isStore: false)}"); - - for (int argIndex = operation.SourcesCount - arity + 2; argIndex < operation.SourcesCount; argIndex++) - { - builder.Append($", {GetSourceExpr(context, operation.GetSource(argIndex), dstType)}, memory_order_relaxed"); - } - } - else - { - for (int argIndex = 0; argIndex < arity; argIndex++) - { - if (argIndex != 0) - { - builder.Append(", "); - } - - AggregateType dstType = GetSrcVarType(inst, argIndex); - - builder.Append(GetSourceExpr(context, operation.GetSource(argIndex), dstType)); - } - - if ((operation.Inst & Instruction.Mask) == Instruction.SwizzleAdd) - { - // SwizzleAdd takes one last argument, the thread_index_in_simdgroup - builder.Append(", thread_index_in_simdgroup"); - } - } - - return $"{info.OpName}({builder})"; - } - else if ((info.Type & InstType.Op) != 0) - { - string op = info.OpName; - - if (inst == Instruction.Return && operation.SourcesCount != 0) - { - return $"{op} {GetSourceExpr(context, operation.GetSource(0), context.CurrentFunction.ReturnType)}"; - } - if (inst == Instruction.Return && context.Definitions.Stage is ShaderStage.Vertex or ShaderStage.Fragment) - { - return $"{op} out"; - } - - int arity = (int)(info.Type & InstType.ArityMask); - - string[] expr = new string[arity]; - - for (int index = 0; index < arity; index++) - { - IAstNode src = operation.GetSource(index); - - string srcExpr = GetSourceExpr(context, src, GetSrcVarType(inst, index)); - - bool isLhs = arity == 2 && index == 0; - - expr[index] = Enclose(srcExpr, src, inst, info, isLhs); - } - - switch (arity) - { - case 0: - return op; - - case 1: - return op + expr[0]; - - case 2: - if (operation.ForcePrecise) - { - string func = (inst & Instruction.Mask) switch - { - Instruction.Add => "PreciseFAdd", - Instruction.Subtract => "PreciseFSub", - Instruction.Multiply => "PreciseFMul", - _ => throw new NotImplementedException() - }; - - return $"{func}({expr[0]}, {expr[1]})"; - } - - return $"{expr[0]} {op} {expr[1]}"; - - case 3: - return $"{expr[0]} {op[0]} {expr[1]} {op[1]} {expr[2]}"; - } - } - else if ((info.Type & InstType.Special) != 0) - { - switch (inst & Instruction.Mask) - { - case Instruction.Ballot: - return Ballot(context, operation); - case Instruction.Call: - return Call(context, operation); - case Instruction.FSIBegin: - case Instruction.FSIEnd: - return "// FSI implemented with raster order groups in MSL"; - case Instruction.GroupMemoryBarrier: - case Instruction.MemoryBarrier: - case Instruction.Barrier: - return Barrier(context, operation); - case Instruction.ImageLoad: - case Instruction.ImageStore: - case Instruction.ImageAtomic: - return ImageLoadOrStore(context, operation); - case Instruction.Load: - return Load(context, operation); - case Instruction.Lod: - return Lod(context, operation); - case Instruction.Store: - return Store(context, operation); - case Instruction.TextureSample: - return TextureSample(context, operation); - case Instruction.TextureQuerySamples: - return TextureQuerySamples(context, operation); - case Instruction.TextureQuerySize: - return TextureQuerySize(context, operation); - case Instruction.PackHalf2x16: - return PackHalf2x16(context, operation); - case Instruction.UnpackHalf2x16: - return UnpackHalf2x16(context, operation); - case Instruction.VectorExtract: - return VectorExtract(context, operation); - case Instruction.VoteAllEqual: - return VoteAllEqual(context, operation); - } - } - - // TODO: Return this to being an error - return $"Unexpected instruction type \"{info.Type}\"."; - } - } -} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenBallot.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenBallot.cs deleted file mode 100644 index 19a065d77..000000000 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenBallot.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Ryujinx.Graphics.Shader.StructuredIr; -using Ryujinx.Graphics.Shader.Translation; - -using static Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions.InstGenHelper; -using static Ryujinx.Graphics.Shader.StructuredIr.InstructionInfo; - -namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions -{ - static class InstGenBallot - { - public static string Ballot(CodeGenContext context, AstOperation operation) - { - AggregateType dstType = GetSrcVarType(operation.Inst, 0); - - string arg = GetSourceExpr(context, operation.GetSource(0), dstType); - char component = "xyzw"[operation.Index]; - - return $"uint4(as_type((simd_vote::vote_t)simd_ballot({arg})), 0, 0).{component}"; - } - - public static string VoteAllEqual(CodeGenContext context, AstOperation operation) - { - AggregateType dstType = GetSrcVarType(operation.Inst, 0); - - string arg = GetSourceExpr(context, operation.GetSource(0), dstType); - - return $"simd_all({arg}) || !simd_any({arg})"; - } - } -} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenBarrier.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenBarrier.cs deleted file mode 100644 index 198b701d6..000000000 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenBarrier.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Ryujinx.Graphics.Shader.IntermediateRepresentation; -using Ryujinx.Graphics.Shader.StructuredIr; - -namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions -{ - static class InstGenBarrier - { - public static string Barrier(CodeGenContext context, AstOperation operation) - { - bool device = (operation.Inst & Instruction.Mask) == Instruction.MemoryBarrier; - - return $"threadgroup_barrier(mem_flags::mem_{(device ? "device" : "threadgroup")})"; - } - } -} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenCall.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenCall.cs deleted file mode 100644 index 98a8a140e..000000000 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenCall.cs +++ /dev/null @@ -1,60 +0,0 @@ -using Ryujinx.Graphics.Shader.StructuredIr; - -using static Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions.InstGenHelper; - -namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions -{ - static class InstGenCall - { - public static string Call(CodeGenContext context, AstOperation operation) - { - AstOperand funcId = (AstOperand)operation.GetSource(0); - - StructuredFunction function = context.GetFunction(funcId.Value); - - int argCount = operation.SourcesCount - 1; - int additionalArgCount = CodeGenContext.AdditionalArgCount + (context.Definitions.Stage != ShaderStage.Compute ? 1 : 0); - bool needsThreadIndex = false; - - // TODO: Replace this with a proper flag - if (function.Name.Contains("Shuffle")) - { - needsThreadIndex = true; - additionalArgCount++; - } - - string[] args = new string[argCount + additionalArgCount]; - - // Additional arguments - if (context.Definitions.Stage != ShaderStage.Compute) - { - args[0] = "in"; - args[1] = "constant_buffers"; - args[2] = "storage_buffers"; - - if (needsThreadIndex) - { - args[3] = "thread_index_in_simdgroup"; - } - } - else - { - args[0] = "constant_buffers"; - args[1] = "storage_buffers"; - - if (needsThreadIndex) - { - args[2] = "thread_index_in_simdgroup"; - } - } - - int argIndex = additionalArgCount; - for (int i = 0; i < argCount; i++) - { - args[argIndex++] = GetSourceExpr(context, operation.GetSource(i + 1), function.GetArgumentType(i)); - } - - return $"{function.Name}({string.Join(", ", args)})"; - } - } -} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenHelper.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenHelper.cs deleted file mode 100644 index 49f3c63aa..000000000 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenHelper.cs +++ /dev/null @@ -1,222 +0,0 @@ -using Ryujinx.Graphics.Shader.IntermediateRepresentation; -using Ryujinx.Graphics.Shader.StructuredIr; -using Ryujinx.Graphics.Shader.Translation; - -using static Ryujinx.Graphics.Shader.CodeGen.Msl.TypeConversion; - -namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions -{ - static class InstGenHelper - { - private static readonly InstInfo[] _infoTable; - - static InstGenHelper() - { - _infoTable = new InstInfo[(int)Instruction.Count]; - -#pragma warning disable IDE0055 // Disable formatting - Add(Instruction.AtomicAdd, InstType.AtomicBinary, "atomic_fetch_add_explicit"); - Add(Instruction.AtomicAnd, InstType.AtomicBinary, "atomic_fetch_and_explicit"); - Add(Instruction.AtomicCompareAndSwap, InstType.AtomicBinary, "atomic_compare_exchange_weak_explicit"); - Add(Instruction.AtomicMaxU32, InstType.AtomicBinary, "atomic_fetch_max_explicit"); - Add(Instruction.AtomicMinU32, InstType.AtomicBinary, "atomic_fetch_min_explicit"); - Add(Instruction.AtomicOr, InstType.AtomicBinary, "atomic_fetch_or_explicit"); - Add(Instruction.AtomicSwap, InstType.AtomicBinary, "atomic_exchange_explicit"); - Add(Instruction.AtomicXor, InstType.AtomicBinary, "atomic_fetch_xor_explicit"); - Add(Instruction.Absolute, InstType.CallUnary, "abs"); - Add(Instruction.Add, InstType.OpBinaryCom, "+", 2); - Add(Instruction.Ballot, InstType.Special); - Add(Instruction.Barrier, InstType.Special); - Add(Instruction.BitCount, InstType.CallUnary, "popcount"); - Add(Instruction.BitfieldExtractS32, InstType.CallTernary, "extract_bits"); - Add(Instruction.BitfieldExtractU32, InstType.CallTernary, "extract_bits"); - Add(Instruction.BitfieldInsert, InstType.CallQuaternary, "insert_bits"); - Add(Instruction.BitfieldReverse, InstType.CallUnary, "reverse_bits"); - Add(Instruction.BitwiseAnd, InstType.OpBinaryCom, "&", 6); - Add(Instruction.BitwiseExclusiveOr, InstType.OpBinaryCom, "^", 7); - Add(Instruction.BitwiseNot, InstType.OpUnary, "~", 0); - Add(Instruction.BitwiseOr, InstType.OpBinaryCom, "|", 8); - Add(Instruction.Call, InstType.Special); - Add(Instruction.Ceiling, InstType.CallUnary, "ceil"); - Add(Instruction.Clamp, InstType.CallTernary, "clamp"); - Add(Instruction.ClampU32, InstType.CallTernary, "clamp"); - Add(Instruction.CompareEqual, InstType.OpBinaryCom, "==", 5); - Add(Instruction.CompareGreater, InstType.OpBinary, ">", 4); - Add(Instruction.CompareGreaterOrEqual, InstType.OpBinary, ">=", 4); - Add(Instruction.CompareGreaterOrEqualU32, InstType.OpBinary, ">=", 4); - Add(Instruction.CompareGreaterU32, InstType.OpBinary, ">", 4); - Add(Instruction.CompareLess, InstType.OpBinary, "<", 4); - Add(Instruction.CompareLessOrEqual, InstType.OpBinary, "<=", 4); - Add(Instruction.CompareLessOrEqualU32, InstType.OpBinary, "<=", 4); - Add(Instruction.CompareLessU32, InstType.OpBinary, "<", 4); - Add(Instruction.CompareNotEqual, InstType.OpBinaryCom, "!=", 5); - Add(Instruction.ConditionalSelect, InstType.OpTernary, "?:", 12); - Add(Instruction.ConvertFP32ToFP64, 0); // MSL does not have a 64-bit FP - Add(Instruction.ConvertFP64ToFP32, 0); // MSL does not have a 64-bit FP - Add(Instruction.ConvertFP32ToS32, InstType.CallUnary, "int"); - Add(Instruction.ConvertFP32ToU32, InstType.CallUnary, "uint"); - Add(Instruction.ConvertFP64ToS32, 0); // MSL does not have a 64-bit FP - Add(Instruction.ConvertFP64ToU32, 0); // MSL does not have a 64-bit FP - Add(Instruction.ConvertS32ToFP32, InstType.CallUnary, "float"); - Add(Instruction.ConvertS32ToFP64, 0); // MSL does not have a 64-bit FP - Add(Instruction.ConvertU32ToFP32, InstType.CallUnary, "float"); - Add(Instruction.ConvertU32ToFP64, 0); // MSL does not have a 64-bit FP - Add(Instruction.Cosine, InstType.CallUnary, "cos"); - Add(Instruction.Ddx, InstType.CallUnary, "dfdx"); - Add(Instruction.Ddy, InstType.CallUnary, "dfdy"); - Add(Instruction.Discard, InstType.CallNullary, "discard_fragment"); - Add(Instruction.Divide, InstType.OpBinary, "/", 1); - Add(Instruction.EmitVertex, 0); // MSL does not have geometry shaders - Add(Instruction.EndPrimitive, 0); // MSL does not have geometry shaders - Add(Instruction.ExponentB2, InstType.CallUnary, "exp2"); - Add(Instruction.FSIBegin, InstType.Special); - Add(Instruction.FSIEnd, InstType.Special); - Add(Instruction.FindLSB, InstType.CallUnary, HelperFunctionNames.FindLSB); - Add(Instruction.FindMSBS32, InstType.CallUnary, HelperFunctionNames.FindMSBS32); - Add(Instruction.FindMSBU32, InstType.CallUnary, HelperFunctionNames.FindMSBU32); - Add(Instruction.Floor, InstType.CallUnary, "floor"); - Add(Instruction.FusedMultiplyAdd, InstType.CallTernary, "fma"); - Add(Instruction.GroupMemoryBarrier, InstType.Special); - Add(Instruction.ImageLoad, InstType.Special); - Add(Instruction.ImageStore, InstType.Special); - Add(Instruction.ImageAtomic, InstType.Special); // Metal 3.1+ - Add(Instruction.IsNan, InstType.CallUnary, "isnan"); - Add(Instruction.Load, InstType.Special); - Add(Instruction.Lod, InstType.Special); - Add(Instruction.LogarithmB2, InstType.CallUnary, "log2"); - Add(Instruction.LogicalAnd, InstType.OpBinaryCom, "&&", 9); - Add(Instruction.LogicalExclusiveOr, InstType.OpBinaryCom, "^", 10); - Add(Instruction.LogicalNot, InstType.OpUnary, "!", 0); - Add(Instruction.LogicalOr, InstType.OpBinaryCom, "||", 11); - Add(Instruction.LoopBreak, InstType.OpNullary, "break"); - Add(Instruction.LoopContinue, InstType.OpNullary, "continue"); - Add(Instruction.PackDouble2x32, 0); // MSL does not have a 64-bit FP - Add(Instruction.PackHalf2x16, InstType.Special); - Add(Instruction.Maximum, InstType.CallBinary, "max"); - Add(Instruction.MaximumU32, InstType.CallBinary, "max"); - Add(Instruction.MemoryBarrier, InstType.Special); - Add(Instruction.Minimum, InstType.CallBinary, "min"); - Add(Instruction.MinimumU32, InstType.CallBinary, "min"); - Add(Instruction.Modulo, InstType.CallBinary, "fmod"); - Add(Instruction.Multiply, InstType.OpBinaryCom, "*", 1); - Add(Instruction.MultiplyHighS32, InstType.CallBinary, "mulhi"); - Add(Instruction.MultiplyHighU32, InstType.CallBinary, "mulhi"); - Add(Instruction.Negate, InstType.OpUnary, "-"); - Add(Instruction.ReciprocalSquareRoot, InstType.CallUnary, "rsqrt"); - Add(Instruction.Return, InstType.OpNullary, "return"); - Add(Instruction.Round, InstType.CallUnary, "round"); - Add(Instruction.ShiftLeft, InstType.OpBinary, "<<", 3); - Add(Instruction.ShiftRightS32, InstType.OpBinary, ">>", 3); - Add(Instruction.ShiftRightU32, InstType.OpBinary, ">>", 3); - Add(Instruction.Shuffle, InstType.CallBinary, "simd_shuffle"); - Add(Instruction.ShuffleDown, InstType.CallBinary, "simd_shuffle_down"); - Add(Instruction.ShuffleUp, InstType.CallBinary, "simd_shuffle_up"); - Add(Instruction.ShuffleXor, InstType.CallBinary, "simd_shuffle_xor"); - Add(Instruction.Sine, InstType.CallUnary, "sin"); - Add(Instruction.SquareRoot, InstType.CallUnary, "sqrt"); - Add(Instruction.Store, InstType.Special); - Add(Instruction.Subtract, InstType.OpBinary, "-", 2); - Add(Instruction.SwizzleAdd, InstType.CallTernary, HelperFunctionNames.SwizzleAdd); - Add(Instruction.TextureSample, InstType.Special); - Add(Instruction.TextureQuerySamples, InstType.Special); - Add(Instruction.TextureQuerySize, InstType.Special); - Add(Instruction.Truncate, InstType.CallUnary, "trunc"); - Add(Instruction.UnpackDouble2x32, 0); // MSL does not have a 64-bit FP - Add(Instruction.UnpackHalf2x16, InstType.Special); - Add(Instruction.VectorExtract, InstType.Special); - Add(Instruction.VoteAll, InstType.CallUnary, "simd_all"); - Add(Instruction.VoteAllEqual, InstType.Special); - Add(Instruction.VoteAny, InstType.CallUnary, "simd_any"); -#pragma warning restore IDE0055 - } - - private static void Add(Instruction inst, InstType flags, string opName = null, int precedence = 0) - { - _infoTable[(int)inst] = new InstInfo(flags, opName, precedence); - } - - public static InstInfo GetInstructionInfo(Instruction inst) - { - return _infoTable[(int)(inst & Instruction.Mask)]; - } - - public static string GetSourceExpr(CodeGenContext context, IAstNode node, AggregateType dstType) - { - return ReinterpretCast(context, node, OperandManager.GetNodeDestType(context, node), dstType); - } - - public static string Enclose(string expr, IAstNode node, Instruction pInst, bool isLhs) - { - InstInfo pInfo = GetInstructionInfo(pInst); - - return Enclose(expr, node, pInst, pInfo, isLhs); - } - - public static string Enclose(string expr, IAstNode node, Instruction pInst, InstInfo pInfo, bool isLhs = false) - { - if (NeedsParenthesis(node, pInst, pInfo, isLhs)) - { - expr = "(" + expr + ")"; - } - - return expr; - } - - public static bool NeedsParenthesis(IAstNode node, Instruction pInst, InstInfo pInfo, bool isLhs) - { - // If the node isn't an operation, then it can only be an operand, - // and those never needs to be surrounded in parentheses. - if (node is not AstOperation operation) - { - // This is sort of a special case, if this is a negative constant, - // and it is consumed by a unary operation, we need to put on the parenthesis, - // as in MSL, while a sequence like ~-1 is valid, --2 is not. - if (IsNegativeConst(node) && pInfo.Type == InstType.OpUnary) - { - return true; - } - - return false; - } - - if ((pInfo.Type & (InstType.Call | InstType.Special)) != 0) - { - return false; - } - - InstInfo info = _infoTable[(int)(operation.Inst & Instruction.Mask)]; - - if ((info.Type & (InstType.Call | InstType.Special)) != 0) - { - return false; - } - - if (info.Precedence < pInfo.Precedence) - { - return false; - } - - if (info.Precedence == pInfo.Precedence && isLhs) - { - return false; - } - - if (pInst == operation.Inst && info.Type == InstType.OpBinaryCom) - { - return false; - } - - return true; - } - - private static bool IsNegativeConst(IAstNode node) - { - if (node is not AstOperand operand) - { - return false; - } - - return operand.Type == OperandType.Constant && operand.Value < 0; - } - } -} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs deleted file mode 100644 index a84a75e4f..000000000 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs +++ /dev/null @@ -1,672 +0,0 @@ -using Ryujinx.Common.Logging; -using Ryujinx.Graphics.Shader.IntermediateRepresentation; -using Ryujinx.Graphics.Shader.StructuredIr; -using Ryujinx.Graphics.Shader.Translation; -using System; -using System.Text; -using static Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions.InstGenHelper; -using static Ryujinx.Graphics.Shader.StructuredIr.InstructionInfo; - -namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions -{ - static class InstGenMemory - { - public static string GenerateLoadOrStore(CodeGenContext context, AstOperation operation, bool isStore) - { - StorageKind storageKind = operation.StorageKind; - - string varName; - AggregateType varType; - int srcIndex = 0; - bool isStoreOrAtomic = operation.Inst == Instruction.Store || operation.Inst.IsAtomic(); - int inputsCount = isStoreOrAtomic ? operation.SourcesCount - 1 : operation.SourcesCount; - bool fieldHasPadding = false; - - if (operation.Inst == Instruction.AtomicCompareAndSwap) - { - inputsCount--; - } - - string fieldName = string.Empty; - switch (storageKind) - { - case StorageKind.ConstantBuffer: - case StorageKind.StorageBuffer: - if (operation.GetSource(srcIndex++) is not AstOperand bindingIndex || bindingIndex.Type != OperandType.Constant) - { - throw new InvalidOperationException($"First input of {operation.Inst} with {storageKind} storage must be a constant operand."); - } - - int binding = bindingIndex.Value; - BufferDefinition buffer = storageKind == StorageKind.ConstantBuffer - ? context.Properties.ConstantBuffers[binding] - : context.Properties.StorageBuffers[binding]; - - if (operation.GetSource(srcIndex++) is not AstOperand fieldIndex || fieldIndex.Type != OperandType.Constant) - { - throw new InvalidOperationException($"Second input of {operation.Inst} with {storageKind} storage must be a constant operand."); - } - - StructureField field = buffer.Type.Fields[fieldIndex.Value]; - - fieldHasPadding = buffer.Layout == BufferLayout.Std140 - && ((field.Type & AggregateType.Vector4) == 0) - && ((field.Type & AggregateType.Array) != 0); - - varName = storageKind == StorageKind.ConstantBuffer - ? "constant_buffers" - : "storage_buffers"; - varName += "." + buffer.Name; - varName += "->" + field.Name; - varType = field.Type; - break; - - case StorageKind.LocalMemory: - case StorageKind.SharedMemory: - if (operation.GetSource(srcIndex++) is not AstOperand { Type: OperandType.Constant } bindingId) - { - throw new InvalidOperationException($"First input of {operation.Inst} with {storageKind} storage must be a constant operand."); - } - - MemoryDefinition memory = storageKind == StorageKind.LocalMemory - ? context.Properties.LocalMemories[bindingId.Value] - : context.Properties.SharedMemories[bindingId.Value]; - - varName = memory.Name; - varType = memory.Type; - break; - - case StorageKind.Input: - case StorageKind.InputPerPatch: - case StorageKind.Output: - case StorageKind.OutputPerPatch: - if (operation.GetSource(srcIndex++) is not AstOperand varId || varId.Type != OperandType.Constant) - { - throw new InvalidOperationException($"First input of {operation.Inst} with {storageKind} storage must be a constant operand."); - } - - IoVariable ioVariable = (IoVariable)varId.Value; - bool isOutput = storageKind.IsOutput(); - bool isPerPatch = storageKind.IsPerPatch(); - int location = -1; - int component = 0; - - if (context.Definitions.HasPerLocationInputOrOutput(ioVariable, isOutput)) - { - if (operation.GetSource(srcIndex++) is not AstOperand vecIndex || vecIndex.Type != OperandType.Constant) - { - throw new InvalidOperationException($"Second input of {operation.Inst} with {storageKind} storage must be a constant operand."); - } - - location = vecIndex.Value; - - if (operation.SourcesCount > srcIndex && - operation.GetSource(srcIndex) is AstOperand elemIndex && - elemIndex.Type == OperandType.Constant && - context.Definitions.HasPerLocationInputOrOutputComponent(ioVariable, vecIndex.Value, elemIndex.Value, isOutput)) - { - component = elemIndex.Value; - srcIndex++; - } - } - - (varName, varType) = IoMap.GetMslBuiltIn( - context.Definitions, - ioVariable, - location, - component, - isOutput, - isPerPatch); - break; - - default: - throw new InvalidOperationException($"Invalid storage kind {storageKind}."); - } - - for (; srcIndex < inputsCount; srcIndex++) - { - IAstNode src = operation.GetSource(srcIndex); - - if ((varType & AggregateType.ElementCountMask) != 0 && - srcIndex == inputsCount - 1 && - src is AstOperand elementIndex && - elementIndex.Type == OperandType.Constant) - { - varName += "." + "xyzw"[elementIndex.Value & 3]; - } - else - { - varName += $"[{GetSourceExpr(context, src, AggregateType.S32)}]"; - } - } - varName += fieldName; - varName += fieldHasPadding ? ".x" : string.Empty; - - if (isStore) - { - varType &= AggregateType.ElementTypeMask; - varName = $"{varName} = {GetSourceExpr(context, operation.GetSource(srcIndex), varType)}"; - } - - return varName; - } - - public static string ImageLoadOrStore(CodeGenContext context, AstOperation operation) - { - AstTextureOperation texOp = (AstTextureOperation)operation; - - bool isArray = (texOp.Type & SamplerType.Array) != 0; - - StringBuilder texCallBuilder = new(); - - int srcIndex = 0; - - string Src(AggregateType type) - { - return GetSourceExpr(context, texOp.GetSource(srcIndex++), type); - } - - string imageName = GetImageName(context, texOp, ref srcIndex); - texCallBuilder.Append(imageName); - texCallBuilder.Append('.'); - - if (texOp.Inst == Instruction.ImageAtomic) - { - texCallBuilder.Append((texOp.Flags & TextureFlags.AtomicMask) switch - { - TextureFlags.Add => "atomic_fetch_add", - TextureFlags.Minimum => "atomic_min", - TextureFlags.Maximum => "atomic_max", - TextureFlags.Increment => "atomic_fetch_add", - TextureFlags.Decrement => "atomic_fetch_sub", - TextureFlags.BitwiseAnd => "atomic_fetch_and", - TextureFlags.BitwiseOr => "atomic_fetch_or", - TextureFlags.BitwiseXor => "atomic_fetch_xor", - TextureFlags.Swap => "atomic_exchange", - TextureFlags.CAS => "atomic_compare_exchange_weak", - _ => "atomic_fetch_add", - }); - } - else - { - texCallBuilder.Append(texOp.Inst == Instruction.ImageLoad ? "read" : "write"); - } - - texCallBuilder.Append('('); - - StringBuilder coordsBuilder = new(); - - int coordsCount = texOp.Type.GetDimensions(); - - if (coordsCount > 1) - { - string[] elems = new string[coordsCount]; - - for (int index = 0; index < coordsCount; index++) - { - elems[index] = Src(AggregateType.S32); - } - - coordsBuilder.Append($"uint{coordsCount}({string.Join(", ", elems)})"); - } - else - { - coordsBuilder.Append($"uint({Src(AggregateType.S32)})"); - } - - if (isArray) - { - coordsBuilder.Append(", "); - coordsBuilder.Append(Src(AggregateType.S32)); - } - - if (texOp.Inst == Instruction.ImageStore) - { - AggregateType type = texOp.Format.GetComponentType(); - - string[] cElems = new string[4]; - - for (int index = 0; index < 4; index++) - { - if (srcIndex < texOp.SourcesCount) - { - cElems[index] = Src(type); - } - else - { - cElems[index] = type switch - { - AggregateType.S32 => NumberFormatter.FormatInt(0), - AggregateType.U32 => NumberFormatter.FormatUint(0), - _ => NumberFormatter.FormatFloat(0), - }; - } - } - - string prefix = type switch - { - AggregateType.S32 => "int", - AggregateType.U32 => "uint", - AggregateType.FP32 => "float", - _ => string.Empty, - }; - - texCallBuilder.Append($"{prefix}4({string.Join(", ", cElems)})"); - texCallBuilder.Append(", "); - } - - texCallBuilder.Append(coordsBuilder); - - if (texOp.Inst == Instruction.ImageAtomic) - { - texCallBuilder.Append(", "); - - AggregateType type = texOp.Format.GetComponentType(); - - if ((texOp.Flags & TextureFlags.AtomicMask) == TextureFlags.CAS) - { - texCallBuilder.Append(Src(type)); // Compare value. - } - - string value = (texOp.Flags & TextureFlags.AtomicMask) switch - { - TextureFlags.Increment => NumberFormatter.FormatInt(1, type), // TODO: Clamp value - TextureFlags.Decrement => NumberFormatter.FormatInt(-1, type), // TODO: Clamp value - _ => Src(type), - }; - - texCallBuilder.Append(value); - // This doesn't match what the MSL spec document says so either - // it is wrong or the MSL compiler has a bug. - texCallBuilder.Append(")[0]"); - } - else - { - texCallBuilder.Append(')'); - - if (texOp.Inst == Instruction.ImageLoad) - { - texCallBuilder.Append(GetMaskMultiDest(texOp.Index)); - } - } - - return texCallBuilder.ToString(); - } - - public static string Load(CodeGenContext context, AstOperation operation) - { - return GenerateLoadOrStore(context, operation, isStore: false); - } - - public static string Lod(CodeGenContext context, AstOperation operation) - { - AstTextureOperation texOp = (AstTextureOperation)operation; - - int coordsCount = texOp.Type.GetDimensions(); - int coordsIndex = 0; - - string textureName = GetTextureName(context, texOp, ref coordsIndex); - string samplerName = GetSamplerName(context, texOp, ref coordsIndex); - - string coordsExpr; - - if (coordsCount > 1) - { - string[] elems = new string[coordsCount]; - - for (int index = 0; index < coordsCount; index++) - { - elems[index] = GetSourceExpr(context, texOp.GetSource(coordsIndex + index), AggregateType.FP32); - } - - coordsExpr = "float" + coordsCount + "(" + string.Join(", ", elems) + ")"; - } - else - { - coordsExpr = GetSourceExpr(context, texOp.GetSource(coordsIndex), AggregateType.FP32); - } - - string clamped = $"{textureName}.calculate_clamped_lod({samplerName}, {coordsExpr})"; - string unclamped = $"{textureName}.calculate_unclamped_lod({samplerName}, {coordsExpr})"; - - return $"float2({clamped}, {unclamped}){GetMask(texOp.Index)}"; - } - - public static string Store(CodeGenContext context, AstOperation operation) - { - return GenerateLoadOrStore(context, operation, isStore: true); - } - - public static string TextureSample(CodeGenContext context, AstOperation operation) - { - AstTextureOperation texOp = (AstTextureOperation)operation; - - bool isGather = (texOp.Flags & TextureFlags.Gather) != 0; - bool hasDerivatives = (texOp.Flags & TextureFlags.Derivatives) != 0; - bool intCoords = (texOp.Flags & TextureFlags.IntCoords) != 0; - bool hasLodBias = (texOp.Flags & TextureFlags.LodBias) != 0; - bool hasLodLevel = (texOp.Flags & TextureFlags.LodLevel) != 0; - bool hasOffset = (texOp.Flags & TextureFlags.Offset) != 0; - bool hasOffsets = (texOp.Flags & TextureFlags.Offsets) != 0; - - bool isArray = (texOp.Type & SamplerType.Array) != 0; - bool isShadow = (texOp.Type & SamplerType.Shadow) != 0; - - StringBuilder texCallBuilder = new(); - - bool colorIsVector = isGather || !isShadow; - - int srcIndex = 0; - - string Src(AggregateType type) - { - return GetSourceExpr(context, texOp.GetSource(srcIndex++), type); - } - - string textureName = GetTextureName(context, texOp, ref srcIndex); - string samplerName = GetSamplerName(context, texOp, ref srcIndex); - - texCallBuilder.Append(textureName); - texCallBuilder.Append('.'); - - if (intCoords) - { - texCallBuilder.Append("read("); - } - else - { - if (isGather) - { - texCallBuilder.Append("gather"); - } - else - { - texCallBuilder.Append("sample"); - } - - if (isShadow) - { - texCallBuilder.Append("_compare"); - } - - texCallBuilder.Append($"({samplerName}, "); - } - - int coordsCount = texOp.Type.GetDimensions(); - - int pCount = coordsCount; - - bool appended = false; - void Append(string str) - { - if (appended) - { - texCallBuilder.Append(", "); - } - else - { - appended = true; - } - - texCallBuilder.Append(str); - } - - AggregateType coordType = intCoords ? AggregateType.S32 : AggregateType.FP32; - - string AssemblePVector(int count) - { - string coords; - if (count > 1) - { - string[] elems = new string[count]; - - for (int index = 0; index < count; index++) - { - elems[index] = Src(coordType); - } - - coords = string.Join(", ", elems); - } - else - { - coords = Src(coordType); - } - - string prefix = intCoords ? "uint" : "float"; - - return prefix + (count > 1 ? count : string.Empty) + "(" + coords + ")"; - } - - Append(AssemblePVector(pCount)); - - if (isArray) - { - Append(Src(AggregateType.S32)); - } - - if (isShadow) - { - Append(Src(AggregateType.FP32)); - } - - if (hasDerivatives) - { - Logger.Warning?.PrintMsg(LogClass.Gpu, "Unused sampler derivatives!"); - } - - if (hasLodBias) - { - Logger.Warning?.PrintMsg(LogClass.Gpu, "Unused sample LOD bias!"); - } - - if (hasLodLevel) - { - if (intCoords) - { - Append(Src(coordType)); - } - else - { - Append($"level({Src(coordType)})"); - } - } - - string AssembleOffsetVector(int count) - { - if (count > 1) - { - string[] elems = new string[count]; - - for (int index = 0; index < count; index++) - { - elems[index] = Src(AggregateType.S32); - } - - return "int" + count + "(" + string.Join(", ", elems) + ")"; - } - else - { - return Src(AggregateType.S32); - } - } - - // TODO: Support reads with offsets - if (!intCoords) - { - if (hasOffset) - { - Append(AssembleOffsetVector(coordsCount)); - } - else if (hasOffsets) - { - Logger.Warning?.PrintMsg(LogClass.Gpu, "Multiple offsets on gathers are not yet supported!"); - } - } - - texCallBuilder.Append(')'); - texCallBuilder.Append(colorIsVector ? GetMaskMultiDest(texOp.Index) : string.Empty); - - return texCallBuilder.ToString(); - } - - private static string GetTextureName(CodeGenContext context, AstTextureOperation texOp, ref int srcIndex) - { - TextureDefinition textureDefinition = context.Properties.Textures[texOp.GetTextureSetAndBinding()]; - string name = textureDefinition.Name; - string setName = Declarations.GetNameForSet(textureDefinition.Set, true); - - if (textureDefinition.ArrayLength != 1) - { - name = $"{name}[{GetSourceExpr(context, texOp.GetSource(srcIndex++), AggregateType.S32)}]"; - } - - return $"{setName}.tex_{name}"; - } - - private static string GetSamplerName(CodeGenContext context, AstTextureOperation texOp, ref int srcIndex) - { - SetBindingPair index = texOp.IsSeparate ? texOp.GetSamplerSetAndBinding() : texOp.GetTextureSetAndBinding(); - int sourceIndex = texOp.IsSeparate ? srcIndex++ : srcIndex + 1; - - TextureDefinition samplerDefinition = context.Properties.Textures[index]; - string name = samplerDefinition.Name; - string setName = Declarations.GetNameForSet(samplerDefinition.Set, true); - - if (samplerDefinition.ArrayLength != 1) - { - name = $"{name}[{GetSourceExpr(context, texOp.GetSource(sourceIndex), AggregateType.S32)}]"; - } - - return $"{setName}.samp_{name}"; - } - - private static string GetImageName(CodeGenContext context, AstTextureOperation texOp, ref int srcIndex) - { - TextureDefinition imageDefinition = context.Properties.Images[texOp.GetTextureSetAndBinding()]; - string name = imageDefinition.Name; - string setName = Declarations.GetNameForSet(imageDefinition.Set, true); - - if (imageDefinition.ArrayLength != 1) - { - name = $"{name}[{GetSourceExpr(context, texOp.GetSource(srcIndex++), AggregateType.S32)}]"; - } - - return $"{setName}.{name}"; - } - - private static string GetMaskMultiDest(int mask) - { - if (mask == 0x0) - { - return string.Empty; - } - - string swizzle = "."; - - for (int i = 0; i < 4; i++) - { - if ((mask & (1 << i)) != 0) - { - swizzle += "xyzw"[i]; - } - } - - return swizzle; - } - - public static string TextureQuerySamples(CodeGenContext context, AstOperation operation) - { - AstTextureOperation texOp = (AstTextureOperation)operation; - - int srcIndex = 0; - - string textureName = GetTextureName(context, texOp, ref srcIndex); - - return $"{textureName}.get_num_samples()"; - } - - public static string TextureQuerySize(CodeGenContext context, AstOperation operation) - { - AstTextureOperation texOp = (AstTextureOperation)operation; - - StringBuilder texCallBuilder = new(); - - int srcIndex = 0; - - string textureName = GetTextureName(context, texOp, ref srcIndex); - texCallBuilder.Append(textureName); - texCallBuilder.Append('.'); - - if (texOp.Index == 3) - { - texCallBuilder.Append("get_num_mip_levels()"); - } - else - { - context.Properties.Textures.TryGetValue(texOp.GetTextureSetAndBinding(), out TextureDefinition definition); - bool hasLod = !definition.Type.HasFlag(SamplerType.Multisample) && (definition.Type & SamplerType.Mask) != SamplerType.TextureBuffer; - bool isArray = definition.Type.HasFlag(SamplerType.Array); - texCallBuilder.Append("get_"); - - if (texOp.Index == 0) - { - texCallBuilder.Append("width"); - } - else if (texOp.Index == 1) - { - texCallBuilder.Append("height"); - } - else - { - if (isArray) - { - texCallBuilder.Append("array_size"); - } - else - { - texCallBuilder.Append("depth"); - } - } - - texCallBuilder.Append('('); - - if (hasLod && !isArray) - { - IAstNode lod = operation.GetSource(0); - string lodExpr = GetSourceExpr(context, lod, GetSrcVarType(operation.Inst, 0)); - - texCallBuilder.Append(lodExpr); - } - - texCallBuilder.Append(')'); - } - - return texCallBuilder.ToString(); - } - - public static string PackHalf2x16(CodeGenContext context, AstOperation operation) - { - IAstNode src0 = operation.GetSource(0); - IAstNode src1 = operation.GetSource(1); - - string src0Expr = GetSourceExpr(context, src0, GetSrcVarType(operation.Inst, 0)); - string src1Expr = GetSourceExpr(context, src1, GetSrcVarType(operation.Inst, 1)); - - return $"as_type(half2({src0Expr}, {src1Expr}))"; - } - - public static string UnpackHalf2x16(CodeGenContext context, AstOperation operation) - { - IAstNode src = operation.GetSource(0); - - string srcExpr = GetSourceExpr(context, src, GetSrcVarType(operation.Inst, 0)); - - return $"float2(as_type({srcExpr})){GetMask(operation.Index)}"; - } - - private static string GetMask(int index) - { - return $".{"xy".AsSpan(index, 1)}"; - } - } -} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenVector.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenVector.cs deleted file mode 100644 index 9d8dae543..000000000 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenVector.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Ryujinx.Graphics.Shader.IntermediateRepresentation; -using Ryujinx.Graphics.Shader.StructuredIr; - -using static Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions.InstGenHelper; -using static Ryujinx.Graphics.Shader.StructuredIr.InstructionInfo; - -namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions -{ - static class InstGenVector - { - public static string VectorExtract(CodeGenContext context, AstOperation operation) - { - IAstNode vector = operation.GetSource(0); - IAstNode index = operation.GetSource(1); - - string vectorExpr = GetSourceExpr(context, vector, OperandManager.GetNodeDestType(context, vector)); - - if (index is AstOperand indexOperand && indexOperand.Type == OperandType.Constant) - { - char elem = "xyzw"[indexOperand.Value]; - - return $"{vectorExpr}.{elem}"; - } - else - { - string indexExpr = GetSourceExpr(context, index, GetSrcVarType(operation.Inst, 1)); - - return $"{vectorExpr}[{indexExpr}]"; - } - } - } -} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstInfo.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstInfo.cs deleted file mode 100644 index 5e5d04d6b..000000000 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstInfo.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions -{ - readonly struct InstInfo - { - public InstType Type { get; } - - public string OpName { get; } - - public int Precedence { get; } - - public InstInfo(InstType type, string opName, int precedence) - { - Type = type; - OpName = opName; - Precedence = precedence; - } - } -} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstType.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstType.cs deleted file mode 100644 index d8f6bfed1..000000000 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstType.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System; -using System.Diagnostics.CodeAnalysis; - -namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions -{ - [Flags] - [SuppressMessage("Design", "CA1069: Enums values should not be duplicated")] - public enum InstType - { - OpNullary = Op | 0, - OpUnary = Op | 1, - OpBinary = Op | 2, - OpBinaryCom = Op | 2 | Commutative, - OpTernary = Op | 3, - - CallNullary = Call | 0, - CallUnary = Call | 1, - CallBinary = Call | 2, - CallTernary = Call | 3, - CallQuaternary = Call | 4, - - // The atomic instructions have one extra operand, - // for the storage slot and offset pair. - AtomicBinary = Call | Atomic | 3, - AtomicTernary = Call | Atomic | 4, - - Commutative = 1 << 8, - Op = 1 << 9, - Call = 1 << 10, - Atomic = 1 << 11, - Special = 1 << 12, - - ArityMask = 0xff, - } -} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs deleted file mode 100644 index 118612c66..000000000 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs +++ /dev/null @@ -1,83 +0,0 @@ -using Ryujinx.Common.Logging; -using Ryujinx.Graphics.Shader.IntermediateRepresentation; -using Ryujinx.Graphics.Shader.Translation; -using System.Globalization; - -namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions -{ - static class IoMap - { - public static (string, AggregateType) GetMslBuiltIn( - ShaderDefinitions definitions, - IoVariable ioVariable, - int location, - int component, - bool isOutput, - bool isPerPatch) - { - (string, AggregateType) returnValue = ioVariable switch - { - IoVariable.BaseInstance => ("base_instance", AggregateType.U32), - IoVariable.BaseVertex => ("base_vertex", AggregateType.U32), - IoVariable.CtaId => ("threadgroup_position_in_grid", AggregateType.Vector3 | AggregateType.U32), - IoVariable.ClipDistance => ("out.clip_distance", AggregateType.Array | AggregateType.FP32), - IoVariable.FragmentOutputColor => ($"out.color{location}", definitions.GetFragmentOutputColorType(location)), - IoVariable.FragmentOutputDepth => ("out.depth", AggregateType.FP32), - IoVariable.FrontFacing => ("in.front_facing", AggregateType.Bool), - IoVariable.GlobalId => ("thread_position_in_grid", AggregateType.Vector3 | AggregateType.U32), - IoVariable.InstanceId => ("instance_id", AggregateType.U32), - IoVariable.InstanceIndex => ("instance_index", AggregateType.U32), - IoVariable.InvocationId => ("INVOCATION_ID", AggregateType.S32), - IoVariable.PointCoord => ("in.point_coord", AggregateType.Vector2 | AggregateType.FP32), - IoVariable.PointSize => ("out.point_size", AggregateType.FP32), - IoVariable.Position => ("out.position", AggregateType.Vector4 | AggregateType.FP32), - IoVariable.PrimitiveId => ("in.primitive_id", AggregateType.U32), - IoVariable.SubgroupEqMask => ("thread_index_in_simdgroup >= 32 ? uint4(0, (1 << (thread_index_in_simdgroup - 32)), uint2(0)) : uint4(1 << thread_index_in_simdgroup, uint3(0))", AggregateType.Vector4 | AggregateType.U32), - IoVariable.SubgroupGeMask => ("uint4(insert_bits(0u, 0xFFFFFFFF, thread_index_in_simdgroup, 32 - thread_index_in_simdgroup), uint3(0)) & (uint4((uint)((simd_vote::vote_t)simd_ballot(true) & 0xFFFFFFFF), (uint)(((simd_vote::vote_t)simd_ballot(true) >> 32) & 0xFFFFFFFF), 0, 0))", AggregateType.Vector4 | AggregateType.U32), - IoVariable.SubgroupGtMask => ("uint4(insert_bits(0u, 0xFFFFFFFF, thread_index_in_simdgroup + 1, 32 - thread_index_in_simdgroup - 1), uint3(0)) & (uint4((uint)((simd_vote::vote_t)simd_ballot(true) & 0xFFFFFFFF), (uint)(((simd_vote::vote_t)simd_ballot(true) >> 32) & 0xFFFFFFFF), 0, 0))", AggregateType.Vector4 | AggregateType.U32), - IoVariable.SubgroupLaneId => ("thread_index_in_simdgroup", AggregateType.U32), - IoVariable.SubgroupLeMask => ("uint4(extract_bits(0xFFFFFFFF, 0, min(thread_index_in_simdgroup + 1, 32u)), extract_bits(0xFFFFFFFF, 0, (uint)max((int)thread_index_in_simdgroup + 1 - 32, 0)), uint2(0))", AggregateType.Vector4 | AggregateType.U32), - IoVariable.SubgroupLtMask => ("uint4(extract_bits(0xFFFFFFFF, 0, min(thread_index_in_simdgroup, 32u)), extract_bits(0xFFFFFFFF, 0, (uint)max((int)thread_index_in_simdgroup - 32, 0)), uint2(0))", AggregateType.Vector4 | AggregateType.U32), - IoVariable.ThreadKill => ("simd_is_helper_thread()", AggregateType.Bool), - IoVariable.UserDefined => GetUserDefinedVariableName(definitions, location, component, isOutput, isPerPatch), - IoVariable.ThreadId => ("thread_position_in_threadgroup", AggregateType.Vector3 | AggregateType.U32), - IoVariable.VertexId => ("vertex_id", AggregateType.S32), - // gl_VertexIndex does not have a direct equivalent in MSL - IoVariable.VertexIndex => ("vertex_id", AggregateType.U32), - IoVariable.ViewportIndex => ("viewport_array_index", AggregateType.S32), - IoVariable.FragmentCoord => ("in.position", AggregateType.Vector4 | AggregateType.FP32), - _ => (null, AggregateType.Invalid), - }; - - if (returnValue.Item2 == AggregateType.Invalid) - { - Logger.Warning?.PrintMsg(LogClass.Gpu, $"Unable to find type for IoVariable {ioVariable}!"); - } - - return returnValue; - } - - private static (string, AggregateType) GetUserDefinedVariableName(ShaderDefinitions definitions, int location, int component, bool isOutput, bool isPerPatch) - { - string name = isPerPatch - ? Defaults.PerPatchAttributePrefix - : (isOutput ? Defaults.OAttributePrefix : Defaults.IAttributePrefix); - - if (location < 0) - { - return (name, definitions.GetUserDefinedType(0, isOutput)); - } - - name += location.ToString(CultureInfo.InvariantCulture); - - if (definitions.HasPerLocationInputOrOutputComponent(IoVariable.UserDefined, location, component, isOutput)) - { - name += "_" + "xyzw"[component & 3]; - } - - string prefix = isOutput ? "out" : "in"; - - return (prefix + "." + name, definitions.GetUserDefinedType(location, isOutput)); - } - } -} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs deleted file mode 100644 index 8ca24fcd3..000000000 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs +++ /dev/null @@ -1,286 +0,0 @@ -using Ryujinx.Common.Logging; -using Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions; -using Ryujinx.Graphics.Shader.StructuredIr; -using Ryujinx.Graphics.Shader.Translation; -using System; -using System.Linq; -using static Ryujinx.Graphics.Shader.CodeGen.Msl.TypeConversion; - -namespace Ryujinx.Graphics.Shader.CodeGen.Msl -{ - static class MslGenerator - { - public static string Generate(StructuredProgramInfo info, CodeGenParameters parameters) - { - if (parameters.Definitions.Stage is not (ShaderStage.Vertex or ShaderStage.Fragment or ShaderStage.Compute)) - { - Logger.Warning?.Print(LogClass.Gpu, $"Attempted to generate unsupported shader type {parameters.Definitions.Stage}!"); - return string.Empty; - } - - CodeGenContext context = new(info, parameters); - - int[] sets = Declarations.Declare(context, info); - - if (info.Functions.Count != 0) - { - for (int i = 1; i < info.Functions.Count; i++) - { - PrintFunction(context, info.Functions[i], parameters.Definitions.Stage, sets); - - context.AppendLine(); - } - } - - PrintFunction(context, info.Functions[0], parameters.Definitions.Stage, sets, true); - - return context.GetCode(); - } - - private static void PrintFunction(CodeGenContext context, StructuredFunction function, ShaderStage stage, int[] sets, bool isMainFunc = false) - { - context.CurrentFunction = function; - - context.AppendLine(GetFunctionSignature(context, function, stage, sets, isMainFunc)); - context.EnterScope(); - - Declarations.DeclareLocals(context, function, stage, isMainFunc); - - PrintBlock(context, function.MainBlock, isMainFunc); - - // In case the shader hasn't returned, return - if (isMainFunc && stage != ShaderStage.Compute) - { - context.AppendLine("return out;"); - } - - context.LeaveScope(); - } - - private static string GetFunctionSignature( - CodeGenContext context, - StructuredFunction function, - ShaderStage stage, - int[] sets, - bool isMainFunc = false) - { - int additionalArgCount = isMainFunc ? 0 : CodeGenContext.AdditionalArgCount + (context.Definitions.Stage != ShaderStage.Compute ? 1 : 0); - bool needsThreadIndex = false; - - // TODO: Replace this with a proper flag - if (function.Name.Contains("Shuffle")) - { - needsThreadIndex = true; - additionalArgCount++; - } - - string[] args = new string[additionalArgCount + function.InArguments.Length + function.OutArguments.Length]; - - // All non-main functions need to be able to access the support_buffer as well - if (!isMainFunc) - { - if (stage != ShaderStage.Compute) - { - args[0] = stage == ShaderStage.Vertex ? "VertexIn in" : "FragmentIn in"; - args[1] = "constant ConstantBuffers &constant_buffers"; - args[2] = "device StorageBuffers &storage_buffers"; - - if (needsThreadIndex) - { - args[3] = "uint thread_index_in_simdgroup"; - } - } - else - { - args[0] = "constant ConstantBuffers &constant_buffers"; - args[1] = "device StorageBuffers &storage_buffers"; - - if (needsThreadIndex) - { - args[2] = "uint thread_index_in_simdgroup"; - } - } - } - - int argIndex = additionalArgCount; - for (int i = 0; i < function.InArguments.Length; i++) - { - args[argIndex++] = $"{Declarations.GetVarTypeName(function.InArguments[i])} {OperandManager.GetArgumentName(i)}"; - } - - for (int i = 0; i < function.OutArguments.Length; i++) - { - int j = i + function.InArguments.Length; - - args[argIndex++] = $"thread {Declarations.GetVarTypeName(function.OutArguments[i])} &{OperandManager.GetArgumentName(j)}"; - } - - string funcKeyword = "inline"; - string funcName = null; - string returnType = Declarations.GetVarTypeName(function.ReturnType); - - if (isMainFunc) - { - if (stage == ShaderStage.Vertex) - { - funcKeyword = "vertex"; - funcName = "vertexMain"; - returnType = "VertexOut"; - } - else if (stage == ShaderStage.Fragment) - { - funcKeyword = "fragment"; - funcName = "fragmentMain"; - returnType = "FragmentOut"; - } - else if (stage == ShaderStage.Compute) - { - funcKeyword = "kernel"; - funcName = "kernelMain"; - returnType = "void"; - } - - if (stage == ShaderStage.Vertex) - { - args = args.Prepend("VertexIn in [[stage_in]]").ToArray(); - } - else if (stage == ShaderStage.Fragment) - { - args = args.Prepend("FragmentIn in [[stage_in]]").ToArray(); - } - - // TODO: add these only if they are used - if (stage == ShaderStage.Vertex) - { - args = args.Append("uint vertex_id [[vertex_id]]").ToArray(); - args = args.Append("uint instance_id [[instance_id]]").ToArray(); - args = args.Append("uint base_instance [[base_instance]]").ToArray(); - args = args.Append("uint base_vertex [[base_vertex]]").ToArray(); - } - else if (stage == ShaderStage.Compute) - { - args = args.Append("uint3 threadgroup_position_in_grid [[threadgroup_position_in_grid]]").ToArray(); - args = args.Append("uint3 thread_position_in_grid [[thread_position_in_grid]]").ToArray(); - args = args.Append("uint3 thread_position_in_threadgroup [[thread_position_in_threadgroup]]").ToArray(); - args = args.Append("uint thread_index_in_simdgroup [[thread_index_in_simdgroup]]").ToArray(); - } - - args = args.Append($"constant ConstantBuffers &constant_buffers [[buffer({Defaults.ConstantBuffersIndex})]]").ToArray(); - args = args.Append($"device StorageBuffers &storage_buffers [[buffer({Defaults.StorageBuffersIndex})]]").ToArray(); - - foreach (int set in sets) - { - long bindingIndex = set + Defaults.BaseSetIndex; - args = args.Append($"constant {Declarations.GetNameForSet(set)} &{Declarations.GetNameForSet(set, true)} [[buffer({bindingIndex})]]").ToArray(); - } - } - - string funcPrefix = $"{funcKeyword} {returnType} {funcName ?? function.Name}("; - string indent = new(' ', funcPrefix.Length); - - return $"{funcPrefix}{string.Join($", \n{indent}", args)})"; - } - - private static void PrintBlock(CodeGenContext context, AstBlock block, bool isMainFunction) - { - AstBlockVisitor visitor = new(block); - - visitor.BlockEntered += (sender, e) => - { - switch (e.Block.Type) - { - case AstBlockType.DoWhile: - context.AppendLine("do"); - break; - - case AstBlockType.Else: - context.AppendLine("else"); - break; - - case AstBlockType.ElseIf: - context.AppendLine($"else if ({GetCondExpr(context, e.Block.Condition)})"); - break; - - case AstBlockType.If: - context.AppendLine($"if ({GetCondExpr(context, e.Block.Condition)})"); - break; - - default: - throw new InvalidOperationException($"Found unexpected block type \"{e.Block.Type}\"."); - } - - context.EnterScope(); - }; - - visitor.BlockLeft += (sender, e) => - { - context.LeaveScope(); - - if (e.Block.Type == AstBlockType.DoWhile) - { - context.AppendLine($"while ({GetCondExpr(context, e.Block.Condition)});"); - } - }; - - bool supportsBarrierDivergence = context.HostCapabilities.SupportsShaderBarrierDivergence; - bool mayHaveReturned = false; - - foreach (IAstNode node in visitor.Visit()) - { - if (node is AstOperation operation) - { - if (!supportsBarrierDivergence) - { - if (operation.Inst == IntermediateRepresentation.Instruction.Barrier) - { - // Barrier on divergent control flow paths may cause the GPU to hang, - // so skip emitting the barrier for those cases. - if (visitor.Block.Type != AstBlockType.Main || mayHaveReturned || !isMainFunction) - { - context.Logger.Log($"Shader has barrier on potentially divergent block, the barrier will be removed."); - - continue; - } - } - else if (operation.Inst == IntermediateRepresentation.Instruction.Return) - { - mayHaveReturned = true; - } - } - - string expr = InstGen.GetExpression(context, operation); - - if (expr != null) - { - context.AppendLine(expr + ";"); - } - } - else if (node is AstAssignment assignment) - { - AggregateType dstType = OperandManager.GetNodeDestType(context, assignment.Destination); - AggregateType srcType = OperandManager.GetNodeDestType(context, assignment.Source); - - string dest = InstGen.GetExpression(context, assignment.Destination); - string src = ReinterpretCast(context, assignment.Source, srcType, dstType); - - context.AppendLine(dest + " = " + src + ";"); - } - else if (node is AstComment comment) - { - context.AppendLine("// " + comment.Comment); - } - else - { - throw new InvalidOperationException($"Found unexpected node type \"{node?.GetType().Name ?? "null"}\"."); - } - } - } - - private static string GetCondExpr(CodeGenContext context, IAstNode cond) - { - AggregateType srcType = OperandManager.GetNodeDestType(context, cond); - - return ReinterpretCast(context, cond, srcType, AggregateType.Bool); - } - } -} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/NumberFormatter.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/NumberFormatter.cs deleted file mode 100644 index 86cdfc0e6..000000000 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/NumberFormatter.cs +++ /dev/null @@ -1,94 +0,0 @@ -using Ryujinx.Graphics.Shader.Translation; -using System; -using System.Globalization; - -namespace Ryujinx.Graphics.Shader.CodeGen.Msl -{ - static class NumberFormatter - { - private const int MaxDecimal = 256; - - public static bool TryFormat(int value, AggregateType dstType, out string formatted) - { - switch (dstType) - { - case AggregateType.FP32: - return TryFormatFloat(BitConverter.Int32BitsToSingle(value), out formatted); - case AggregateType.S32: - formatted = FormatInt(value); - break; - case AggregateType.U32: - formatted = FormatUint((uint)value); - break; - case AggregateType.Bool: - formatted = value != 0 ? "true" : "false"; - break; - default: - throw new ArgumentException($"Invalid variable type \"{dstType}\"."); - } - - return true; - } - - public static string FormatFloat(float value) - { - if (!TryFormatFloat(value, out string formatted)) - { - throw new ArgumentException("Failed to convert float value to string."); - } - - return formatted; - } - - public static bool TryFormatFloat(float value, out string formatted) - { - if (float.IsNaN(value) || float.IsInfinity(value)) - { - formatted = null; - - return false; - } - - formatted = value.ToString("G9", CultureInfo.InvariantCulture); - - if (!(formatted.Contains('.') || - formatted.Contains('e') || - formatted.Contains('E'))) - { - formatted += ".0f"; - } - - return true; - } - - public static string FormatInt(int value, AggregateType dstType) - { - return dstType switch - { - AggregateType.S32 => FormatInt(value), - AggregateType.U32 => FormatUint((uint)value), - _ => throw new ArgumentException($"Invalid variable type \"{dstType}\".") - }; - } - - public static string FormatInt(int value) - { - if (value <= MaxDecimal && value >= -MaxDecimal) - { - return value.ToString(CultureInfo.InvariantCulture); - } - - return $"as_type(0x{value.ToString("X", CultureInfo.InvariantCulture)})"; - } - - public static string FormatUint(uint value) - { - if (value <= MaxDecimal && value >= 0) - { - return value.ToString(CultureInfo.InvariantCulture) + "u"; - } - - return $"as_type(0x{value.ToString("X", CultureInfo.InvariantCulture)})"; - } - } -} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/OperandManager.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/OperandManager.cs deleted file mode 100644 index e131a645e..000000000 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/OperandManager.cs +++ /dev/null @@ -1,176 +0,0 @@ -using Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions; -using Ryujinx.Graphics.Shader.IntermediateRepresentation; -using Ryujinx.Graphics.Shader.StructuredIr; -using Ryujinx.Graphics.Shader.Translation; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using static Ryujinx.Graphics.Shader.StructuredIr.InstructionInfo; - -namespace Ryujinx.Graphics.Shader.CodeGen.Msl -{ - class OperandManager - { - private readonly Dictionary _locals; - - public OperandManager() - { - _locals = new Dictionary(); - } - - public string DeclareLocal(AstOperand operand) - { - string name = $"{Defaults.LocalNamePrefix}_{_locals.Count}"; - - _locals.Add(operand, name); - - return name; - } - - public string GetExpression(CodeGenContext context, AstOperand operand) - { - return operand.Type switch - { - OperandType.Argument => GetArgumentName(operand.Value), - OperandType.Constant => NumberFormatter.FormatInt(operand.Value), - OperandType.LocalVariable => _locals[operand], - OperandType.Undefined => Defaults.UndefinedName, - _ => throw new ArgumentException($"Invalid operand type \"{operand.Type}\"."), - }; - } - - public static string GetArgumentName(int argIndex) - { - return $"{Defaults.ArgumentNamePrefix}{argIndex}"; - } - - public static AggregateType GetNodeDestType(CodeGenContext context, IAstNode node) - { - if (node is AstOperation operation) - { - if (operation.Inst == Instruction.Load || operation.Inst.IsAtomic()) - { - switch (operation.StorageKind) - { - case StorageKind.ConstantBuffer: - case StorageKind.StorageBuffer: - if (operation.GetSource(0) is not AstOperand bindingIndex || bindingIndex.Type != OperandType.Constant) - { - throw new InvalidOperationException($"First input of {operation.Inst} with {operation.StorageKind} storage must be a constant operand."); - } - - if (operation.GetSource(1) is not AstOperand fieldIndex || fieldIndex.Type != OperandType.Constant) - { - throw new InvalidOperationException($"Second input of {operation.Inst} with {operation.StorageKind} storage must be a constant operand."); - } - - BufferDefinition buffer = operation.StorageKind == StorageKind.ConstantBuffer - ? context.Properties.ConstantBuffers[bindingIndex.Value] - : context.Properties.StorageBuffers[bindingIndex.Value]; - StructureField field = buffer.Type.Fields[fieldIndex.Value]; - - return field.Type & AggregateType.ElementTypeMask; - - case StorageKind.LocalMemory: - case StorageKind.SharedMemory: - if (operation.GetSource(0) is not AstOperand { Type: OperandType.Constant } bindingId) - { - throw new InvalidOperationException($"First input of {operation.Inst} with {operation.StorageKind} storage must be a constant operand."); - } - - MemoryDefinition memory = operation.StorageKind == StorageKind.LocalMemory - ? context.Properties.LocalMemories[bindingId.Value] - : context.Properties.SharedMemories[bindingId.Value]; - - return memory.Type & AggregateType.ElementTypeMask; - - case StorageKind.Input: - case StorageKind.InputPerPatch: - case StorageKind.Output: - case StorageKind.OutputPerPatch: - if (operation.GetSource(0) is not AstOperand varId || varId.Type != OperandType.Constant) - { - throw new InvalidOperationException($"First input of {operation.Inst} with {operation.StorageKind} storage must be a constant operand."); - } - - IoVariable ioVariable = (IoVariable)varId.Value; - bool isOutput = operation.StorageKind == StorageKind.Output || operation.StorageKind == StorageKind.OutputPerPatch; - bool isPerPatch = operation.StorageKind == StorageKind.InputPerPatch || operation.StorageKind == StorageKind.OutputPerPatch; - int location = 0; - int component = 0; - - if (context.Definitions.HasPerLocationInputOrOutput(ioVariable, isOutput)) - { - if (operation.GetSource(1) is not AstOperand vecIndex || vecIndex.Type != OperandType.Constant) - { - throw new InvalidOperationException($"Second input of {operation.Inst} with {operation.StorageKind} storage must be a constant operand."); - } - - location = vecIndex.Value; - - if (operation.SourcesCount > 2 && - operation.GetSource(2) is AstOperand elemIndex && - elemIndex.Type == OperandType.Constant && - context.Definitions.HasPerLocationInputOrOutputComponent(ioVariable, location, elemIndex.Value, isOutput)) - { - component = elemIndex.Value; - } - } - - (_, AggregateType varType) = IoMap.GetMslBuiltIn( - context.Definitions, - ioVariable, - location, - component, - isOutput, - isPerPatch); - - return varType & AggregateType.ElementTypeMask; - } - } - else if (operation.Inst == Instruction.Call) - { - AstOperand funcId = (AstOperand)operation.GetSource(0); - - Debug.Assert(funcId.Type == OperandType.Constant); - - return context.GetFunction(funcId.Value).ReturnType; - } - else if (operation.Inst == Instruction.VectorExtract) - { - return GetNodeDestType(context, operation.GetSource(0)) & ~AggregateType.ElementCountMask; - } - else if (operation is AstTextureOperation texOp) - { - if (texOp.Inst == Instruction.ImageLoad || - texOp.Inst == Instruction.ImageStore || - texOp.Inst == Instruction.ImageAtomic) - { - return texOp.GetVectorType(texOp.Format.GetComponentType()); - } - else if (texOp.Inst == Instruction.TextureSample) - { - return texOp.GetVectorType(GetDestVarType(operation.Inst)); - } - } - - return GetDestVarType(operation.Inst); - } - else if (node is AstOperand operand) - { - if (operand.Type == OperandType.Argument) - { - int argIndex = operand.Value; - - return context.CurrentFunction.GetArgumentType(argIndex); - } - - return OperandInfo.GetVarType(operand); - } - else - { - throw new ArgumentException($"Invalid node type \"{node?.GetType().Name ?? "null"}\"."); - } - } - } -} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/TypeConversion.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/TypeConversion.cs deleted file mode 100644 index e145bb8b0..000000000 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/TypeConversion.cs +++ /dev/null @@ -1,93 +0,0 @@ -using Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions; -using Ryujinx.Graphics.Shader.IntermediateRepresentation; -using Ryujinx.Graphics.Shader.StructuredIr; -using Ryujinx.Graphics.Shader.Translation; -using System; - -namespace Ryujinx.Graphics.Shader.CodeGen.Msl -{ - static class TypeConversion - { - public static string ReinterpretCast( - CodeGenContext context, - IAstNode node, - AggregateType srcType, - AggregateType dstType) - { - if (node is AstOperand operand && operand.Type == OperandType.Constant) - { - if (NumberFormatter.TryFormat(operand.Value, dstType, out string formatted)) - { - return formatted; - } - } - - string expr = InstGen.GetExpression(context, node); - - return ReinterpretCast(expr, node, srcType, dstType); - } - - private static string ReinterpretCast(string expr, IAstNode node, AggregateType srcType, AggregateType dstType) - { - if (srcType == dstType) - { - return expr; - } - - if (srcType == AggregateType.FP32) - { - switch (dstType) - { - case AggregateType.Bool: - return $"(as_type({expr}) != 0)"; - case AggregateType.S32: - return $"as_type({expr})"; - case AggregateType.U32: - return $"as_type({expr})"; - } - } - else if (dstType == AggregateType.FP32) - { - switch (srcType) - { - case AggregateType.Bool: - return $"as_type({ReinterpretBoolToInt(expr, node, AggregateType.S32)})"; - case AggregateType.S32: - return $"as_type({expr})"; - case AggregateType.U32: - return $"as_type({expr})"; - } - } - else if (srcType == AggregateType.Bool) - { - return ReinterpretBoolToInt(expr, node, dstType); - } - else if (dstType == AggregateType.Bool) - { - expr = InstGenHelper.Enclose(expr, node, Instruction.CompareNotEqual, isLhs: true); - - return $"({expr} != 0)"; - } - else if (dstType == AggregateType.S32) - { - return $"int({expr})"; - } - else if (dstType == AggregateType.U32) - { - return $"uint({expr})"; - } - - throw new ArgumentException($"Invalid reinterpret cast from \"{srcType}\" to \"{dstType}\"."); - } - - private static string ReinterpretBoolToInt(string expr, IAstNode node, AggregateType dstType) - { - string trueExpr = NumberFormatter.FormatInt(IrConsts.True, dstType); - string falseExpr = NumberFormatter.FormatInt(IrConsts.False, dstType); - - expr = InstGenHelper.Enclose(expr, node, Instruction.ConditionalSelect, isLhs: false); - - return $"({expr} ? {trueExpr} : {falseExpr})"; - } - } -} diff --git a/src/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj b/src/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj index 8b05d8829..cfb188daf 100644 --- a/src/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj +++ b/src/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj @@ -15,11 +15,4 @@ - - - - - - - diff --git a/src/Ryujinx.Graphics.Shader/SamplerType.cs b/src/Ryujinx.Graphics.Shader/SamplerType.cs index f81d512fa..a693495fa 100644 --- a/src/Ryujinx.Graphics.Shader/SamplerType.cs +++ b/src/Ryujinx.Graphics.Shader/SamplerType.cs @@ -155,51 +155,5 @@ namespace Ryujinx.Graphics.Shader return typeName; } - - public static string ToMslTextureType(this SamplerType type, AggregateType aggregateType, bool image = false) - { - string typeName; - - if ((type & SamplerType.Shadow) != 0) - { - typeName = (type & SamplerType.Mask) switch - { - SamplerType.Texture2D => "depth2d", - SamplerType.TextureCube => "depthcube", - _ => throw new ArgumentException($"Invalid shadow texture type \"{type}\"."), - }; - } - else - { - typeName = (type & SamplerType.Mask) switch - { - SamplerType.Texture1D => "texture1d", - SamplerType.TextureBuffer => "texture_buffer", - SamplerType.Texture2D => "texture2d", - SamplerType.Texture3D => "texture3d", - SamplerType.TextureCube => "texturecube", - _ => throw new ArgumentException($"Invalid texture type \"{type}\"."), - }; - } - - if ((type & SamplerType.Multisample) != 0) - { - typeName += "_ms"; - } - - if ((type & SamplerType.Array) != 0) - { - typeName += "_array"; - } - - string format = aggregateType switch - { - AggregateType.S32 => "int", - AggregateType.U32 => "uint", - _ => "float" - }; - - return $"{typeName}<{format}{(image ? ", access::read_write" : string.Empty)}>"; - } } } diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/HelperFunctionsMask.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/HelperFunctionsMask.cs index b70def78c..2a3d65e75 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/HelperFunctionsMask.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/HelperFunctionsMask.cs @@ -7,14 +7,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr { MultiplyHighS32 = 1 << 2, MultiplyHighU32 = 1 << 3, - - FindLSB = 1 << 5, - FindMSBS32 = 1 << 6, - FindMSBU32 = 1 << 7, - SwizzleAdd = 1 << 10, FSI = 1 << 11, - - Precise = 1 << 13 } } diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs index 1ae669aa9..824aea195 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs @@ -18,10 +18,9 @@ namespace Ryujinx.Graphics.Shader.StructuredIr ShaderDefinitions definitions, ResourceManager resourceManager, TargetLanguage targetLanguage, - bool precise, bool debugMode) { - StructuredProgramContext context = new(attributeUsage, definitions, resourceManager, precise, debugMode); + StructuredProgramContext context = new(attributeUsage, definitions, resourceManager, debugMode); for (int funcIndex = 0; funcIndex < functions.Count; funcIndex++) { @@ -323,9 +322,8 @@ namespace Ryujinx.Graphics.Shader.StructuredIr } // Those instructions needs to be emulated by using helper functions, - // because they are NVIDIA specific or because the target language has - // no direct equivalent. Those flags helps the backend to decide which - // helper functions are needed on the final generated code. + // because they are NVIDIA specific. Those flags helps the backend to + // decide which helper functions are needed on the final generated code. switch (operation.Inst) { case Instruction.MultiplyHighS32: @@ -334,15 +332,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr case Instruction.MultiplyHighU32: context.Info.HelperFunctionsMask |= HelperFunctionsMask.MultiplyHighU32; break; - case Instruction.FindLSB: - context.Info.HelperFunctionsMask |= HelperFunctionsMask.FindLSB; - break; - case Instruction.FindMSBS32: - context.Info.HelperFunctionsMask |= HelperFunctionsMask.FindMSBS32; - break; - case Instruction.FindMSBU32: - context.Info.HelperFunctionsMask |= HelperFunctionsMask.FindMSBU32; - break; case Instruction.SwizzleAdd: context.Info.HelperFunctionsMask |= HelperFunctionsMask.SwizzleAdd; break; diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs index a5887e80d..60bdacc02 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs @@ -36,10 +36,9 @@ namespace Ryujinx.Graphics.Shader.StructuredIr AttributeUsage attributeUsage, ShaderDefinitions definitions, ResourceManager resourceManager, - bool precise, bool debugMode) { - Info = new StructuredProgramInfo(precise); + Info = new StructuredProgramInfo(); Definitions = definitions; ResourceManager = resourceManager; diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs index 2f8675069..ded2f2a89 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs @@ -10,16 +10,11 @@ namespace Ryujinx.Graphics.Shader.StructuredIr public HelperFunctionsMask HelperFunctionsMask { get; set; } - public StructuredProgramInfo(bool precise) + public StructuredProgramInfo() { - Functions = []; + Functions = new List(); - IoDefinitions = []; - - if (precise) - { - HelperFunctionsMask |= HelperFunctionsMask.Precise; - } + IoDefinitions = new HashSet(); } } } diff --git a/src/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs b/src/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs index 26c924e89..82a54db83 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs @@ -26,6 +26,5 @@ namespace Ryujinx.Graphics.Shader.Translation SharedMemory = 1 << 11, Store = 1 << 12, VtgAsCompute = 1 << 13, - Precise = 1 << 14, } } diff --git a/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs b/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs index e10182747..94691a5b4 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs @@ -14,7 +14,7 @@ namespace Ryujinx.Graphics.Shader.Translation private const int DefaultLocalMemorySize = 128; private const int DefaultSharedMemorySize = 4096; - private static readonly string[] _stagePrefixes = ["cp", "vp", "tcp", "tep", "gp", "fp"]; + private static readonly string[] _stagePrefixes = new string[] { "cp", "vp", "tcp", "tep", "gp", "fp" }; private readonly IGpuAccessor _gpuAccessor; private readonly ShaderStage _stage; @@ -43,11 +43,6 @@ namespace Ryujinx.Graphics.Shader.Translation private readonly Dictionary _usedTextures; private readonly Dictionary _usedImages; - private readonly List _vacConstantBuffers; - private readonly List _vacStorageBuffers; - private readonly List _vacTextures; - private readonly List _vacImages; - public int LocalMemoryId { get; private set; } public int SharedMemoryId { get; private set; } @@ -78,16 +73,11 @@ namespace Ryujinx.Graphics.Shader.Translation _sbSlots = new(); _sbSlotsReverse = new(); - _usedConstantBufferBindings = []; + _usedConstantBufferBindings = new(); _usedTextures = new(); _usedImages = new(); - _vacConstantBuffers = []; - _vacStorageBuffers = []; - _vacTextures = []; - _vacImages = []; - Properties.AddOrUpdateConstantBuffer(new(BufferLayout.Std140, 0, SupportBuffer.Binding, "support_buffer", SupportBuffer.GetStructureType())); LocalMemoryId = -1; @@ -103,7 +93,7 @@ namespace Ryujinx.Graphics.Shader.Translation size = DefaultLocalMemorySize; } - MemoryDefinition lmem = new("local_memory", AggregateType.Array | AggregateType.U32, BitUtils.DivRoundUp(size, sizeof(uint))); + var lmem = new MemoryDefinition("local_memory", AggregateType.Array | AggregateType.U32, BitUtils.DivRoundUp(size, sizeof(uint))); LocalMemoryId = Properties.AddLocalMemory(lmem); } @@ -122,7 +112,7 @@ namespace Ryujinx.Graphics.Shader.Translation size = DefaultSharedMemorySize; } - MemoryDefinition smem = new("shared_memory", AggregateType.Array | AggregateType.U32, BitUtils.DivRoundUp(size, sizeof(uint))); + var smem = new MemoryDefinition("shared_memory", AggregateType.Array | AggregateType.U32, BitUtils.DivRoundUp(size, sizeof(uint))); SharedMemoryId = Properties.AddSharedMemory(smem); } @@ -283,16 +273,16 @@ namespace Ryujinx.Graphics.Shader.Translation bool coherent, bool separate) { - int dimensions = type == SamplerType.None ? 0 : type.GetDimensions(); - Dictionary dict = isImage ? _usedImages : _usedTextures; + var dimensions = type == SamplerType.None ? 0 : type.GetDimensions(); + var dict = isImage ? _usedImages : _usedTextures; - TextureUsageFlags usageFlags = TextureUsageFlags.None; + var usageFlags = TextureUsageFlags.None; if (intCoords) { usageFlags |= TextureUsageFlags.NeedsScaleValue; - bool canScale = _stage.SupportsRenderScale() && arrayLength == 1 && !write && dimensions == 2; + var canScale = _stage.SupportsRenderScale() && arrayLength == 1 && !write && dimensions == 2; if (!canScale) { @@ -314,9 +304,9 @@ namespace Ryujinx.Graphics.Shader.Translation // For array textures, we also want to use type as key, // since we may have texture handles stores in the same buffer, but for textures with different types. - SamplerType keyType = arrayLength > 1 ? type : SamplerType.None; - TextureInfo info = new(cbufSlot, handle, arrayLength, separate, keyType, format); - TextureMeta meta = new() + var keyType = arrayLength > 1 ? type : SamplerType.None; + var info = new TextureInfo(cbufSlot, handle, arrayLength, separate, keyType, format); + var meta = new TextureMeta() { AccurateType = accurateType, Type = type, @@ -326,7 +316,7 @@ namespace Ryujinx.Graphics.Shader.Translation int setIndex; int binding; - if (dict.TryGetValue(info, out TextureMeta existingMeta)) + if (dict.TryGetValue(info, out var existingMeta)) { dict[info] = MergeTextureMeta(meta, existingMeta); setIndex = existingMeta.Set; @@ -383,7 +373,7 @@ namespace Ryujinx.Graphics.Shader.Translation nameSuffix = cbufSlot < 0 ? $"{prefix}_tcb_{handle:X}" : $"{prefix}_cb{cbufSlot}_{handle:X}"; } - TextureDefinition definition = new( + var definition = new TextureDefinition( setIndex, binding, arrayLength, @@ -443,8 +433,8 @@ namespace Ryujinx.Graphics.Shader.Translation { selectedMeta.UsageFlags |= TextureUsageFlags.NeedsScaleValue; - int dimensions = type.GetDimensions(); - bool canScale = _stage.SupportsRenderScale() && selectedInfo.ArrayLength == 1 && dimensions == 2; + var dimensions = type.GetDimensions(); + var canScale = _stage.SupportsRenderScale() && selectedInfo.ArrayLength == 1 && dimensions == 2; if (!canScale) { @@ -464,7 +454,7 @@ namespace Ryujinx.Graphics.Shader.Translation public BufferDescriptor[] GetConstantBufferDescriptors() { - BufferDescriptor[] descriptors = new BufferDescriptor[_usedConstantBufferBindings.Count]; + var descriptors = new BufferDescriptor[_usedConstantBufferBindings.Count]; int descriptorIndex = 0; @@ -488,7 +478,7 @@ namespace Ryujinx.Graphics.Shader.Translation public BufferDescriptor[] GetStorageBufferDescriptors() { - BufferDescriptor[] descriptors = new BufferDescriptor[_sbSlots.Count]; + var descriptors = new BufferDescriptor[_sbSlots.Count]; int descriptorIndex = 0; @@ -524,7 +514,7 @@ namespace Ryujinx.Graphics.Shader.Translation private static TextureDescriptor[] GetDescriptors(IReadOnlyDictionary usedResources, bool includeArrays) { - List descriptors = []; + List descriptors = new(); bool hasAnyArray = false; @@ -573,75 +563,6 @@ namespace Ryujinx.Graphics.Shader.Translation return descriptors.ToArray(); } - public ShaderProgramInfo GetVertexAsComputeInfo(bool isVertex = false) - { - BufferDescriptor[] cbDescriptors = new BufferDescriptor[_vacConstantBuffers.Count]; - int cbDescriptorIndex = 0; - - foreach (BufferDefinition definition in _vacConstantBuffers) - { - cbDescriptors[cbDescriptorIndex++] = new BufferDescriptor(definition.Set, definition.Binding, 0, 0, 0, BufferUsageFlags.None); - } - - BufferDescriptor[] sbDescriptors = new BufferDescriptor[_vacStorageBuffers.Count]; - int sbDescriptorIndex = 0; - - foreach (BufferDefinition definition in _vacStorageBuffers) - { - sbDescriptors[sbDescriptorIndex++] = new BufferDescriptor(definition.Set, definition.Binding, 0, 0, 0, BufferUsageFlags.Write); - } - - TextureDescriptor[] tDescriptors = new TextureDescriptor[_vacTextures.Count]; - int tDescriptorIndex = 0; - - foreach (TextureDefinition definition in _vacTextures) - { - tDescriptors[tDescriptorIndex++] = new TextureDescriptor( - definition.Set, - definition.Binding, - definition.Type, - definition.Format, - 0, - 0, - definition.ArrayLength, - definition.Separate, - definition.Flags); - } - - TextureDescriptor[] iDescriptors = new TextureDescriptor[_vacImages.Count]; - int iDescriptorIndex = 0; - - foreach (TextureDefinition definition in _vacImages) - { - iDescriptors[iDescriptorIndex++] = new TextureDescriptor( - definition.Set, - definition.Binding, - definition.Type, - definition.Format, - 0, - 0, - definition.ArrayLength, - definition.Separate, - definition.Flags); - } - - return new ShaderProgramInfo( - cbDescriptors, - sbDescriptors, - tDescriptors, - iDescriptors, - isVertex ? ShaderStage.Vertex : ShaderStage.Compute, - 0, - 0, - 0, - false, - false, - false, - false, - 0, - 0); - } - public bool TryGetCbufSlotAndHandleForTexture(int binding, out int cbufSlot, out int handle) { foreach ((TextureInfo info, TextureMeta meta) in _usedTextures) @@ -690,46 +611,24 @@ namespace Ryujinx.Graphics.Shader.Translation private void AddNewConstantBuffer(int setIndex, int binding, string name) { - StructureType type = new([ - new StructureField(AggregateType.Array | AggregateType.Vector4 | AggregateType.FP32, "data", Constants.ConstantBufferSize / 16) - ]); + StructureType type = new(new[] + { + new StructureField(AggregateType.Array | AggregateType.Vector4 | AggregateType.FP32, "data", Constants.ConstantBufferSize / 16), + }); Properties.AddOrUpdateConstantBuffer(new(BufferLayout.Std140, setIndex, binding, name, type)); } private void AddNewStorageBuffer(int setIndex, int binding, string name) { - StructureType type = new([ - new StructureField(AggregateType.Array | AggregateType.U32, "data", 0) - ]); + StructureType type = new(new[] + { + new StructureField(AggregateType.Array | AggregateType.U32, "data", 0), + }); Properties.AddOrUpdateStorageBuffer(new(BufferLayout.Std430, setIndex, binding, name, type)); } - public void AddVertexAsComputeConstantBuffer(BufferDefinition definition) - { - _vacConstantBuffers.Add(definition); - Properties.AddOrUpdateConstantBuffer(definition); - } - - public void AddVertexAsComputeStorageBuffer(BufferDefinition definition) - { - _vacStorageBuffers.Add(definition); - Properties.AddOrUpdateStorageBuffer(definition); - } - - public void AddVertexAsComputeTexture(TextureDefinition definition) - { - _vacTextures.Add(definition); - Properties.AddOrUpdateTexture(definition); - } - - public void AddVertexAsComputeImage(TextureDefinition definition) - { - _vacImages.Add(definition); - Properties.AddOrUpdateImage(definition); - } - public static string GetShaderStagePrefix(ShaderStage stage) { uint index = (uint)stage; diff --git a/src/Ryujinx.Graphics.Shader/Translation/TargetApi.cs b/src/Ryujinx.Graphics.Shader/Translation/TargetApi.cs index 66ed3dd45..519600937 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/TargetApi.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/TargetApi.cs @@ -4,6 +4,5 @@ namespace Ryujinx.Graphics.Shader.Translation { OpenGL, Vulkan, - Metal } } diff --git a/src/Ryujinx.Graphics.Shader/Translation/TargetLanguage.cs b/src/Ryujinx.Graphics.Shader/Translation/TargetLanguage.cs index 9d58cb926..863c7447b 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/TargetLanguage.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/TargetLanguage.cs @@ -4,6 +4,6 @@ namespace Ryujinx.Graphics.Shader.Translation { Glsl, Spirv, - Msl + Arb, } } diff --git a/src/Ryujinx.Graphics.Shader/Translation/Transforms/ForcePreciseEnable.cs b/src/Ryujinx.Graphics.Shader/Translation/Transforms/ForcePreciseEnable.cs index c774816a3..6b7e1410f 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Transforms/ForcePreciseEnable.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Transforms/ForcePreciseEnable.cs @@ -27,8 +27,6 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms addOp.Inst == (Instruction.FP32 | Instruction.Add) && addOp.GetSource(1).Type == OperandType.Constant) { - context.UsedFeatures |= FeatureFlags.Precise; - addOp.ForcePrecise = true; } diff --git a/src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs b/src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs index ff8fb255a..a579433f9 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs @@ -1,6 +1,5 @@ using Ryujinx.Graphics.Shader.CodeGen; using Ryujinx.Graphics.Shader.CodeGen.Glsl; -using Ryujinx.Graphics.Shader.CodeGen.Msl; using Ryujinx.Graphics.Shader.CodeGen.Spirv; using Ryujinx.Graphics.Shader.Decoders; using Ryujinx.Graphics.Shader.IntermediateRepresentation; @@ -243,8 +242,8 @@ namespace Ryujinx.Graphics.Shader.Translation usedFeatures |= FeatureFlags.VtgAsCompute; } - ControlFlowGraph[] cfgs = new ControlFlowGraph[functions.Length]; - RegisterUsage.FunctionRegisterUsage[] frus = new RegisterUsage.FunctionRegisterUsage[functions.Length]; + var cfgs = new ControlFlowGraph[functions.Length]; + var frus = new RegisterUsage.FunctionRegisterUsage[functions.Length]; for (int i = 0; i < functions.Length; i++) { @@ -267,14 +266,14 @@ namespace Ryujinx.Graphics.Shader.Translation for (int i = 0; i < functions.Length; i++) { - ControlFlowGraph cfg = cfgs[i]; + var cfg = cfgs[i]; int inArgumentsCount = 0; int outArgumentsCount = 0; if (i != 0) { - RegisterUsage.FunctionRegisterUsage fru = frus[i]; + var fru = frus[i]; inArgumentsCount = fru.InArguments.Length; outArgumentsCount = fru.OutArguments.Length; @@ -326,13 +325,12 @@ namespace Ryujinx.Graphics.Shader.Translation FeatureFlags usedFeatures, byte clipDistancesWritten) { - StructuredProgramInfo sInfo = StructuredProgram.MakeStructuredProgram( + var sInfo = StructuredProgram.MakeStructuredProgram( funcs, attributeUsage, definitions, resourceManager, Options.TargetLanguage, - usedFeatures.HasFlag(FeatureFlags.Precise), Options.Flags.HasFlag(TranslationFlags.DebugMode)); int geometryVerticesPerPrimitive = Definitions.OutputTopology switch @@ -342,7 +340,7 @@ namespace Ryujinx.Graphics.Shader.Translation _ => 1 }; - ShaderProgramInfo info = new( + var info = new ShaderProgramInfo( resourceManager.GetConstantBufferDescriptors(), resourceManager.GetStorageBufferDescriptors(), resourceManager.GetTextureDescriptors(), @@ -358,7 +356,7 @@ namespace Ryujinx.Graphics.Shader.Translation clipDistancesWritten, originalDefinitions.OmapTargets); - HostCapabilities hostCapabilities = new( + var hostCapabilities = new HostCapabilities( GpuAccessor.QueryHostReducedPrecision(), GpuAccessor.QueryHostSupportsFragmentShaderInterlock(), GpuAccessor.QueryHostSupportsFragmentShaderOrderingIntel(), @@ -369,13 +367,12 @@ namespace Ryujinx.Graphics.Shader.Translation GpuAccessor.QueryHostSupportsTextureShadowLod(), GpuAccessor.QueryHostSupportsViewportMask()); - CodeGenParameters parameters = new(attributeUsage, definitions, resourceManager.Properties, hostCapabilities, GpuAccessor, Options.TargetApi); + var parameters = new CodeGenParameters(attributeUsage, definitions, resourceManager.Properties, hostCapabilities, GpuAccessor, Options.TargetApi); return Options.TargetLanguage switch { TargetLanguage.Glsl => new ShaderProgram(info, TargetLanguage.Glsl, GlslGenerator.Generate(sInfo, parameters)), TargetLanguage.Spirv => new ShaderProgram(info, TargetLanguage.Spirv, SpirvGenerator.Generate(sInfo, parameters)), - TargetLanguage.Msl => new ShaderProgram(info, TargetLanguage.Msl, MslGenerator.Generate(sInfo, parameters)), _ => throw new NotImplementedException(Options.TargetLanguage.ToString()), }; } @@ -386,15 +383,16 @@ namespace Ryujinx.Graphics.Shader.Translation if (IsTransformFeedbackEmulated) { - StructureType tfeDataStruct = new([ - new(AggregateType.Array | AggregateType.U32, "data", 0) - ]); + StructureType tfeDataStruct = new(new StructureField[] + { + new StructureField(AggregateType.Array | AggregateType.U32, "data", 0) + }); for (int i = 0; i < ResourceReservations.TfeBuffersCount; i++) { int binding = resourceManager.Reservations.GetTfeBufferStorageBufferBinding(i); BufferDefinition tfeDataBuffer = new(BufferLayout.Std430, 1, binding, $"tfe_data{i}", tfeDataStruct); - resourceManager.AddVertexAsComputeStorageBuffer(tfeDataBuffer); + resourceManager.Properties.AddOrUpdateStorageBuffer(tfeDataBuffer); } } @@ -402,21 +400,22 @@ namespace Ryujinx.Graphics.Shader.Translation { int vertexInfoCbBinding = resourceManager.Reservations.VertexInfoConstantBufferBinding; BufferDefinition vertexInfoBuffer = new(BufferLayout.Std140, 0, vertexInfoCbBinding, "vb_info", VertexInfoBuffer.GetStructureType()); - resourceManager.AddVertexAsComputeConstantBuffer(vertexInfoBuffer); + resourceManager.Properties.AddOrUpdateConstantBuffer(vertexInfoBuffer); - StructureType vertexOutputStruct = new([ - new(AggregateType.Array | AggregateType.FP32, "data", 0) - ]); + StructureType vertexOutputStruct = new(new StructureField[] + { + new StructureField(AggregateType.Array | AggregateType.FP32, "data", 0) + }); int vertexOutputSbBinding = resourceManager.Reservations.VertexOutputStorageBufferBinding; BufferDefinition vertexOutputBuffer = new(BufferLayout.Std430, 1, vertexOutputSbBinding, "vertex_output", vertexOutputStruct); - resourceManager.AddVertexAsComputeStorageBuffer(vertexOutputBuffer); + resourceManager.Properties.AddOrUpdateStorageBuffer(vertexOutputBuffer); if (Stage == ShaderStage.Vertex) { SetBindingPair ibSetAndBinding = resourceManager.Reservations.GetIndexBufferTextureSetAndBinding(); TextureDefinition indexBuffer = new(ibSetAndBinding.SetIndex, ibSetAndBinding.Binding, "ib_data", SamplerType.TextureBuffer); - resourceManager.AddVertexAsComputeTexture(indexBuffer); + resourceManager.Properties.AddOrUpdateTexture(indexBuffer); int inputMap = _program.AttributeUsage.UsedInputAttributes; @@ -425,7 +424,7 @@ namespace Ryujinx.Graphics.Shader.Translation int location = BitOperations.TrailingZeroCount(inputMap); SetBindingPair setAndBinding = resourceManager.Reservations.GetVertexBufferTextureSetAndBinding(location); TextureDefinition vaBuffer = new(setAndBinding.SetIndex, setAndBinding.Binding, $"vb_data{location}", SamplerType.TextureBuffer); - resourceManager.AddVertexAsComputeTexture(vaBuffer); + resourceManager.Properties.AddOrUpdateTexture(vaBuffer); inputMap &= ~(1 << location); } @@ -434,19 +433,20 @@ namespace Ryujinx.Graphics.Shader.Translation { SetBindingPair trbSetAndBinding = resourceManager.Reservations.GetTopologyRemapBufferTextureSetAndBinding(); TextureDefinition remapBuffer = new(trbSetAndBinding.SetIndex, trbSetAndBinding.Binding, "trb_data", SamplerType.TextureBuffer); - resourceManager.AddVertexAsComputeTexture(remapBuffer); + resourceManager.Properties.AddOrUpdateTexture(remapBuffer); int geometryVbOutputSbBinding = resourceManager.Reservations.GeometryVertexOutputStorageBufferBinding; BufferDefinition geometryVbOutputBuffer = new(BufferLayout.Std430, 1, geometryVbOutputSbBinding, "geometry_vb_output", vertexOutputStruct); - resourceManager.AddVertexAsComputeStorageBuffer(geometryVbOutputBuffer); + resourceManager.Properties.AddOrUpdateStorageBuffer(geometryVbOutputBuffer); - StructureType geometryIbOutputStruct = new([ - new(AggregateType.Array | AggregateType.U32, "data", 0) - ]); + StructureType geometryIbOutputStruct = new(new StructureField[] + { + new StructureField(AggregateType.Array | AggregateType.U32, "data", 0) + }); int geometryIbOutputSbBinding = resourceManager.Reservations.GeometryIndexOutputStorageBufferBinding; BufferDefinition geometryIbOutputBuffer = new(BufferLayout.Std430, 1, geometryIbOutputSbBinding, "geometry_ib_output", geometryIbOutputStruct); - resourceManager.AddVertexAsComputeStorageBuffer(geometryIbOutputBuffer); + resourceManager.Properties.AddOrUpdateStorageBuffer(geometryIbOutputBuffer); } resourceManager.SetVertexAsComputeLocalMemories(Definitions.Stage, Definitions.InputTopology); @@ -479,40 +479,36 @@ namespace Ryujinx.Graphics.Shader.Translation return new ResourceReservations(GpuAccessor, IsTransformFeedbackEmulated, vertexAsCompute: true, _vertexOutput, ioUsage); } - public ShaderProgramInfo GetVertexAsComputeInfo() - { - return CreateResourceManager(true).GetVertexAsComputeInfo(); - } - public void SetVertexOutputMapForGeometryAsCompute(TranslatorContext vertexContext) { _vertexOutput = vertexContext._program.GetIoUsage(); } - public (ShaderProgram, ShaderProgramInfo) GenerateVertexPassthroughForCompute() + public ShaderProgram GenerateVertexPassthroughForCompute() { - AttributeUsage attributeUsage = new(GpuAccessor); - ResourceManager resourceManager = new(ShaderStage.Vertex, GpuAccessor); + var attributeUsage = new AttributeUsage(GpuAccessor); + var resourceManager = new ResourceManager(ShaderStage.Vertex, GpuAccessor); - ResourceReservations reservations = GetResourceReservations(); + var reservations = GetResourceReservations(); int vertexInfoCbBinding = reservations.VertexInfoConstantBufferBinding; if (Stage == ShaderStage.Vertex) { BufferDefinition vertexInfoBuffer = new(BufferLayout.Std140, 0, vertexInfoCbBinding, "vb_info", VertexInfoBuffer.GetStructureType()); - resourceManager.AddVertexAsComputeConstantBuffer(vertexInfoBuffer); + resourceManager.Properties.AddOrUpdateConstantBuffer(vertexInfoBuffer); } - StructureType vertexInputStruct = new([ - new(AggregateType.Array | AggregateType.FP32, "data", 0) - ]); + StructureType vertexInputStruct = new(new StructureField[] + { + new StructureField(AggregateType.Array | AggregateType.FP32, "data", 0) + }); int vertexDataSbBinding = reservations.VertexOutputStorageBufferBinding; BufferDefinition vertexOutputBuffer = new(BufferLayout.Std430, 1, vertexDataSbBinding, "vb_input", vertexInputStruct); - resourceManager.AddVertexAsComputeStorageBuffer(vertexOutputBuffer); + resourceManager.Properties.AddOrUpdateStorageBuffer(vertexOutputBuffer); - EmitterContext context = new(); + var context = new EmitterContext(); Operand vertexIndex = Options.TargetApi == TargetApi.OpenGL ? context.Load(StorageKind.Input, IoVariable.VertexId) @@ -557,25 +553,25 @@ namespace Ryujinx.Graphics.Shader.Translation } } - Operation[] operations = context.GetOperations(); - ControlFlowGraph cfg = ControlFlowGraph.Create(operations); - Function function = new(cfg.Blocks, "main", false, 0, 0); + var operations = context.GetOperations(); + var cfg = ControlFlowGraph.Create(operations); + var function = new Function(cfg.Blocks, "main", false, 0, 0); - TransformFeedbackOutput[] transformFeedbackOutputs = GetTransformFeedbackOutputs(GpuAccessor, out ulong transformFeedbackVecMap); + var transformFeedbackOutputs = GetTransformFeedbackOutputs(GpuAccessor, out ulong transformFeedbackVecMap); - ShaderDefinitions definitions = new(ShaderStage.Vertex, transformFeedbackVecMap, transformFeedbackOutputs) + var definitions = new ShaderDefinitions(ShaderStage.Vertex, transformFeedbackVecMap, transformFeedbackOutputs) { LastInVertexPipeline = true }; - return (Generate( - [function], + return Generate( + new[] { function }, attributeUsage, definitions, definitions, resourceManager, FeatureFlags.None, - 0), resourceManager.GetVertexAsComputeInfo(isVertex: true)); + 0); } public ShaderProgram GenerateGeometryPassthrough() @@ -608,10 +604,10 @@ namespace Ryujinx.Graphics.Shader.Translation break; } - AttributeUsage attributeUsage = new(GpuAccessor); - ResourceManager resourceManager = new(ShaderStage.Geometry, GpuAccessor); + var attributeUsage = new AttributeUsage(GpuAccessor); + var resourceManager = new ResourceManager(ShaderStage.Geometry, GpuAccessor); - EmitterContext context = new(); + var context = new EmitterContext(); for (int v = 0; v < maxOutputVertices; v++) { @@ -652,11 +648,11 @@ namespace Ryujinx.Graphics.Shader.Translation context.EndPrimitive(); - Operation[] operations = context.GetOperations(); - ControlFlowGraph cfg = ControlFlowGraph.Create(operations); - Function function = new(cfg.Blocks, "main", false, 0, 0); + var operations = context.GetOperations(); + var cfg = ControlFlowGraph.Create(operations); + var function = new Function(cfg.Blocks, "main", false, 0, 0); - ShaderDefinitions definitions = new( + var definitions = new ShaderDefinitions( ShaderStage.Geometry, GpuAccessor.QueryGraphicsState(), false, @@ -665,7 +661,7 @@ namespace Ryujinx.Graphics.Shader.Translation maxOutputVertices); return Generate( - [function], + new[] { function }, attributeUsage, definitions, definitions, diff --git a/src/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj b/src/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj new file mode 100644 index 000000000..fe535e6d5 --- /dev/null +++ b/src/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj @@ -0,0 +1,72 @@ + + + + win-x64;osx-x64;linux-x64 + Exe + true + 1.0.0-dirty + $(DefineConstants);$(ExtraDefineConstants) + - + true + $(DefaultItemExcludes);._* + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Always + THIRDPARTY.md + + + Always + LICENSE.txt + + + + + + Always + + + + + + + + + + false + ..\Ryujinx\Ryujinx.ico + + + + true + true + partial + + diff --git a/src/Ryujinx.ShaderTools/Program.cs b/src/Ryujinx.ShaderTools/Program.cs index 564960c6f..a84d7b466 100644 --- a/src/Ryujinx.ShaderTools/Program.cs +++ b/src/Ryujinx.ShaderTools/Program.cs @@ -116,7 +116,7 @@ namespace Ryujinx.ShaderTools if (options.VertexPassthrough) { - (program, _) = translatorContext.GenerateVertexPassthroughForCompute(); + program = translatorContext.GenerateVertexPassthroughForCompute(); } else { diff --git a/src/Ryujinx/AppHost.cs b/src/Ryujinx/AppHost.cs index 4147b8a5e..8cc196a58 100644 --- a/src/Ryujinx/AppHost.cs +++ b/src/Ryujinx/AppHost.cs @@ -33,7 +33,6 @@ using Ryujinx.Common.Utilities; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL.Multithreading; using Ryujinx.Graphics.Gpu; -using Ryujinx.Graphics.Metal; using Ryujinx.Graphics.OpenGL; using Ryujinx.Graphics.Vulkan; using Ryujinx.HLE; @@ -892,14 +891,10 @@ namespace Ryujinx.Ava VirtualFileSystem.ReloadKeySet(); // Initialize Renderer. - GraphicsBackend backend = TitleIDs.SelectGraphicsBackend(ApplicationId.ToString("X16"), ConfigurationState.Instance.Graphics.GraphicsBackend); + GraphicsBackend backend = ConfigurationState.Instance.Graphics.GraphicsBackend; IRenderer renderer = backend switch { -#pragma warning disable CA1416 // This call site is reachable on all platforms - // SelectGraphicsBackend does a check for Mac, on top of checking if it's an ARM Mac. This isn't a problem. - GraphicsBackend.Metal => new MetalRenderer((RendererHost.EmbeddedWindow as EmbeddedWindowMetal)!.CreateSurface), -#pragma warning restore CA1416 GraphicsBackend.Vulkan => VulkanRenderer.Create( ConfigurationState.Instance.Graphics.PreferredGpu, (RendererHost.EmbeddedWindow as EmbeddedWindowVulkan)!.CreateSurface, @@ -1126,7 +1121,6 @@ namespace Ryujinx.Ava { GraphicsBackend.Vulkan => "Vulkan", GraphicsBackend.OpenGl => "OpenGL", - GraphicsBackend.Metal => "Metal", _ => throw new NotImplementedException() }; diff --git a/src/Ryujinx/Assets/locales.json b/src/Ryujinx/Assets/locales.json index dd9459cd6..1fc7b474d 100644 --- a/src/Ryujinx/Assets/locales.json +++ b/src/Ryujinx/Assets/locales.json @@ -24073,4 +24073,4 @@ } } ] -} \ No newline at end of file +} diff --git a/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs b/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs index aa8238fc6..3ebfee751 100644 --- a/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs +++ b/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs @@ -12,7 +12,6 @@ using Ryujinx.Common.Logging; using Ryujinx.Common.Utilities; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL.Multithreading; -using Ryujinx.Graphics.Metal; using Ryujinx.Graphics.OpenGL; using Ryujinx.Graphics.Vulkan; using Ryujinx.HLE; @@ -310,11 +309,6 @@ namespace Ryujinx.Headless preferredGpuId); } - if (options.GraphicsBackend == GraphicsBackend.Metal && window is MetalWindow metalWindow && OperatingSystem.IsMacOS()) - { - return new MetalRenderer(metalWindow.GetLayer); - } - return new OpenGLRenderer(); } diff --git a/src/Ryujinx/Headless/HeadlessRyujinx.cs b/src/Ryujinx/Headless/HeadlessRyujinx.cs index 003b8a31e..9a69c56dd 100644 --- a/src/Ryujinx/Headless/HeadlessRyujinx.cs +++ b/src/Ryujinx/Headless/HeadlessRyujinx.cs @@ -358,9 +358,6 @@ namespace Ryujinx.Headless return options.GraphicsBackend switch { GraphicsBackend.Vulkan => new VulkanWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableMouse, options.HideCursorMode, options.IgnoreControllerApplet), - GraphicsBackend.Metal => OperatingSystem.IsMacOS() ? - new MetalWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableKeyboard, options.HideCursorMode, options.IgnoreControllerApplet) : - throw new Exception("Attempted to use Metal renderer on non-macOS platform!"), _ => new OpenGLWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableMouse, options.HideCursorMode, options.IgnoreControllerApplet) }; } diff --git a/src/Ryujinx/Headless/Windows/MetalWindow.cs b/src/Ryujinx/Headless/Windows/MetalWindow.cs deleted file mode 100644 index d79bd7938..000000000 --- a/src/Ryujinx/Headless/Windows/MetalWindow.cs +++ /dev/null @@ -1,47 +0,0 @@ -using Ryujinx.Common.Configuration; -using Ryujinx.Input.HLE; -using Ryujinx.SDL2.Common; -using SharpMetal.QuartzCore; -using System.Runtime.Versioning; -using static SDL2.SDL; - -namespace Ryujinx.Headless -{ - [SupportedOSPlatform("macos")] - class MetalWindow : WindowBase - { - private CAMetalLayer _caMetalLayer; - - public CAMetalLayer GetLayer() - { - return _caMetalLayer; - } - - public MetalWindow( - InputManager inputManager, - GraphicsDebugLevel glLogLevel, - AspectRatio aspectRatio, - bool enableMouse, - HideCursorMode hideCursorMode, - bool ignoreControllerApplet) - : base(inputManager, glLogLevel, aspectRatio, enableMouse, hideCursorMode, ignoreControllerApplet) { } - - public override SDL_WindowFlags WindowFlags => SDL_WindowFlags.SDL_WINDOW_METAL; - - protected override void InitializeWindowRenderer() - { - void CreateLayer() - { - _caMetalLayer = new CAMetalLayer(SDL_Metal_GetLayer(SDL_Metal_CreateView(WindowHandle))); - } - - SDL2Driver.MainThreadDispatcher?.Invoke(CreateLayer); - } - - protected override void InitializeRenderer() { } - - protected override void FinalizeWindowRenderer() { } - - protected override void SwapBuffers() { } - } -} diff --git a/src/Ryujinx/Program.cs b/src/Ryujinx/Program.cs index d7987534f..9b8ded44b 100644 --- a/src/Ryujinx/Program.cs +++ b/src/Ryujinx/Program.cs @@ -204,7 +204,6 @@ namespace Ryujinx.Ava { "opengl" => GraphicsBackend.OpenGl, "vulkan" => GraphicsBackend.Vulkan, - "metal" => GraphicsBackend.Metal, _ => ConfigurationState.Instance.Graphics.GraphicsBackend }; diff --git a/src/Ryujinx/Ryujinx.csproj b/src/Ryujinx/Ryujinx.csproj index 174aadeb0..7d0b22231 100644 --- a/src/Ryujinx/Ryujinx.csproj +++ b/src/Ryujinx/Ryujinx.csproj @@ -13,10 +13,9 @@ $(DefaultItemExcludes);._* - - - - + + + @@ -48,11 +47,11 @@ + + - - @@ -72,8 +71,6 @@ - - @@ -81,6 +78,7 @@ + @@ -114,10 +112,6 @@ Designer - - - - MSBuild:Compile @@ -127,6 +121,7 @@ + @@ -169,8 +164,4 @@ - - - - diff --git a/src/Ryujinx/UI/Renderer/EmbeddedWindowMetal.cs b/src/Ryujinx/UI/Renderer/EmbeddedWindowMetal.cs deleted file mode 100644 index 9e92d9289..000000000 --- a/src/Ryujinx/UI/Renderer/EmbeddedWindowMetal.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Ryujinx.Common.Helper; -using SharpMetal.QuartzCore; -using System; - -namespace Ryujinx.Ava.UI.Renderer -{ - public class EmbeddedWindowMetal : EmbeddedWindow - { - public CAMetalLayer CreateSurface() - { - if (OperatingSystem.IsMacOS() && RunningPlatform.IsArm) - { - return new CAMetalLayer(MetalLayer); - } - - throw new NotSupportedException($"Cannot create a {nameof(CAMetalLayer)} without being on ARM Mac."); - } - } -} diff --git a/src/Ryujinx/UI/Renderer/RendererHost.cs b/src/Ryujinx/UI/Renderer/RendererHost.cs index f755b6d70..69275ecb2 100644 --- a/src/Ryujinx/UI/Renderer/RendererHost.cs +++ b/src/Ryujinx/UI/Renderer/RendererHost.cs @@ -24,8 +24,7 @@ namespace Ryujinx.Ava.UI.Renderer EmbeddedWindow = ConfigurationState.Instance.Graphics.GraphicsBackend.Value switch { GraphicsBackend.OpenGl => new EmbeddedWindowOpenGL(), - GraphicsBackend.Metal => new EmbeddedWindowMetal(), - GraphicsBackend.Vulkan or GraphicsBackend.Auto => new EmbeddedWindowVulkan(), + GraphicsBackend.Vulkan => new EmbeddedWindowVulkan(), _ => throw new NotSupportedException() }; @@ -37,7 +36,6 @@ namespace Ryujinx.Ava.UI.Renderer { EmbeddedWindowVulkan => GraphicsBackend.Vulkan, EmbeddedWindowOpenGL => GraphicsBackend.OpenGl, - EmbeddedWindowMetal => GraphicsBackend.Metal, _ => throw new NotImplementedException() }; @@ -47,12 +45,11 @@ namespace Ryujinx.Ava.UI.Renderer FlowDirection = FlowDirection.LeftToRight; EmbeddedWindow = -#pragma warning disable CS8509 - TitleIDs.SelectGraphicsBackend(titleId, ConfigurationState.Instance.Graphics.GraphicsBackend) switch -#pragma warning restore CS8509 +#pragma warning disable CS8524 + ConfigurationState.Instance.Graphics.GraphicsBackend.Value switch +#pragma warning restore CS8524 { GraphicsBackend.OpenGl => new EmbeddedWindowOpenGL(), - GraphicsBackend.Metal => new EmbeddedWindowMetal(), GraphicsBackend.Vulkan => new EmbeddedWindowVulkan(), }; @@ -60,7 +57,6 @@ namespace Ryujinx.Ava.UI.Renderer { EmbeddedWindowVulkan => "Vulkan", EmbeddedWindowOpenGL => "OpenGL", - EmbeddedWindowMetal => "Metal", _ => throw new NotImplementedException() }; @@ -107,4 +103,3 @@ namespace Ryujinx.Ava.UI.Renderer } } } - diff --git a/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs b/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs index 1dd4a6b6d..23cafbc13 100644 --- a/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs @@ -141,7 +141,8 @@ namespace Ryujinx.Ava.UI.ViewModels // For an example of this, download canary 1.2.95, then open the settings menu, and look at the icon in the top-left. // The border gets reduced to colored pixels in the 4 corners. public static readonly Bitmap IconBitmap = - new(Assembly.GetAssembly(typeof(MainWindowViewModel))!.GetManifestResourceStream("Ryujinx.Assets.UIImages.Logo_Ryujinx_AntiAlias.png")!); + new(Assembly.GetAssembly(typeof(MainWindowViewModel))! + .GetManifestResourceStream("Ryujinx.Assets.UIImages.Logo_Ryujinx_AntiAlias.png")!); public MainWindow Window { get; init; } @@ -1574,7 +1575,7 @@ namespace Ryujinx.Ava.UI.ViewModels PrepareLoadScreen(); - RendererHostControl = new RendererHost(application.Id.ToString("X16")); + RendererHostControl = new RendererHost(); AppHost = new AppHost( RendererHostControl, diff --git a/src/Ryujinx/UI/ViewModels/SettingsHacksViewModel.cs b/src/Ryujinx/UI/ViewModels/SettingsHacksViewModel.cs index 5096a716d..167b8a857 100644 --- a/src/Ryujinx/UI/ViewModels/SettingsHacksViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/SettingsHacksViewModel.cs @@ -16,21 +16,6 @@ namespace Ryujinx.Ava.UI.ViewModels } [ObservableProperty] private bool _xc2MenuSoftlockFix = ConfigurationState.Instance.Hacks.Xc2MenuSoftlockFix; - [ObservableProperty] private bool _shaderTranslationDelayEnabled = ConfigurationState.Instance.Hacks.EnableShaderTranslationDelay; - private int _shaderTranslationSleepDelay = ConfigurationState.Instance.Hacks.ShaderTranslationDelay; - - public string ShaderTranslationDelayValueText => $"{ShaderTranslationDelay}ms"; - - public int ShaderTranslationDelay - { - get => _shaderTranslationSleepDelay; - set - { - _shaderTranslationSleepDelay = value; - - OnPropertiesChanged(nameof(ShaderTranslationDelay), nameof(ShaderTranslationDelayValueText)); - } - } public static string Xc2MenuFixTooltip { get; } = Lambda.String(sb => { @@ -44,13 +29,5 @@ namespace Ryujinx.Ava.UI.ViewModels "there is a low chance that the game will softlock, " + "the submenu won't show up, while background music is still there."); }); - - public static string ShaderTranslationDelayTooltip { get; } = Lambda.String(sb => - { - sb.AppendLine("This hack applies the delay you specify every time shaders are attempted to be translated.") - .AppendLine(); - - sb.Append("Configurable via slider, only when this option is enabled."); - }); } } diff --git a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs index fe71a7420..689d872a1 100644 --- a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs @@ -699,8 +699,6 @@ namespace Ryujinx.Ava.UI.ViewModels // Dirty Hacks config.Hacks.Xc2MenuSoftlockFix.Value = DirtyHacks.Xc2MenuSoftlockFix; - config.Hacks.EnableShaderTranslationDelay.Value = DirtyHacks.ShaderTranslationDelayEnabled; - config.Hacks.ShaderTranslationDelay.Value = DirtyHacks.ShaderTranslationDelay; config.ToFileFormat().SaveConfig(Program.ConfigurationPath); diff --git a/src/Ryujinx/UI/Views/Settings/SettingsGraphicsView.axaml b/src/Ryujinx/UI/Views/Settings/SettingsGraphicsView.axaml index 42515a4e9..219efcef8 100644 --- a/src/Ryujinx/UI/Views/Settings/SettingsGraphicsView.axaml +++ b/src/Ryujinx/UI/Views/Settings/SettingsGraphicsView.axaml @@ -8,7 +8,6 @@ xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia" xmlns:ext="clr-namespace:Ryujinx.Ava.Common.Markup" xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels" - xmlns:helper="clr-namespace:Ryujinx.Common.Helper;assembly=Ryujinx.Common" Design.Width="1000" mc:Ignorable="d" x:DataType="viewModels:SettingsViewModel"> @@ -34,24 +33,16 @@ ToolTip.Tip="{ext:Locale SettingsTabGraphicsBackendTooltip}" Text="{ext:Locale SettingsTabGraphicsBackend}" Width="250" /> - - - - + - - - diff --git a/src/Ryujinx/UI/Views/Settings/SettingsHacksView.axaml b/src/Ryujinx/UI/Views/Settings/SettingsHacksView.axaml index 9c597d65f..4cf96366a 100644 --- a/src/Ryujinx/UI/Views/Settings/SettingsHacksView.axaml +++ b/src/Ryujinx/UI/Views/Settings/SettingsHacksView.axaml @@ -43,39 +43,6 @@ Text="Xenoblade Chronicles 2 Menu Softlock Fix" /> - - - - - - - - - diff --git a/src/Ryujinx/UI/Windows/SettingsWindow.axaml.cs b/src/Ryujinx/UI/Windows/SettingsWindow.axaml.cs index 701c3ddaf..4d1fa899a 100644 --- a/src/Ryujinx/UI/Windows/SettingsWindow.axaml.cs +++ b/src/Ryujinx/UI/Windows/SettingsWindow.axaml.cs @@ -64,34 +64,34 @@ namespace Ryujinx.Ava.UI.Windows { switch (navItem.Tag.ToString()) { - case nameof(UiPage): + case "UiPage": UiPage.ViewModel = ViewModel; NavPanel.Content = UiPage; break; - case nameof(InputPage): + case "InputPage": NavPanel.Content = InputPage; break; - case nameof(HotkeysPage): + case "HotkeysPage": NavPanel.Content = HotkeysPage; break; - case nameof(SystemPage): + case "SystemPage": SystemPage.ViewModel = ViewModel; NavPanel.Content = SystemPage; break; - case nameof(CpuPage): + case "CpuPage": NavPanel.Content = CpuPage; break; - case nameof(GraphicsPage): + case "GraphicsPage": NavPanel.Content = GraphicsPage; break; - case nameof(AudioPage): + case "AudioPage": NavPanel.Content = AudioPage; break; - case nameof(NetworkPage): + case "NetworkPage": NetworkPage.ViewModel = ViewModel; NavPanel.Content = NetworkPage; break; - case nameof(LoggingPage): + case "LoggingPage": NavPanel.Content = LoggingPage; break; case nameof(HacksPage): diff --git a/src/Ryujinx/Utilities/AppLibrary/ApplicationData.cs b/src/Ryujinx/Utilities/AppLibrary/ApplicationData.cs index 2658352d7..192a2768b 100644 --- a/src/Ryujinx/Utilities/AppLibrary/ApplicationData.cs +++ b/src/Ryujinx/Utilities/AppLibrary/ApplicationData.cs @@ -16,7 +16,6 @@ using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.Loaders.Processes.Extensions; using System; using System.IO; -using System.Text; using System.Text.Json.Serialization; namespace Ryujinx.Ava.Utilities.AppLibrary diff --git a/src/Ryujinx/Utilities/Configuration/ConfigurationState.cs b/src/Ryujinx/Utilities/Configuration/ConfigurationState.cs index 02a91e51d..4fdf7c4f0 100644 --- a/src/Ryujinx/Utilities/Configuration/ConfigurationState.cs +++ b/src/Ryujinx/Utilities/Configuration/ConfigurationState.cs @@ -318,15 +318,14 @@ namespace Ryujinx.Ava.Utilities.Configuration private static GraphicsBackend DefaultGraphicsBackend() { - // Any system running macOS should default to auto, so it uses Vulkan everywhere and Metal in games where it works well. - if (OperatingSystem.IsMacOS()) - return GraphicsBackend.Auto; - - // Any system returning any amount of valid Vulkan devices should default to Vulkan. + // Any system running macOS or returning any amount of valid Vulkan devices should default to Vulkan. // Checks for if the Vulkan version and featureset is compatible should be performed within VulkanRenderer. - return VulkanRenderer.GetPhysicalDevices().Length > 0 - ? GraphicsBackend.Vulkan - : GraphicsBackend.OpenGl; + if (OperatingSystem.IsMacOS() || VulkanRenderer.GetPhysicalDevices().Length > 0) + { + return GraphicsBackend.Vulkan; + } + + return GraphicsBackend.OpenGl; + } + } } - } -} From 3ffcc721174fd866ede801077e7635525df3521a Mon Sep 17 00:00:00 2001 From: Evan Husted Date: Sat, 22 Feb 2025 23:48:47 -0600 Subject: [PATCH 13/24] UI: Fix compatibility list crashing (missing font) --- src/Ryujinx/Ryujinx.csproj | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Ryujinx/Ryujinx.csproj b/src/Ryujinx/Ryujinx.csproj index 7d0b22231..698b392ee 100644 --- a/src/Ryujinx/Ryujinx.csproj +++ b/src/Ryujinx/Ryujinx.csproj @@ -112,6 +112,10 @@ Designer + + + + MSBuild:Compile From a3596ba858890790ed0f18e092916f11f656a3d9 Mon Sep 17 00:00:00 2001 From: LotP1 <68976644+LotP1@users.noreply.github.com> Date: Tue, 25 Feb 2025 22:34:21 +0100 Subject: [PATCH 14/24] Reset in-memory JIT cache on game quit + fix Purge PPTC (#709) Jit cache now fully resets when booting a game multiple times. This should fix random jit cache crashes. Also removed some redundant code related to region allocation and fixed PPTC Purge not fully purging all PPTC files in the backup folder. --- src/ARMeilleure/Translation/Cache/JitCache.cs | 61 ++++++++++--------- .../Translation/Cache/JitUnwindWindows.cs | 22 +++++++ .../Controls/ApplicationContextMenu.axaml.cs | 2 +- 3 files changed, 56 insertions(+), 29 deletions(-) diff --git a/src/ARMeilleure/Translation/Cache/JitCache.cs b/src/ARMeilleure/Translation/Cache/JitCache.cs index 7b5f2ca81..e480985b1 100644 --- a/src/ARMeilleure/Translation/Cache/JitCache.cs +++ b/src/ARMeilleure/Translation/Cache/JitCache.cs @@ -24,7 +24,7 @@ namespace ARMeilleure.Translation.Cache private static JitCacheInvalidation _jitCacheInvalidator; - private static CacheMemoryAllocator _cacheAllocator; + private static List _cacheAllocators = []; private static readonly List _cacheEntries = []; @@ -40,37 +40,48 @@ namespace ARMeilleure.Translation.Cache public static void Initialize(IJitMemoryAllocator allocator) { - if (_initialized) - { - return; - } - lock (_lock) { if (_initialized) { - return; + if (OperatingSystem.IsWindows()) + { + JitUnwindWindows.RemoveFunctionTableHandler( + _jitRegions[0].Pointer); + } + + for (int i = 0; i < _jitRegions.Count; i++) + { + _jitRegions[i].Dispose(); + } + + _jitRegions.Clear(); + _cacheAllocators.Clear(); } + else + { + _initialized = true; + } + + _activeRegionIndex = 0; ReservedRegion firstRegion = new(allocator, CacheSize); _jitRegions.Add(firstRegion); - _activeRegionIndex = 0; + + CacheMemoryAllocator firstCacheAllocator = new(CacheSize); + _cacheAllocators.Add(firstCacheAllocator); if (!OperatingSystem.IsWindows() && !OperatingSystem.IsMacOS()) { _jitCacheInvalidator = new JitCacheInvalidation(allocator); } - _cacheAllocator = new CacheMemoryAllocator(CacheSize); - if (OperatingSystem.IsWindows()) { JitUnwindWindows.InstallFunctionTableHandler( firstRegion.Pointer, CacheSize, firstRegion.Pointer + Allocate(_pageSize) ); } - - _initialized = true; } } @@ -136,7 +147,7 @@ namespace ARMeilleure.Translation.Cache if (TryFind(funcOffset, out CacheEntry entry, out int entryIndex) && entry.Offset == funcOffset) { - _cacheAllocator.Free(funcOffset, AlignCodeSize(entry.Size)); + _cacheAllocators[_activeRegionIndex].Free(funcOffset, AlignCodeSize(entry.Size)); _cacheEntries.RemoveAt(entryIndex); } @@ -167,30 +178,24 @@ namespace ARMeilleure.Translation.Cache { codeSize = AlignCodeSize(codeSize); - for (int i = _activeRegionIndex; i < _jitRegions.Count; i++) + int allocOffset = _cacheAllocators[_activeRegionIndex].Allocate(codeSize); + + if (allocOffset >= 0) { - int allocOffset = _cacheAllocator.Allocate(codeSize); - - if (allocOffset >= 0) - { - _jitRegions[i].ExpandIfNeeded((ulong)allocOffset + (ulong)codeSize); - _activeRegionIndex = i; - return allocOffset; - } + _jitRegions[_activeRegionIndex].ExpandIfNeeded((ulong)allocOffset + (ulong)codeSize); + return allocOffset; } int exhaustedRegion = _activeRegionIndex; ReservedRegion newRegion = new(_jitRegions[0].Allocator, CacheSize); _jitRegions.Add(newRegion); _activeRegionIndex = _jitRegions.Count - 1; - - int newRegionNumber = _activeRegionIndex; - Logger.Warning?.Print(LogClass.Cpu, $"JIT Cache Region {exhaustedRegion} exhausted, creating new Cache Region {newRegionNumber} ({((long)(newRegionNumber + 1) * CacheSize).Bytes()} Total Allocation)."); - - _cacheAllocator = new CacheMemoryAllocator(CacheSize); + Logger.Warning?.Print(LogClass.Cpu, $"JIT Cache Region {exhaustedRegion} exhausted, creating new Cache Region {_activeRegionIndex} ({((long)(_activeRegionIndex + 1) * CacheSize).Bytes()} Total Allocation)."); - int allocOffsetNew = _cacheAllocator.Allocate(codeSize); + _cacheAllocators.Add(new CacheMemoryAllocator(CacheSize)); + + int allocOffsetNew = _cacheAllocators[_activeRegionIndex].Allocate(codeSize); if (allocOffsetNew < 0) { throw new OutOfMemoryException("Failed to allocate in new Cache Region!"); diff --git a/src/ARMeilleure/Translation/Cache/JitUnwindWindows.cs b/src/ARMeilleure/Translation/Cache/JitUnwindWindows.cs index 01b2aa8ed..15a1051fa 100644 --- a/src/ARMeilleure/Translation/Cache/JitUnwindWindows.cs +++ b/src/ARMeilleure/Translation/Cache/JitUnwindWindows.cs @@ -52,6 +52,11 @@ namespace ARMeilleure.Translation.Cache nint context, [MarshalAs(UnmanagedType.LPWStr)] string outOfProcessCallbackDll); + [LibraryImport("kernel32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + private static unsafe partial bool RtlDeleteFunctionTable( + ulong tableIdentifier); + private static GetRuntimeFunctionCallback _getRuntimeFunctionCallback; private static int _sizeOfRuntimeFunction; @@ -91,6 +96,23 @@ namespace ARMeilleure.Translation.Cache } } + public static void RemoveFunctionTableHandler(nint codeCachePointer) + { + ulong codeCachePtr = (ulong)codeCachePointer.ToInt64(); + + bool result; + + unsafe + { + result = RtlDeleteFunctionTable(codeCachePtr | 3); + } + + if (!result) + { + throw new InvalidOperationException("Failure removing function table callback."); + } + } + private static unsafe RuntimeFunction* FunctionTableHandler(ulong controlPc, nint context) { int offset = (int)((long)controlPc - context.ToInt64()); diff --git a/src/Ryujinx/UI/Controls/ApplicationContextMenu.axaml.cs b/src/Ryujinx/UI/Controls/ApplicationContextMenu.axaml.cs index 32ed6de39..881f6d556 100644 --- a/src/Ryujinx/UI/Controls/ApplicationContextMenu.axaml.cs +++ b/src/Ryujinx/UI/Controls/ApplicationContextMenu.axaml.cs @@ -200,7 +200,7 @@ namespace Ryujinx.Ava.UI.Controls if (backupDir.Exists) { cacheFiles.AddRange(backupDir.EnumerateFiles("*.cache")); - cacheFiles.AddRange(mainDir.EnumerateFiles("*.info")); + cacheFiles.AddRange(backupDir.EnumerateFiles("*.info")); } if (cacheFiles.Count > 0) From 1c8276197f3baeee5bf51bafa4ef38ffbc4cf1a6 Mon Sep 17 00:00:00 2001 From: FluffyOMC <45863583+FluffyOMC@users.noreply.github.com> Date: Tue, 25 Feb 2025 16:48:35 -0500 Subject: [PATCH 15/24] SSBU DRPC - Stage Editing (#707) Adds it so the Rich Presence now notices when the player edits a custom stage! --- src/Ryujinx/Utilities/PlayReport/PlayReports.Formatters.cs | 5 +++++ src/Ryujinx/Utilities/PlayReport/PlayReports.cs | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Ryujinx/Utilities/PlayReport/PlayReports.Formatters.cs b/src/Ryujinx/Utilities/PlayReport/PlayReports.Formatters.cs index 43e830819..a64379ab1 100644 --- a/src/Ryujinx/Utilities/PlayReport/PlayReports.Formatters.cs +++ b/src/Ryujinx/Utilities/PlayReport/PlayReports.Formatters.cs @@ -115,6 +115,11 @@ namespace Ryujinx.Ava.Utilities.PlayReport return $"Achievement Unlocked - ID: {anniversary}"; } + if (values.Matched.ContainsKey("is_created")) + { + return "Edited a Custom Stage!"; + } + if (values.Matched.ContainsKey("adv_slot")) { return diff --git a/src/Ryujinx/Utilities/PlayReport/PlayReports.cs b/src/Ryujinx/Utilities/PlayReport/PlayReports.cs index 9feb888b3..0de36a3ce 100644 --- a/src/Ryujinx/Utilities/PlayReport/PlayReports.cs +++ b/src/Ryujinx/Utilities/PlayReport/PlayReports.cs @@ -71,7 +71,7 @@ namespace Ryujinx.Ava.Utilities.PlayReport [ // Metadata to figure out what PlayReport we have. "match_mode", "match_submode", "anniversary", "fighter", "reason", "challenge_count", - "adv_slot", + "adv_slot", "is_created", // List of Fighters "player_1_fighter", "player_2_fighter", "player_3_fighter", "player_4_fighter", "player_5_fighter", "player_6_fighter", "player_7_fighter", "player_8_fighter", From 332bcdfaf1efd84707581aedc7fcd53b78f1b09a Mon Sep 17 00:00:00 2001 From: Evan Husted Date: Tue, 25 Feb 2025 17:34:48 -0600 Subject: [PATCH 16/24] UI: Updater: Add support for eventual Windows on ARM updates --- src/Ryujinx.Common/Helpers/RunningPlatform.cs | 23 ++++++++++- src/Ryujinx/Updater.cs | 41 ++++++++++++++----- 2 files changed, 52 insertions(+), 12 deletions(-) diff --git a/src/Ryujinx.Common/Helpers/RunningPlatform.cs b/src/Ryujinx.Common/Helpers/RunningPlatform.cs index 8d85c4a3c..7ec2f18df 100644 --- a/src/Ryujinx.Common/Helpers/RunningPlatform.cs +++ b/src/Ryujinx.Common/Helpers/RunningPlatform.cs @@ -5,15 +5,34 @@ using System.Runtime.InteropServices; namespace Ryujinx.Common.Helper { + public enum OperatingSystemType + { + MacOS, + Linux, + Windows + } + public static class RunningPlatform { + public static readonly OperatingSystemType CurrentOS + = IsMacOS + ? OperatingSystemType.MacOS + : IsWindows + ? OperatingSystemType.Windows + : IsLinux + ? OperatingSystemType.Linux + : throw new PlatformNotSupportedException(); + + public static Architecture Architecture => RuntimeInformation.OSArchitecture; + public static Architecture CurrentProcessArchitecture => RuntimeInformation.ProcessArchitecture; + public static bool IsMacOS => OperatingSystem.IsMacOS(); public static bool IsWindows => OperatingSystem.IsWindows(); public static bool IsLinux => OperatingSystem.IsLinux(); - public static bool IsArm => RuntimeInformation.OSArchitecture is Architecture.Arm64; + public static bool IsArm => Architecture is Architecture.Arm64; - public static bool IsX64 => RuntimeInformation.OSArchitecture is Architecture.X64; + public static bool IsX64 => Architecture is Architecture.X64; public static bool IsIntelMac => IsMacOS && IsX64; public static bool IsArmMac => IsMacOS && IsArm; diff --git a/src/Ryujinx/Updater.cs b/src/Ryujinx/Updater.cs index 338e9de43..7ca5e885a 100644 --- a/src/Ryujinx/Updater.cs +++ b/src/Ryujinx/Updater.cs @@ -43,17 +43,9 @@ namespace Ryujinx.Ava private const int ConnectionCount = 4; private static string _buildVer; + - private static readonly string _platformExt = - RunningPlatform.IsMacOS - ? "macos_universal.app.tar.gz" - : RunningPlatform.IsWindows - ? "win_x64.zip" - : RunningPlatform.IsX64Linux - ? "linux_x64.tar.gz" - : RunningPlatform.IsArmLinux - ? "linux_arm64.tar.gz" - : throw new PlatformNotSupportedException(); + private static readonly string _platformExt = BuildPlatformExtension(); private static string _buildUrl; private static long _buildSize; @@ -780,5 +772,34 @@ namespace Ryujinx.Ava public static void CleanupUpdate() => Directory.GetFiles(_homeDir, "*.ryuold", SearchOption.AllDirectories) .ForEach(File.Delete); + + private static string BuildPlatformExtension() + { + if (RunningPlatform.IsMacOS) + return "macos_universal.app.tar.gz"; + +#pragma warning disable CS8509 // It is exhaustive for any values this can contain. + string osPrefix = RunningPlatform.CurrentOS switch + { + OperatingSystemType.Linux => "linux", + OperatingSystemType.Windows => "win" + }; + + string archSuffix = RunningPlatform.Architecture switch + { + Architecture.Arm64 => "arm64", + Architecture.X64 => "x64", + _ => throw new PlatformNotSupportedException($"Unknown architecture {Enum.GetName(RunningPlatform.Architecture)}."), + }; + + string fileExtension = RunningPlatform.CurrentOS switch +#pragma warning restore CS8509 + { + OperatingSystemType.Linux => "tar.gz", + OperatingSystemType.Windows => "zip" + }; + + return $"{osPrefix}_{archSuffix}.{fileExtension}"; + } } } From 9227cbe5a7768e9bd02b937df8fa52f3b78e06b9 Mon Sep 17 00:00:00 2001 From: Piplup <100526773+piplup55@users.noreply.github.com> Date: Tue, 25 Feb 2025 23:48:47 +0000 Subject: [PATCH 17/24] Dynamic RPC: Improve Pokemon Scarlet/Violet (#723) Updated Pokemon Scarlet and Violet to use multi parser it now displays if your in a team circle and area of the game ![image](https://github.com/user-attachments/assets/6d8d52c5-65a9-4ac3-af91-cb0c03576ad6) --- .../PlayReport/PlayReports.Formatters.cs | 18 +++++++++++------- .../Utilities/PlayReport/PlayReports.cs | 5 ++--- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/Ryujinx/Utilities/PlayReport/PlayReports.Formatters.cs b/src/Ryujinx/Utilities/PlayReport/PlayReports.Formatters.cs index a64379ab1..b5215c693 100644 --- a/src/Ryujinx/Utilities/PlayReport/PlayReports.Formatters.cs +++ b/src/Ryujinx/Utilities/PlayReport/PlayReports.Formatters.cs @@ -59,12 +59,13 @@ namespace Ryujinx.Ava.Utilities.PlayReport "Race" => "Racing", _ => FormattedValue.ForceReset }; - - private static FormattedValue PokemonSVUnionCircle(SingleValue value) - => value.Matched.BoxedValue is 0 ? "Playing Alone" : "Playing in a group"; - - private static FormattedValue PokemonSVArea(SingleValue value) - => value.Matched.StringValue switch + + private static FormattedValue PokemonSV(MultiValue values) + { + + string playStatus = values.Matched[0].BoxedValue is 0 ? "Playing Alone" : "Playing in a group"; + + FormattedValue locations = values.Matched[1].ToString() switch { // Base Game Locations "a_w01" => "South Area One", @@ -92,10 +93,13 @@ namespace Ryujinx.Ava.Utilities.PlayReport "a_w24" => "South Paldean Sea", "a_w25" => "West Paldean Sea", "a_w26" => "East Paldean Sea", - "a_w27" => "Nouth Paldean Sea", + "a_w27" => "North Paldean Sea", //TODO DLC Locations _ => FormattedValue.ForceReset }; + + return$"{playStatus} in {locations}"; + } private static FormattedValue SuperSmashBrosUltimate_Mode(SparseMultiValue values) { diff --git a/src/Ryujinx/Utilities/PlayReport/PlayReports.cs b/src/Ryujinx/Utilities/PlayReport/PlayReports.cs index 0de36a3ce..c46ae2be5 100644 --- a/src/Ryujinx/Utilities/PlayReport/PlayReports.cs +++ b/src/Ryujinx/Utilities/PlayReport/PlayReports.cs @@ -59,9 +59,8 @@ namespace Ryujinx.Ava.Utilities.PlayReport .AddSpec( ["0100a3d008c5c000", "01008f6008c5e000"], spec => spec - .WithDescription("based on what area of Paldea you're exploring.") - .AddValueFormatter("area_no", PokemonSVArea) - .AddValueFormatter("team_circle", PokemonSVUnionCircle) + .WithDescription("based on if you're playing alone or in a group and what area of Paldea you're exploring.") + .AddMultiValueFormatter(["team_circle", "area_no"], PokemonSV) ) .AddSpec( "01006a800016e000", From 2e4de17472f13a4f00c78e7add13b02f85de7d01 Mon Sep 17 00:00:00 2001 From: Vladimir Sokolov Date: Wed, 26 Feb 2025 13:11:20 +1000 Subject: [PATCH 18/24] Custom configuration for each game (#632) ![image](https://github.com/user-attachments/assets/5dd139b4-2004-4c13-85d1-fc3378382adf) ![image](https://github.com/user-attachments/assets/9bcb8524-a403-428f-9f98-e8c03c75f079) Now you can make a separate configuration (independent file) for each game. All emulator settings are available except for some UI functionality ones. The configuration file can be changed and deleted from a separate menu. The user configuration menu is available through the context menu on a given application. --------- Co-authored-by: Evan Husted --- src/Ryujinx/AppHost.cs | 7 + src/Ryujinx/Assets/Styles/Styles.xaml | 33 ++- src/Ryujinx/Assets/Styles/Themes.xaml | 8 +- src/Ryujinx/Assets/locales.json | 225 ++++++++++++++++++ src/Ryujinx/Common/LocaleManager.cs | 1 + src/Ryujinx/Program.cs | 71 ++++++ src/Ryujinx/Rebooter.cs | 76 ++++++ .../UI/Controls/ApplicationContextMenu.axaml | 12 + .../Controls/ApplicationContextMenu.axaml.cs | 17 +- .../UI/Controls/ApplicationGridView.axaml | 39 ++- .../UI/Controls/ApplicationListView.axaml | 8 + .../UI/ViewModels/MainWindowViewModel.cs | 52 +++- .../UI/ViewModels/SettingsViewModel.cs | 103 +++++++- .../UI/Views/Main/MainMenuBarView.axaml.cs | 23 +- .../Views/Settings/SettingsSystemView.axaml | 12 +- .../UI/Views/Settings/SettingsUIView.axaml | 74 ++++-- .../Windows/GameSpecificSettingsWindow.axaml | 155 ++++++++++++ .../GameSpecificSettingsWindow.axaml.cs | 121 ++++++++++ src/Ryujinx/UI/Windows/MainWindow.axaml.cs | 4 +- .../Utilities/AppLibrary/ApplicationData.cs | 1 + .../AppLibrary/ApplicationLibrary.cs | 5 +- src/Ryujinx/Utilities/CommandLineState.cs | 64 ++++- .../ConfigurationState.Migration.cs | 101 ++++---- src/Ryujinx/Utilities/ShortcutHelper.cs | 27 ++- 24 files changed, 1133 insertions(+), 106 deletions(-) create mode 100644 src/Ryujinx/Rebooter.cs create mode 100644 src/Ryujinx/UI/Windows/GameSpecificSettingsWindow.axaml create mode 100644 src/Ryujinx/UI/Windows/GameSpecificSettingsWindow.axaml.cs diff --git a/src/Ryujinx/AppHost.cs b/src/Ryujinx/AppHost.cs index 8cc196a58..b741eb977 100644 --- a/src/Ryujinx/AppHost.cs +++ b/src/Ryujinx/AppHost.cs @@ -1113,6 +1113,13 @@ namespace Ryujinx.Ava }); (RendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.MakeCurrent(true); + + // Reload settings when the game is turned off + // (resets custom settings if there were any) + Program.ReloadConfig(); + + // Reload application list (changes the status of the user setting if it was added or removed during the game) + Dispatcher.UIThread.Post(() => RyujinxApp.MainWindow.LoadApplications()); } public void InitStatus() diff --git a/src/Ryujinx/Assets/Styles/Styles.xaml b/src/Ryujinx/Assets/Styles/Styles.xaml index 5523f551a..eb40e853c 100644 --- a/src/Ryujinx/Assets/Styles/Styles.xaml +++ b/src/Ryujinx/Assets/Styles/Styles.xaml @@ -1,7 +1,8 @@ - + Content="Add" + Classes="red"/> + + + + + + + + + + diff --git a/src/Ryujinx/Assets/Styles/Themes.xaml b/src/Ryujinx/Assets/Styles/Themes.xaml index 3a0bd4217..de7584240 100644 --- a/src/Ryujinx/Assets/Styles/Themes.xaml +++ b/src/Ryujinx/Assets/Styles/Themes.xaml @@ -1,4 +1,4 @@ - @@ -12,11 +12,13 @@ #C1C1C1 #b3ffffff #80cccccc + #FF6347 #A0000000 #fffcd12a #FF2EEAC9 #FFFF4554 #6483F5 + #800080 #C1C1C1 #b3ffffff #80cccccc + #FF6347 #A0000000 #fffcd12a #13c3a4 #FFFF4554 #6483F5 + #800080 #3D3D3D #0FFFFFFF #1EFFFFFF + #FF6347 #A0FFFFFF #fffcd12a #FF2EEAC9 #FFFF4554 #6483F5 + #FFA500 diff --git a/src/Ryujinx/Assets/locales.json b/src/Ryujinx/Assets/locales.json index 1fc7b474d..272fc01cd 100644 --- a/src/Ryujinx/Assets/locales.json +++ b/src/Ryujinx/Assets/locales.json @@ -2747,6 +2747,56 @@ "zh_TW": "建立桌面捷徑,啟動選取的應用程式" } }, + { + "ID": "GameListContextMenuCreateCustomConfiguration", + "Translations": { + "ar_SA": "", + "de_DE": "", + "el_GR": "", + "en_US": "Create Custom Configuration", + "es_ES": "", + "fr_FR": "", + "he_IL": "", + "it_IT": "", + "ja_JP": "", + "ko_KR": "", + "no_NO": "", + "pl_PL": "", + "pt_BR": "", + "ru_RU": "", + "sv_SE": "", + "th_TH": "", + "tr_TR": "", + "uk_UA": "", + "zh_CN": "", + "zh_TW": "" + } + }, + { + "ID": "GameListContextMenuEditCustomConfiguration", + "Translations": { + "ar_SA": "", + "de_DE": "", + "el_GR": "", + "en_US": "Edit Custom Configuration", + "es_ES": "", + "fr_FR": "", + "he_IL": "", + "it_IT": "", + "ja_JP": "", + "ko_KR": "", + "no_NO": "", + "pl_PL": "", + "pt_BR": "", + "ru_RU": "", + "sv_SE": "", + "th_TH": "", + "tr_TR": "", + "uk_UA": "", + "zh_CN": "", + "zh_TW": "" + } + }, { "ID": "GameListContextMenuCreateShortcutToolTipMacOS", "Translations": { @@ -2772,6 +2822,56 @@ "zh_TW": "在 macOS 的應用程式資料夾中建立捷徑,啟動選取的應用程式" } }, + { + "ID": "CreateCustomConfigurationToolTip", + "Translations": { + "ar_SA": "ينشئ تكوينًا مستقلًا للعبة الحالية", + "de_DE": "Erstellt eine unabhängige Konfiguration für das aktuelle Spiel", + "el_GR": "Δημιουργεί μια ανεξάρτητη διαμόρφωση για το τρέχον παιχνίδι", + "en_US": "Creates an independent configuration for the selected game", + "es_ES": "Crea una configuración independiente para el juego actual", + "fr_FR": "Crée une configuration indépendante pour le jeu en cours", + "he_IL": "יוצר תצורה עצמאית למשחק הנוכחי", + "it_IT": "Crea una configurazione indipendente per il gioco attuale", + "ja_JP": "現在のゲーム用の独立した設定を作成します", + "ko_KR": "현재 게임에 대한 독립적인 설정을 생성합니다", + "no_NO": "Oppretter en uavhengig konfigurasjon for det gjeldende spillet", + "pl_PL": "Tworzy niezależną konfigurację dla bieżącej gry", + "pt_BR": "Cria uma configuração independente para o jogo atual", + "ru_RU": "Создает независимую конфигурацию для текущей игры", + "sv_SE": "Skapar en oberoende konfiguration för det aktuella spelet", + "th_TH": "สร้างการกำหนดค่าที่เป็นอิสระสำหรับเกมปัจจุบัน", + "tr_TR": "Mevcut oyun için bağımsız bir yapılandırma oluşturur", + "uk_UA": "Створює незалежну конфігурацію для поточної гри", + "zh_CN": "为当前游戏创建独立的配置", + "zh_TW": "為當前遊戲創建獨立的配置" + } + }, + { + "ID": "EditCustomConfigurationToolTip", + "Translations": { + "ar_SA": "", + "de_DE": "", + "el_GR": "", + "en_US": "Edit your existing independent configuration for the selected game", + "es_ES": "", + "fr_FR": "", + "he_IL": "", + "it_IT": "", + "ja_JP": "", + "ko_KR": "", + "no_NO": "", + "pl_PL": "", + "pt_BR": "", + "ru_RU": "", + "sv_SE": "", + "th_TH": "", + "tr_TR": "", + "uk_UA": "", + "zh_CN": "", + "zh_TW": "" + } + }, { "ID": "GameListContextMenuShowCompatEntry", "Translations": { @@ -3297,6 +3397,31 @@ "zh_TW": "設定" } }, + { + "ID": "SettingsWithInfo", + "Translations": { + "ar_SA": "{0} - إعدادات", + "de_DE": "Einstellungen - {0}", + "el_GR": "Ρυθμίσεις - {0}", + "en_US": "Settings - {0}", + "es_ES": "Configuración - {0}", + "fr_FR": "Paramètres - {0}", + "he_IL": "{0} - הגדרות", + "it_IT": "Impostazioni - {0}", + "ja_JP": "設定 - {0}", + "ko_KR": "설정 - {0}", + "no_NO": "Innstillinger - {0}", + "pl_PL": "Ustawienia - {0}", + "pt_BR": "Configurações - {0}", + "ru_RU": "Параметры - {0}", + "sv_SE": "Inställningar - {0}", + "th_TH": "ตั้งค่า - {0}", + "tr_TR": "Ayarlar - {0}", + "uk_UA": "Налаштування - {0}", + "zh_CN": "设置 - {0}", + "zh_TW": "設定 - {0}" + } + }, { "ID": "SettingsTabGeneral", "Translations": { @@ -12647,6 +12772,31 @@ "zh_TW": "正在下載更新..." } }, + { + "ID": "DialogRebooterMessage", + "Translations": { + "ar_SA": "من فضلك انتظر، المحاكي في طور إعادة التشغيل", + "de_DE": "Bitte warten Sie, der Emulator wird neu gestartet", + "el_GR": "Παρακαλώ περιμένετε, ο εξομοιωτής επανεκκινείται", + "en_US": "Please wait, the emulator is restarting", + "es_ES": "Por favor, espere, el emulador se está reiniciando", + "fr_FR": "Veuillez patienter, l'émulateur est en train de redémarrer", + "he_IL": "אנא המתן, המחקה מתארגן מחדש", + "it_IT": "Attendere prego, l'emulatore si sta riavviando", + "ja_JP": "お待ちください、エミュレーターが再起動しています", + "ko_KR": "잠시만 기다려 주세요, 에뮬레이터가 재시작 중입니다", + "no_NO": "Vennligst vent, emulatoren starter på nytt", + "pl_PL": "Proszę czekać, emulator jest w trakcie ponownego uruchamiania", + "pt_BR": "Por favor, aguarde, o emulador está reiniciando", + "ru_RU": "Пожалуйста, подождите, эмулятор перезапускается", + "sv_SE": "Vänligen vänta, emulatorn startar om", + "th_TH": "กรุณารอสักครู่, ตัวจำลองกำลังเริ่มใหม่", + "tr_TR": "Lütfen bekleyin, emülatör yeniden başlatılıyor", + "uk_UA": "Будь ласка, зачекайте, емулятор перезавантажується", + "zh_CN": "请稍等,模拟器正在重新启动", + "zh_TW": "請稍候,模擬器正在重新啟動" + } + }, { "ID": "DialogUpdaterExtractionMessage", "Translations": { @@ -19522,6 +19672,31 @@ "zh_TW": "{0} 更新程式" } }, + { + "ID": "RyujinxRebooter", + "Translations": { + "ar_SA": "إعادة تشغيل {0}", + "de_DE": "Neustart von {0}", + "el_GR": "Επανεκκίνηση {0}", + "en_US": "{0} Reboot", + "es_ES": "Reinicio de {0}", + "fr_FR": "Redémarrage de {0}", + "he_IL": "אתחול {0}", + "it_IT": "Riavvio di {0}", + "ja_JP": "{0} 再起動", + "ko_KR": "{0} 재부팅", + "no_NO": "Omstart av {0}", + "pl_PL": "Ponowne uruchomienie {0}", + "pt_BR": "Reinício de {0}", + "ru_RU": "{0} Перезагрузка", + "sv_SE": "Ominläsning av {0}", + "th_TH": "เริ่มต้นใหม่ {0}", + "tr_TR": "{0} Yeniden Başlatma", + "uk_UA": "Перезавантаження {0}", + "zh_CN": "{0} 重启", + "zh_TW": "{0} 重新啟動" + } + }, { "ID": "SettingsTabHotkeys", "Translations": { @@ -23997,6 +24172,56 @@ "zh_TW": "" } }, + { + "ID": "GameSpecificConfigurationHeader", + "Translations": { + "ar_SA": "", + "de_DE": "", + "el_GR": "", + "en_US": "Custom Config", + "es_ES": "", + "fr_FR": "", + "he_IL": "", + "it_IT": "", + "ja_JP": "", + "ko_KR": "", + "no_NO": "", + "pl_PL": "", + "pt_BR": "", + "ru_RU": "", + "sv_SE": "", + "th_TH": "", + "tr_TR": "", + "uk_UA": "", + "zh_CN": "", + "zh_TW": "" + } + }, + { + "ID": "GameSpecificConfigurationGlobal", + "Translations": { + "ar_SA": "", + "de_DE": "", + "el_GR": "", + "en_US": "(Global)", + "es_ES": "", + "fr_FR": "", + "he_IL": "", + "it_IT": "", + "ja_JP": "", + "ko_KR": "", + "no_NO": "", + "pl_PL": "", + "pt_BR": "", + "ru_RU": "", + "sv_SE": "", + "th_TH": "", + "tr_TR": "", + "uk_UA": "", + "zh_CN": "", + "zh_TW": "" + } + }, { "ID": "ExtractAocListHeader", "Translations": { diff --git a/src/Ryujinx/Common/LocaleManager.cs b/src/Ryujinx/Common/LocaleManager.cs index 4c86a6177..f60cff49b 100644 --- a/src/Ryujinx/Common/LocaleManager.cs +++ b/src/Ryujinx/Common/LocaleManager.cs @@ -54,6 +54,7 @@ namespace Ryujinx.Ava.Common.Locale SetDynamicValues(LocaleKeys.RyujinxInfo, RyujinxApp.FullAppName); SetDynamicValues(LocaleKeys.RyujinxConfirm, RyujinxApp.FullAppName); SetDynamicValues(LocaleKeys.RyujinxUpdater, RyujinxApp.FullAppName); + SetDynamicValues(LocaleKeys.RyujinxRebooter, RyujinxApp.FullAppName); } public string this[LocaleKeys key] diff --git a/src/Ryujinx/Program.cs b/src/Ryujinx/Program.cs index 9b8ded44b..195a6c066 100644 --- a/src/Ryujinx/Program.cs +++ b/src/Ryujinx/Program.cs @@ -32,8 +32,10 @@ namespace Ryujinx.Ava public static double DesktopScaleFactor { get; set; } = 1.0; public static string Version { get; private set; } public static string ConfigurationPath { get; private set; } + public static string GlobalConfigurationPath { get; private set; } public static bool PreviewerDetached { get; private set; } public static bool UseHardwareAcceleration { get; private set; } + public static string BackendThreadingArg { get; private set; } [LibraryImport("user32.dll", SetLastError = true)] public static partial int MessageBoxA(nint hWnd, [MarshalAs(UnmanagedType.LPStr)] string text, [MarshalAs(UnmanagedType.LPStr)] string caption, uint type); @@ -156,11 +158,48 @@ namespace Ryujinx.Ava } } + public static bool FindGameConfig(string gameDir) + { + if (File.Exists(gameDir)) + { + return true; + } + + return false; + } + + public static string GetDirGameUserConfig(string gameId, bool rememberGlobalDir = false, bool changeFolderForGame = false) + { + if (string.IsNullOrEmpty(gameId)) + { + return ""; + } + + string gameDir = Path.Combine(AppDataManager.GamesDirPath, gameId, ReleaseInformation.ConfigName); + + // Should load with the game if there is a custom setting for the game + if (rememberGlobalDir) + { + GlobalConfigurationPath = ConfigurationPath; + } + + if (changeFolderForGame) + { + ConfigurationPath = gameDir; + } + + return gameDir; + } + public static void ReloadConfig() { + //It is necessary that when a user setting appears, the global setting remains available + GlobalConfigurationPath = null; + string localConfigurationPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ReleaseInformation.ConfigName); string appDataConfigurationPath = Path.Combine(AppDataManager.BaseDirPath, ReleaseInformation.ConfigName); + // Now load the configuration as the other subsystems are now registered if (File.Exists(localConfigurationPath)) { @@ -217,6 +256,11 @@ namespace Ryujinx.Ava _ => ConfigurationState.Instance.Graphics.BackendThreading }; + if (CommandLineState.OverrideBackendThreadingAfterReboot is not null) + { + BackendThreadingArg = CommandLineState.OverrideBackendThreadingAfterReboot; + } + // Check if docked mode was overriden. if (CommandLineState.OverrideDockedMode.HasValue) ConfigurationState.Instance.System.EnableDockedMode.Value = CommandLineState.OverrideDockedMode.Value; @@ -232,6 +276,33 @@ namespace Ryujinx.Ava _ => ConfigurationState.Instance.HideCursor, }; + // Check if memoryManagerMode was overridden. + if (CommandLineState.OverrideMemoryManagerMode is not null) + if (Enum.TryParse(CommandLineState.OverrideMemoryManagerMode, true, out MemoryManagerMode result)) + { + ConfigurationState.Instance.System.MemoryManagerMode.Value = result; + } + + // Check if PPTC was overridden. + if (CommandLineState.OverridePPTC is not null) + if (Enum.TryParse(CommandLineState.OverridePPTC, true, out bool result)) + { + ConfigurationState.Instance.System.EnablePtc.Value = result; + } + + // Check if region was overridden. + if (CommandLineState.OverrideSystemRegion is not null) + if (Enum.TryParse(CommandLineState.OverrideSystemRegion, true, out Ryujinx.HLE.HOS.SystemState.RegionCode result)) + { + ConfigurationState.Instance.System.Region.Value = (Utilities.Configuration.System.Region)result; + } + + //Check if language was overridden. + if (CommandLineState.OverrideSystemLanguage is not null) + if (Enum.TryParse(CommandLineState.OverrideSystemLanguage, true, out Ryujinx.HLE.HOS.SystemState.SystemLanguage result)) + { + ConfigurationState.Instance.System.Language.Value = (Utilities.Configuration.System.Language)result; + } // Check if hardware-acceleration was overridden. if (CommandLineState.OverrideHardwareAcceleration != null) diff --git a/src/Ryujinx/Rebooter.cs b/src/Ryujinx/Rebooter.cs new file mode 100644 index 000000000..8c7755a4c --- /dev/null +++ b/src/Ryujinx/Rebooter.cs @@ -0,0 +1,76 @@ +using FluentAvalonia.UI.Controls; +using Ryujinx.Ava.Common.Locale; +using Ryujinx.Ava.UI.ViewModels; +using Ryujinx.Ava.Utilities; +using SkiaSharp; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Threading.Tasks; + +namespace Ryujinx.Ava +{ + internal static class Rebooter + { + + private static readonly string _updateDir = Path.Combine(Path.GetTempPath(), "Ryujinx", "update"); + + + public static void RebootAppWithGame(string gamePath, List args) + { + _ = Reboot(gamePath, args); + + } + + private static async Task Reboot(string gamePath, List args) + { + + bool shouldRestart = true; + + TaskDialog taskDialog = new() + { + Header = LocaleManager.Instance[LocaleKeys.RyujinxRebooter], + SubHeader = LocaleManager.Instance[LocaleKeys.DialogRebooterMessage], + IconSource = new SymbolIconSource { Symbol = Symbol.Games }, + XamlRoot = RyujinxApp.MainWindow, + }; + + if (shouldRestart) + { + List arguments = CommandLineState.Arguments.ToList(); + string executableDirectory = AppDomain.CurrentDomain.BaseDirectory; + + var dialogTask = taskDialog.ShowAsync(true); + await Task.Delay(500); + + // Find the process name. + string ryuName = Path.GetFileName(Environment.ProcessPath) ?? string.Empty; + + // Fallback if the executable could not be found. + if (ryuName.Length == 0 || !Path.Exists(Path.Combine(executableDirectory, ryuName))) + { + ryuName = OperatingSystem.IsWindows() ? "Ryujinx.exe" : "Ryujinx"; + } + + ProcessStartInfo processStart = new(ryuName) + { + UseShellExecute = true, + WorkingDirectory = executableDirectory, + }; + + foreach (var arg in args) + { + processStart.ArgumentList.Add(arg); + } + + processStart.ArgumentList.Add(gamePath); + + Process.Start(processStart); + + Environment.Exit(0); + } + } + } +} diff --git a/src/Ryujinx/UI/Controls/ApplicationContextMenu.axaml b/src/Ryujinx/UI/Controls/ApplicationContextMenu.axaml index 3e47a1910..64b30e211 100644 --- a/src/Ryujinx/UI/Controls/ApplicationContextMenu.axaml +++ b/src/Ryujinx/UI/Controls/ApplicationContextMenu.axaml @@ -19,6 +19,18 @@ Header="{ext:Locale GameListContextMenuCreateShortcut}" Icon="{ext:Icon fa-solid fa-bookmark}" ToolTip.Tip="{OnPlatform Default={ext:Locale GameListContextMenuCreateShortcutToolTip}, macOS={ext:Locale GameListContextMenuCreateShortcutToolTipMacOS}}" /> + + - + + + + @@ -86,10 +93,28 @@ Margin="5,5,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" - FontSize="16" + FontSize="18" Foreground="{DynamicResource FavoriteApplicationIconColor}" IsVisible="{Binding Favorite}" Symbol="StarFilled" /> + + + + + diff --git a/src/Ryujinx/UI/Controls/ApplicationListView.axaml b/src/Ryujinx/UI/Controls/ApplicationListView.axaml index c6b7268b9..5ed7acc20 100644 --- a/src/Ryujinx/UI/Controls/ApplicationListView.axaml +++ b/src/Ryujinx/UI/Controls/ApplicationListView.axaml @@ -6,6 +6,7 @@ xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers" xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:ext="clr-namespace:Ryujinx.Ava.Common.Markup" d:DesignHeight="450" d:DesignWidth="800" Focusable="True" @@ -156,6 +157,13 @@ Text="{Binding Converter={x:Static helpers:MultiplayerInfoConverter.Instance}}" TextAlignment="Start" TextWrapping="Wrap"/> + null, }; } + set + { + ListSelectedApplication = value; + GridSelectedApplication = value; + } } public bool HasCompatibilityEntry => SelectedApplication.HasPlayabilityInfo; @@ -1085,7 +1090,7 @@ namespace Ryujinx.Ava.UI.ViewModels _rendererWaitEvent.WaitOne(); AppHost?.Start(); - + AppHost?.DisposeContext(); } @@ -1551,8 +1556,50 @@ namespace Ryujinx.Ava.UI.ViewModels } } + public bool InitializeUserConfig(ApplicationData application) + { + // Code where conditions will be met before loading the user configuration (Global Config) + BackendThreading backendThreadingValue = ConfigurationState.Instance.Graphics.BackendThreading.Value; + string BackendThreadingInit = Program.BackendThreadingArg; + + if (BackendThreadingInit is null) + { + BackendThreadingInit = ConfigurationState.Instance.Graphics.BackendThreading.Value.ToString(); + } + + // If a configuration is found in the "/games/xxxxxxxxxxxxxx" folder, the program will load the user setting. + string idGame = application.IdBaseString; + if (ConfigurationFileFormat.TryLoad(Program.GetDirGameUserConfig(idGame), out ConfigurationFileFormat configurationFileFormat)) + { + // Loads the user configuration, having previously changed the global configuration to the user configuration + ConfigurationState.Instance.Load(configurationFileFormat, Program.GetDirGameUserConfig(idGame, true, true), idGame); + } + + // Code where conditions will be executed after loading user configuration + if (ConfigurationState.Instance.Graphics.BackendThreading.Value.ToString() != BackendThreadingInit) + { + + List Arguments = new List + { + "--bt", ConfigurationState.Instance.Graphics.BackendThreading.Value.ToString() // BackendThreading + }; + + Rebooter.RebootAppWithGame(application.Path, Arguments); + + return true; + } + + return false; + } + public async Task LoadApplication(ApplicationData application, bool startFullscreen = false, BlitStruct? customNacpData = null) { + + if (InitializeUserConfig(application)) + { + return; + } + if (AppHost != null) { await ContentDialogHelper.CreateInfoDialog( @@ -1568,7 +1615,7 @@ namespace Ryujinx.Ava.UI.ViewModels #if RELEASE await PerformanceCheck(); #endif - + Logger.RestartTime(); SelectedIcon ??= ApplicationLibrary.GetApplicationIcon(application.Path, ConfigurationState.Instance.System.Language, application.Id); @@ -1613,6 +1660,7 @@ namespace Ryujinx.Ava.UI.ViewModels Thread gameThread = new(InitializeGame) { Name = "GUI.WindowThread" }; gameThread.Start(); + } public void SwitchToRenderer(bool startFullscreen) => diff --git a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs index 689d872a1..d0a6c6d8a 100644 --- a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs @@ -1,5 +1,6 @@ using Avalonia.Collections; using Avalonia.Controls; +using Avalonia.Media.Imaging; using Avalonia.Threading; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; @@ -27,6 +28,7 @@ using Ryujinx.HLE.HOS.Services.Time.TimeZone; using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.IO; using System.Linq; using System.Net.NetworkInformation; using System.Threading.Tasks; @@ -68,6 +70,19 @@ namespace Ryujinx.Ava.UI.ViewModels public SettingsHacksViewModel DirtyHacks { get; } + private readonly bool _isGameRunning; + private Bitmap _gameIcon; + private string _gameTitle; + private string _gamePath; + private string _gameId; + public bool IsGameRunning => _isGameRunning; + public Bitmap GameIcon => _gameIcon; + public string GamePath => _gamePath; + public string GameTitle => _gameTitle; + public string GameId => _gameId; + public bool IsGameTitleNotNull => !string.IsNullOrEmpty(GameTitle); + public double PanelOpacity => IsGameTitleNotNull ? 0.5 : 1; + public int ResolutionScale { get => _resolutionScale; @@ -335,7 +350,7 @@ namespace Ryujinx.Ava.UI.ViewModels public bool IsInvalidLdnPassphraseVisible { get; set; } - public SettingsViewModel(VirtualFileSystem virtualFileSystem, ContentManager contentManager) : this() + public SettingsViewModel(VirtualFileSystem virtualFileSystem, ContentManager contentManager) : this(false) { _virtualFileSystem = virtualFileSystem; _contentManager = contentManager; @@ -348,7 +363,51 @@ namespace Ryujinx.Ava.UI.ViewModels } } - public SettingsViewModel() + public SettingsViewModel( + VirtualFileSystem virtualFileSystem, + ContentManager contentManager, + bool gameRunning, + string gamePath, + string gameName, + string gameId, + byte[] gameIconData, + bool enableToLoadCustomConfig) : this(enableToLoadCustomConfig) + { + _virtualFileSystem = virtualFileSystem; + _contentManager = contentManager; + + if (gameIconData != null && gameIconData.Length > 0) + { + using (var ms = new MemoryStream(gameIconData)) + { + _gameIcon = new Bitmap(ms); + } + } + + _isGameRunning = gameRunning; + _gamePath = gamePath; + _gameTitle = gameName; + _gameId = gameId; + + if (enableToLoadCustomConfig) // During the game. If there is no user config, then load the global config window + { + string gameDir = Program.GetDirGameUserConfig(gameId, false, true); + if (ConfigurationFileFormat.TryLoad(gameDir, out ConfigurationFileFormat configurationFileFormat)) + { + ConfigurationState.Instance.Load(configurationFileFormat, gameDir, gameId); + } + + LoadCurrentConfiguration(); // Needed to load custom configuration + } + + if (Program.PreviewerDetached) + { + Task.Run(LoadTimeZones); + + } + } + + public SettingsViewModel(bool noLoadGlobalConfig = false) { GameDirectories = []; AutoloadDirectories = []; @@ -363,7 +422,9 @@ namespace Ryujinx.Ava.UI.ViewModels if (Program.PreviewerDetached) { Task.Run(LoadAvailableGpus); - LoadCurrentConfiguration(); + + // if (!noLoadGlobalConfig)// Default is false, but loading custom config avoids double call + LoadCurrentConfiguration(); DirtyHacks = new SettingsHacksViewModel(this); } @@ -592,8 +653,8 @@ namespace Ryujinx.Ava.UI.ViewModels config.HideCursor.Value = (HideCursorMode)HideCursor; config.UpdateCheckerType.Value = (UpdaterType)UpdateCheckerType; config.FocusLostActionType.Value = (FocusLostType)FocusLostActionType; - config.UI.GameDirs.Value = [..GameDirectories]; - config.UI.AutoloadDirs.Value = [..AutoloadDirectories]; + config.UI.GameDirs.Value = [.. GameDirectories]; + config.UI.AutoloadDirs.Value = [.. AutoloadDirectories]; config.UI.BaseStyle.Value = BaseStyleIndex switch { @@ -614,10 +675,10 @@ namespace Ryujinx.Ava.UI.ViewModels // System config.System.Region.Value = (Region)Region; - + if (config.System.Language.Value != (Language)Language) GameListNeedsRefresh = true; - + config.System.Language.Value = (Language)Language; if (_validTzRegions.Contains(TimeZone)) { @@ -696,7 +757,7 @@ namespace Ryujinx.Ava.UI.ViewModels config.Multiplayer.DisableP2p.Value = DisableP2P; config.Multiplayer.LdnPassphrase.Value = LdnPassphrase; config.Multiplayer.LdnServer.Value = LdnServer; - + // Dirty Hacks config.Hacks.Xc2MenuSoftlockFix.Value = DirtyHacks.Xc2MenuSoftlockFix; @@ -712,7 +773,11 @@ namespace Ryujinx.Ava.UI.ViewModels private static void RevertIfNotSaved() { - Program.ReloadConfig(); + // maybe this is an unnecessary check(all options need to be tested) + if (string.IsNullOrEmpty(Program.GlobalConfigurationPath)) + { + Program.ReloadConfig(); + } } public void ApplyButton() @@ -720,6 +785,26 @@ namespace Ryujinx.Ava.UI.ViewModels SaveSettings(); } + public void DeleteConfigGame() + { + string gameDir = Program.GetDirGameUserConfig(GameId,false,false); + + if (File.Exists(gameDir)) + { + File.Delete(gameDir); + } + + RevertIfNotSaved(); + CloseWindow?.Invoke(); + } + + public void SaveUserConfig() + { + SaveSettings(); + RevertIfNotSaved(); // Revert global configuration after saving user configuration + CloseWindow?.Invoke(); + } + public void OkButton() { SaveSettings(); diff --git a/src/Ryujinx/UI/Views/Main/MainMenuBarView.axaml.cs b/src/Ryujinx/UI/Views/Main/MainMenuBarView.axaml.cs index d1931ae2f..f1bb2de55 100644 --- a/src/Ryujinx/UI/Views/Main/MainMenuBarView.axaml.cs +++ b/src/Ryujinx/UI/Views/Main/MainMenuBarView.axaml.cs @@ -130,9 +130,26 @@ namespace Ryujinx.Ava.UI.Views.Main Window.SettingsWindow = new(Window.VirtualFileSystem, Window.ContentManager); Rainbow.Enable(); - - await Window.SettingsWindow.ShowDialog(Window); - + + if (ViewModel.SelectedApplication is null) // Checks if game data exists + { + await Window.SettingsWindow.ShowDialog(Window); + } + else + { + bool userConfigExist = Program.FindGameConfig(Program.GetDirGameUserConfig(ViewModel.SelectedApplication.IdString, false, false)); + + if (!ViewModel.IsGameRunning || !userConfigExist) + { + await Window.SettingsWindow.ShowDialog(Window); // The game is not running, or if the user configuration does not exist + } + else + { + // If there is a custom configuration in the folder + await new GameSpecificSettingsWindow(ViewModel, userConfigExist).ShowDialog((Window)ViewModel.TopLevel); + } + } + Rainbow.Disable(); Rainbow.Reset(); diff --git a/src/Ryujinx/UI/Views/Settings/SettingsSystemView.axaml b/src/Ryujinx/UI/Views/Settings/SettingsSystemView.axaml index 5daa7f69f..dd6858ee6 100644 --- a/src/Ryujinx/UI/Views/Settings/SettingsSystemView.axaml +++ b/src/Ryujinx/UI/Views/Settings/SettingsSystemView.axaml @@ -156,6 +156,8 @@ ValueMemberBinding="{Binding Mode=OneWay, Converter={x:Static helpers:TimeZoneConverter.Instance}}" /> + + - + + - + + + - - + + + + + - - + + + + + - - + + + + + - + @@ -64,7 +86,11 @@ - + @@ -81,8 +107,11 @@ + - + @@ -100,7 +129,11 @@ - + + - + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +